初始化
初始化就不解释的太详细了,多看看数据手册,这里我直接贴代码加注释
/**
* @brief EEPROM_I2C_Config初始化GPIO及I2C的模式
* @param 无
* @retval 无
*/
void EEPROM_I2C_GPIO_Config(void)
{
//GPIO初始化结构体
GPIO_InitTypeDef GPIO_InitStructure;
/* 使能 I2C 外设时钟 */
RCC_APB1PeriphClockCmd(EEPROM_I2C_CLK, ENABLE);
//使能AHB1总线上SCL和SDA所在的时钟
RCC_AHB1PeriphClockCmd(EEPROM_I2C_SCL_GPIO_CLK|EEPROM_I2C_SDA_GPIO_CLK,ENABLE);
/* 备用功能配置(除ADC和DAC外的所有非GPIO功能),使用该函数GPIO_PinAFConfig(); */
/* 连接 PXx 到 I2C_SCL */
GPIO_PinAFConfig(EEPROM_I2C_SCL_GPIO_PORT,EEPROM_I2C_SCL_SOURCE,EEPROM_I2C_SCL_AF); //把引脚连接到外设
/* 连接 PXx 到 I2C_SDA */
GPIO_PinAFConfig(EEPROM_I2C_SDA_GPIO_PORT,EEPROM_I2C_SDA_SOURCE,EEPROM_I2C_SDA_AF); //把引脚连接到外设
/* GPIO初始化 */
GPIO_InitStructure.GPIO_OType = GPIO_OType_OD; //i2c GPIO口必须使用开漏输出 不能使用推挽输出 开漏(OD)与推挽(PP)电路
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; //可以选择UP或者NOPULL 因为外部电路有上拉电阻 内部弱上拉意义不大
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //引脚速率
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //引脚复用功能正常开启
/* 配置SCL引脚 */
GPIO_InitStructure.GPIO_Pin = EEPROM_I2C_SCL_PIN ; //配置SCL对应的引脚号 SCL:PB8 SDA:PB9 在stm32f407英文手册查询,引脚不唯一
GPIO_Init(EEPROM_I2C_SCL_GPIO_PORT, &GPIO_InitStructure); //初始化GPIO
/* 配置SDA引脚 */
GPIO_InitStructure.GPIO_Pin = EEPROM_I2C_SDA_PIN; //配置SDA引脚号
GPIO_Init(EEPROM_I2C_SDA_GPIO_PORT, &GPIO_InitStructure); //初始化GPIO
}
/**
* @brief I2C 工作模式配置
* @param 无
* @retval 无
*/
void I2C_Mode_Config(void)
{
I2C_InitTypeDef I2C_InitStructure;
/* --------------------I2C 配置 ---------------------------*/
/* I2C 模式配置 */
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
/* I2C 占空比周期配置 low/high=2/1 */
I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2; /* 高电平数据稳定,低电平数据变化 SCL 时钟线的占空比 */
/* I2C STM32 的 I2C 设备自己的地址长度配置 */
I2C_InitStructure.I2C_OwnAddress1 =I2C_OWN_ADDRESS7;
/* I2C 应答使能配置 */
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
/* I2C 寻址长度配置 7bit|10bit */
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; /* I2C的寻址模式 */
/* I2C 速率配置 */
I2C_InitStructure.I2C_ClockSpeed = I2C_Speed; /* 通信速率 */
/* I2C 初始化 */
I2C_Init(EEPROM_I2C, &I2C_InitStructure); /* I2C1 初始化 */
/* I2C 配置 */
I2C_Cmd(EEPROM_I2C, ENABLE); /* 使能 I2C1 */
/* I2C 应答使能配置 */
I2C_AcknowledgeConfig(EEPROM_I2C, ENABLE);
}
void I2C_EE_init(void)
{
/* I2C GPIO配置 */
EEPROM_I2C_GPIO_Config();
/* I2C配置 */
I2C_Mode_Config();
}
读写过程原理
使用
I2C
外设通讯时,在通讯的不同阶段它会对“状态寄存器
(SR1及SR2)
”的不同数据位写入参数,通过读取这些寄存器标志来了解通讯状态。
发送
接收
?下面的EV事件是必须等待响应后才能继续执行下一步
EV事件看不明白的看STM32数据手册? 有对I2C的寄存器的每位的作用都有讲解
?关于怎么检测EV事件响应I2C库中给了I2C_CheckEvent()函数非常方便
上面的注释给了各种EV事件应该对应选择什么参数
/**
* @brief Checks whether the last I2Cx Event is equal to the one passed
* as parameter.
* @param I2Cx: where x can be 1, 2 or 3 to select the I2C peripheral.
* @param I2C_EVENT: specifies the event to be checked.
* This parameter can be one of the following values:
* @arg I2C_EVENT_SLAVE_TRANSMITTER_ADDRESS_MATCHED: EV1
* @arg I2C_EVENT_SLAVE_RECEIVER_ADDRESS_MATCHED: EV1
* @arg I2C_EVENT_SLAVE_TRANSMITTER_SECONDADDRESS_MATCHED: EV1
* @arg I2C_EVENT_SLAVE_RECEIVER_SECONDADDRESS_MATCHED: EV1
* @arg I2C_EVENT_SLAVE_GENERALCALLADDRESS_MATCHED: EV1
* @arg I2C_EVENT_SLAVE_BYTE_RECEIVED: EV2
* @arg (I2C_EVENT_SLAVE_BYTE_RECEIVED | I2C_FLAG_DUALF): EV2
* @arg (I2C_EVENT_SLAVE_BYTE_RECEIVED | I2C_FLAG_GENCALL): EV2
* @arg I2C_EVENT_SLAVE_BYTE_TRANSMITTED: EV3
* @arg (I2C_EVENT_SLAVE_BYTE_TRANSMITTED | I2C_FLAG_DUALF): EV3
* @arg (I2C_EVENT_SLAVE_BYTE_TRANSMITTED | I2C_FLAG_GENCALL): EV3
* @arg I2C_EVENT_SLAVE_ACK_FAILURE: EV3_2
* @arg I2C_EVENT_SLAVE_STOP_DETECTED: EV4
* @arg I2C_EVENT_MASTER_MODE_SELECT: EV5
* @arg I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED: EV6
* @arg I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED: EV6
* @arg I2C_EVENT_MASTER_BYTE_RECEIVED: EV7
* @arg I2C_EVENT_MASTER_BYTE_TRANSMITTING: EV8
* @arg I2C_EVENT_MASTER_BYTE_TRANSMITTED: EV8_2
* @arg I2C_EVENT_MASTER_MODE_ADDRESS10: EV9
*
* @note For detailed description of Events, please refer to section I2C_Events
* in stm32f4xx_i2c.h file.
*
* @retval An ErrorStatus enumeration value:
* - SUCCESS: Last event is equal to the I2C_EVENT
* - ERROR: Last event is different from the I2C_EVENT
*/
ErrorStatus I2C_CheckEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT)
{
uint32_t lastevent = 0;
uint32_t flag1 = 0, flag2 = 0;
ErrorStatus status = ERROR;
/* Check the parameters */
assert_param(IS_I2C_ALL_PERIPH(I2Cx));
assert_param(IS_I2C_EVENT(I2C_EVENT));
/* Read the I2Cx status register */
flag1 = I2Cx->SR1;
flag2 = I2Cx->SR2;
flag2 = flag2 << 16;
/* Get the last event value from I2C status register */
lastevent = (flag1 | flag2) & FLAG_MASK;
/* Check whether the last event contains the I2C_EVENT */
if ((lastevent & I2C_EVENT) == I2C_EVENT)
{
/* SUCCESS: last event is equal to I2C_EVENT */
status = SUCCESS;
}
else
{
/* ERROR: last event is different from I2C_EVENT */
status = ERROR;
}
/* Return status */
return status;
}
写代码讲解
其中对EV事件响应设置了超时等待,如果超时了还未响应则返回回调函数打印出报错信息
/* ------------------对EEPROM写入1字节数据----------------------- */
//Buffer :要写入的数据
//Addr :要写入EEPROM的单元格地址
uint32_t EEPROM_byte_write(u8 Buffer , u8 Addr)
{
//发送起始信号
I2C_GenerateSTART(EEPROM_I2C,ENABLE);
//等待EV5事件响应
TimeOut_count = I2C_time_out;
while(!I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_MODE_SELECT))
{
if((TimeOut_count--) == 0) return I2C_timeout_callback(0);
}
//发送要写入的EEPROM的地址和读写方向
I2C_Send7bitAddress(EEPROM_I2C , EEPROM_ADDRESS , I2C_Direction_Transmitter);
//等待EV事件响应
TimeOut_count = I2C_time_out;
while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) != SUCCESS)
{
if((TimeOut_count--) == 0) return I2C_timeout_callback(1);
}
//发送要写入EEPROM的单元地方
I2C_SendData(EEPROM_I2C,Addr);
//等待EV事件响应
TimeOut_count = I2C_time_out;
while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS)
{
if((TimeOut_count--) == 0) return I2C_timeout_callback(2);
}
//发送要写入的数据
I2C_SendData(EEPROM_I2C , Buffer);
//等待EV事件响应
TimeOut_count = I2C_time_out;
while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS)
{
if((TimeOut_count--) == 0) return I2C_timeout_callback(3);
}
//发送停止信号
I2C_GenerateSTOP(I2C1,ENABLE);
return 0;
}
读代码
//addr : 要读取的单元格地址
//DATA :读取的内容存放区
uint32_t EPPROM_Random_Read(u8 addr , u8 *Data)
{
//发送起始信号
I2C_GenerateSTART(EEPROM_I2C,ENABLE);
//等待EV5事件响应
TimeOut_count = I2C_time_out;
while(!I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_MODE_SELECT))
{
if((TimeOut_count--) == 0) return I2C_timeout_callback(4);
}
//发送要写入的EEPROM的地址和读写方向 先选择写方向写入单元格地址
I2C_Send7bitAddress(EEPROM_I2C , EEPROM_ADDRESS , I2C_Direction_Transmitter);
//等待EV6事件响应
TimeOut_count = I2C_time_out;
while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) != SUCCESS)
{
if((TimeOut_count--) == 0) return I2C_timeout_callback(5);
}
//发送要读取EEPROM的单元地方
I2C_SendData(EEPROM_I2C,addr);
//等待EV8_2事件响应
TimeOut_count = I2C_time_out;
while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS)
{
if((TimeOut_count--) == 0) return I2C_timeout_callback(6);
}
//---------------------------------------------------------------------------------------------------------
//-----------------------产生第二次起始信号---------------------------------------------------------
//发送起始信号
I2C_GenerateSTART(EEPROM_I2C,ENABLE);
//等待EV事件响应
TimeOut_count = I2C_time_out;
while(!I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_MODE_SELECT))
{
if((TimeOut_count--) == 0) return I2C_timeout_callback(7);
}
//*********************************读方向************************************************
//发送要写入的EEPROM的地址和读方向
I2C_Send7bitAddress(EEPROM_I2C , EEPROM_ADDRESS , I2C_Direction_Receiver);
//等待EV事件响应
TimeOut_count = I2C_time_out;
while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED) != SUCCESS)
{
if((TimeOut_count--) == 0) return I2C_timeout_callback(8);
}
I2C_AcknowledgeConfig(EEPROM_I2C , DISABLE);
//等待EV事件响应
TimeOut_count = I2C_time_out;
while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_BYTE_RECEIVED) != SUCCESS)
{
if((TimeOut_count--) == 0) return I2C_timeout_callback(9);
}
*Data = I2C_ReceiveData(EEPROM_I2C);
//发送停止信号
I2C_GenerateSTOP(I2C1,ENABLE);
return 0;
}
注意事项
STM32对EEPROM写入后立马读取是会报错的
必须等待写入事件响应后才能读取,因为在数据写入的时候
位 7 TxE:数据寄存器为空 (Data register empty)(发送器) 还没发送完毕的时候是不能读取的?
所以写了个wait函数用来等待发送完毕检测
//等待内部写入操作完成
//写入完成返回1 失败发送0
uint8_t Wait_for_StandBy(void)
{
uint32_t wait_count = 0xFFFF;
while(wait_count--)
{
//发送起始信号
I2C_GenerateSTART(EEPROM_I2C,ENABLE);
//等待EV事件响应
TimeOut_count = I2C_time_out;
while(!I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_MODE_SELECT))
{
if((TimeOut_count--) == 0) return I2C_timeout_callback(10);
}
//发送要写入的EEPROM的地址和读写方向
I2C_Send7bitAddress(EEPROM_I2C , EEPROM_ADDRESS , I2C_Direction_Transmitter);
//等待EV事件响应
TimeOut_count = I2C_time_out;
while(TimeOut_count--)
{
if(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) == SUCCESS)
{
//发送停止信号
I2C_GenerateSTOP(I2C1,ENABLE);
return 1;
}
}
}
//发送停止信号
I2C_GenerateSTOP(I2C1,ENABLE);
return 0;
}
注意在检测的时候也是需要起始信号和停止信号的,对EEPROM的地址一直写入直到成功代表STM32已经对EEPROM写入完成
这个函数只需要添加在读写函数之间就好了
int main(void)
{
/*初始化UART 配置模式为 115200 8-N-1,中断接收*/
Debug_USART_Config();
printf("这是一个EEPROM读写实验\n");
printf("\r\n 欢迎使用野火 STM32 F407 开发板。\r\n");
printf("\r\n 这是一个I2C外设(AT24C02)读写测试例程 \r\n");
I2C_EE_init();
u8 Data;
//在EEPROM的0x00单元地址 写入0x12
EEPROM_byte_write(0x12 , 0x00);
//写完马上读会出错 在写入时是不能读的
Wait_for_StandBy();
//读取EEPROM的单元格0x00地址数据存入 Data中
EPPROM_Random_Read(0x00 , &Data);
printf(" \r\n EEPROM读写实验结束 data = 0x%02x \n",Data);
while(1)
{
}
}
?
?
报错回调函数
uint32_t I2C_timeout_callback(uint8_t errorCode)
{
printf("I2C 等待超时 , 错误代码 errorCode = %d \r\n",errorCode);
return 0;
}
|