当前位置:   article > 正文

stm32的hal库软件模拟iic通信学习(以M24C02/AT24C02为例) 从数据手册到配置到代码看这一个就够啦

stm32的hal库软件模拟iic通信学习(以M24C02/AT24C02为例) 从数据手册到配置到代码看这一个就够啦

关于iic的概念我就不多说了,csdn很多资料。

我将讲述英语不会的情况下如何查看英文的手册

从原理到代码一步一步记录如何与芯片进行通信。

在阅读本篇文章时,请去学习iic的通信规范,不然比较难懂

开篇:

不废话,电脑安装一个有道词典并且启动就行

首先:

我用的是蓝桥杯嵌入式的stm32g431开发板,使用的M24C02芯片,接线图如下

可以看出,

PB6   接的是     SCL

PB7   接的是     SDA

并且还外接了两个上拉电阻,在通信时起到上拉的作用

这两个引脚并没有硬件iic功能,所以只能软件iic通信

由于iic支持一主多从,图中还有一个MCP4017芯片,这个芯片不做讲解,没有影响。

引脚配置

现在知道了两个引脚了,那就配置两个引脚

打开cubemx

找到pb6,pb7,设置为gpio_output模式,即引脚输出模式

选择好后,进入system core->gpio

点开pb6,和pb7,配置成如下的代码

释义:

GPIO output level:High
引脚默认输出电平,这里选择high,在开漏模式下属于高阻态,电平取决于外部电路


GPIO mode:Output Open Drain
引脚的输出模式,这里是开漏输出,只有高阻态和下拉


GPIO Pull-up/Pull-down:No pull-up and no pull-down
这个是是否配置上拉,我选择的是无上下拉,如果上面的原理图中没有上拉电阻,那这里就要配置成上拉电阻,即GPIO Pull-up/Pull-down:pull-up


Maximum output speed:Low
输出速度,默认,慢
Fast Mode:Disable
快速模式,默认,不启动

User Label:

用户重命名,默认空,不动。

pb6和pb7都按这个配置配置完后生成代码

驱动部分

芯片的规范

首先在工程中配置好两个空白的myiic.c和.h文件

不会的看这个链接
http://t.csdnimg.cn/6lkJx
 

之后会参考数据手册,

m24c02数据手册获取

链接:https://pan.baidu.com/s/1hE7OGJWPdsbPWSUG9vwXYg 
提取码:8888

打开数据手册,看第一页

没什么好看的,就是参数介绍,继续往下看

目录,非常明显,我们需要参考的地方就在红色的框框里面

图中标记的数据大概就是SDA或者SCL电平保持的时间不能低于多少时间,因为我们使用软件iic模拟,所以基本上不会出现时间过短导致传输失败,不做过多解释,往下一大半

这里讲了大概的引脚规范,其中提到了芯片的两个引脚是开漏输出模式。与我们的stm32的两个引脚配置的引脚是开漏输出相呼应,并且建议使用上拉电阻,对应了iic的协议规范。

这里提到了本芯片的iic寻址是的7位地址,而且是高位起始,意思是数据从高位开始往地位发送,并且第八位是命令位,代表读或者写。

通过查看原理图和手册,E1,E2,E3都接地,即0.

所以本芯片的ip地址是1010000(),最后一位是命令位

这个就很重要了,讲述了iic通信中的各组标志。
起始信号:SCL为高电平期间,SDA信号线由高电平向低电平的变化表示起始信号;
终止信号:SCL为高电平期间,SDA由低电平向高电平的变化表示终止信号。

即在SCL拉高时
SDA:由低到高 – 起始信号
SDA:由高到低 – 终止信号

起始与终止的代码撰写

回到开头的引脚原理图部分

进行启动和停止代码的描写

首先,我们将pb6和pb7进行封装

打开myiic.h

  1. #ifndef _MY_IIC_H
  2. #define _MY_IIC_H
  3. //scl的端口
  4. #define MYSCL_PORT GPIOB
  5. #define MYSCL_PIN GPIO_PIN_6
  6. //sda的端口
  7. #define MYSDA_PORT GPIOB
  8. #define MYSDA_PIN GPIO_PIN_7
  9. #endif

然后加入4个函数用来封装SDA与SCL的控制

  1. #ifndef _MY_IIC_H
  2. #define _MY_IIC_H
  3. //scl的端口
  4. #define MYSCL_PORT GPIOB
  5. #define MYSCL_PIN GPIO_PIN_6
  6. //sda的端口
  7. #define MYSDA_PORT GPIOB
  8. #define MYSDA_PIN GPIO_PIN_7
  9. void MYIIC_W_SCL(unsigned char x);//控制scl电平
  10. void MYIIC_W_SDA(unsigned char x);//控制sda电平
  11. unsigned char MYIIC_R_SDA(void);//读取sda电平
  12. void delay_us(unsigned int delay);//微妙级延迟
  13. #endif

对应的.c代码是:

  1. #include "myiic.h"
  2. #define WAIT_TIME 50
  3. static void delay1(unsigned int n)
  4. {
  5. uint32_t i;
  6. for ( i = 0; i < n; ++i);
  7. }
  8. void MYIIC_W_SCL(unsigned char x)
  9. {
  10. HAL_GPIO_WritePin(MYSCL_PORT, MYSCL_PIN,(GPIO_PinState)x);
  11. delay1(WAIT_TIME);
  12. }
  13. void MYIIC_W_SDA(unsigned char x)
  14. {
  15. HAL_GPIO_WritePin(MYSDA_PORT, MYSDA_PIN,(GPIO_PinState)x);
  16. delay1(WAIT_TIME);
  17. }
  18. unsigned char MYIIC_R_SDA(void)
  19. {
  20. unsigned char u8;
  21. u8 = HAL_GPIO_ReadPin(MYSDA_PORT, MYSDA_PIN);
  22. delay1(WAIT_TIME);
  23. return u8;
  24. }

其中有一个小tip,当引脚配置为开漏输出时,在高阻态的情况下是可以读取引脚的真实电平的,所以这里读取SDA电平直接readpin就行

为了降低篇幅,之后的代码就不放出.h中的函数声明,下面出现的函数都可以在头文件中声明。

好了,引脚控制有了,可以开始起始与终止的代码

起始位:
参考数据手册

即:在SCL为高电平期间将SDA拉低,随后SCL拉低

  1. void MYIIC_Start(void)
  2. {
  3. MYIIC_W_SDA(1); // 一定要先拉高SDA再拉高SCL
  4. MYIIC_W_SCL(1);
  5. MYIIC_W_SDA(0);//在SCL拉高的时候拉低SDA
  6. MYIIC_W_SCL(0);//随后拉低SCL
  7. }
终止位:
参考数据手册

即SCL为高电平期间,SDA由低电平向高电平的变化表示终止信号。

  1. void MYIIC_Stop(void)
  2. {
  3. MYIIC_W_SCL(0);//先拉低SCL,控制
  4. MYIIC_W_SDA(0);//拉低SDA
  5. MYIIC_W_SCL(1);//拉高SCL
  6. MYIIC_W_SDA(1);//拉高的SCL期间拉高SDA。将iic总线释放,表示stop信号
  7. }

发送ACK与接收ACK代码的撰写

接收ACK

即无论前面做了啥,只要发送了8位的数据且是有效的,芯片就会在下一个SCL拉高前将SDA拉低,表示ACK(收到)

  1. //代码比较复杂,加了循环是为了防止在数据传输出错后不会卡在等待ack的循环里面
  2. unsigned char MYIIC_WaitAck(void)
  3. {
  4. unsigned short cErrTime = 10;//设置错误循环为10次
  5. MYIIC_W_SDA(1);//拉高SDA,即释放SDA的控制
  6. MYIIC_W_SCL(1);//拉高SCL
  7. while(MYIIC_R_SDA())//读取SDA,如果为0,则跳出循环
  8. {
  9. cErrTime--;
  10. if (0 == cErrTime)//错误处理
  11. {
  12. MYIIC_Stop();
  13. return ERROR;
  14. }
  15. }
  16. MYIIC_W_SCL(0);//读取完毕,重新拉低SCL
  17. return SUCCESS;//代码执行到这里说明接收ACK成功,返回success,即0
  18. }
发送ACK(在进行读操作时需要)

这里数据手册没讲

我们根据逻辑来

当接收了芯片发来的8位数据时,如果还需要继续接收下一位地址,我们则需要发送ACK(0)给芯片,芯片就会自动发送下一个地址的数据。否则发送非ACK(1)。

  1. void MYIIC_SentAck(unsigned char bit)
  2. {
  3. if(bit == 0)//判断要ACK还是非ACK
  4. MYIIC_W_SDA(0);
  5. else
  6. MYIIC_W_SDA(1);
  7. MYIIC_W_SCL(1);//拉高SCL,从机读取SDA判断是否ACK
  8. MYIIC_W_SCL(0);//拉低SCL
  9. }

以下内容编排中,待定

写数据的的撰写

指定地址写

读数据的的撰写

当前地址读
指定地址读
指定地址多次读

应用层的理解与代码

M24C02关于存储的描述与控制

芯片信息
芯片控制参数
芯片控制代码

总源代码

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/229368?site
推荐阅读
相关标签
  

闽ICP备14008679号