STM32CubeMX-硬件IIC读取AT24C02(阻塞、中断、DMA三种方式)
教程包含通用步骤以及专用步骤,其中,通用步骤为STM32CubeMX配置其他外设工程的通用操作,STM32CubeMX系列笔记基本通用,专用操作则是针对当前工程进行的配置
一、初始准备
1.硬件平台
使用正点原子STM32F4探索者
2.软件平台
STM32CubeMX软件平台 V6.2.1
Keil5软件平台 V5.32
STM32CubeProgrammer下载平台
二、操作步骤
1.CubeMX生成初始化代码
1.1 建立工程(通用步骤)
- 芯片选择
打开cube软件,选择从芯片来创建工程,一般开发都是使用这个来开发,有的时候也可能使用另外两个,但不多,第二个基于ST提供的开发板创建工程,针对性高,第三个则选择ST提供的例程来创建工程
F4探索者的主控为STM32F407ZET6,所以在搜索框找到STM32F407ZE后点击具体芯片,再开始工程
- 配置时钟源
我们点开SystemCore(系统内核设置),再点击RCC配置HSE和LSE时钟源,这里我都选择使用外部时钟,配置后,我们可以看到右边芯片引脚分配图的两个时钟源引脚点亮,表示时钟配置为外部源
- 配置时钟树
我们进入ClockConfiguration配置时钟树,使时钟的输入路径和大小符合我们预期,探索者的晶振和时钟倍频如下
一般配置正确时颜色蓝白为主,配置错误时则会出现紫色,提示我们要修改值
具体时钟树的了解可以看我很久之前的文章,有做一些分析
1.2 IIC配置步骤
IIC是一种串行通信总线,一般用于短距离设备间通信,是一种同步通信方式,具体协议参考之前的博客:IIC协议讲解
补充知识:
同步是指:发送方发出数据后,等接收方发回响应以后才发下一个数据包的通讯方式。
异步是指:发送方发出数据后,不等接收方发回响应,接着发送下个数据包的通讯方式。
同步是阻塞模式,异步是非阻塞模式。
其中SPI、IIC为同步通信 UART为异步通信
关于ST硬件IIC的坊间小传言:
大家在学习正点原子F1的时候原子哥说过,ST的F1的IIC设计的有BUG,容易出现通信问题,所以不推荐使用,但许多人说用起来没感觉,数据传输很正常,没什么问题,我查询了相关文档,发现这个不是空穴来风,ST公司出过STM32F1的勘误手册,上面有提到STM32F10X的IIC在通信时会发生一些错误,CSDN上也有关于遇到过硬件IIC BUG的文章:STM32硬件IIC的BUG问题;
不过这个也是有一定的概率,大多数情况下应该不会出问题,而且我使用的是F4,我也看了STM32F4的勘误表,相对于STM32F1的IIC,IIC外设的BUG少了很多,基本可以稳定运行!!!勘误表IIC部分截图如下,具体两个文档我整理发布到CSDN了:0积分下载链接;(这里的文档在ST官网也可以下载,需要可以自行去下载!)
- STM32F10X勘误表
- STM32F40X勘误表
话不多说,下面我们进入配置
1.2.1 引脚选择
查看正点原子探索者F4原理图
AT24C02原理图如下:
对应芯片的连接引脚如下,IO口可以复用为硬件IIC1,来和AT24C02进行通信!
1.2.2 开启IIC
点击Connectivity后选择I2C1,使能I2C模式
1.2.3 设置IIC
配置界面选项如下:
对应功能:
-
I2C Speed Mode: IIC模式设置 快速模式和标准模式。标准模式-速率上限为 100kb/s;快速模式-速率上限为 400kb/s,我们使用标准模式,快速模式对硬件设计要求很高
-
I2C Clock Speed:I2C传输速率,默认为100KHz,我们使用默认
-
Clock No Stretch Mode: 时钟没有扩展模式(时钟拉伸clock stretching通过将SCL线拉低来暂停一个传输.直到释放SCL线为高电平,传输才继续进行.clock stretching是可选的,实际上大多数从设备不包括SCL驱动,所以它们不能stretch时钟)
-
Primary Address Length selection: 从设备地址长度 一般为7位,通讯时7位地址+1位读写做开头;
-
Dual Address Acknowledged: 双地址确认,当主地址是7位长度时,我们可以有一个双地址
-
Primary slave address: 从设备初始地址(地址值从0到127,且生成的地址值左移1位)
-
General call address detecion:一般呼叫地址检测,关闭不需要
对应功能的具体配置按照上图来选择,设置完成可以看到对应IO口变绿
但此时IO口,并不是我们需要的IO口,原理图上IO口引脚对应的为PB8、PB9,所以我们需要改变一下复用IO口,直接在芯片引脚上选择就可以了;
补充配置1: 到这里硬件IIC已经可以使用阻塞延迟发送了,如果需要开启中断方式发送,这要在开启一下中断,配置如下
同时配置中断优先级
补充配置2: 如果在需要开启DMA的话,需要在加上如下步骤,这样代码里面就可以使用DMA发送接收了
以上就是IIC在STM32Cube的配置步骤
1.3 串口配置
配置串口用于与上位机通讯,方便检测IIC读写AT24C02有没有成功,具体配置和使用教程可以看我之前的文章:STM32CubeMX-串口中断实验
1.4 生成代码(通用步骤)
点击进入Project Manager 配置生成工程的名字,存储路径 (不要有中文) 以及编译器,这里我们选MDK-ARM(Keil被收购后改名)
配置生成选项,主要为下面三大块,第一个我们选择只拷贝必要的库,第二个选择为每个外设生成.c和.h文件,保存之前的用户代码,以及删除之前的生成代码,第三个不选择
PS:用户代码段是一下注释之间的代码,只有原始的用户代码段注释才有效,用户自己添加的无效
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
最后点击生成代码
2.编写代码
首先介绍一下IIC的驱动代码
IIC主要函数和串口等通讯协议主要函数基本相同,一个是发送,一个是接受,在HAL库中,发送和读取主要有三个方式,第一种读写是超时读写,第二种是中断读写,第三个是DMA中断读写,其中第一种阻塞方式发送,CPU资源占有较大,后面两种与中断结合发送接收,CPU资源使用较少,常用函数接口代码介绍如下:
//阻塞IIC发送、接受代码原型
HAL_StatusTypeDef HAL_I2C_Master_Transmit(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout);
HAL_StatusTypeDef HAL_I2C_Master_Receive(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout);
HAL_StatusTypeDef HAL_I2C_Mem_Write(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size, uint32_t Timeout);
HAL_StatusTypeDef HAL_I2C_Mem_Read(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size, uint32_t Timeout);
HAL_StatusTypeDef HAL_I2C_IsDeviceReady(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint32_t Trials, uint32_t Timeout);
HAL_I2C_Master_Transmit()主模式下以阻塞模式传输大量数据。
- 传入参数:hi2c—使用的IIC设备
- DevAddress—从机地址
- pData—写入数据的地址
- Size—写入数据长度
- Timeout—传输时间上限
HAL_I2C_Master_Receive()主模式下以阻塞模式接受大量数据。
- 传入参数:hi2c—使用的IIC设备
- DevAddress—从机地址
- pData—读入数据存储首地址
- Size—读取数据长度
- Timeout—读取时间上限
HAL_I2C_Mem_Write()将阻塞模式下的大量数据写入从机特定的内存地址
-
传入参数:hi2c—使用的IIC设备
-
DevAddress—从机地址
-
MemAddress—写入内存首地址(写入过程中会自加)
-
MemAddSize—写入内存的大小(8位或16位)
-
pData—指向数据缓冲区的指针
-
Size—要发送的数据量
-
Timeout—超时持续时间
HAL_I2C_Mem_Read()将阻塞模式下的大量数据写入从机特定的内存地址
-
传入参数:hi2c—使用的IIC设备
-
DevAddress—从机地址
-
MemAddress—读取内存首地址(读取过程中会自加)
-
MemAddSize—读取内存的大小(8位或16位)
-
pData—指向数据缓冲区的指针
-
Size—要发送的数据量
-
Timeout—超时持续时间
HAL_I2C_IsDeviceReady()检查目标设备是否准备好通信
- 传入参数:hi2c—使用的IIC设备
- DevAddress—从机地址
- Trials—测试次数
- Timeout—超时持续时间
//非阻塞普通中断IIC发送、接受代码原型
HAL_StatusTypeDef HAL_I2C_Master_Transmit_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_I2C_Master_Receive_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_I2C_Mem_Write_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_I2C_Mem_Read_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size);
传入参数与上面相同,只是少了一个超时时间
//非阻塞DMA中断IIC发送、接受代码原型
HAL_StatusTypeDef HAL_I2C_Master_Transmit_DMA(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_I2C_Master_Receive_DMA(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_I2C_Mem_Write_DMA(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_I2C_Mem_Read_DMA(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size);
了解以上IIC的调用接口后,我们来测试AT24C02代码,首先打开工程:
在main.c头部插入以下代码
/* USER CODE BEGIN PV */
//读写地址
#define AT24C02_Write 0xA0
#define AT24C02_Read 0xA1
//三次写入的字符串
unsigned char str1[]={
"jeck666"};
unsigned char str2[]={
"1234567"};
unsigned char str3[]={
"abcdefg"};
//读取缓存区
uint8_t ReadBuffer[50];
/* USER CODE END PV */
main初始化后代码段插入以下,代码功能:以三种不同的功能,对24C02进行不同数据读写;
/* USER CODE BEGIN 2 */
HAL_UART_Transmit_IT(&huart1,"Init Ok!\r\n",sizeof("Init Ok!\r\n"));
HAL_Delay(100);
//阻塞方式写入读取
if(HAL_I2C_Mem_Write(&hi2c1,AT24C02_Write,0,I2C_MEMADD_SIZE_8BIT,str1,sizeof(str1),1000)==HAL_OK)
HAL_UART_Transmit_IT(&huart1,"STR1_Write_OK\r\n",sizeof("STR1_Write_OK\r\n"));
HAL_Delay(1000);
HAL_I2C_Mem_Read(&hi2c1,AT24C02_Read,0,I2C_MEMADD_SIZE_8BIT,ReadBuffer,sizeof(str1),1000);
HAL_Delay(1000);
HAL_UART_Transmit_IT(&huart1,ReadBuffer,sizeof(str1));
HAL_Delay(1000);
//中断方式写入读取
if(HAL_I2C_Mem_Write_IT(&hi2c1,AT24C02_Write,0,I2C_MEMADD_SIZE_8BIT,str2,sizeof(str2))==HAL_OK)
HAL_UART_Transmit_IT(&huart1,"STR2_Write_OK\r\n",sizeof("STR2_Write_OK\r\n"));
HAL_Delay(1000);
HAL_I2C_Mem_Read_IT(&hi2c1,AT24C02_Read,0,I2C_MEMADD_SIZE_8BIT,ReadBuffer,sizeof(str2));
HAL_Delay(1000);
HAL_UART_Transmit_IT(&huart1,ReadBuffer,sizeof(str2));
HAL_Delay(1000);
//DMA中断方式写入读取
if(HAL_I2C_Mem_Write_DMA(&hi2c1,AT24C02_Write,0,I2C_MEMADD_SIZE_8BIT,str3,sizeof(str3))==HAL_OK)
HAL_UART_Transmit_IT(&huart1,"STR3_Write_OK\r\n",sizeof("STR3_Write_OK\r\n"));
HAL_Delay(1000);
HAL_I2C_Mem_Read_DMA(&hi2c1,AT24C02_Read,0,I2C_MEMADD_SIZE_8BIT,ReadBuffer,sizeof(str3));
HAL_Delay(1000);
HAL_UART_Transmit_IT(&huart1,ReadBuffer,sizeof(str3));
HAL_Delay(1000);
/* USER CODE END 2 */
3.程序下载,观察现象(通用步骤)
程序下载我一般用两种方式:
第一种是使用MDK自带的下载环境下载程序,我们给单片机连接ST-Link后配置下载,点击魔术棒,选择debug
选择ST-link后,点击setting
添加对应F4的Flash
keil界面点击下载
第二种是使用Stm32Programmer下载软件,该下载软件下载方式多,下载快,下面我使用st-link下载
打开软件,点击connect左边选择stlink后再点击connect连接下载器
点击open file,找到工程路径下MDK文件夹下工程生成的hex文件
之后点击downlod下载,下载结果如下
3.实验现象
通过串口助手进行调试,上电复位后显示三次读写的值,但此处DMA方式存在一个BUG,写入的第一个字节为0,具体原因还在研究中,可能是AT24C02写入间隔时间过短,至少要求间隔5ms,不过由于是测试实验,基本满足使用要求
转载:https://blog.csdn.net/qq_45396672/article/details/117253441