引言
前面陆陆续续利用寄存器方式实现了软件模拟I2C、硬件实现I2C的案例。接下来,我们使用HAL库的方式实现以下硬件I2C案例。当然了,本次案例可能复用前面寄存器实现的硬件I2C案例中实现的某些代码块,所以建议看这之前可以先看看前面寄存器实现的案例介绍。
一、需求描述
二、硬件电路设计
本次案例属于是前面寄存器实现的硬件I2C案例的另一种实现方式,故需求和硬件设计不变,不清楚的可参考下面文章:
三、软件设计
接下来,我们首先进入STM32CubeMX中创建工程并进行相应的配置。
3.1 STM32CubeMX中的配置
进入STM32CubeMX后,左键单击【ACCESS TO MCU SELECTOR】,找到使用的STM32芯片类型,接着双击创建新工程。
新工程创建好后,就会进入下图界面
3.1.1 选择Debug
首先开始配置调试工具,进入【System Core】的【SYS】,然后选择【Serial Wire】穿行单线调试即可。
3.1.2 配置RCC时钟
同样,进入【System Core】的【RCC】中,先配置外部时钟源,如下图HSE和LSE对应的均选择石英晶体震荡源【Crystal/Ceramic Resonator】即可。
然后,在此基础上,继续进入【Clock Configuration】,配置内部系统时钟,如下图配置即可。
3.1.3 配置串口
本次案例中串口的作用是将数据输出到串口助手上,因此直接利用串口重定向printf即可,在STM32CubeMX中的配置如下图(使用USART1)
3.1.4 配置I2C
前面硬件电路设计中,我们使用的I2C外设为I2C2,所以这里配置使用的也是I2C2,配置参考如下图
3.1.5 工程管理配置
最后,进入【Project Manager】,设置工程名称、路径、工具链等,然后勾选相关项即可。配置参考下图
最后,点击【GENERATE CODE】生成代码,接着在keil中打开工程即可。
3.2 Keil中的配置
进入keil工程进行相关的配置。
3.2.1 魔法棒配置
3.2.2 品的配置
【品】中是用来添加文件或目录组的。原本是不需要再【品】中配置的,但是本案例需要使用M24C02与32进行I2C通讯,所以和前面一样需要M24C02相关代码文件,这里方便起见,直接复用前面的M24C02的头源文件。
所以这里需要将俩文件添加到相关目录中即可。参考下图即可
这样,keil中的配置就OK了。
3.3 代码完善
进入VSCode,打开对应工程完善代码。
3.3.1 串口部分
串口我们之间重定向printf,因此首先在usart.h中引入stdio头文件,然后在usart.c文件中重写fputc函数即可。
3.3.2 I2C部分
先看看相关文件内容
I2C部分通过软件中的配置产生,代码自动生成,我们不用新增代码,相关函数HAL库已经存在,后面只需直接调用即可。
3.3.3 M24C02部分
HAL库已经对不同I2C通讯设备使用的函数进行包装,所以关于存储器(Mem)使用的函数也可以直接调用,所以我们对M24C02部分的函数实现使用HAL库函数进行替换即可。
注:这里笔者希望大家多主动理解相关HAL库函数代码,故这里不再赘述下面代码涉及到的相关函数,相信对大家来说比较容易。
1、m24c02.h
#ifndef __M24C02_H
#define __M24C02_H
#include "i2c.h"
// 宏定义
#define W_ADDR (0xA0)
#define R_ADDR (0xA1)
// 初始化
void M24C02_Init(void);
// 写入一个字节的数据
void M24C02_Writebyte(uint8_t innerAddr, uint8_t byte);
// 读取一个字节的数据
uint8_t M24C02_Readbyte(uint8_t innerAddr);
// 连续写入多个字节的数据(页写)
void M24C02_Writebytes(uint8_t innerAddr, uint8_t * bytes, uint8_t size);
// 连续读取多个字节的数据
void M24C02_Readbytes(uint8_t innerAddr, uint8_t * buffer, uint8_t size);
#endif
2、m24c02.c
#include "m24c02.h"
// 初始化
void M24C02_Init(void)
{
MX_I2C2_Init();
}
// 主机写入一个字节的数据
void M24C02_Writebyte(uint8_t innerAddr, uint8_t byte)
{
HAL_I2C_Mem_Write(&hi2c2, W_ADDR, innerAddr, I2C_MEMADD_SIZE_8BIT, &byte, 1, 1000);
// 写入周期
HAL_Delay(5);
}
// 读取一个字节的数据
uint8_t M24C02_Readbyte(uint8_t innerAddr)
{
uint8_t data;
HAL_I2C_Mem_Read(&hi2c2, R_ADDR, innerAddr, I2C_MEMADD_SIZE_8BIT, &data, 1, 1000);
return data;
}
// 连续写入多个字节的数据(页写)
void M24C02_Writebytes(uint8_t innerAddr, uint8_t *bytes, uint8_t size)
{
HAL_I2C_Mem_Write(&hi2c2, W_ADDR, innerAddr, I2C_MEMADD_SIZE_8BIT, bytes, size, 1000);
// 写入周期
HAL_Delay(5);
}
// 连续读取多个字节的数据
void M24C02_Readbytes(uint8_t innerAddr, uint8_t *buffer, uint8_t size)
{
HAL_I2C_Mem_Read(&hi2c2, R_ADDR, innerAddr, I2C_MEMADD_SIZE_8BIT, buffer, size, 1000);
}
3.3.4 main部分
各功能已经写好,直接在main.c中编写测试代码即可。
1、引入头文件
(1)使用memset()函数,将字符数组缓冲区进行清零操作,故引入string,h
(2)使用M24C02与STM32进行通讯,故引入M24C02头文件
2、测试代码
直接使用前面案例使用的测试代码即可,因为测试代码使用的函数就是m24c02.c中实现的函数,而本次对其中的函数名没有做修改,所以可以直接复用测试代码。
/* USER CODE BEGIN 2 */
printf("use HAL to hardware I2C will start...\n");
// 2. 向m24c02中写入单字符
M24C02_Writebyte(0x00, 'a');
M24C02_Writebyte(0x01, 'b');
M24C02_Writebyte(0x02, 'c');
// 3. 向m24c02读取数据
uint8_t byte1 = M24C02_Readbyte(0x00);
uint8_t byte2 = M24C02_Readbyte(0x01);
uint8_t byte3 = M24C02_Readbyte(0x02);
// 4. 串口输出打印
printf("byte1 = %c\t byte2 = %c\t byte3 = %c\n", byte1, byte2, byte3);
// 5. 向m24c02写入字符串
M24C02_Writebytes(0x00, "123456", 6);
// 6. 向m24c02读取数据
uint8_t buffer[100] = {0};
M24C02_Readbytes(0x00, buffer, 6);
// 7. 串口输出打印
printf("buffer = %s\n", buffer);
// 8. 测试页写超过数据范围
// 缓冲区清零
memset(buffer, 0, sizeof(buffer));
M24C02_Writebytes(0x00, "1234567890abcdefghijk", 21);
M24C02_Readbytes(0x00, buffer, 21);
printf("test -> buffer = %s\n", buffer);
/* USER CODE END 2 */
3.4 测试
编译烧录看看现象
如上图,烧录完成后串口助手上输出的数据,与测试代码实现的一致,即测试成功!
以上便是本次文章的所有内容,欢迎各位朋友在评论区讨论,本人也是一名初学小白,愿大家共同努力,一起进步吧!
鉴于笔者能力有限,难免出现一些纰漏和不足,望大家在评论区批评指正,谢谢!