第四篇 STM32F030硬件I2C从设备测试

2019-07-21 07:58发布

一、准备工程文件
1.png
      硬件I2C初始化和寄存器列表相关信息文件
二、建立完成文件之后,开始编写I2C硬件初始化信息,为了速度,I2C初始化和中断操作均使用寄存器操作方式
[mw_shl_code=applescript,true]/**
  *****************************************************************************
  * @name   : 硬件IIC初始化
  *
  * @Brief  : none
  *
  * @Input  : I2Cx:           IIC组
  *           SlaveAdd:       作为从设备时识别地址
  *           F103IIC1_Remap: 针对F103的IIC1是否重映射
  *                           0: 不重映射
  *                           1: 重映射。PB.06 -> PB.08;PB.07 -> PB.09
  *
  * @Output : none
  *
  * @return : none
  *****************************************************************************
**/
void Hard_I2C_Init(I2C_TypeDef* I2Cx, uint8_t SlaveAdd, uint32_t Freq_SCL, uint8_t F103IIC1_Remap/* 针对F103的IIC1重映射的 */)
{
        NVIC_InitTypeDef NVIC_InitStructure;
        uint32_t i2c_temp = 0;
       
        //
        //根据不同的I2C设备进行初始化
        //
        if (I2Cx == I2C1)
        {
                RCC->AHBENR  |= RCC_AHBENR_GPIOBEN;
                RCC->APB1ENR |= RCC_APB1ENR_I2C1EN;
                //
                //管脚复用
                //
                GPIOB->AFR[0] &= ~(GPIO_AFRL_AFR6 | GPIO_AFRL_AFR7);
                GPIOB->AFR[0] |= (0x01<<(4 * 6)) | (0x01<<(4 * 7));
                //
                //初始化管脚
                //
                GPIOB->MODER   &= ~(GPIO_MODER_MODER6 | GPIO_MODER_MODER7);
                GPIOB->OTYPER  &= ~(GPIO_OTYPER_OT_6 | GPIO_OTYPER_OT_7);
                GPIOB->OSPEEDR &= ~(GPIO_OSPEEDER_OSPEEDR6 | GPIO_OSPEEDER_OSPEEDR7);
                GPIOB->UPDR   &= ~(GPIO_PUPDR_PUPDR6 | GPIO_PUPDR_PUPDR7);
               
                GPIOB->MODER   |= (GPIO_MODER_MODER6_1 | GPIO_MODER_MODER7_1);  //复用功能
                GPIOB->OTYPER  |= (GPIO_OTYPER_OT_6 | GPIO_OTYPER_OT_7);
                GPIOB->OSPEEDR |= (GPIO_OSPEEDER_OSPEEDR6 | GPIO_OSPEEDER_OSPEEDR7);
//                GPIOB->UPDR   |= (GPIO_PUPDR_PUPDR6_0 | GPIO_PUPDR_PUPDR7_0);
                //
                //复位I2C设备
                //
                RCC->APB1RSTR |= RCC_APB1RSTR_I2C1RST;
                RCC->APB1RSTR &= ~RCC_APB1RSTR_I2C1RST;
        }
        else if (I2Cx == I2C2)
        {
                RCC->AHBENR  |= RCC_AHBENR_GPIOBEN;
                RCC->APB1ENR |= RCC_APB1ENR_I2C2EN;
                //
                //管脚复用
                //
                GPIOB->AFR[1] &= ~(GPIO_AFRH_AFR10 | GPIO_AFRH_AFR11);
                GPIOB->AFR[1] |= (0x01<<(4 * 2)) | (0x01<<(4 * 3));
                //
                //初始化管脚
                //
                GPIOB->MODER   &= ~(GPIO_MODER_MODER10 | GPIO_MODER_MODER11);
                GPIOB->OTYPER  &= ~(GPIO_OTYPER_OT_10 | GPIO_OTYPER_OT_11);
                GPIOB->OSPEEDR &= ~(GPIO_OSPEEDER_OSPEEDR10 | GPIO_OSPEEDER_OSPEEDR11);
                GPIOB->UPDR   &= ~(GPIO_PUPDR_PUPDR10 | GPIO_PUPDR_PUPDR11);
               
                GPIOB->MODER   |= (GPIO_MODER_MODER10_1 | GPIO_MODER_MODER11_1);  //复用功能
                GPIOB->OTYPER  |= (GPIO_OTYPER_OT_10 | GPIO_OTYPER_OT_11);
                GPIOB->OSPEEDR |= (GPIO_OSPEEDER_OSPEEDR10 | GPIO_OSPEEDER_OSPEEDR11);
//                GPIOB->UPDR   |= (GPIO_PUPDR_PUPDR10_0 | GPIO_PUPDR_PUPDR11_0);
                //
                //复位I2C设备
                //
                RCC->APB1RSTR |= RCC_APB1RSTR_I2C2RST;
                RCC->APB1RSTR &= ~RCC_APB1RSTR_I2C2RST;
        }
       
        //
        //初始化I2C寄存器
        //
        I2Cx->CR1 &= ~I2C_CR1_PE;  /*!< Disable I2C */
        I2Cx->CR1 &= 0x00CFE0FF;
       
        /*!< 时钟延展功能 */
        #if I2C_NOSTRETCH_EN
        I2Cx->CR1 &= ~I2C_CR1_NOSTRETCH;  /*!< Enable Nostretch */
        #else
        I2Cx->CR1 |= I2C_CR1_NOSTRETCH;  /*!< Disable Nostretch */
        #endif
       
        I2Cx->TIMINGR = 0x50E30000;  /*!< Timing register
                                          standard mode(100KHz) 0x50E30000
                                          fast mode(400KHz) 0x00100000 */
        I2Cx->CR1 |= I2C_CR1_PE;  /*!< Enable I2C */
       
        i2c_temp = 0;
        I2Cx->OAR1 = i2c_temp;
        i2c_temp = (uint32_t)(SlaveAdd & 0x00FF);
        I2Cx->OAR1 |= i2c_temp;  /*!< Slave Address */
        I2Cx->OAR1 |= I2C_OAR1_OA1EN;  /*!< Own Address1 enable */
       
        I2Cx->CR2 &= 0x07FF7FFF;
        I2Cx->CR2 &= ~I2C_CR2_NACK;  /*!< ACK enable */
       
        //
        //事件中断优先级配置
        //
        if (I2Cx == I2C2)
        {
                NVIC_InitStructure.NVIC_IRQChannel     = I2C2_IRQn;  /*!< Event IRQn */
        }
        else
        {
                NVIC_InitStructure.NVIC_IRQChannel     = I2C1_IRQn;  /*!< Event IRQn */
        }
       
        NVIC_InitStructure.NVIC_IRQChannelPriority = 0;
        NVIC_InitStructure.NVIC_IRQChannelCmd      = ENABLE;
        NVIC_Init(&NVIC_InitStructure);
       
        //
        //开启中断
        //
        i2c_temp = 0;
        i2c_temp = (I2C_CR1_ERRIE/*!< Error interrupts enable */  |
                    I2C_CR1_STOPIE/*!< STOP detection Interrupt enable */ |
                    I2C_CR1_NACKIE/*!< Not acknowledge received Interrupt enable */ |
                    I2C_CR1_ADDRIE/*!< Address match Interrupt enable (slave only) */ |
                    I2C_CR1_RXIE/*!< RX Interrupt enable */ |
                    I2C_CR1_TXIE/*!< TX Interrupt enable */);
        I2Cx->CR1 |= i2c_temp;
}[/mw_shl_code]
      简单,对照数据手册寄存器来操作就行了,不难
三、初始化就完事了,就要一个函数,接下来开始I2C寄存器列表了,定义一个数组
      3.1. uint8_t I2C_REG_TEST[I2C_REG_TEST_SIZE];为了方便移植,使用了宏定义方式
      3.2. 接下来两个函数,是在USMART组件中调用调试的
            [mw_shl_code=applescript,true]/**
  *****************************************************************************
  * @Name   : 获取IIC寄存器数值
  *
  * @Brief  : none
  *
  * @Input  : iicreg: 寄存器地址
  *
  * @Output : none
  *
  * @Return : 对应寄存器的数值
  *****************************************************************************
**/
uint8_t I2C_DeviceGetRegister(uint8_t iicreg)
{
        uint8_t reg = 0, off = 0;
       
        reg = iicreg & 0xF0;
        off = iicreg & 0x0F;
       
        if (reg == I2C_REG_TEST_ADD)
        {
                return I2C_REG_TEST[off];
        }
       
        return 0x00;
}

/**
  *****************************************************************************
  * @Name   : 设置IIC寄存器数值
  *
  * @Brief  : none
  *
  * @Input  : iicreg: 寄存器地址
  *
  * @Output : none
  *
  * @Return : 对应寄存器的数值
  *****************************************************************************
**/
void I2C_DeviceSetRegister(uint8_t iicreg, uint8_t Data)
{
        uint8_t reg = 0, off = 0;
       
        reg = iicreg & 0xF0;
        off = iicreg & 0x0F;
       
        if (reg == I2C_REG_TEST_ADD)
        {
                I2C_REG_TEST[off] = Data;
        }
}[/mw_shl_code]      3.3. 重头戏来了,就是I2C的中断服务函数了,这里的操作流程仿照EEPROM的操作流程实现的,仅供参考
2.png
四、以上准备完毕,开始编写main函数内容了,代码不多
3.png
五、编译,下载
六、用另外一块板子编写一个I2C主机读写程序,作为通讯调试用,采用IO模拟方式,硬件也行,省略1万字
七、准备就绪,I2C测试主机这边写入一个数据
4.png
八、然后从机这边获取对应的寄存器数据
5.png
九、附上逻辑分析仪波形
波形.png
十、汇总
      10.1. 刚开始是在STM32F302芯片上面实现的
      10.2. ST官方解析在STM32F3上已经解决了STM32F1系列的I2C的bug,从设备在STM32F1系列上也测试过,勉强能用,还是不稳定,STM32F4也试过,和F1一样效果
      10.3. 在看STM32F0的数据手册时,发现I2C寄存器和时序图什么的和F3系列的很像,对照过后,发现几乎一模一样,心想,是不是也可以使用呢,就有了这个帖子了
      10.4. I2C从设备SCL频率不能过高,经过测试,当频率超过230K的时候,出错率直线上升,甚至100%失败
      10.5. I2C作为主机的还没测试,盘友们手上有F0或者F3系列芯片的可以编写例程测试
      10.6. 本帖I2C从设备经过多次的读写操作,相对比较稳定,建议打开I2C的时钟延展功能,关闭好像会出bug,在主机读取操作时,第一个字节会丢弃,F0上试过关闭未出现丢数据现象,建议大伙们还是打开时钟延展功能使用
十一、详细件附件
友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
该问题目前已经被作者或者管理员关闭, 无法添加新回复
10条回答
黑夜之狼
1楼-- · 2019-07-21 10:57
弄一个地址贴,以后更新一篇就往里面补地址
八度空间
2楼-- · 2019-07-21 14:33
 精彩回答 2  元偷偷看……
l6931639
3楼-- · 2019-07-21 14:48
HALдзжжDMADMAжCR1PE λ
l6931639
4楼-- · 2019-07-21 15:27
我用HAL库函数写主从通信,没有发现漏洞,但是必须用中断中断,改成DMA多字节收发时任意一个重启或故障容易出现链接不上,必须重启。DMA中断时出现死机后不能用CR1的PE 位不能释放总线,
l6931639
5楼-- · 2019-07-21 20:28
我用HAL库函数写主从通信,没有发现漏洞,但是必须用中断中断,改成DMA多字节收发时任意一个重启或故障容易出现链接不上,必须重启。DMA中断时出现死机后不能用CR1的PE 位不能释放总线,
八度空间
6楼-- · 2019-07-21 23:42
l6931639 发表于 2017-12-3 18:43
我用HAL库函数写主从通信,没有发现漏洞,但是必须用中断中断,改成DMA多字节收发时任意一个重启或故障容易 ...

没试过DMA,我用着中断没事啊,你是不是什么地方没有配置好导致的,这个仿真找问题比较容易,DMA开了后就等接收或者发送TC标志就可以了,硬件仿真看下什么现象

一周热门 更多>