[STM32F4] 基于STM32F407(模拟&硬件)I2C通讯

[复制链接]
2936|35
 楼主| yellow555 发表于 2023-5-27 00:16 | 显示全部楼层
写一页数据代码
  1. void I2C_Page_Write(uint8_t ADDR,uint8_t *Data,uint16_t size)
  2. {
  3.         I2C_Start();
  4.        
  5.         I2C_SendByte(EEPROM_I2C_Write);//发送写指令
  6.        
  7.         while(I2C_WaitAck());//等待从机响应
  8.        
  9.         I2C_SendByte(ADDR);//发送写在哪
  10.        
  11.         while(I2C_WaitAck());//等待从机响应
  12.        
  13.         while(size--)//发送数据
  14.         {
  15.                 I2C_SendByte(*Data);
  16.                 while(I2C_WaitAck());//等待从机响应
  17.                 Data++;
  18.         }
  19.        
  20.         I2C_Stop();
  21.        
  22.         Wait_For_Writed();//等待写入完成
  23. }
 楼主| yellow555 发表于 2023-5-27 00:19 | 显示全部楼层
写一页数据代码
  1. void I2C_Page_Write(uint8_t ADDR,uint8_t *Data,uint16_t size)
  2. {
  3.         I2C_Start();
  4.        
  5.         I2C_SendByte(EEPROM_I2C_Write);//发送写指令
  6.        
  7.         while(I2C_WaitAck());//等待从机响应
  8.        
  9.         I2C_SendByte(ADDR);//发送写在哪
  10.        
  11.         while(I2C_WaitAck());//等待从机响应
  12.        
  13.         while(size--)//发送数据
  14.         {
  15.                 I2C_SendByte(*Data);
  16.                 while(I2C_WaitAck());//等待从机响应
  17.                 Data++;
  18.         }
  19.        
  20.         I2C_Stop();
  21.        
  22.         Wait_For_Writed();//等待写入完成
  23. }
注意:一定要等待一会,要不然数卡死或者数据错误。
 楼主| yellow555 发表于 2023-5-27 00:20 | 显示全部楼层
写大量数据代码
  1. void I2C_Buff_Write(uint8_t ADDR , uint8_t *Data,uint16_t size)
  2. {
  3.         uint16_t num_of_page = size / EEPROM_PAGE_SIZE;//算出来有几页
  4.         uint16_t single_byte = size % EEPROM_PAGE_SIZE;//算出来有几个单个的字节数
  5.         uint16_t single_addr = ADDR % EEPROM_PAGE_SIZE;//算出来这一页有几个位置
  6.        
  7.         if(single_addr == 0)//如果地址对齐
  8.         {
  9.                 while(num_of_page--)
  10.                 {
  11.                         I2C_Page_Write(ADDR,Data,EEPROM_PAGE_SIZE) ;
  12.                        
  13.                         ADDR += EEPROM_PAGE_SIZE;
  14.                         Data += EEPROM_PAGE_SIZE;
  15.                 }
  16.                 I2C_Page_Write(ADDR,Data,single_byte) ;
  17.         }
  18.         else
  19.         {
  20.                 /* 第一次写入的数据字节数(为了让地址对齐) */
  21.                 uint16_t first_write_size = EEPROM_PAGE_SIZE - single_addr;//算出来这一页还差几个位置不全一页
  22.                
  23.                 I2C_Page_Write(ADDR,Data,first_write_size);//补全
  24.                
  25.                 uint16_t surplus = size - first_write_size;//剩余的数据字节数
  26.                 Data += first_write_size;
  27.                 ADDR += first_write_size;
  28.                
  29.                 num_of_page = surplus / EEPROM_PAGE_SIZE;//算出来有几页
  30.                 single_byte = surplus % EEPROM_PAGE_SIZE;//算出来有几个单个的字节数
  31.                
  32.                 while(num_of_page--)
  33.                 {
  34.                         I2C_Page_Write(ADDR,Data,EEPROM_PAGE_SIZE) ;
  35.                        
  36.                         ADDR += EEPROM_PAGE_SIZE;
  37.                         Data += EEPROM_PAGE_SIZE;
  38.                 }
  39.                 I2C_Page_Write(ADDR,Data,single_byte) ;
  40.         }
  41. }
 楼主| yellow555 发表于 2023-5-27 00:20 | 显示全部楼层
读大量数据代码
  1. void I2C_Buff_Read(uint8_t ADDR , uint8_t *Data,uint16_t size)
  2. {
  3.         I2C_Start();
  4.        
  5.         I2C_SendByte(EEPROM_I2C_Write);//发送写指令
  6.        
  7.         while(I2C_WaitAck());//等待从机响应
  8.        
  9.         I2C_SendByte(ADDR);//发送写在哪
  10.        
  11.         while(I2C_WaitAck());//等待从机响应
  12.        
  13.         I2C_Start();
  14.        
  15.         I2C_SendByte(EEPROM_I2C_Read);//发送读指令
  16.        
  17.         while(I2C_WaitAck());//等待从机响应
  18.        
  19.         while(size--)
  20.         {
  21.                 *Data = I2C_ReadByte();//读取数据
  22.                
  23.                 if(size == 0)//接收了最后一个数据主机发送非应答
  24.                         I2C_NAck();
  25.                 else
  26.                         I2C_Ack();
  27.                 Data++;
  28.         }
  29.        
  30.         I2C_Stop();
  31. }
 楼主| yellow555 发表于 2023-5-27 00:20 | 显示全部楼层
硬件I2C代码
硬件主发送、接收时序图
        (摘自stm32f407中文手册) 933426470dc569ac2f.png 421036470dc5b87432.png
 楼主| yellow555 发表于 2023-5-27 00:21 | 显示全部楼层
硬件I2C代码相关定义
  1. #define EEPROM_I2C_ADDR                        0xA0      
  2. #define EEPROM_PAGE_SIZE                                 8

  3. #define I2C                                                                I2C1
  4. #define I2C_CLK                                                 RCC_APB1Periph_I2C1
  5. #define I2C_Speed                                                 400000
  6. #define I2C_OWN_ADDR                                        0x78                        //地址只需要不同于总线上的其他设备地址即可

  7. /* I2C_SCL——PB8 */       
  8. #define I2C_SCL_PIN                                          GPIO_Pin_8
  9. #define I2C_SCL_GPIO_PORT                                GPIOB
  10. #define I2C_SCL_GPIO_CLK                                RCC_AHB1Periph_GPIOB
  11. #define I2C_SCL_SOURCE                                        GPIO_PinSource8
  12. #define I2C_SCL_AF                                                GPIO_AF_I2C1

  13. /* I2C_SDA——PB9 */
  14. #define I2C_SDA_PIN                                          GPIO_Pin_9
  15. #define I2C_SDA_GPIO_PORT                                GPIOB
  16. #define I2C_SDA_GPIO_CLK                                RCC_AHB1Periph_GPIOB
  17. #define I2C_SDA_SOURCE                                        GPIO_PinSource9
  18. #define I2C_SDA_AF                                                GPIO_AF_I2C1

  19. void I2C_Config(void);

  20. void I2C_Byte_Write(uint8_t ADDR , uint8_t Data);//向EEPROM中ADDR地址写入一个字节的数据
  21. void I2C_Random_Read(uint8_t ADDR , uint8_t *Data);//随机读取EEPROM中ADDR地址的数据(一个字节)

  22. void I2C_Page_Write(uint8_t ADDR,uint8_t *Data,uint16_t size) ;//写入一页的数据


  23. void I2C_Buffer_Write(uint8_t ADDR , uint8_t *Data,uint16_t size);//批量写入数据
  24. void I2C_Buffer_Read(uint8_t ADDR , uint8_t *Data,uint16_t size);//批量读取数据
 楼主| yellow555 发表于 2023-5-27 00:21 | 显示全部楼层
I2C相关配置代码
  1. void I2C_Config(void)
  2. {
  3.         GPIO_InitTypeDef GPIO_InitStructure;
  4.         I2C_InitTypeDef I2C_InitStructure;
  5.        
  6.         /* 使能 I2C 引脚的 GPIO 时钟 */
  7.         RCC_AHB1PeriphClockCmd(I2C_SCL_GPIO_CLK | I2C_SDA_GPIO_CLK,ENABLE);
  8.         /* 使能 I2C 时钟 */
  9.         RCC_APB1PeriphClockCmd(I2C_CLK,ENABLE);
  10.        
  11.         /* 将对应的IO口连接到外设,开始启动复用功能 */
  12.         GPIO_PinAFConfig(I2C_SCL_GPIO_PORT,I2C_SCL_SOURCE,I2C_SCL_AF);
  13.         GPIO_PinAFConfig(I2C_SDA_GPIO_PORT,I2C_SDA_SOURCE,I2C_SDA_AF);
  14.        
  15.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF ;//复用功能
  16.         GPIO_InitStructure.GPIO_OType = GPIO_OType_OD ;//开漏输出
  17.         GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP ;//上拉
  18.         GPIO_InitStructure.GPIO_Speed = GPIO_High_Speed ;//高速
  19.        
  20.         GPIO_InitStructure.GPIO_Pin = I2C_SCL_PIN;//配置发送引脚
  21.         GPIO_Init (I2C_SCL_GPIO_PORT,&GPIO_InitStructure);
  22.        
  23.         GPIO_InitStructure.GPIO_Pin = I2C_SDA_PIN;//配置发送引脚
  24.         GPIO_Init (I2C_SDA_GPIO_PORT,&GPIO_InitStructure);
  25.        
  26.         I2C_InitStructure.I2C_Ack = I2C_Ack_Enable ;
  27.         I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit ;
  28.         I2C_InitStructure.I2C_ClockSpeed = I2C_Speed;
  29.         I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2 ;
  30.         I2C_InitStructure.I2C_Mode = I2C_Mode_I2C ;//I2C 模式
  31.         I2C_InitStructure.I2C_OwnAddress1 = I2C_OWN_ADDR;
  32.        
  33.         I2C_Init(I2C, &I2C_InitStructure);
  34.        
  35.         I2C_Cmd(I2C,ENABLE);
  36. }
 楼主| yellow555 发表于 2023-5-27 00:21 | 显示全部楼层
等待EEPROM写入完成代码
  1. /* 等待RRPROM内部写入完成 */
  2. static void Wait_For_EEPROM(void)
  3. {
  4.         uint32_t check_count = 0xFFFFF;
  5.         uint32_t count_wait  = 0xFFFF;
  6.         while(check_count--)
  7.         {
  8.                         /* 产生一个起始信号 */
  9.                 I2C_GenerateSTART(I2C ,ENABLE);
  10.                
  11.                 /* 等待EV5事件,直到成功 */
  12.                 while(I2C_CheckEvent(I2C, I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS);
  13.                
  14.                 /* 发送EEPROM设备的地址,设置为写方向 */
  15.                 I2C_Send7bitAddress(I2C, EEPROM_I2C_ADDR, I2C_Direction_Transmitter );
  16.                
  17.                 /* 重置TIME_OUT */
  18.                 count_wait = 0xFFFF;
  19.                 /* 等待EV6事件,直到成功,说明EEPROM写完了,可以相应我们了 */
  20.                 while(count_wait --)//只有RRPROM写完了,我们寻址,他才会响应我们,否则是不会响应我们的
  21.                 {
  22.                         /* 若检测到响应,说明内部写时序完成,跳出等待函数 */
  23.                         if(I2C_CheckEvent(I2C, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) == SUCCESS )
  24.                         {
  25.                                 I2C_GenerateSTOP(I2C, ENABLE );//产生一个停止信号
  26.                                 return ;
  27.                         }
  28.                 }
  29.         }
  30.         I2
 楼主| yellow555 发表于 2023-5-27 00:21 | 显示全部楼层
写入一个字节代码
  1. /* 向EEPROM中ADDR地址写入一个字节的数据 */
  2. void I2C_Byte_Write(uint8_t ADDR , uint8_t Data)
  3. {
  4.         I2C_GenerateSTART(I2C, ENABLE);//产生一个起始信号

  5.         while(I2C_CheckEvent(I2C, I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS);//等待EV5事件产生
  6.        
  7.         I2C_Send7bitAddress(I2C, EEPROM_I2C_ADDR, I2C_Direction_Transmitter);//发送地址,为写方向

  8.         while(I2C_CheckEvent(I2C, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) != SUCCESS);//等待EV6事件产生

  9.         I2C_SendData(I2C,  ADDR);//发送存放数据的地址

  10.         /*(EV8事件是用来检测数据是否正在发送,发送完成会产生一个EV8_2事件,因此我们检测该事件)*/
  11.         while(I2C_CheckEvent(I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS);//等待EV8_2事件产生
  12.        
  13.         I2C_SendData(I2C,  Data);//发送存放数据

  14.         while(I2C_CheckEvent(I2C,I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS);//等待EV8_2事件产生

  15.         I2C_GenerateSTOP(I2C, ENABLE);

  16.         Wait_For_EEPROM();//等待EEPROM写入完成
  17. }
 楼主| yellow555 发表于 2023-5-27 00:22 | 显示全部楼层
读一个字节代码
  1. /* 随机读取EEPROM中ADDR地址的数据,用Data返回 */
  2. void I2C_Random_Read(uint8_t ADDR , uint8_t *Data)
  3. {
  4.         /* 第一个起始信号用于写入我们要读取数据的地址 */
  5.         I2C_GenerateSTART(I2C, ENABLE);//产生一个起始信号

  6.         while(I2C_CheckEvent(I2C, I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS);//等待EV5事件产生
  7.        
  8.         I2C_Send7bitAddress(I2C, EEPROM_I2C_ADDR, I2C_Direction_Transmitter);//发送地址,为写方向

  9.         while(I2C_CheckEvent(I2C, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) != SUCCESS);//等待EV6事件产生
  10.        
  11.         I2C_SendData(I2C,  ADDR);//发送存放数据的地址

  12.         /*(EV8事件是用来检测数据是否正在发送,发送完成会产生一个EV8_2事件,因此我们检测该事件)*/
  13.         while(I2C_CheckEvent(I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS);//等待EV8_2事件产生
  14.        
  15.        
  16.         /* 产生第二个信号,用于读取数据 */
  17.         I2C_GenerateSTART(I2C, ENABLE);//产生一个起始信号

  18.         while(I2C_CheckEvent(I2C, I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS);//等待EV5事件产生
  19.        
  20.         I2C_Send7bitAddress(I2C, EEPROM_I2C_ADDR, I2C_Direction_Receiver);//发送地址,为写方向
  21.        
  22.         I2C_AcknowledgeConfig(I2C, DISABLE);//非应答信号
  23.        
  24.         while(I2C_CheckEvent(I2C, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED) != SUCCESS);//等待EV6事件产生

  25.         /* 先发非应答信号,然后在等待数据传送到DR寄存器,否则在停止前会接收到错误的数据 */
  26.         while(I2C_CheckEvent(I2C, I2C_EVENT_MASTER_BYTE_RECEIVED) != SUCCESS);//等待EV7事件产生
  27.        
  28.         *Data = I2C_ReceiveData(I2C);
  29.        
  30.         I2C_GenerateSTOP(I2C, ENABLE);
  31. }
 楼主| yellow555 发表于 2023-5-27 00:22 | 显示全部楼层
   注意:在接收数据完成时要提前发送非应答数据 。

907446470dcb43db64.png
 楼主| yellow555 发表于 2023-5-27 00:22 | 显示全部楼层
写一页数据代码
  1. /* 写入一页的数据,size不可大于8,否则数据错误 */
  2. void I2C_Page_Write(uint8_t ADDR,uint8_t *Data,uint16_t size)
  3. {
  4.         I2C_GenerateSTART(I2C, ENABLE);//产生一个起始信号

  5.         while(I2C_CheckEvent(I2C, I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS);//等待EV5事件产生
  6.        
  7.         I2C_Send7bitAddress(I2C, EEPROM_I2C_ADDR, I2C_Direction_Transmitter);//发送地址,为写方向

  8.         while(I2C_CheckEvent(I2C, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) != SUCCESS);//等待EV6事件产生

  9.         I2C_SendData(I2C,  ADDR);//发送存放数据的地址

  10.         /*(EV8事件是用来检测数据是否正在发送,发送完成会产生一个EV8_2事件,因此我们检测该事件)*/
  11.         while(I2C_CheckEvent(I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS);//等待EV8_2事件产生
  12.        
  13.         while(size--)
  14.         {
  15.                 I2C_SendData(I2C, *Data);//发送存放数据的地址
  16.                
  17.                 while(I2C_CheckEvent(I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS);//等待EV8_2事件产生
  18.                
  19.                 Data++;
  20.         }
  21.        
  22.         I2C_GenerateSTOP(I2C, ENABLE);

  23.         Wait_For_EEPROM();//等待EEPROM写入完成
  24. }
 楼主| yellow555 发表于 2023-5-27 00:22 | 显示全部楼层
写大量数据代码
  1. /* 批量写入数据 */
  2. void I2C_Buffer_Write(uint8_t ADDR , uint8_t *Data,uint16_t size)
  3. {
  4.         uint16_t num_of_page = size / EEPROM_PAGE_SIZE;//算出来有几页
  5.         uint16_t single_byte = size % EEPROM_PAGE_SIZE;//算出来有几个单个的字节数
  6.         uint16_t single_addr = ADDR % EEPROM_PAGE_SIZE;//算出来这一页有几个位置
  7.        
  8.         if(single_addr == 0)//说明地址对齐
  9.         {
  10.                 while(num_of_page--)
  11.                 {
  12.                         I2C_Page_Write(ADDR,Data,EEPROM_PAGE_SIZE) ;
  13.                        
  14.                         ADDR += EEPROM_PAGE_SIZE;
  15.                         Data += EEPROM_PAGE_SIZE;
  16.                 }
  17.                 I2C_Page_Write(ADDR,Data,single_byte) ;
  18.         }
  19.         else
  20.         {
  21.                 /* 第一次写入的数据字节数(为了让地址对齐) */
  22.                 uint16_t first_write_size = EEPROM_PAGE_SIZE - single_addr;//算出来这一页还差几个位置不全一页
  23.                
  24.                 I2C_Page_Write(ADDR,Data,first_write_size);//补全
  25.                
  26.                 uint16_t surplus = size - first_write_size;//剩余的数据字节数
  27.                 Data += first_write_size;
  28.                 ADDR += first_write_size;
  29.                
  30.                 num_of_page = surplus / EEPROM_PAGE_SIZE;//算出来有几页
  31.                 single_byte = surplus % EEPROM_PAGE_SIZE;//算出来有几个单个的字节数
  32.                
  33.                 while(num_of_page--)
  34.                 {
  35.                         I2C_Page_Write(ADDR,Data,EEPROM_PAGE_SIZE) ;
  36.                        
  37.                         ADDR += EEPROM_PAGE_SIZE;
  38.                         Data += EEPROM_PAGE_SIZE;
  39.                 }
  40.                 I2C_Page_Write(ADDR,Data,single_byte) ;
  41.         }
  42. }
 楼主| yellow555 发表于 2023-5-27 00:22 | 显示全部楼层
读大量数据代码
  1. /* 批量读取数据 */
  2. void I2C_Buffer_Read(uint8_t ADDR , uint8_t *Data,uint16_t size)
  3. {
  4.         I2C_GenerateSTART(I2C, ENABLE);//产生一个起始信号

  5.         while(I2C_CheckEvent(I2C, I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS);//等待EV5事件产生
  6.        
  7.         I2C_Send7bitAddress(I2C, EEPROM_I2C_ADDR, I2C_Direction_Transmitter);//发送地址,为写方向

  8.         while(I2C_CheckEvent(I2C, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) != SUCCESS);//等待EV6事件产生
  9.        
  10.         I2C_SendData(I2C,  ADDR);//发送存放数据的地址

  11.         /*(EV8事件是用来检测数据是否正在发送,发送完成会产生一个EV8_2事件,因此我们检测该事件)*/
  12.         while(I2C_CheckEvent(I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS);//等待EV8_2事件产生
  13.        
  14.        
  15.         /* 产生第二个信号,用于读取数据 */
  16.         I2C_GenerateSTART(I2C, ENABLE);//产生一个起始信号

  17.         while(I2C_CheckEvent(I2C, I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS);//等待EV5事件产生
  18.        
  19.         I2C_Send7bitAddress(I2C, EEPROM_I2C_ADDR, I2C_Direction_Receiver);//发送地址,为写方向
  20.        
  21.         while(I2C_CheckEvent(I2C, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED) != SUCCESS);//等待EV6事件产生
  22.        
  23.         while(size--)
  24.         {
  25.                 if(size == 0)
  26.                         I2C_AcknowledgeConfig(I2C, DISABLE);//非应答信号
  27.                 else
  28.                         I2C_AcknowledgeConfig(I2C, ENABLE);//应答信号
  29.                 /* 读取数据 */
  30.                 while(I2C_CheckEvent(I2C, I2C_EVENT_MASTER_BYTE_RECEIVED) != SUCCESS);//等待EV7事件产生
  31.                        
  32.                 *Data++ = I2C_ReceiveData(I2C);
  33.         }
  34.        
  35.         I2C_GenerateSTOP(I2C, ENABLE);
  36. }
 楼主| yellow555 发表于 2023-5-27 00:23 | 显示全部楼层
主函数中(串口通讯请自行配置)
  1. int main(void)
  2. {
  3.         uint8_t Tx_Buff[256];
  4.         uint8_t Rx_Buff[256];
  5.        
  6.         USART_Config();
  7.        
  8.         I2C_Config();
  9.        

  10.         for(int i = 0;i<TEST_SIZE;i++)//给buff赋值
  11.         {
  12.                 Tx_Buff[i] = i;
  13.         }
  14.        
  15.         I2C_Buffer_Write(0x00 ,Tx_Buff,256);
  16.         I2C_Buffer_Read(0x00 , Rx_Buff,256);
  17.        
  18.         for(int i = 0;i<TEST_SIZE;i++)//给buff赋值
  19.         {
  20.                 printf("0x%02x ",Rx_Buff[i]);
  21.         }

  22.         printf("\n一切OK\n");
  23.        


  24.         while (1)
  25.         {
  26.         }
  27. }
 楼主| yellow555 发表于 2023-5-27 00:23 | 显示全部楼层
实验结果

263186470dcf4c03ce.png
您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 在线客服 返回列表 返回顶部