• STM32中I2C与EEPROM字写读写实现(硬件方式)


    初始化

    初始化就不解释的太详细了,多看看数据手册,这里我直接贴代码加注释

    1. /**
    2. * @brief EEPROM_I2C_Config初始化GPIO及I2C的模式
    3. * @param 无
    4. * @retval 无
    5. */
    6. void EEPROM_I2C_GPIO_Config(void)
    7. {
    8. //GPIO初始化结构体
    9. GPIO_InitTypeDef GPIO_InitStructure;
    10. /* 使能 I2C 外设时钟 */
    11. RCC_APB1PeriphClockCmd(EEPROM_I2C_CLK, ENABLE);
    12. //使能AHB1总线上SCL和SDA所在的时钟
    13. RCC_AHB1PeriphClockCmd(EEPROM_I2C_SCL_GPIO_CLK|EEPROM_I2C_SDA_GPIO_CLK,ENABLE);
    14. /* 备用功能配置(除ADC和DAC外的所有非GPIO功能),使用该函数GPIO_PinAFConfig(); */
    15. /* 连接 PXx 到 I2C_SCL */
    16. GPIO_PinAFConfig(EEPROM_I2C_SCL_GPIO_PORT,EEPROM_I2C_SCL_SOURCE,EEPROM_I2C_SCL_AF); //把引脚连接到外设
    17. /* 连接 PXx 到 I2C_SDA */
    18. GPIO_PinAFConfig(EEPROM_I2C_SDA_GPIO_PORT,EEPROM_I2C_SDA_SOURCE,EEPROM_I2C_SDA_AF); //把引脚连接到外设
    19. /* GPIO初始化 */
    20. GPIO_InitStructure.GPIO_OType = GPIO_OType_OD; //i2c GPIO口必须使用开漏输出 不能使用推挽输出 开漏(OD)与推挽(PP)电路
    21. GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; //可以选择UP或者NOPULL 因为外部电路有上拉电阻 内部弱上拉意义不大
    22. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //引脚速率
    23. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //引脚复用功能正常开启
    24. /* 配置SCL引脚 */
    25. GPIO_InitStructure.GPIO_Pin = EEPROM_I2C_SCL_PIN ; //配置SCL对应的引脚号 SCL:PB8 SDA:PB9 在stm32f407英文手册查询,引脚不唯一
    26. GPIO_Init(EEPROM_I2C_SCL_GPIO_PORT, &GPIO_InitStructure); //初始化GPIO
    27. /* 配置SDA引脚 */
    28. GPIO_InitStructure.GPIO_Pin = EEPROM_I2C_SDA_PIN; //配置SDA引脚号
    29. GPIO_Init(EEPROM_I2C_SDA_GPIO_PORT, &GPIO_InitStructure); //初始化GPIO
    30. }
    31. /**
    32. * @brief I2C 工作模式配置
    33. * @param 无
    34. * @retval 无
    35. */
    36. void I2C_Mode_Config(void)
    37. {
    38. I2C_InitTypeDef I2C_InitStructure;
    39. /* --------------------I2C 配置 ---------------------------*/
    40. /* I2C 模式配置 */
    41. I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
    42. /* I2C 占空比周期配置 low/high=2/1 */
    43. I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2; /* 高电平数据稳定,低电平数据变化 SCL 时钟线的占空比 */
    44. /* I2C STM32 的 I2C 设备自己的地址长度配置 */
    45. I2C_InitStructure.I2C_OwnAddress1 =I2C_OWN_ADDRESS7;
    46. /* I2C 应答使能配置 */
    47. I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
    48. /* I2C 寻址长度配置 7bit|10bit */
    49. I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; /* I2C的寻址模式 */
    50. /* I2C 速率配置 */
    51. I2C_InitStructure.I2C_ClockSpeed = I2C_Speed; /* 通信速率 */
    52. /* I2C 初始化 */
    53. I2C_Init(EEPROM_I2C, &I2C_InitStructure); /* I2C1 初始化 */
    54. /* I2C 配置 */
    55. I2C_Cmd(EEPROM_I2C, ENABLE); /* 使能 I2C1 */
    56. /* I2C 应答使能配置 */
    57. I2C_AcknowledgeConfig(EEPROM_I2C, ENABLE);
    58. }
    59. void I2C_EE_init(void)
    60. {
    61. /* I2C GPIO配置 */
    62. EEPROM_I2C_GPIO_Config();
    63. /* I2C配置 */
    64. I2C_Mode_Config();
    65. }

    读写过程原理

    使用 I2C 外设通讯时,在通讯的不同阶段它会对“状态寄存器 (SR1及SR2) ”的不同数据位写入参数,通过读取这些寄存器标志来了解通讯状态。

    发送

    接收

     下面的EV事件是必须等待响应后才能继续执行下一步

    EV事件看不明白的看STM32数据手册  有对I2C的寄存器的每位的作用都有讲解

     关于怎么检测EV事件响应I2C库中给了I2C_CheckEvent()函数非常方便

    上面的注释给了各种EV事件应该对应选择什么参数

    1. /**
    2. * @brief Checks whether the last I2Cx Event is equal to the one passed
    3. * as parameter.
    4. * @param I2Cx: where x can be 1, 2 or 3 to select the I2C peripheral.
    5. * @param I2C_EVENT: specifies the event to be checked.
    6. * This parameter can be one of the following values:
    7. * @arg I2C_EVENT_SLAVE_TRANSMITTER_ADDRESS_MATCHED: EV1
    8. * @arg I2C_EVENT_SLAVE_RECEIVER_ADDRESS_MATCHED: EV1
    9. * @arg I2C_EVENT_SLAVE_TRANSMITTER_SECONDADDRESS_MATCHED: EV1
    10. * @arg I2C_EVENT_SLAVE_RECEIVER_SECONDADDRESS_MATCHED: EV1
    11. * @arg I2C_EVENT_SLAVE_GENERALCALLADDRESS_MATCHED: EV1
    12. * @arg I2C_EVENT_SLAVE_BYTE_RECEIVED: EV2
    13. * @arg (I2C_EVENT_SLAVE_BYTE_RECEIVED | I2C_FLAG_DUALF): EV2
    14. * @arg (I2C_EVENT_SLAVE_BYTE_RECEIVED | I2C_FLAG_GENCALL): EV2
    15. * @arg I2C_EVENT_SLAVE_BYTE_TRANSMITTED: EV3
    16. * @arg (I2C_EVENT_SLAVE_BYTE_TRANSMITTED | I2C_FLAG_DUALF): EV3
    17. * @arg (I2C_EVENT_SLAVE_BYTE_TRANSMITTED | I2C_FLAG_GENCALL): EV3
    18. * @arg I2C_EVENT_SLAVE_ACK_FAILURE: EV3_2
    19. * @arg I2C_EVENT_SLAVE_STOP_DETECTED: EV4
    20. * @arg I2C_EVENT_MASTER_MODE_SELECT: EV5
    21. * @arg I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED: EV6
    22. * @arg I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED: EV6
    23. * @arg I2C_EVENT_MASTER_BYTE_RECEIVED: EV7
    24. * @arg I2C_EVENT_MASTER_BYTE_TRANSMITTING: EV8
    25. * @arg I2C_EVENT_MASTER_BYTE_TRANSMITTED: EV8_2
    26. * @arg I2C_EVENT_MASTER_MODE_ADDRESS10: EV9
    27. *
    28. * @note For detailed description of Events, please refer to section I2C_Events
    29. * in stm32f4xx_i2c.h file.
    30. *
    31. * @retval An ErrorStatus enumeration value:
    32. * - SUCCESS: Last event is equal to the I2C_EVENT
    33. * - ERROR: Last event is different from the I2C_EVENT
    34. */
    35. ErrorStatus I2C_CheckEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT)
    36. {
    37. uint32_t lastevent = 0;
    38. uint32_t flag1 = 0, flag2 = 0;
    39. ErrorStatus status = ERROR;
    40. /* Check the parameters */
    41. assert_param(IS_I2C_ALL_PERIPH(I2Cx));
    42. assert_param(IS_I2C_EVENT(I2C_EVENT));
    43. /* Read the I2Cx status register */
    44. flag1 = I2Cx->SR1;
    45. flag2 = I2Cx->SR2;
    46. flag2 = flag2 << 16;
    47. /* Get the last event value from I2C status register */
    48. lastevent = (flag1 | flag2) & FLAG_MASK;
    49. /* Check whether the last event contains the I2C_EVENT */
    50. if ((lastevent & I2C_EVENT) == I2C_EVENT)
    51. {
    52. /* SUCCESS: last event is equal to I2C_EVENT */
    53. status = SUCCESS;
    54. }
    55. else
    56. {
    57. /* ERROR: last event is different from I2C_EVENT */
    58. status = ERROR;
    59. }
    60. /* Return status */
    61. return status;
    62. }

    写代码讲解

    其中对EV事件响应设置了超时等待,如果超时了还未响应则返回回调函数打印出报错信息

    1. /* ------------------对EEPROM写入1字节数据----------------------- */
    2. //Buffer :要写入的数据
    3. //Addr :要写入EEPROM的单元格地址
    4. uint32_t EEPROM_byte_write(u8 Buffer , u8 Addr)
    5. {
    6. //发送起始信号
    7. I2C_GenerateSTART(EEPROM_I2C,ENABLE);
    8. //等待EV5事件响应
    9. TimeOut_count = I2C_time_out;
    10. while(!I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_MODE_SELECT))
    11. {
    12. if((TimeOut_count--) == 0) return I2C_timeout_callback(0);
    13. }
    14. //发送要写入的EEPROM的地址和读写方向
    15. I2C_Send7bitAddress(EEPROM_I2C , EEPROM_ADDRESS , I2C_Direction_Transmitter);
    16. //等待EV事件响应
    17. TimeOut_count = I2C_time_out;
    18. while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) != SUCCESS)
    19. {
    20. if((TimeOut_count--) == 0) return I2C_timeout_callback(1);
    21. }
    22. //发送要写入EEPROM的单元地方
    23. I2C_SendData(EEPROM_I2C,Addr);
    24. //等待EV事件响应
    25. TimeOut_count = I2C_time_out;
    26. while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS)
    27. {
    28. if((TimeOut_count--) == 0) return I2C_timeout_callback(2);
    29. }
    30. //发送要写入的数据
    31. I2C_SendData(EEPROM_I2C , Buffer);
    32. //等待EV事件响应
    33. TimeOut_count = I2C_time_out;
    34. while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS)
    35. {
    36. if((TimeOut_count--) == 0) return I2C_timeout_callback(3);
    37. }
    38. //发送停止信号
    39. I2C_GenerateSTOP(I2C1,ENABLE);
    40. return 0;
    41. }

    读代码

    1. //addr : 要读取的单元格地址
    2. //DATA :读取的内容存放区
    3. uint32_t EPPROM_Random_Read(u8 addr , u8 *Data)
    4. {
    5. //发送起始信号
    6. I2C_GenerateSTART(EEPROM_I2C,ENABLE);
    7. //等待EV5事件响应
    8. TimeOut_count = I2C_time_out;
    9. while(!I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_MODE_SELECT))
    10. {
    11. if((TimeOut_count--) == 0) return I2C_timeout_callback(4);
    12. }
    13. //发送要写入的EEPROM的地址和读写方向 先选择写方向写入单元格地址
    14. I2C_Send7bitAddress(EEPROM_I2C , EEPROM_ADDRESS , I2C_Direction_Transmitter);
    15. //等待EV6事件响应
    16. TimeOut_count = I2C_time_out;
    17. while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) != SUCCESS)
    18. {
    19. if((TimeOut_count--) == 0) return I2C_timeout_callback(5);
    20. }
    21. //发送要读取EEPROM的单元地方
    22. I2C_SendData(EEPROM_I2C,addr);
    23. //等待EV8_2事件响应
    24. TimeOut_count = I2C_time_out;
    25. while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS)
    26. {
    27. if((TimeOut_count--) == 0) return I2C_timeout_callback(6);
    28. }
    29. //---------------------------------------------------------------------------------------------------------
    30. //-----------------------产生第二次起始信号---------------------------------------------------------
    31. //发送起始信号
    32. I2C_GenerateSTART(EEPROM_I2C,ENABLE);
    33. //等待EV事件响应
    34. TimeOut_count = I2C_time_out;
    35. while(!I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_MODE_SELECT))
    36. {
    37. if((TimeOut_count--) == 0) return I2C_timeout_callback(7);
    38. }
    39. //*********************************读方向************************************************
    40. //发送要写入的EEPROM的地址和读方向
    41. I2C_Send7bitAddress(EEPROM_I2C , EEPROM_ADDRESS , I2C_Direction_Receiver);
    42. //等待EV事件响应
    43. TimeOut_count = I2C_time_out;
    44. while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED) != SUCCESS)
    45. {
    46. if((TimeOut_count--) == 0) return I2C_timeout_callback(8);
    47. }
    48. I2C_AcknowledgeConfig(EEPROM_I2C , DISABLE);
    49. //等待EV事件响应
    50. TimeOut_count = I2C_time_out;
    51. while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_BYTE_RECEIVED) != SUCCESS)
    52. {
    53. if((TimeOut_count--) == 0) return I2C_timeout_callback(9);
    54. }
    55. *Data = I2C_ReceiveData(EEPROM_I2C);
    56. //发送停止信号
    57. I2C_GenerateSTOP(I2C1,ENABLE);
    58. return 0;
    59. }

    注意事项

    STM32对EEPROM写入后立马读取是会报错的

    必须等待写入事件响应后才能读取,因为在数据写入的时候

    7 TxE:数据寄存器为空 (Data register empty)(发送器) 还没发送完毕的时候是不能读取的 

    所以写了个wait函数用来等待发送完毕检测

    1. //等待内部写入操作完成
    2. //写入完成返回1 失败发送0
    3. uint8_t Wait_for_StandBy(void)
    4. {
    5. uint32_t wait_count = 0xFFFF;
    6. while(wait_count--)
    7. {
    8. //发送起始信号
    9. I2C_GenerateSTART(EEPROM_I2C,ENABLE);
    10. //等待EV事件响应
    11. TimeOut_count = I2C_time_out;
    12. while(!I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_MODE_SELECT))
    13. {
    14. if((TimeOut_count--) == 0) return I2C_timeout_callback(10);
    15. }
    16. //发送要写入的EEPROM的地址和读写方向
    17. I2C_Send7bitAddress(EEPROM_I2C , EEPROM_ADDRESS , I2C_Direction_Transmitter);
    18. //等待EV事件响应
    19. TimeOut_count = I2C_time_out;
    20. while(TimeOut_count--)
    21. {
    22. if(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) == SUCCESS)
    23. {
    24. //发送停止信号
    25. I2C_GenerateSTOP(I2C1,ENABLE);
    26. return 1;
    27. }
    28. }
    29. }
    30. //发送停止信号
    31. I2C_GenerateSTOP(I2C1,ENABLE);
    32. return 0;
    33. }

    注意在检测的时候也是需要起始信号和停止信号的,对EEPROM的地址一直写入直到成功代表STM32已经对EEPROM写入完成

    这个函数只需要添加在读写函数之间就好了

    1. int main(void)
    2. {
    3. /*初始化UART 配置模式为 115200 8-N-1,中断接收*/
    4. Debug_USART_Config();
    5. printf("这是一个EEPROM读写实验\n");
    6. printf("\r\n 欢迎使用野火 STM32 F407 开发板。\r\n");
    7. printf("\r\n 这是一个I2C外设(AT24C02)读写测试例程 \r\n");
    8. I2C_EE_init();
    9. u8 Data;
    10. //在EEPROM的0x00单元地址 写入0x12
    11. EEPROM_byte_write(0x12 , 0x00);
    12. //写完马上读会出错 在写入时是不能读的
    13. Wait_for_StandBy();
    14. //读取EEPROM的单元格0x00地址数据存入 Data中
    15. EPPROM_Random_Read(0x00 , &Data);
    16. printf(" \r\n EEPROM读写实验结束 data = 0x%02x \n",Data);
    17. while(1)
    18. {
    19. }
    20. }

     

     

    报错回调函数

    1. uint32_t I2C_timeout_callback(uint8_t errorCode)
    2. {
    3. printf("I2C 等待超时 , 错误代码 errorCode = %d \r\n",errorCode);
    4. return 0;
    5. }

  • 相关阅读:
    用HTML5的<canvas>元素实现刮刮乐游戏
    接口测试流程
    R语言ggplot2可视化:使用ggpubr包的ggdensity函数可视化密度图、使用fill参数自定义密度图的填充色
    STM32MP157_TF-A源码编译报错
    Zabbix
    ArcGIs创建企业级数据库
    二次元商业计划书PPT模版
    SpringBoot:CORS是什么?SpringBoot如何解决跨域问题?
    Python实战:获取bing必应壁纸首页的每日一图(仅做学习用)
    python -【十】单例模式与工厂模式
  • 原文地址:https://blog.csdn.net/weixin_47321452/article/details/127351256