OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 20155|回复: 24

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

[复制链接]

120

主题

7878

帖子

13

精华

资深版主

Rank: 8Rank: 8

积分
12012
金钱
12012
注册时间
2013-9-10
在线时间
427 小时
发表于 2017-9-30 22:13:14 | 显示全部楼层 |阅读模式
一、准备工程文件
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上试过关闭未出现丢数据现象,建议大伙们还是打开时钟延展功能使用

十一、详细件附件

STM32F030R8_I2C_Slave.rar

962.92 KB, 下载次数: 2287

现在,程序把烂铜烂铁变得智能化了,人呢,一旦离开了这烂铜烂铁就不知道干啥了
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

55

主题

1231

帖子

0

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
2625
金钱
2625
注册时间
2014-2-13
在线时间
514 小时
发表于 2017-10-2 11:46:10 | 显示全部楼层
弄一个地址贴,以后更新一篇就往里面补地址
技术交流,Sell 中颖单片机,欢迎私信骚扰
回复 支持 反对

使用道具 举报

120

主题

7878

帖子

13

精华

资深版主

Rank: 8Rank: 8

积分
12012
金钱
12012
注册时间
2013-9-10
在线时间
427 小时
 楼主| 发表于 2017-10-2 12:16:36 | 显示全部楼层
黑夜之狼 发表于 2017-10-2 11:46
弄一个地址贴,以后更新一篇就往里面补地址

现在,程序把烂铜烂铁变得智能化了,人呢,一旦离开了这烂铜烂铁就不知道干啥了
回复 支持 反对

使用道具 举报

17

主题

354

帖子

0

精华

高级会员

Rank: 4

积分
797
金钱
797
注册时间
2017-5-21
在线时间
340 小时
发表于 2017-12-3 18:42:44 来自手机 | 显示全部楼层
HALдзжжDMADMAжCR1PE λ
回复 支持 反对

使用道具 举报

17

主题

354

帖子

0

精华

高级会员

Rank: 4

积分
797
金钱
797
注册时间
2017-5-21
在线时间
340 小时
发表于 2017-12-3 18:43:22 来自手机 | 显示全部楼层
我用HAL库函数写主从通信,没有发现漏洞,但是必须用中断中断,改成DMA多字节收发时任意一个重启或故障容易出现链接不上,必须重启。DMA中断时出现死机后不能用CR1的PE 位不能释放总线,
回复 支持 反对

使用道具 举报

17

主题

354

帖子

0

精华

高级会员

Rank: 4

积分
797
金钱
797
注册时间
2017-5-21
在线时间
340 小时
发表于 2017-12-3 18:43:33 来自手机 | 显示全部楼层
我用HAL库函数写主从通信,没有发现漏洞,但是必须用中断中断,改成DMA多字节收发时任意一个重启或故障容易出现链接不上,必须重启。DMA中断时出现死机后不能用CR1的PE 位不能释放总线,
回复 支持 反对

使用道具 举报

120

主题

7878

帖子

13

精华

资深版主

Rank: 8Rank: 8

积分
12012
金钱
12012
注册时间
2013-9-10
在线时间
427 小时
 楼主| 发表于 2017-12-4 00:33:55 | 显示全部楼层
l6931639 发表于 2017-12-3 18:43
我用HAL库函数写主从通信,没有发现漏洞,但是必须用中断中断,改成DMA多字节收发时任意一个重启或故障容易 ...

没试过DMA,我用着中断没事啊,你是不是什么地方没有配置好导致的,这个仿真找问题比较容易,DMA开了后就等接收或者发送TC标志就可以了,硬件仿真看下什么现象
现在,程序把烂铜烂铁变得智能化了,人呢,一旦离开了这烂铜烂铁就不知道干啥了
回复 支持 反对

使用道具 举报

1

主题

83

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
3379
金钱
3379
注册时间
2017-7-10
在线时间
376 小时
发表于 2018-10-11 17:29:25 | 显示全部楼层
非常棒
回复 支持 反对

使用道具 举报

6

主题

18

帖子

0

精华

初级会员

Rank: 2

积分
129
金钱
129
注册时间
2014-2-25
在线时间
17 小时
发表于 2018-11-12 18:25:57 来自手机 | 显示全部楼层
我下载到f030c8t6开发板,用iic去写这个从设备没反应
回复 支持 反对

使用道具 举报

120

主题

7878

帖子

13

精华

资深版主

Rank: 8Rank: 8

积分
12012
金钱
12012
注册时间
2013-9-10
在线时间
427 小时
 楼主| 发表于 2018-11-14 20:24:25 | 显示全部楼层
shao15232 发表于 2018-11-12 18:25
我下载到f030c8t6开发板,用iic去写这个从设备没反应

管脚什么的要对应上
现在,程序把烂铜烂铁变得智能化了,人呢,一旦离开了这烂铜烂铁就不知道干啥了
回复 支持 反对

使用道具 举报

13

主题

633

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1331
金钱
1331
注册时间
2016-8-1
在线时间
229 小时
发表于 2018-11-15 13:59:36 | 显示全部楼层
顶一下楼主
回复 支持 反对

使用道具 举报

6

主题

24

帖子

0

精华

初级会员

Rank: 2

积分
67
金钱
67
注册时间
2019-3-27
在线时间
21 小时
发表于 2019-12-24 15:07:00 | 显示全部楼层
顶一下楼主,楼主写过f1的i2c从设备程序吗?
回复 支持 反对

使用道具 举报

120

主题

7878

帖子

13

精华

资深版主

Rank: 8Rank: 8

积分
12012
金钱
12012
注册时间
2013-9-10
在线时间
427 小时
 楼主| 发表于 2019-12-27 11:10:32 | 显示全部楼层
411772111 发表于 2019-12-24 15:07
顶一下楼主,楼主写过f1的i2c从设备程序吗?

F1从设备有坑,不好出来
现在,程序把烂铜烂铁变得智能化了,人呢,一旦离开了这烂铜烂铁就不知道干啥了
回复 支持 反对

使用道具 举报

0

主题

1

帖子

0

精华

新手入门

积分
19
金钱
19
注册时间
2019-11-5
在线时间
4 小时
发表于 2020-2-16 16:44:01 | 显示全部楼层
楼主你好, 我用你的代码,用主机去读没有ACK应答,是怎么回事?
回复 支持 反对

使用道具 举报

120

主题

7878

帖子

13

精华

资深版主

Rank: 8Rank: 8

积分
12012
金钱
12012
注册时间
2013-9-10
在线时间
427 小时
 楼主| 发表于 2020-2-22 16:30:40 | 显示全部楼层
zenghaibo 发表于 2020-2-16 16:44
楼主你好, 我用你的代码,用主机去读没有ACK应答,是怎么回事?

是不是线没接对?还是从设备地址错误,抓个波形看看就知道了,逻辑分析仪
现在,程序把烂铜烂铁变得智能化了,人呢,一旦离开了这烂铜烂铁就不知道干啥了
回复 支持 反对

使用道具 举报

0

主题

1

帖子

0

精华

新手入门

积分
18
金钱
18
注册时间
2018-4-16
在线时间
5 小时
发表于 2020-4-7 18:20:55 | 显示全部楼层
使用了你的demo,我用主机发送,从设备能够进地址中断,但是我的主机接收不到应答信号
回复 支持 反对

使用道具 举报

120

主题

7878

帖子

13

精华

资深版主

Rank: 8Rank: 8

积分
12012
金钱
12012
注册时间
2013-9-10
在线时间
427 小时
 楼主| 发表于 2020-5-20 15:01:45 | 显示全部楼层
狼者001 发表于 2020-4-7 18:20
使用了你的demo,我用主机发送,从设备能够进地址中断,但是我的主机接收不到应答信号

我测试正常喔,逻辑分析仪/示波器抓下波形看下
现在,程序把烂铜烂铁变得智能化了,人呢,一旦离开了这烂铜烂铁就不知道干啥了
回复 支持 反对

使用道具 举报

5

主题

15

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
209
金钱
209
注册时间
2014-11-6
在线时间
42 小时
发表于 2020-7-3 16:10:52 | 显示全部楼层
本帖最后由 kiritro 于 2020-7-3 17:23 编辑

楼主我测试了下你的固件写没问题,但是读怎么都调不出来我想问下读的过程是怎么样的
我这边试了下关闭时钟延展是可以读的,但是打开就没法读了,这是什么原因
回复 支持 反对

使用道具 举报

120

主题

7878

帖子

13

精华

资深版主

Rank: 8Rank: 8

积分
12012
金钱
12012
注册时间
2013-9-10
在线时间
427 小时
 楼主| 发表于 2020-7-16 08:53:19 | 显示全部楼层
kiritro 发表于 2020-7-3 16:10
楼主我测试了下你的固件写没问题,但是读怎么都调不出来我想问下读的过程是怎么样的
我这边试了下关闭时钟 ...

不会把,我测试都可以啊,你抓个波形看下,卡死在什么地方,不然猜是解决不了问题的
现在,程序把烂铜烂铁变得智能化了,人呢,一旦离开了这烂铜烂铁就不知道干啥了
回复 支持 反对

使用道具 举报

12

主题

108

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1822
金钱
1822
注册时间
2017-9-7
在线时间
501 小时
发表于 2020-11-12 18:38:35 | 显示全部楼层
请问下硬件16位接收该怎么改啊
回复 支持 反对

使用道具 举报

120

主题

7878

帖子

13

精华

资深版主

Rank: 8Rank: 8

积分
12012
金钱
12012
注册时间
2013-9-10
在线时间
427 小时
 楼主| 发表于 2021-1-28 22:28:32 | 显示全部楼层
亦辰 发表于 2020-11-12 18:38
请问下硬件16位接收该怎么改啊

没试过16位的,估计差不多
现在,程序把烂铜烂铁变得智能化了,人呢,一旦离开了这烂铜烂铁就不知道干啥了
回复 支持 反对

使用道具 举报

5

主题

61

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
474
金钱
474
注册时间
2021-11-22
在线时间
154 小时
发表于 2021-11-22 16:55:18 | 显示全部楼层
新手进来学习
回复 支持 反对

使用道具 举报

0

主题

5

帖子

0

精华

新手上路

积分
32
金钱
32
注册时间
2016-7-16
在线时间
5 小时
发表于 2021-11-28 12:03:37 | 显示全部楼层
试试看1
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165377
金钱
165377
注册时间
2010-12-1
在线时间
2111 小时
发表于 2022-1-17 01:00:08 | 显示全部楼层
COOL
回复 支持 反对

使用道具 举报

0

主题

1

帖子

0

精华

新手入门

积分
4
金钱
4
注册时间
2022-3-25
在线时间
1 小时
发表于 2022-3-25 22:19:48 | 显示全部楼层
楼主我把时钟延展打开,为什么主机一直接收不到ACK,关闭了反而正常了
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-2-27 20:41

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表