You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

STM32G474RE的I2C向MCP4728发送数据异常问题问询

问题描述

我正在使用STM32G474RE的I2C接口与MCP4728 DAC芯片通信。预期发送的字节序列为{0xFF, 0x58, 0x04, 0x56},调试显示TX寄存器已写入这些字节,但实际传输时示波器捕获到的是{0xFF, 0x58, 0xFF, 0xFF},而且修改第一个字节时,第三、第四个字节也会跟着变化。

代码片段
int main(void){
    SystemClock_Config();   //16MHz HSI16 Clock
    I2C2_Init();
    while (1){
        MCP4728_Transmit(SINGLE_WRITE, CHANNEL_A, 0x456);
    }
}

void MCP4728_Transmit(uint8_t tx_mode, uint8_t channel, uint16_t data){
    /*If data larger than 12 bits (0xFFF) return*/
    if (data > 0xFFF)
        return;
    
    int tx_buffer_index = 0;
    uint8_t tx_buffer[4];
    tx_buffer[0] = 0xFF;
    tx_buffer[1] = tx_mode | (channel << 1); 
    tx_buffer[2] = (uint8_t) (data >> 8) & 0x0F;
    tx_buffer[3] = (uint8_t) data & 0xFF;
    
    I2C2->CR2 |= 0x04 << I2C_CR2_NBYTES_Pos;
    I2C2->CR2 &= ~I2C_CR2_AUTOEND;
    I2C2->CR2 |= ((0x60<<1)<< I2C_CR2_SADD_Pos);
    I2C2->CR2 |= I2C_CR2_START;
    
    while(!(I2C2->ISR & I2C_ISR_TC)){
        if (I2C2->ISR & I2C_ISR_NACKF)
            return;
        if (!(I2C2->ISR & I2C_ISR_TXIS))
            return;
        else{
            I2C2->TXDR = tx_buffer[tx_buffer_index];
            tx_buffer_index++;
        }
    }
    I2C2->CR2 |= I2C_CR2_STOP;
}

void I2C2_Init(void){
    /*Enable I2C and GPIOA clock*/
    RCC->APB1ENR1 |= RCC_APB1ENR1_I2C2EN;
    RCC->AHB2ENR |= RCC_AHB2ENR_GPIOAEN;
    
    /*Config PA8 and PA9*/
    //PA8 and PA9 Alternate Function (AF4)
    GPIOA->MODER &= ~(GPIO_MODER_MODE8 | GPIO_MODER_MODE9);
    GPIOA->MODER |= (0x02 << GPIO_MODER_MODE8_Pos | 0x02 << GPIO_MODER_MODE9_Pos);
    GPIOA->AFR[1] |= (0x04 << GPIO_AFRH_AFSEL8_Pos | 0x04 <<GPIO_AFRH_AFSEL9_Pos);
    //Output type Open Drain
    GPIOA->OTYPER |= (GPIO_OTYPER_OT8 | GPIO_OTYPER_OT9);
    //High Speed
    GPIOA->OSPEEDR &= ~(GPIO_OSPEEDR_OSPEED8 | GPIO_OSPEEDR_OSPEED9);
    GPIOA->OSPEEDR |= (0x02 << GPIO_OSPEEDR_OSPEED8_Pos | 0x02 << GPIO_OSPEEDR_OSPEED9_Pos);
    //Enable Pull Up
    GPIOA->PUPDR &= ~(GPIO_PUPDR_PUPD8 | GPIO_PUPDR_PUPD9);
    GPIOA->PUPDR |= (0x01 << GPIO_PUPDR_PUPD8_Pos | 0x01 << GPIO_PUPDR_PUPD9_Pos);
    
    /*Configure I2C2*/
    //Disable I2C
    I2C2->CR1 &= ~I2C_CR1_PE;
    //Default filter
    I2C2->CR1 &= ~I2C_CR1_ANFOFF;
    I2C2->CR1 &= ~I2C_CR1_DNF;
    //TIMINGR value for 16MHz taken from table in Reference Manual (page xxx)
    I2C2->TIMINGR |= (0x03 << I2C_TIMINGR_PRESC_Pos  | 0x04 << I2C_TIMINGR_SCLDEL_Pos | \
                      0x02 << I2C_TIMINGR_SDADEL_Pos | 0x0F << I2C_TIMINGR_SCLH_Pos   | \
                      0x13 << I2C_TIMINGR_SCLL_Pos);
    //Enable stretching
    I2C2->CR1 &= ~I2C_CR1_NOSTRETCH;
    //Enable I2C
    I2C2->CR1 |= I2C_CR1_PE;
}
问题分析与修复方案

核心问题点

  1. I2C_CR2寄存器未清零旧值:每次传输直接对NBYTES、SADD位做或操作,残留的旧配置会导致字节计数、地址识别错误,进而干扰后续字节传输。
  2. TXIS检查逻辑缺陷:仅单次检查TXIS位,若未及时置位就直接返回,导致后续字节无法写入TXDR,最终传输寄存器默认值0xFF。
  3. NACK处理不完整:收到NACK后未发送停止信号,可能导致总线处于异常挂起状态。

修改后的传输函数

void MCP4728_Transmit(uint8_t tx_mode, uint8_t channel, uint16_t data){
    /*If data larger than 12 bits (0xFFF) return*/
    if (data > 0xFFF)
        return;
    
    int tx_buffer_index = 0;
    uint8_t tx_buffer[4];
    tx_buffer[0] = 0xFF;
    tx_buffer[1] = tx_mode | (channel << 1); 
    tx_buffer[2] = (uint8_t) (data >> 8) & 0x0F;
    tx_buffer[3] = (uint8_t) data & 0xFF;
    
    // 先清除CR2中NBYTES、SADD、读写方向位,避免旧配置干扰
    I2C2->CR2 &= ~(I2C_CR2_NBYTES_Msk | I2C_CR2_SADD_Msk | I2C_CR2_RD_WRN);
    // 配置发送字节数
    I2C2->CR2 |= 0x04 << I2C_CR2_NBYTES_Pos;
    // 禁用自动结束模式
    I2C2->CR2 &= ~I2C_CR2_AUTOEND;
    // 配置MCP4728的7位从机地址(左移1位后写入SADD字段)
    I2C2->CR2 |= (0x60 << 1) << I2C_CR2_SADD_Pos;
    // 发起起始信号
    I2C2->CR2 |= I2C_CR2_START;
    
    while(!(I2C2->ISR & I2C_ISR_TC)){
        // 处理NACK:发送停止信号后退出
        if (I2C2->ISR & I2C_ISR_NACKF){
            I2C2->CR2 |= I2C_CR2_STOP;
            return;
        }
        // 循环等待TXIS置位,确保可以写入下一个字节
        while(!(I2C2->ISR & I2C_ISR_TXIS)){
            // 可选:添加超时检测,防止死循环
        }
        I2C2->TXDR = tx_buffer[tx_buffer_index];
        tx_buffer_index++;
    }
    // 传输完成后发送停止信号
    I2C2->CR2 |= I2C_CR2_STOP;
}

修复说明

  • CR2寄存器先清零再配置:每次传输前清除相关控制位,确保新配置完全生效,避免旧值叠加导致的参数错误。
  • 循环等待TXIS:改为持续等待TXIS位置位,保证每个字节都能在合适时机写入TXDR,解决部分字节未发送的问题。
  • 完善NACK处理:收到NACK后主动发送停止信号,避免总线挂起,提升通信稳定性。

内容的提问来源于stack exchange,提问作者th3virtuos0

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 11:54:52