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

STM32裸机I2C的CCR、TRISE配置错误致MMA8452Q通信异常排查

遵循@wovano的建议,我将精简问题表述,问题历史版本可查看编辑记录。

概述

项目采用STM32F407ZGT6作为主控,外接MMA8452Q加速度计实现I2C通信,全程使用裸机开发方式,加速度计功能调用gschorcht库实现,目前手头无示波器、FT232H调试工具。

问题描述

当前无法确认I2C是否正常工作,存在两处核心异常:

  • 读取设备ID返回值为0x22,而非芯片手册规定的0x2A*;
  • 无法循环读取数据,使用示例代码中if (mma845x_new_data (sensor) && mma845x_get_float_data (sensor, &data))判断语句时,条件永远无法成立;按如下方式修改代码后,调试时会进入Default_Handler: Infinite_Loop死循环:
void read_data(void)
{
    mma845x_float_data_t  data;
    if (mma845x_new_data (sensor))
    {
        if(mma845x_get_float_data (sensor, &data))
        {
        // max. full scale is +-16 g and best resolution is 1 mg, i.e. 5 digits
        printf("[MMA845X (xyz)[g]] ax=%+7.3f ay=%+7.3f az=%+7.3f\n", data.ax, data.ay, data.az);
        return;
        }
    }
    printf("[not new MMA845X (xyz)[g]] ax=%+7.3f ay=%+7.3f az=%+7.3f\n", data.ax, data.ay, data.az);
}

调试选项卡显示触发了跟踪/断点陷阱线程:
调试跟踪/断点陷阱截图

我怀疑CCR或TRISE寄存器配置有误(下文附参数计算过程)。当前系统时钟配置为SYSCLK=168MHz,APB1总线时钟为42MHz(APB1PRESC分频系数=4),目前测试I2C标准模式(100kbps)。

脚注:该偏差有可能是因为使用了不同品牌的兼容传感器,但0x2A二进制为0b0010 1010、0x22为0b0010 0010,存在位丢失的嫌疑;更换另一块加速度计测试时读取ID为0x05,怀疑存在位传输丢失问题但暂无法确认。

已确认无误的配置项
  • 硬件板卡上I2C线路已正确配置上拉电阻
  • GPIO的MODER(模式寄存器)、AFR(复用功能寄存器)配置正确
  • SA0引脚电平配置与库中定义的设备地址匹配
  • 读取DR(数据寄存器)后已正确发送STOP信号
相关代码
void init_gpio(void)
{
    RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN;    //Port B

    GPIOB->MODER  |= (2U << 12U);   //MMA845_SCL || PB6
    GPIOB->AFR[0] |= (4U << 24U);   //AF4
    GPIOB->OTYPER |= (1U << 6U);    //Output Type to Open Drain
    GPIOB->PUPDR  |= (1U << 12U);   //Set to Pull-Up

    GPIOB->MODER  |= (2U << 14U);   //MMA845_SDA || PB7
    GPIOB->AFR[0] |= (4U << 28U);   //AF4
    GPIOB->OTYPER |= (1U << 7U);    //Output Type to Open Drain
    GPIOB->PUPDR  |= (1U << 14U);   //Set to Pull-Up
}

/*i2c_init*/
void i2c_init(I2C_TypeDef *I2Cx)
{
    /*Enable clock access to I2Cs*/
    RCC->APB1ENR |= RCC_APB1ENR_I2C1EN; //enable I2C1
//  RCC->APB1ENR |= RCC_APB1ENR_I2C2EN; //enable I2C2
//  RCC->APB1ENR |= RCC_APB1ENR_I2C3EN; //enable I2C3
    RCC->APB1ENR;

    /*Reset I2C*/
    I2Cx->CR1 |= I2C_CR1_SWRST;    /*!BUSY ON */
    I2Cx->CR1 &= ~I2C_CR1_SWRST;   /*!BUSY OFF*/

    int CCR_VAL;
    int TRISE_VAL;
    int MAX_FRQ;

    /*Set I2C frequency same as APB*/
    MAX_FRQ = (SYSCLK/APB1PRESC)/1000000;   //= 42 @ max clock speed
    I2Cx->CR2 &= ~msk(6,0);        /*!BUSY ON*/
    I2Cx->CR2 |= MAX_FRQ;

    if(!MASTER_MODE){
        /* Standard Mode = 100kb/s*/
        /* CCR calculated by half-period of 100k
         * divided by the period of the PCLK
         * CCR = ((1/100'000)/2)/(1/42'000'000)
         * CCR = 5000ns/~24ns = 210
         */
        CCR_VAL = 210;
        /* TRISE calculated by PCLK (in MHz) + 1
         * TRISE = ((42'000'000/1'000'000) + 1)
         * TRISE = 42 + 1 = 43
         */
        TRISE_VAL = 43;
    }
    else
    {
        /*Fast Mode = 400kb/s*/
        if(DUTY_MODE)
        {
            /*16:9 ratio*/
            /* CCR calculated by half-period of 100k
             * divided by the sum of the duty ratios
             * divided by the period of the PCLK
             * t_high = 9 * CCR * TPCLK
             * t_low  = 16 * CCR * TPCLK
             * t_high + t_low = 25 * CCR * TPCLK
             * CCR = ((1/400'000)/2) / (25/42'000'000)
             * CCR = 1250ns/~595ns = 2.1 ~ 2
             */
            CCR_VAL = 2;
        }
        else
        {
            //2:1 ratio
            /* CCR calculated by half-period of 100k
             * divided by the sum of the duty ratios
             * divided by the period of the PCLK
             * t_high = CCR * TPCLK
             * t_low  = 2 * CCR * TPCLK
             * t_high + t_low = 3 * CCR * TPCLK
             * CCR = ((1/400'000)/2) / (3/42'000'000)
             * CCR = 1250ns/~71ns = 17.5 ~ 17
             */
            CCR_VAL = 17;
        }
        /* TRISE calculated by PCLK (in MHz) * 0.3ms + 1
         * TRISE = ((42'000'000/1'000'000)*(300/1000) + 1)
         * TRISE = 42 * 0.3 + 1 = 13.6 ~ 14
         */
        TRISE_VAL = 14;
    }

    /*Set Clock Control value*/
    I2Cx->CCR |= CCR_VAL;   //assuming APB1 = 42MHz, SM = True
    I2Cx->CCR |= (set(DUTY_MODE,I2C_CCR_DUTY_Pos) | set(MASTER_MODE,I2C_CCR_FS_Pos));

    I2Cx->TRISE |= TRISE_VAL;   //assuming APB1 = 42MHz, SM = True
    /*Enable I2C*/
    I2Cx->CR1 |= I2C_CR1_PE;
}

void set_addr(I2C_TypeDef *I2Cx, uint8_t sAddr, const uint8_t *mAddr, int read)
{
    volatile int temp;
    /*Check I2C is not busy*/
    while(I2Cx->SR2 & I2C_SR2_BUSY);

    /*(1) Generate a START, wait for start flag*/
    I2Cx->CR1 |= I2C_CR1_START;
    while (!(I2Cx->SR1 & I2C_SR1_SB));

    /*Transmit slave address + 'write'*/
    I2Cx->DR = sAddr << 1;

    /*Wait for the address flag*/
    while (!(I2Cx->SR1 & I2C_SR1_ADDR));
    temp = I2Cx->SR2; //Clear address flag

    /*Send memory address*/
    //Make sure TXE is emtpy
    while (!(I2Cx->SR1 & I2C_SR1_TXE));
    I2Cx->DR = mAddr;
    //Wait until transmitter is emtpy
    while (!(I2Cx->SR1 & I2C_SR1_TXE));

    if(read)
    {
        /*(2) Generate a START, wait for start flag*/
        I2Cx->CR1 |= I2C_CR1_START;
        while (!(I2Cx->SR1 & I2C_SR1_SB));

        /*Transmit slave address + 'read'*/
        I2Cx->DR = (sAddr << 1) | 1;

        /*Wait for the address flag*/
        while (!(I2Cx->SR1 & I2C_SR1_ADDR));
        temp = I2Cx->SR2; //Clear address flag
    }
}

int i2c_burst_read(I2C_TypeDef *I2Cx, uint8_t sAddr, const uint8_t *mAddr, uint8_t *string, uint32_t size)
{
    if(size == 0)
    {
        return 0;
    }
    uint8_t *pData = string;    //points at the first byte (char) of string
    set_addr(I2Cx, sAddr, mAddr, 1);

    /*Enable ACK*/
    I2Cx->CR1 |= I2C_CR1_ACK;

    while(size > 0U)
    {
        if(size-- == 1)
        {
            /*Disable ACK*/
            I2Cx->CR1 &= ~I2C_CR1_ACK;

            /*Wait until the receive flag is set*/
            while(!(I2Cx->SR1 & I2C_SR1_RXNE));

            //"Generate STOP" was here before

            /*Read data from the DR*/
            *pData++ = I2Cx->DR;
            break;
        }
        else
        {
            /*Wait until the receive flag is set*/
            while(!(I2Cx->SR1 & I2C_SR1_RXNE));

            /*Read data from the DR*/
            *pData++ = I2Cx->DR;
        }
    }
    /*Generate a STOP after receiving*/
    I2Cx->CR1 |= I2C_CR1_STOP;
    return 1;
}

int i2c_burst_write(I2C_TypeDef *I2Cx, uint8_t sAddr, const uint8_t *mAddr, uint8_t *string, uint32_t size)
{
    uint8_t *pData = string;    //points at the first byte (char) of string
    set_addr(I2Cx, sAddr, mAddr, 0);

    while(size > 0U)
    {
        /*Wait for transmitter to be empty*/
        while(!(I2Cx->SR1 & I2C_SR1_TXE));

        I2Cx->DR = *pData++;
        size--;
    }

    /*Wait for byte transfer finished flag*/
    while(!(I2Cx->SR1 & I2C_SR1_BTF));

    /*Generate a STOP after writing*/
    I2Cx->CR1 |= I2C_CR1_STOP;
    return 1;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 20:55:00