软件IIC和硬件IIC读写AT24C2
一、硬件IIC读取AT24C2
1.硬件IIC配置
i2c.c
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file i2c.c
* @brief This file provides code for the configuration
* of the I2C instances.
******************************************************************************
* @attention
*
* Copyright (c) 2023 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "i2c.h"
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
I2C_HandleTypeDef hi2c1;
/* I2C1 init function */
void MX_I2C1_Init(void)
{
/* USER CODE BEGIN I2C1_Init 0 */
/* USER CODE END I2C1_Init 0 */
/* USER CODE BEGIN I2C1_Init 1 */
/* USER CODE END I2C1_Init 1 */
hi2c1.Instance = I2C1;
hi2c1.Init.ClockSpeed = 100000;
hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;
hi2c1.Init.OwnAddress1 = 0;
hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
hi2c1.Init.OwnAddress2 = 0;
hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
if (HAL_I2C_Init(&hi2c1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN I2C1_Init 2 */
/* USER CODE END I2C1_Init 2 */
}
void HAL_I2C_MspInit(I2C_HandleTypeDef* i2cHandle)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(i2cHandle->Instance==I2C1)
{
/* USER CODE BEGIN I2C1_MspInit 0 */
/* USER CODE END I2C1_MspInit 0 */
__HAL_RCC_GPIOB_CLK_ENABLE();
/* I2C1 clock enable */
__HAL_RCC_I2C1_CLK_ENABLE();
/**I2C1 GPIO Configuration
PB6 ------> I2C1_SCL
PB7 ------> I2C1_SDA
*/
GPIO_InitStruct.Pin = IIC1_SCL_Pin|IIC1_SDA_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* USER CODE BEGIN I2C1_MspInit 1 */
/* USER CODE END I2C1_MspInit 1 */
}
}
void HAL_I2C_MspDeInit(I2C_HandleTypeDef* i2cHandle)
{
if(i2cHandle->Instance==I2C1)
{
/* USER CODE BEGIN I2C1_MspDeInit 0 */
/* USER CODE END I2C1_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_I2C1_CLK_DISABLE();
/**I2C1 GPIO Configuration
PB6 ------> I2C1_SCL
PB7 ------> I2C1_SDA
*/
HAL_GPIO_DeInit(IIC1_SCL_GPIO_Port, IIC1_SCL_Pin);
HAL_GPIO_DeInit(IIC1_SDA_GPIO_Port, IIC1_SDA_Pin);
/* USER CODE BEGIN I2C1_MspDeInit 1 */
/* USER CODE END I2C1_MspDeInit 1 */
}
}
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
二、软件IIC配置
1.配置SCL和SDA引脚
bsp_soft_iic.h
#ifndef _BOARD_I2C_H_
#define _BOARD_I2C_H_
/*********************************************************************
* INCLUDES
*/
#include "main.h"
/*********************************************************************
* DEFINITIONS
// I2C_SCL时钟
#define IIC1_SCL_GPIO_Port GPIOB // GPIO端口
#define IIC1_SCL_Pin GPIO_PIN_6 // GPIO引脚
// I2C_SDA时钟
#define IIC1_SDA_GPIO_Port GPIOB // GPIO端口
#define IIC1_SDA_Pin GPIO_PIN_7 // GPIO引脚
*/
/*********************************************************************
* MACROS
*/
#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);
#endif /* _BOARD_I2C_H_ */
bsp_soft_iic.c
/*********************************************************************
* INCLUDES
*/
#include "bsp_I2C_Simu.h"
static void SDA_OUT_MODE(void);
static void SDA_IN_MODE(void);
/*********************************************************************
* PUBLIC FUNCTIONS
*/
/**
@brief I2C驱动初始化,采用模拟IO的方式实现
@param 无
@return 无
*/
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 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 = 0
osDelay(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 = 1
osDelay(1);
IIC_SCL_1(); // CPU产生1个时钟
osDelay(1);
IIC_SCL_0();
osDelay(1);
}
/**
@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(); // 初始化I2C
IIC_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);
}
/****************************************************END OF FILE****************************************************/
三、配置硬件和软件IIC AT24C02驱动文件
bsp_at24c02.h
#ifndef _BSP_24CXX_H
#define _BSP_24CXX_H
#include "main.h"
#define AT24C01 127
#define AT24C02 255
#define AT24C04 511
#define AT24C08 1023
#define AT24C16 2047
#define AT24C32 4095
#define AT24C64 8191
#define AT24C128 16383
#define AT24C256 32767
//我使用的是AT24C02
#define EE_TYPE AT24C02
#define AT24C02_ADDR_WRITE 0xA0
#define AT24C02_ADDR_READ 0xA1
#define IIC_OUTTIMES 500
void AT24CXX_soft_Init(void);
void AT24CXX_hw_ReadOneByte(u16 ReadAddr, u8 *read_data);
void AT24CXX_soft_WriteOneByte(u16 WriteAddr,u8 DataToWrite);
void AT24CXX_soft_Read(u16 ReadAddr,u8 *pBuffer,u16 NumToRead);
void AT24CXX_soft_Write(u16 WriteAddr,u8 *pBuffer,u16 NumToWrite);
void AT24CXX_hw_ReadOneByte(u16 ReadAddr, u8 *read_data); //指定地址读取一个字节
void AT24CXX_hw_WriteOneByte(u16 WriteAddr,u8 *write_data); //指定地址写入一个字节
void AT24CXX_hw_Read(u16 ReadAddr,u8 *pBuffer,u16 NumToRead); //从指定地址开始写入指定长度的数据
void AT24CXX_hw_Write(u16 WriteAddr,u8 *pBuffer,u16 NumToWrite); //从指定地址开始读出指定长度的数据
u8 AT24CXX_Check(void);
void AT24CXX_soft_Init(void);
#endif
bsp_at24c02.c
#include "bsp_24cxx.h"
//三次写入的字符串
u8 str1[]={"jeck666"};
u8 str2[]={"1234567"};
u8 str3[]={"abcdefg"};
uint8_t ReadBuffer[50];
/***************************软件IIC读写******************************/
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)
{
}
/***************************硬件IIC读写******************************/
void AT24CXX_hw_ReadOneByte(u16 ReadAddr, u8 *read_data)
{
HAL_I2C_Mem_Read(&hi2c1, AT24C02_ADDR_READ, ReadAddr, I2C_MEMADD_SIZE_8BIT, read_data, 1, IIC_OUTTIMES);
}
void AT24CXX_hw_WriteOneByte(u16 WriteAddr,u8 *write_data)
{
HAL_I2C_Mem_Write(&hi2c1, AT24C02_ADDR_WRITE, WriteAddr, I2C_MEMADD_SIZE_8BIT, write_data, 1, IIC_OUTTIMES);
}
void AT24CXX_hw_Read(u16 ReadAddr,u8 *pBuffer,u16 NumToRead)
{
HAL_I2C_Mem_Read(&hi2c1, AT24C02_ADDR_READ, ReadAddr, I2C_MEMADD_SIZE_8BIT, pBuffer, NumToRead, IIC_OUTTIMES);
}
void AT24CXX_hw_Write(u16 WriteAddr,u8 *pBuffer,u16 NumToWrite)
{
HAL_I2C_Mem_Write(&hi2c1, AT24C02_ADDR_WRITE, WriteAddr, I2C_MEMADD_SIZE_8BIT, pBuffer, NumToWrite, IIC_OUTTIMES);
}
//检查AT24CXX是否正常
//这里用了24XX的最后一个地址(255)来存储标志字.
//如果用其他24C系列,这个地址要修改
//返回1:检测失败
//返回0:检测成功
u8 AT24CXX_Check(void)
{
AT24CXX_soft_Init();
uint8_t temp;
uint16_t ReadAddr = 127;
uint8_t data = 0XAB;
AT24CXX_soft_ReadOneByte(ReadAddr,&temp);//避免每次开机都写AT24CXX
if(temp == 0XAB)
return 1;
else//排除第一次初始化的情况
{
AT24CXX_soft_WriteOneByte(ReadAddr,data);
AT24CXX_soft_ReadOneByte(ReadAddr,&temp);
if(temp == 0XAB)
return 1;
}
return 0;
}