一、硬件I2CHAL库配置
1、配置时钟树
根据自己使用的芯片和具体需求配置,我使用的是stm32f103c8t6芯片,晶振配置为8,设置72MHZ分频。
2、打开RCC外部时钟
3、打开Debug
4、根据需要选择打开I2C1或I2C2
***********************************其他参数保持不变*****************************************
5、生成工程
二、软件I2C
1、配置GPIO引脚
软件I2C需要将两个引脚配置成输出模式,用于当作数据线SDA和时钟线SCL。这里我选择的是PB6,PB7。
2、编写I2C文件代码
(1)I2C.c文件代码
#include "i2c_hal.h"
#include "main.h"
#define DELAY_TIME 20
/**
* @brief SDA线输入模式配置
* @param None
* @retval None
*/
void SDA_Input_Mode()
{
GPIO_InitTypeDef GPIO_InitStructure = {0};
GPIO_InitStructure.Pin = GPIO_PIN_7;
GPIO_InitStructure.Mode = GPIO_MODE_INPUT;
GPIO_InitStructure.Pull = GPIO_PULLUP;
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);
}
/**
* @brief SDA线输出模式配置
* @param None
* @retval None
*/
void SDA_Output_Mode()
{
GPIO_InitTypeDef GPIO_InitStructure = {0};
GPIO_InitStructure.Pin = GPIO_PIN_7;
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_OD;
GPIO_InitStructure.Pull = GPIO_NOPULL;
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);
}
/**
* @brief SDA线输出一个位
* @param val 输出的数据
* @retval None
*/
void SDA_Output( uint16_t val )
{
if ( val )
{
GPIOB->BSRR |= GPIO_PIN_7;
}
else
{
GPIOB->BRR |= GPIO_PIN_7;
}
}
/**
* @brief SCL线输出一个位
* @param val 输出的数据
* @retval None
*/
void SCL_Output( uint16_t val )
{
if ( val )
{
GPIOB->BSRR |= GPIO_PIN_6;
}
else
{
GPIOB->BRR |= GPIO_PIN_6;
}
}
/**
* @brief SDA输入一位
* @param None
* @retval GPIO读入一位
*/
uint8_t SDA_Input(void)
{
if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_7) == GPIO_PIN_SET){
return 1;
}else{
return 0;
}
}
/**
* @brief I2C的短暂延时
* @param None
* @retval None
*/
static void delay1(unsigned int n)
{
uint32_t i;
for ( i = 0; i < n; ++i);
}
/**
* @brief I2C起始信号
* @param None
* @retval None
*/
void I2CStart(void)
{
SDA_Output(1);
delay1(DELAY_TIME);
SCL_Output(1);
delay1(DELAY_TIME);
SDA_Output(0);
delay1(DELAY_TIME);
SCL_Output(0);
delay1(DELAY_TIME);
}
/**
* @brief I2C结束信号
* @param None
* @retval None
*/
void I2CStop(void)
{
SCL_Output(0);
delay1(DELAY_TIME);
SDA_Output(0);
delay1(DELAY_TIME);
SCL_Output(1);
delay1(DELAY_TIME);
SDA_Output(1);
delay1(DELAY_TIME);
}
/**
* @brief I2C等待确认信号
* @param None
* @retval None
*/
unsigned char I2CWaitAck(void)
{
unsigned short cErrTime = 5;
SDA_Input_Mode();
delay1(DELAY_TIME);
SCL_Output(1);
delay1(DELAY_TIME);
while(SDA_Input())
{
cErrTime--;
delay1(DELAY_TIME);
if (0 == cErrTime)
{
SDA_Output_Mode();
I2CStop();
return ERROR;
}
}
SDA_Output_Mode();
SCL_Output(0);
delay1(DELAY_TIME);
return SUCCESS;
}
/**
* @brief I2C发送确认信号
* @param None
* @retval None
*/
void I2CSendAck(void)
{
SDA_Output(0);
delay1(DELAY_TIME);
delay1(DELAY_TIME);
SCL_Output(1);
delay1(DELAY_TIME);
SCL_Output(0);
delay1(DELAY_TIME);
}
/**
* @brief I2C发送非确认信号
* @param None
* @retval None
*/
void I2CSendNotAck(void)
{
SDA_Output(1);
delay1(DELAY_TIME);
delay1(DELAY_TIME);
SCL_Output(1);
delay1(DELAY_TIME);
SCL_Output(0);
delay1(DELAY_TIME);
}
/**
* @brief I2C发送一个字节
* @param cSendByte 需要发送的字节
* @retval None
*/
void I2CSendByte(unsigned char cSendByte)
{
unsigned char i = 8;
while (i--)
{
SCL_Output(0);
delay1(DELAY_TIME);
SDA_Output(cSendByte & 0x80);
delay1(DELAY_TIME);
cSendByte += cSendByte;
delay1(DELAY_TIME);
SCL_Output(1);
delay1(DELAY_TIME);
}
SCL_Output(0);
delay1(DELAY_TIME);
}
/**
* @brief I2C接收一个字节
* @param None
* @retval 接收到的字节
*/
unsigned char I2CReceiveByte(void)
{
unsigned char i = 8;
unsigned char cR_Byte = 0;
SDA_Input_Mode();
while (i--)
{
cR_Byte += cR_Byte;
SCL_Output(0);
delay1(DELAY_TIME);
delay1(DELAY_TIME);
SCL_Output(1);
delay1(DELAY_TIME);
cR_Byte |= SDA_Input();
}
SCL_Output(0);
delay1(DELAY_TIME);
SDA_Output_Mode();
return cR_Byte;
}
//
void I2CInit(void)
{
GPIO_InitTypeDef GPIO_InitStructure = {0};
GPIO_InitStructure.Pin = GPIO_PIN_7 | GPIO_PIN_6;
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStructure.Pull = GPIO_PULLUP;
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);
}
(2)I2C.文件代码
此代码来源于蓝桥杯嵌入式比赛官方提供,具体了解可以搜索蓝桥杯嵌入式I2C。
3、EEPROM的读取和写入
/**
* @brief 读取寄存器中的数据
* @param 读取数据的寄存器地址
* @retval
*/
char eeprom_read_I2C(char adrr)
{
uchar data;
I2CStart();
I2CSendByte(0xa0);
I2CWaitAck();
I2CSendByte(adrr);
I2CWaitAck();
I2CStop();
I2CStart();
I2CSendByte(0xa1);
I2CWaitAck();
data=I2CReceiveByte();
I2CWaitAck();
I2CStop();
return data;
}
/**
* @brief 将数据写入寄存器中
* @param 写入寄存器中的地址
* @param 写入寄存器中的数据
* @retval
*/
void eeprom_send_I2C(char addr,char data)
{
I2CStart();
I2CSendByte(0xa0);
I2CWaitAck();
I2CSendByte(addr);
I2CWaitAck();
I2CSendByte(data);
I2CWaitAck();
I2CStop();
}