freertos-软件模拟IIC读写at24c02
参考:https://blog.csdn.net/qq_52952281/article/details/134364016
直接移植,实测用
at24c02_drv.c
// #include "24c02_drv.h"
// #include "stdint.h"//注意:初版PCB不支持硬件IIC
//先使用软件模拟IIC过渡------配合osDelay让出cpu资源
//后续改版后,使用硬件IIC+DMA//参考linux驱动的写法:https://www.elecfans.com/d/1894852.html//2K bit ---> 2048bit----->2048/8 BYTE== 256BYTE;
//每页16字节//一共128页#include "24c02_drv.h"
#include "gpio.h"
#include "FreeRTOS.h"
#include "task.h"
#include "log_sys.h"
#include "stm32f4xx_hal.h"
#include "stm32f4xx_hal_def.h"/*@brief I2C驱动初始化,采用模拟IO的方式实现@param 无@return 无
*/#define AT24C02_ADDR_WRITE 0xA0
#define AT24C02_ADDR_READ 0xA1
/**@brief CPU发起I2C总线启动信号@param 无@return 无
*/
void IIC_Start(void)
{SDA_OUT_MODE(); // SDA线输出模式IIC_SDA_1(); IIC_SCL_1();osDelay(1);IIC_SDA_0(); // 当SCL高电平时,SDA出现一个下跳沿表示I2C总线启动信号osDelay(1); IIC_SCL_0(); // 钳住I2C总线,准备发送或接收数据
} /**@brief CPU发起I2C总线停止信号@param 无@return 无
*/
void IIC_Stop(void)
{SDA_OUT_MODE(); // SDA线输出模式IIC_SCL_0();IIC_SDA_0(); IIC_SCL_1();osDelay(1);IIC_SDA_1(); // 当SCL高电平时,SDA出现一个上跳沿表示I2C总线停止信号 osDelay(1);
}/**@brief CPU向I2C总线设备发送8bit数据@param ucByte -[in] 等待发送的字节@return 无
*/
void IIC_SendByte(uint8_t ucByte)
{ uint8_t i; SDA_OUT_MODE(); // SDA线输出模式IIC_SCL_0(); // 拉低时钟开始数据传输for(i = 0; i < 8; i++){ if(ucByte & 0x80) { IIC_SDA_1();}else{IIC_SDA_0();}ucByte <<= 1; osDelay(1); IIC_SCL_1();osDelay(1);IIC_SCL_0();osDelay(1); }
} /**@brief CPU从I2C总线设备读取8bit数据@param 无@return 读到的数据
*/
uint8_t IIC_ReadByte(void)
{uint8_t i = 0;uint8_t value = 0;SDA_IN_MODE(); // SDA线输入模式for(i = 0; i < 8; i++){value <<= 1;IIC_SCL_1();osDelay(1);if(IIC_SDA_READ()){value++;} IIC_SCL_0(); osDelay(1); } IIC_Ack();return value;
}/**@brief CPU产生一个时钟,并读取器件的ACK应答信号@param 无@return 返回0表示正确应答,1表示无器件响应
*/
uint8_t IIC_WaitAck(void)
{uint8_t result = 0; SDA_IN_MODE(); // SDA线输入模式IIC_SDA_1(); // CPU释放SDA总线osDelay(1); IIC_SCL_1(); // CPU驱动SCL = 1, 此时器件会返回ACK应答osDelay(1);if(IIC_SDA_READ()){result = 1;}else{result = 0;}IIC_SCL_0();osDelay(1);return result;
} /**@brief CPU产生一个ACK信号@param 无@return 无
*/
void IIC_Ack(void)
{SDA_OUT_MODE(); // SDA线输出模式IIC_SDA_0(); // CPU驱动SDA = 0osDelay(1);IIC_SCL_1(); // CPU产生1个时钟osDelay(1);IIC_SCL_0();osDelay(1);IIC_SDA_1(); // CPU释放SDA总线
}/**@brief CPU产生1个NACK信号@param 无@return 无
*/
void IIC_NAck(void)
{SDA_OUT_MODE(); // SDA线输出模式IIC_SDA_1(); // CPU驱动SDA = 1osDelay(1);IIC_SCL_1(); // CPU产生1个时钟osDelay(1);IIC_SCL_0();osDelay(1);
}void IIC_Init(void)
{GPIO_InitTypeDef GPIO_InitStruct = {0};/* GPIO Ports Clock Enable */__HAL_RCC_GPIOB_CLK_ENABLE();/*Configure GPIO pin : PtPin */GPIO_InitStruct.Pin = IIC1_SCL_Pin;GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(IIC1_SCL_GPIO_Port, &GPIO_InitStruct); GPIO_InitStruct.Pin = IIC1_SDA_Pin;GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(IIC1_SDA_GPIO_Port, &GPIO_InitStruct);IIC_Stop(); // 给一个停止信号, 复位I2C总线上的所有设备到待机模式
}/**@brief 检测I2C总线设备,CPU向发送设备地址,然后读取设备应答来判断该设备是否存在@param address -[in] 设备的I2C总线地址+读写控制bit(0 = w, 1 = r)@return 0 - 表示正确, 1 - 表示未探测到
*/
uint8_t IIC_CheckDevice(uint8_t address)
{uint8_t ucAck;IIC_Init(); // 初始化I2CIIC_Start(); // 发送启动信号IIC_SendByte(address); // 设备的I2C总线地址+读写控制bit(0 = w, 1 = r)ucAck = IIC_WaitAck(); // 检测设备的ACK应答IIC_Stop(); // 发送停止信号return ucAck;
}/********************************************************************** LOCAL FUNCTIONS*/
/**@brief SDA输出配置 @param 无@return 无
*/
static void SDA_OUT_MODE(void)
{GPIO_InitTypeDef GPIO_InitStruct;GPIO_InitStruct.Pin = IIC1_SDA_Pin;GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(IIC1_SDA_GPIO_Port, &GPIO_InitStruct);}/**@brief SDA输入配置 @param 无@return 无
*/
static void SDA_IN_MODE(void)
{GPIO_InitTypeDef GPIO_InitStruct;GPIO_InitStruct.Pin = IIC1_SDA_Pin;GPIO_InitStruct.Mode = GPIO_MODE_INPUT;HAL_GPIO_Init(IIC1_SDA_GPIO_Port, &GPIO_InitStruct);}void AT24CXX_soft_Init(void)
{IIC_Init();
}void AT24CXX_soft_ReadOneByte(u16 ReadAddr, u8 *read_data)
{ IIC_Start();IIC_SendByte(AT24C02_ADDR_WRITE);IIC_WaitAck();IIC_SendByte(ReadAddr);IIC_WaitAck();IIC_Start();IIC_SendByte(AT24C02_ADDR_READ);IIC_WaitAck();*read_data = IIC_ReadByte();IIC_WaitAck();IIC_Stop();}void AT24CXX_soft_WriteOneByte(u16 WriteAddr,u8 DataToWrite)
{ IIC_Start();IIC_SendByte(AT24C02_ADDR_WRITE);IIC_WaitAck();IIC_SendByte(WriteAddr);IIC_WaitAck();IIC_SendByte(DataToWrite);IIC_WaitAck();IIC_Stop();
}
void AT24CXX_soft_Read(u16 ReadAddr,u8 *pBuffer,u16 NumToRead)
{} void AT24CXX_soft_Write(u16 WriteAddr,u8 *pBuffer,u16 NumToWrite)
{}/*** @brief 从AT24C02读取四字节数据* @param ReadAddr - 读取起始地址* @param read_data - 存储读取到的32位数据*/
void AT24CXX_soft_ReadFourBytes(u16 ReadAddr, uint32_t *read_data)
{uint8_t buf[4];// 确保地址不会越界if(ReadAddr > 0xFF - 3) {LOG_ERROR("AT24C02 read address out of range");*read_data = 0;return;}// 读取四个字节for(int i = 0; i < 4; i++) {AT24CXX_soft_ReadOneByte(ReadAddr + i, &buf[i]);osDelay(1); // 适当延时}// 将四个字节组合成32位数据*read_data = ((uint32_t)buf[0] << 24) | ((uint32_t)buf[1] << 16) | ((uint32_t)buf[2] << 8) | buf[3];
}/*** @brief 向AT24C02写入四字节数据* @param WriteAddr - 写入起始地址* @param DataToWrite - 要写入的32位数据*/
void AT24CXX_soft_WriteFourBytes(u16 WriteAddr, uint32_t DataToWrite)
{uint8_t buf[4];// 确保地址不会越界if(WriteAddr > 0xFF - 3) {LOG_ERROR("AT24C02 write address out of range");return;}// 分解32位数据为四个字节buf[0] = (DataToWrite >> 24) & 0xFF; // 最高字节buf[1] = (DataToWrite >> 16) & 0xFF;buf[2] = (DataToWrite >> 8) & 0xFF;buf[3] = DataToWrite & 0xFF; // 最低字节// 写入四个字节for(int i = 0; i < 4; i++) {AT24CXX_soft_WriteOneByte(WriteAddr + i, buf[i]);osDelay(5); // 写入EEPROM需要时间,根据实际情况调整延时}
}void AT24C02_Test()
{{uint8_t data = 0;USART2_printf("will write\r\n");osDelay(1);AT24CXX_soft_WriteOneByte(0, 100);USART2_printf("weate data\r\n");osDelay(1);AT24CXX_soft_ReadOneByte(0,&data);USART2_printf("data:%d\r\n",data);}{int data_4_byte = 0;USART2_printf("will write\r\n");osDelay(1);AT24CXX_soft_WriteFourBytes(0, 0x11223344);USART2_printf("weate data\r\n");osDelay(1);AT24CXX_soft_ReadFourBytes(0,&data_4_byte);USART2_printf("data:%x\r\n",data_4_byte); }}
at24c02.h
#ifndef _AT24c02_DRV_H
#define _AT24c02_DRV__H#include "stdint.h"
#include "stdbool.h"#define AT24C02 255
#define IIC1_SCL_GPIO_Port GPIOE
#define IIC1_SDA_GPIO_Port GPIOE
#define IIC1_SCL_Pin GPIO_PIN_0
#define IIC1_SDA_Pin GPIO_PIN_1typedef uint8_t u8;typedef uint16_t u16;#define IIC_SCL_0() HAL_GPIO_WritePin(IIC1_SCL_GPIO_Port, IIC1_SCL_Pin, GPIO_PIN_RESET)
#define IIC_SCL_1() HAL_GPIO_WritePin(IIC1_SCL_GPIO_Port, IIC1_SCL_Pin, GPIO_PIN_SET)
#define IIC_SDA_0() HAL_GPIO_WritePin(IIC1_SDA_GPIO_Port, IIC1_SDA_Pin, GPIO_PIN_RESET)
#define IIC_SDA_1() HAL_GPIO_WritePin(IIC1_SDA_GPIO_Port, IIC1_SDA_Pin, GPIO_PIN_SET)
#define IIC_SDA_READ() HAL_GPIO_ReadPin(IIC1_SDA_GPIO_Port, IIC1_SDA_Pin) /********************************************************************** API FUNCTIONS*/
void IIC_Init(void);
void IIC_Start(void);
void IIC_Stop(void);
void IIC_SendByte(u8 ucByte);
u8 IIC_ReadByte(void);
u8 IIC_WaitAck(void);
void IIC_Ack(void);
void IIC_NAck(void);
u8 IIC_CheckDevice(u8 address);static void SDA_OUT_MODE(void);
static void SDA_IN_MODE(void);#endif