数码之家

 找回密码
 立即注册
搜索
查看: 1596|回复: 24

[ARM] 分享STM32F0x/STM32G0x单片机的硬件IIC寄存器级代码

[复制链接]
发表于 2022-8-23 18:11:37 | 显示全部楼层 |阅读模式

爱科技、爱创意、爱折腾、爱极致,我们都是技术控

您需要 登录 才可以下载或查看,没有账号?立即注册

x
最近玩了下STM32G030 感觉这波更新很多,好多外设都有焕然一新的感觉,时钟系统也大改了,玩起来还蛮有意思的。
关键是还挺便宜的,今天研究了下硬件IIC驱动,HAL库实在是又长又臭 不爱看,再加上DMA更乱了,就干脆直接寄存器硬怼吧。
研究了以后发现好像和STM32F030的硬件IIC是同一个核 那就一起都测试下吧。
网上关于STM32G系列的资料太少了 找啥都没有 吧自己研究结果共享下大家一起玩


相关阅读:
stm8的硬件IIC代码-寄存器版|http://bbs.mydigit.cn/read.php?tid=2540435


测试效果
检查外设是否响应-ACK
2.JPG
检查外设是否响应-NAK  已加入简单的错误处理防止nak以后卡死

3.JPG
读和写

4.JPG

以下是代码-G030下的

  1. #define FLAG_TIMEOUT         ((uint32_t)0x1000)
  2. #define LONG_TIMEOUT         ((uint32_t)(10 * FLAG_TIMEOUT))
  3. #define CR1_CLEAR_MASK          ((uint32_t)0x00CFE0FF)
  4. //PA11 I2C_SCL
  5. //PA12 I2C_SDA
  6. void I2C_Init(void)
  7. {
  8.         RCC->IOPENR |= RCC_IOPENR_GPIOAEN;
  9.         RCC->APBENR1 |= RCC_APBENR1_I2C2EN;
  10.         //SYSCFG->CFGR1 |= SYSCFG_CFGR1_PA11_RMP|SYSCFG_CFGR1_PA12_RMP;
  11.        
  12.         GPIOA->MODER &=~ (GPIO_MODER_MODE9|GPIO_MODER_MODE10|GPIO_MODER_MODE11|GPIO_MODER_MODE12);
  13.         GPIOA->MODER |=  GPIO_MODER_MODE11_1|GPIO_MODER_MODE12_1;
  14.         GPIOA->OTYPER |= (GPIO_OTYPER_OT11|GPIO_OTYPER_OT12);
  15.         GPIOA->OSPEEDR |= GPIO_OSPEEDR_OSPEED11|GPIO_OSPEEDR_OSPEED12;
  16.         GPIOA->PUPDR&=~(GPIO_PUPDR_PUPD11|GPIO_PUPDR_PUPD12);
  17.         GPIOA->PUPDR|= GPIO_PUPDR_PUPD11_0|GPIO_PUPDR_PUPD12_0;//pull-up
  18.         GPIOA->AFR[1]&=~(GPIO_AFRH_AFSEL11|GPIO_AFRH_AFSEL12);
  19.         GPIOA->AFR[1]|=(6<<3*4)|(6<<4*4);//PA11/12 AF6 IIC2
  20.        
  21.         I2C2->CR1 &= ((uint32_t)~((uint32_t)I2C_CR1_PE))&CR1_CLEAR_MASK;
  22.         I2C2->CR1 &=~ I2C_CR1_ANFOFF;
  23.         //400KHz<span id="kM0.15701809540889855">@64MHz</span>
  24.         I2C2->TIMINGR = (0x17UL << I2C_TIMINGR_SCLL_Pos)|(0x9UL << I2C_TIMINGR_SCLH_Pos)|(0x2UL << I2C_TIMINGR_SDADEL_Pos)|(0x3UL << I2C_TIMINGR_SCLDEL_Pos)|(0x3UL << I2C_TIMINGR_PRESC_Pos);
  25.         I2C2->CR1 = (0x1UL << I2C_CR1_DNF_Pos);
  26.         I2C2->CR1 |= I2C_CR1_PE;
  27. }

  28. //返回1为失败 0为正常
  29. uint8_t IIC_Wait(uint32_t I2C_FLAG)
  30. {
  31.         uint32_t Timeout;
  32.         Timeout = LONG_TIMEOUT;  
  33.   //while(I2C_GetFlagStatus(I2C2, I2C_ISR_TXIS) == RESET)
  34.         while((I2C2->ISR&I2C_FLAG)==0)
  35.   {
  36.     if((Timeout--) == 0)
  37.                         return 1;
  38.   }
  39.         if(I2C2->ISR&I2C_ISR_NACKF)
  40.         {
  41.                 I2C2->ICR = I2C_ICR_NACKCF;
  42.                 return 1;//收到nak
  43.         }               
  44.         return 0;
  45. }

  46. //0-ok
  47. //other error
  48. uint32_t I2C_Check_Device(uint8_t I2C_Addr)
  49. {
  50.         uint32_t tmpreg;
  51.         //if(IIC_Wait(I2C_ISR_BUSY))return 1;//总线忙
  52.         tmpreg=I2C2->CR2;
  53.         tmpreg&= (uint32_t)~((uint32_t)(I2C_CR2_SADD | I2C_CR2_NBYTES | I2C_CR2_RELOAD | I2C_CR2_AUTOEND | I2C_CR2_RD_WRN | I2C_CR2_START | I2C_CR2_STOP));
  54.         tmpreg|=I2C_Addr|I2C_CR2_START|I2C_CR2_STOP;
  55.         I2C2->CR2 = tmpreg;  
  56.         tmpreg = IIC_Wait(I2C_ISR_STOPF);
  57.         I2C2->ICR = I2C_ICR_STOPCF;
  58.         return tmpreg;
  59. }

  60. //返回实际读取数量
  61. uint8_t IIC_Read(uint8_t Addr,uint8_t Reg,uint8_t Num,uint8_t* Data_Buff)
  62. {
  63.         uint32_t tmpreg,Pointer=0;
  64.         if(Num==0)return 0;
  65.         tmpreg=I2C2->CR2;
  66.         tmpreg&= (uint32_t)~((uint32_t)(I2C_CR2_SADD | I2C_CR2_NBYTES | I2C_CR2_RELOAD | I2C_CR2_AUTOEND | I2C_CR2_RD_WRN | I2C_CR2_START | I2C_CR2_STOP));
  67.         tmpreg|=Addr|I2C_CR2_START|(1<<16);//数量       
  68.         I2C2->CR2 = tmpreg;  
  69.         if(IIC_Wait(I2C_ISR_TXIS))return 0;
  70.         //Send memory address
  71.         I2C2->TXDR=Reg;
  72.         if(IIC_Wait(I2C_ISR_TC))return 0;
  73.        
  74.         tmpreg=I2C2->CR2;
  75.         tmpreg&= (uint32_t)~((uint32_t)(I2C_CR2_SADD | I2C_CR2_NBYTES | I2C_CR2_RELOAD | I2C_CR2_AUTOEND | I2C_CR2_RD_WRN | I2C_CR2_START | I2C_CR2_STOP));
  76.         tmpreg|=Addr|I2C_CR2_START | I2C_CR2_RD_WRN|I2C_CR2_AUTOEND|(Num<<16);
  77.         I2C2->CR2 = tmpreg;
  78.         while(Num)
  79.         {
  80.                 if(IIC_Wait(I2C_ISR_RXNE))return Pointer;
  81.                 Data_Buff[Pointer]=I2C2->RXDR;
  82.                 Pointer++;
  83.                 Num--;
  84.         }
  85.         return Pointer;
  86. }


  87. void IIC_Write(uint8_t Addr,uint8_t Reg,uint8_t Num,uint8_t* Data_Buff)
  88. {
  89.         uint32_t tmpreg,Pointer=0;
  90.         if(Num==0)return;
  91.         tmpreg=I2C2->CR2;
  92.         tmpreg&= (uint32_t)~((uint32_t)(I2C_CR2_SADD | I2C_CR2_NBYTES | I2C_CR2_RELOAD | I2C_CR2_AUTOEND | I2C_CR2_RD_WRN | I2C_CR2_START | I2C_CR2_STOP));
  93.         tmpreg|=Addr|I2C_CR2_START|I2C_CR2_AUTOEND|((Num+1)<<16);//数量       
  94.         I2C2->CR2 = tmpreg;  
  95.         if(IIC_Wait(I2C_ISR_TXE))return;
  96.         //Send memory address
  97.         I2C2->TXDR=Reg;       
  98.         while(Num)
  99.         {
  100.                 if(IIC_Wait(I2C_ISR_TXE))return;
  101.                 I2C2->TXDR=Data_Buff[Pointer];
  102.                 Pointer++;
  103.                 Num--;
  104.         }
  105.         IIC_Wait(I2C_ISR_TXE);
  106.         return;
  107. }

  108. void IIC_Write_REG(uint8_t Addr,uint8_t Reg)
  109. {
  110.         uint32_t tmpreg;
  111.         tmpreg=I2C2->CR2;
  112.         tmpreg&= (uint32_t)~((uint32_t)(I2C_CR2_SADD | I2C_CR2_NBYTES | I2C_CR2_RELOAD | I2C_CR2_AUTOEND | I2C_CR2_RD_WRN | I2C_CR2_START | I2C_CR2_STOP));
  113.         tmpreg|=Addr|I2C_CR2_START|I2C_CR2_AUTOEND|(1<<16);//数量       
  114.         I2C2->CR2 = tmpreg;  
  115.         if(IIC_Wait(I2C_ISR_TXE))return;
  116.         //Send memory address
  117.         I2C2->TXDR=Reg;       
  118.         IIC_Wait(I2C_ISR_TXE);
  119.         return;
  120. }

  121. //返回实际读取数量
  122. uint8_t IIC_Read_Without_Reg_Addr(uint8_t Addr,uint8_t Num,uint8_t* Data_Buff)
  123. {
  124.         uint32_t tmpreg,Pointer=0;
  125.        
  126.         tmpreg=I2C2->CR2;
  127.         tmpreg&= (uint32_t)~((uint32_t)(I2C_CR2_SADD | I2C_CR2_NBYTES | I2C_CR2_RELOAD | I2C_CR2_AUTOEND | I2C_CR2_RD_WRN | I2C_CR2_START | I2C_CR2_STOP));
  128.         tmpreg|=Addr|I2C_CR2_START | I2C_CR2_RD_WRN|I2C_CR2_AUTOEND|(Num<<16);
  129.         I2C2->CR2 = tmpreg;
  130.         while(Num)
  131.         {
  132.                 if(IIC_Wait(I2C_ISR_RXNE))return Pointer;
  133.                 Data_Buff[Pointer]=I2C2->RXDR;
  134.                 Pointer++;
  135.                 Num--;
  136.         }
  137.         return Pointer;
  138. }

  139. void IIC_Write_Without_Reg_Addr(uint8_t Addr,uint8_t Num,uint8_t* Data_Buff)
  140. {
  141.         uint32_t tmpreg,Pointer=0;
  142.         if(Num==0)return;
  143.         tmpreg=I2C2->CR2;
  144.         tmpreg&= (uint32_t)~((uint32_t)(I2C_CR2_SADD | I2C_CR2_NBYTES | I2C_CR2_RELOAD | I2C_CR2_AUTOEND | I2C_CR2_RD_WRN | I2C_CR2_START | I2C_CR2_STOP));
  145.         tmpreg|=Addr|I2C_CR2_START|I2C_CR2_AUTOEND|((Num)<<16);//数量       
  146.         I2C2->CR2 = tmpreg;  
  147.         while(Num)
  148.         {
  149.                 if(IIC_Wait(I2C_ISR_TXE))return;
  150.                 I2C2->TXDR=Data_Buff[Pointer];
  151.                 Pointer++;
  152.                 Num--;
  153.         }
  154.         IIC_Wait(I2C_ISR_TXE);
  155.         return;
  156. }
复制代码
STM32F0上的初始化
如果是TSSOP20的丐版 自己改下引脚到PA脚,然后全局Ctrl+H替换I2C2----->I2C1


  1. void IIC2_Init(void)
  2. {       
  3.         RCC->AHBENR |= RCC_AHBENR_GPIOBEN;
  4.         RCC->APB1ENR |= RCC_APB1ENR_I2C2EN;       
  5.        
  6.        
  7.         GPIOB->OTYPER|=GPIO_OTYPER_OT_11|GPIO_OTYPER_OT_10;//OD
  8.         GPIOB->OSPEEDR|=GPIO_OSPEEDR_OSPEEDR11|GPIO_OSPEEDR_OSPEEDR10;//50M
  9.         GPIOB->MODER&=~(GPIO_MODER_MODER11|GPIO_MODER_MODER10);
  10.         GPIOB->PUPDR&=~(GPIO_PUPDR_PUPDR11|GPIO_PUPDR_PUPDR10);
  11.         GPIOB->MODER|=GPIO_MODER_MODER11_1|GPIO_MODER_MODER10_1;//复用
  12.         GPIOB->PUPDR|=GPIO_PUPDR_PUPDR11_0|GPIO_PUPDR_PUPDR10_0;//pull-up
  13.         GPIOB->AFR[1]&=~(GPIO_AFRH_AFR11|GPIO_AFRH_AFR10);
  14.         GPIOB->AFR[1]|=(1<<3*4)|(1<<2*4);//PB10/11 AF1 IIC
  15.        
  16.         //Disable I2Cx Peripheral
  17.   I2C2->CR1 &= ((uint32_t)~((uint32_t)I2C_CR1_PE))&CR1_CLEAR_MASK;
  18.         //EN AnalogFilter
  19.         I2C2->CR1 &=~ I2C_CR1_ANFOFF;
  20.         //Configure I2Cx: Timing
  21.   I2C2->TIMINGR = 0x00210507;//381K<span id="kM0.5335161359873961">@48MHz</span>
  22.   //Enable I2Cx Peripheral
  23.   I2C2->CR1 |= I2C_CR1_PE;               
  24. }
复制代码


打赏

参与人数 6家元 +105 收起 理由
kkdkj + 20 謝謝分享
玛德陛下 + 20 謝謝分享
mousebat04 + 5 大佬V5
rush + 20 優秀文章
cushion + 20 謝謝分享
jpdd521 + 20 硬核必赏。。

查看全部打赏

发表于 2022-8-23 18:55:44 | 显示全部楼层
先占个楼等会好打广告。。
回复 支持 反对

使用道具 举报

发表于 2022-8-23 19:03:01 来自手机浏览器 | 显示全部楼层
谢谢分享!32位的单片机还没玩过。不知难度大不大!
回复 支持 反对

使用道具 举报

发表于 2022-8-23 19:31:57 来自手机浏览器 | 显示全部楼层
都寄存器操作了,起码应该用中断收发。
回复 支持 反对

使用道具 举报

发表于 2022-8-23 21:58:12 | 显示全部楼层
本帖最后由 inthsunshine 于 2022-8-23 21:59 编辑

怎么会没资料, 官网就有

stm32一般都有对应的硬件手册(datasheet)和参考手册(Reference manual), 前者是硬件配置,管脚,以及性能参数,, 后者就是介绍原理,配置和寄存器的定义等等, 是C编程主要参考.
有这2个手册,编程不愁, 如果深入研究还有个编程手册(Programming manual),主要是汇编指令和一些系统级的寄存器定义,比如NVIC,SCB等

G030 的参考手册编号RM0454,搜一下就有
回复 支持 反对

使用道具 举报

 楼主| 发表于 2022-8-23 23:05:43 来自手机浏览器 | 显示全部楼层
inthsunshine 发表于 2022-8-23 21:58
怎么会没资料, 官网就有

stm32一般都有对应的硬件手册(datasheet)和参考手册(Reference manual), 前者是硬 ...

能白嫖别人现成代码当然是最好的啦,就不用自己debug一天了。我也是看着这两个手册写的呀2333 不看rm玩怎么可能玩的动芯片
回复 支持 反对

使用道具 举报

 楼主| 发表于 2022-8-23 23:08:45 来自手机浏览器 | 显示全部楼层
mmxx2015 发表于 2022-8-23 19:31
都寄存器操作了,起码应该用中断收发。

这个其实用dma更好 stm32新的iic系统 设置好发多少个byte 后面填数据就可以了,不用管状态位 很智能了 连中断都可以省。不过我这里数据量小,没必要
回复 支持 反对

使用道具 举报

发表于 2022-8-23 23:22:30 | 显示全部楼层
大佬出没,必须顶一顶
回复 支持 反对

使用道具 举报

发表于 2022-8-23 23:47:39 | 显示全部楼层
2545889167 发表于 2022-8-23 23:05
能白嫖别人现成代码当然是最好的啦,就不用自己debug一天了。我也是看着这两个手册写的呀2333 不看rm玩怎 ...

STM的硬件I2C有BUG,所以用的人少,大多都是用模拟的,所以硬件I2C资料少。
模拟的移植还方便,跨平台移植也很简单。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2022-8-24 04:35:24 来自手机浏览器 | 显示全部楼层
crazy0qwer 发表于 2022-8-23 23:47
STM的硬件I2C有BUG,所以用的人少,大多都是用模拟的,所以硬件I2C资料少。
模拟的移植还方便,跨平台移 ...

大家对stm32的iic有bug的刻板印象还是停留在stm32f103这颗片子上,但是这是2007年出品的芯片了哎,15年前的东西了。现在新的stm32g系列相对stm32f1,外设版本都更新了至少2-3个大版本了,大清亡了都。现在的iic,只需要填好发送几个字节,设置了start以后填充发送缓存(可以用dma全自动),其他的nak啊,stop啊这些全是自动的
回复 支持 反对

使用道具 举报

发表于 2022-8-24 08:37:59 | 显示全部楼层
crazy0qwer 发表于 2022-8-23 23:47
STM的硬件I2C有BUG,所以用的人少,大多都是用模拟的,所以硬件I2C资料少。
模拟的移植还方便,跨平台移 ...

我经常用STM32F103硬件I2C,还没能遇上。
回复 支持 反对

使用道具 举报

发表于 2022-8-24 10:02:30 来自手机浏览器 | 显示全部楼层
2545889167 发表于 2022-8-24 04:35
大家对stm32的iic有bug的刻板印象还是停留在stm32f103这颗片子上,但是这是2007年出品的芯片了哎,15年前 ...

那没办法,谁叫当初不处理好导致bug说法扩散开来,听说也只是库文件有问题,不是真的硬件问题。还有就是为了规避专利搞得太复杂。时间长了自然就刻板印象了。

不过抛开这些不谈,我还是觉得模拟的比较方便跨平台移植,所以能在网上流传比较广。而且模拟的做好一个模版后,后面再用也很方便。
回复 支持 反对

使用道具 举报

发表于 2022-8-24 10:04:24 来自手机浏览器 | 显示全部楼层
网络孤客 发表于 2022-8-24 08:37
我经常用STM32F103硬件I2C,还没能遇上。

据说是库问题。具体我也不清楚,都是用模拟的。
也许谣言就是被我这样 道听途说,添油加醋造成的!
回复 支持 反对

使用道具 举报

 楼主| 发表于 2022-8-24 10:39:41 来自手机浏览器 | 显示全部楼层
crazy0qwer 发表于 2022-8-24 10:02
那没办法,谁叫当初不处理好导致bug说法扩散开来,听说也只是库文件有问题,不是真的硬件问题。还有就是 ...

模拟在简单情况下(收发数据量小,整个系统对实时性要求不高)确实好用又方便移植。iic毕竟是个慢速设备,随便收发点什么都要占用十几毫秒以上,现在mcu好多都百mhz几百mhz,为了处理慢速设备,耽搁别的算法或者实时响应,就不值当了。或者是你要用到iic作从机,这种模拟也是无能为力的。这些情况还是得求助硬件iic,甚至要加上dma和中断来大大减少对cpu的占用,所以我的理念是用到一款单片机的iic就先把硬件iic调通,花不了多少时间的,等到后面加上实时性需求的时候,再随便改改就能用了。
回复 支持 1 反对 0

使用道具 举报

发表于 2022-8-24 12:54:57 | 显示全部楼层
一直用模拟的,主要是低速度通信,试试硬件好不好用。
回复 支持 反对

使用道具 举报

发表于 2022-8-24 13:26:20 | 显示全部楼层
向大佬学习!
回复 支持 反对

使用道具 举报

发表于 2022-8-24 13:50:51 | 显示全部楼层
i2c太慢, 我更喜欢spi, 再加上dma,中断就更爽了
回复 支持 反对

使用道具 举报

发表于 2022-8-28 13:30:47 | 显示全部楼层
2545889167 发表于 2022-8-23 23:08
这个其实用dma更好 stm32新的iic系统 设置好发多少个byte 后面填数据就可以了,不用管状态位 很智能了 连 ...

大佬, 那里能找到这个"智能DMA"代码的例子呢.  我会一点用libopencm3开发, 有能调通的例子能照猫画虎改一下, 例子不通就彻底没辙了.
我的水平大概率啃不动寄存器手册了. 唯一发现libopencm3的例子很清晰, 勉强搞的了, 但它的例子不全甚至有的例子能跑通但数据异常.

比如adc显示内部温度和和内部参考电压vref的, 读出来的结果大范围胡乱蹦, 像是采集的某个浮空的引脚的.

现在的stm32g0系列比之前的stm32f0系列便宜且资源更多, 就是没有libopencm3的例子程序.

希望大佬能赐教一二, 我在linux下C编程的能力还凑合, 但是玩单片机有点头大, 有些东西和操作系统的概念能对应起来,  但是看到那一堆初始化操作就晕了, 尤其是涉及到ADC的那些操作.

谢谢
回复 支持 反对

使用道具 举报

发表于 2022-8-28 13:45:35 来自手机浏览器 | 显示全部楼层
大佬厉害了
回复 支持 反对

使用道具 举报

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

本版积分规则

APP|手机版|小黑屋|关于我们|联系我们|法律条款|技术知识分享平台

闽公网安备35020502000485号

闽ICP备2021002735号-2

GMT+8, 2024-5-19 21:30 , Processed in 0.171600 second(s), 14 queries , Redis On.

Powered by Discuz!

© 2006-2023 smzj.net

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