我看网上大部分的实例都是使用IIC和E2PROM进行通信,我自己用的正点原子的板子也是连着24C02的芯片,但我不知道大部分的实际项目里面有没有用E2PROM的,是不是直接用Flash的比较多?而IIC通信用作其他一些外设的通信,例如颜色识别传感器。原理基本大同小异,我也还是使用E2PROM来学习一下IIC通信。
正点原子开发板的原理图上24C02的SCL和SDA直接连着F103的PB6和PB7,即I2C1,配置一下CubeMX上的I2C1模块,基本上都是默认配置,速度如果选择高速则有400KHz,普通是100KHz
生成代码后可以看到I2C的函数比较多,仍然是3种方式:常规、中断和DMA。
设计一个这样的测试,先用IIC往E2PROM里写一串数据,之后再使用IIC读取刚刚写的数据,并用串口打印出来,看是否对的上。
这里不打算去研究IIC的通信原理,只学习应用层面。
写操作的顺序:IIC在写操作时先发送一个开始信号,发送器件写操作地址(这里要注意24C02的读地址和写地址不一样,读地址是
0x41
,写地址是0x40
),E2PROM接收到地址后,如果地址和自身的地址一样,E2PROM就会回一个应答信号。单片机接收到应答信号后,再发送要操作的内存地址(24C02的地址从0x00
到0xFF
,一共256个),得到应答后再传输写入的数据,再次等待E2PROM应答后发送结束信号结束传输。
写操作的库函数常用的是HAL_I2C_Mem_Write()
,可以看下i2c.c
里这个函数的定义
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)
一共有7个参数 :
- &hi2c1或者&hi2c2,根据选择的模块
- DevAddress设备地址,比如24C02的写地址为
0x40
- MemAddress,从机内存地址,
- MemAddSize,从机内存地址字节长度,如上所述,24C02应该是8个字节,所以这个参数为
I2C_MEMADD_SIZE_8BIT
- *pData,传输数据的起始地址
- Size,传输数据的大小,多少个字节
- Timeout,传输超时的时间,一般设为1000
通过查询24C02的datasheet,写操作有3种,即Byte Write
,MultiByte Write
和Page Write
,这里我们使用PageWrite
,即一次写8个字节的数据,使用for循环来完成:
for(j=0;j<32;j++)
{
if(HAL_I2C_Mem_Write(&hi2c1, 0xA0, 8*j, I2C_MEMADD_SIZE_8BIT, WriteBuffer+8*j, 8, 1000) == HAL_OK)
{
printf("===E2PROM write done===\r\n");
HAL_Delay(20); //这个很重要,每次IIC写入后都需要有一定延时,否则会出错
}
else
{
HAL_Delay(20);
printf("===E2PROM write NG===\r\n");
}
每次写入 8个字节的数据,然后存储器写入地址加 8。要注意每次执行完写入命令后,需要有一个延时,等待 EEPROM 内部处理完该指令后才能继续写入。
测试时给WriteBuffer初始化:
for(i=0;i<256;i++)
WriteBuffer[i] = i+ 2;
然后使用IIC的读库函数
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_I2C_Mem_Read(&hi2c1, 0xA1, 0, I2C_MEMADD_SIZE_8BIT, ReadBuffer, 256, 1000);
之后可以按字节读出所有ReadBuffer
的值,看是否和WriteBuffer
的一致。测试结果通过