你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【经验分享】STM32的I2C的原理与使用(24C02附代码)

[复制链接]
STMCU小助手 发布时间:2022-5-16 11:39
一、IIC的定义
    I2C(IIC,Inter-Integrated Circuit),两线式串行总线,由PHILIPS开发用于连接微控制器及其外围设备。IIC是一种多向控制总线,就是说多个芯片可以连接到同一总线结构下,同时每个芯片都可以作为实施数据传输的控制源。这种方式简化信号传输总线。

    它由数据线SDA和时钟SCL构成的串行总线,接到I2C总线设备上的串行数据SDA都接到总线的SDA上,各设备的时钟线SCL接到总线的SCL上,可发送接收数据。在CPU与被控IC之间、IC与IC之间进行双向传送,高速IIC总线一般可达400kbps以上。

IIC是半双工通信方式。

二、多主机I2C总线系统结构


}M5]PGQMB1%OM2_4Y0~B8MT.png

2)多设备的使用举例

每个接到I2C总线的设备都有一个唯一的地址

5Z9N)E93D7]4(WCF2HDUC[G.png

三、I2C协议
空闲状态
开始信号
停止信号
应答信号
数据的有效性
数据传输
我们以斗地主为例:

20190611223011427.png

1)空闲状态
I2C总线总线的SDA和SCL两条信号线同时处于高电平时,规定为总线的空闲状态。此时各个器件的输出级场效应管均处在截止状态,即释放总线,由两条信号线各自的上拉电阻把电平拉高。

2)起始信号与停止信号
起始信号:当SCL为高期间,SDA由高到低的跳变;启动信号是一种电平跳变时序信号,而不是一个电平信号。
停止信号:当SCL为高期间,SDA由低到高的跳变;停止信号也是一种电平跳变时序信号,而不是一个电平信号。

20190611223045576.png

3)应答信号ACK
发送器每发送一个字节,就在时钟脉冲9期间释放数据线,由接收器反馈一个应答信号。 应答信号为低电平时,规定为有效应答位(ACK简称应答位),表示接收器已经成功地接收了该字节;应答信号为高电平时,规定为非应答位(NACK),一般表示接收器接收该字节没有成功。

对于反馈有效应答位ACK的要求是,接收器在第9个时钟脉冲之前的低电平期间将SDA线拉低,并且确保在该时钟的高电平期间为稳定的低电平。 如果接收器是主控器,则在它收到最后一个字节后,发送一个NACK信号,以通知被控发送器结束数据发送,并释放SDA线,以便主控接收器发送一个停止信号P。

H1}S%IN@1{{B36EYI66SKD7.png

4)数据有效性
I2C总线进行数据传送时,时钟信号为高电平期间,数据线上的数据必须保持稳定,只有在时钟线上的信号为低电平期间,数据线上的高电平或低电平状态才允许变化。

即:数据在SCL的上升沿到来之前就需准备好。并在在下降沿到来之前必须稳定。

28163734-16f9cd046fe34ad89f098aa2d5179f16.png

5)数据的传送
在I2C总线上传送的每一位数据都有一个时钟脉冲相对应(或同步控制),即在SCL串行时钟的配合下,在SDA上逐位地串行传送每一位数据。数据位的传输是边沿触发。

STM32F4的IIC代码如下:
  1. IIC.c
  2. #include "myiic.h"
  3. #include "delay.h"

  4. //初始化IIC
  5. void IIC_Init(void)
  6. {                        
  7.   GPIO_InitTypeDef  GPIO_InitStructure;

  8.   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);//使能GPIOB时钟

  9.   //GPIOB8,B9初始化设置
  10.   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9;
  11.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式
  12.   GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
  13.   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
  14.   GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
  15.   GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化
  16.         IIC_SCL=1;
  17.         IIC_SDA=1;
  18. }

  19. //产生IIC起始信号
  20. void IIC_Start(void)
  21. {
  22.         SDA_OUT();     //sda线输出
  23.         IIC_SDA=1;                    
  24.         IIC_SCL=1;
  25.         delay_us(4);
  26.          IIC_SDA=0;//START:when CLK is high,DATA change form high to low
  27.         delay_us(4);
  28.         IIC_SCL=0;//钳住I2C总线,准备发送或接收数据
  29. }
  30.          
  31. //产生IIC停止信号
  32. void IIC_Stop(void)
  33. {
  34.         SDA_OUT();//sda线输出
  35.         IIC_SCL=0;
  36.         IIC_SDA=0;//STOP:when CLK is high DATA change form low to high
  37.          delay_us(4);
  38.         IIC_SCL=1;
  39.         IIC_SDA=1;//发送I2C总线结束信号
  40.         delay_us(4);                                                                  
  41. }

  42. //等待应答信号到来
  43. //返回值:1,接收应答失败
  44. //        0,接收应答成功
  45. u8 IIC_Wait_Ack(void)
  46. {
  47.         u8 ucErrTime=0;
  48.         SDA_IN();      //SDA设置为输入  
  49.         IIC_SDA=1;delay_us(1);           
  50.         IIC_SCL=1;delay_us(1);         
  51.         while(READ_SDA)
  52.         {
  53.                 ucErrTime++;
  54.                 if(ucErrTime>250)
  55.                 {
  56.                         IIC_Stop();
  57.                         return 1;
  58.                 }
  59.         }
  60.         IIC_SCL=0;//时钟输出0            
  61.         return 0;  
  62. }

  63. //产生ACK应答
  64. void IIC_Ack(void)
  65. {
  66.         IIC_SCL=0;
  67.         SDA_OUT();
  68.         IIC_SDA=0;
  69.         delay_us(2);
  70.         IIC_SCL=1;
  71.         delay_us(2);
  72.         IIC_SCL=0;
  73. }

  74. //不产生ACK应答                    
  75. void IIC_NAck(void)
  76. {
  77.         IIC_SCL=0;
  78.         SDA_OUT();
  79.         IIC_SDA=1;
  80.         delay_us(2);
  81.         IIC_SCL=1;
  82.         delay_us(2);
  83.         IIC_SCL=0;
  84. }
  85.                                                                               
  86. //IIC发送一个字节
  87. //返回从机有无应答
  88. //1,有应答
  89. //0,无应答                          
  90. void IIC_Send_Byte(u8 txd)
  91. {                        
  92.     u8 t;   
  93.         SDA_OUT();            
  94.     IIC_SCL=0;//拉低时钟开始数据传输
  95.     for(t=0;t<8;t++)
  96.     {              
  97.         IIC_SDA=(txd&0x80)>>7;
  98.         txd<<=1;           
  99.                 delay_us(2);   //对TEA5767这三个延时都是必须的
  100.                 IIC_SCL=1;
  101.                 delay_us(2);
  102.                 IIC_SCL=0;        
  103.                 delay_us(2);
  104.     }         
  105. }
  106.             
  107. //读1个字节,ack=1时,发送ACK,ack=0,发送nACK   
  108. u8 IIC_Read_Byte(unsigned char ack)
  109. {
  110.         unsigned char i,receive=0;
  111.         SDA_IN();//SDA设置为输入
  112.     for(i=0;i<8;i++ )
  113.         {
  114.         IIC_SCL=0;
  115.         delay_us(2);
  116.                 IIC_SCL=1;
  117.         receive<<=1;
  118.         if(READ_SDA)receive++;   
  119.                 delay_us(1);
  120.     }                                         
  121.     if (!ack)
  122.         IIC_NAck();//发送nACK
  123.     else
  124.         IIC_Ack(); //发送ACK   
  125.     return receive;
  126. }
复制代码


  1. IIC.h

  2. #ifndef __MYIIC_H
  3. #define __MYIIC_H
  4. #include "sys.h"

  5. //IO方向设置
  6. #define SDA_IN()  {GPIOB->MODER&=~(3<<(9*2));GPIOB->MODER|=0<<9*2;}        //PB9输入模式
  7. #define SDA_OUT() {GPIOB->MODER&=~(3<<(9*2));GPIOB->MODER|=1<<9*2;} //PB9输出模式
  8. //IO操作函数         
  9. #define IIC_SCL    PBout(8) //SCL
  10. #define IIC_SDA    PBout(9) //SDA         
  11. #define READ_SDA   PBin(9)  //输入SDA

  12. //IIC所有操作函数
  13. void IIC_Init(void);                //初始化IIC的IO口                                 
  14. void IIC_Start(void);                                //发送IIC开始信号
  15. void IIC_Stop(void);                                  //发送IIC停止信号
  16. void IIC_Send_Byte(u8 txd);                        //IIC发送一个字节
  17. u8 IIC_Read_Byte(unsigned char ack);//IIC读取一个字节
  18. u8 IIC_Wait_Ack(void);                                 //IIC等待ACK信号
  19. void IIC_Ack(void);                                        //IIC发送ACK信号
  20. void IIC_NAck(void);                                //IIC不发送ACK信号

  21. void IIC_Write_One_Byte(u8 daddr,u8 addr,u8 data);
  22. u8 IIC_Read_One_Byte(u8 daddr,u8 addr);         

  23. #endif
复制代码

四、EEPROM(24C02)的使用
总容量是256(2K/8)个字节。接口:IIC

芯片图:
20190611223242512.png

这里的A0=A1=A2=0

JS`5JXR7QVA6E`HPDC4A2[S.png

`Y65BNI$J]2G}XA9(LU07`Q.png

24C02读写字节时序

写:

51VD~3~B`7`EF45L0Z3KVVI.png

读:

20190611223436818.png

24C02的代码如下:
  1. 24cxx.c:
  2. #include "24cxx.h"
  3. #include "delay.h"                                 

  4. //初始化IIC接口
  5. void AT24CXX_Init(void)
  6. {
  7.         IIC_Init();//IIC初始化
  8. }
  9. //在AT24CXX指定地址读出一个数据
  10. //ReadAddr:开始读数的地址  
  11. //返回值  :读到的数据
  12. u8 AT24CXX_ReadOneByte(u16 ReadAddr)
  13. {                                 
  14.         u8 temp=0;                                                                                                                                                               
  15.     IIC_Start();  
  16.         if(EE_TYPE>AT24C16)
  17.         {
  18.                 IIC_Send_Byte(0XA0);           //发送写命令
  19.                 IIC_Wait_Ack();
  20.                 IIC_Send_Byte(ReadAddr>>8);//发送高地址            
  21.         }else IIC_Send_Byte(0XA0+((ReadAddr/256)<<1));   //发送器件地址0XA0,写数据            
  22.         IIC_Wait_Ack();
  23.     IIC_Send_Byte(ReadAddr%256);   //发送低地址
  24.         IIC_Wait_Ack();            
  25.         IIC_Start();                     
  26.         IIC_Send_Byte(0XA1);           //进入接收模式                           
  27.         IIC_Wait_Ack();         
  28.     temp=IIC_Read_Byte(0);                  
  29.     IIC_Stop();//产生一个停止条件            
  30.         return temp;
  31. }
  32. //在AT24CXX指定地址写入一个数据
  33. //WriteAddr  :写入数据的目的地址   
  34. //DataToWrite:要写入的数据
  35. void AT24CXX_WriteOneByte(u16 WriteAddr,u8 DataToWrite)
  36. {                                                                                                                                                                                          
  37.     IIC_Start();  
  38.         if(EE_TYPE>AT24C16)
  39.         {
  40.                 IIC_Send_Byte(0XA0);            //发送写命令
  41.                 IIC_Wait_Ack();
  42.                 IIC_Send_Byte(WriteAddr>>8);//发送高地址         
  43.         }else IIC_Send_Byte(0XA0+((WriteAddr/256)<<1));   //发送器件地址0XA0,写数据         
  44.         IIC_Wait_Ack();           
  45.     IIC_Send_Byte(WriteAddr%256);   //发送低地址
  46.         IIC_Wait_Ack();                                                                                                               
  47.         IIC_Send_Byte(DataToWrite);     //发送字节                                                           
  48.         IIC_Wait_Ack();                                 
  49.     IIC_Stop();//产生一个停止条件
  50.         delay_ms(10);         
  51. }
  52. //在AT24CXX里面的指定地址开始写入长度为Len的数据
  53. //该函数用于写入16bit或者32bit的数据.
  54. //WriteAddr  :开始写入的地址  
  55. //DataToWrite:数据数组首地址
  56. //Len        :要写入数据的长度2,4
  57. void AT24CXX_WriteLenByte(u16 WriteAddr,u32 DataToWrite,u8 Len)
  58. {         
  59.         u8 t;
  60.         for(t=0;t<Len;t++)
  61.         {
  62.                 AT24CXX_WriteOneByte(WriteAddr+t,(DataToWrite>>(8*t))&0xff);
  63.         }                                                                                                   
  64. }

  65. //在AT24CXX里面的指定地址开始读出长度为Len的数据
  66. //该函数用于读出16bit或者32bit的数据.
  67. //ReadAddr   :开始读出的地址
  68. //返回值     :数据
  69. //Len        :要读出数据的长度2,4
  70. u32 AT24CXX_ReadLenByte(u16 ReadAddr,u8 Len)
  71. {         
  72.         u8 t;
  73.         u32 temp=0;
  74.         for(t=0;t<Len;t++)
  75.         {
  76.                 temp<<=8;
  77.                 temp+=AT24CXX_ReadOneByte(ReadAddr+Len-t-1);                                             
  78.         }
  79.         return temp;                                                                                                   
  80. }
  81. //检查AT24CXX是否正常
  82. //这里用了24XX的最后一个地址(255)来存储标志字.
  83. //如果用其他24C系列,这个地址要修改
  84. //返回1:检测失败
  85. //返回0:检测成功
  86. u8 AT24CXX_Check(void)
  87. {
  88.         u8 temp;
  89.         temp=AT24CXX_ReadOneByte(255);//避免每次开机都写AT24CXX                           
  90.         if(temp==0X55)return 0;                  
  91.         else//排除第一次初始化的情况
  92.         {
  93.                 AT24CXX_WriteOneByte(255,0X55);
  94.             temp=AT24CXX_ReadOneByte(255);         
  95.                 if(temp==0X55)return 0;
  96.         }
  97.         return 1;                                                                                          
  98. }

  99. //在AT24CXX里面的指定地址开始读出指定个数的数据
  100. //ReadAddr :开始读出的地址 对24c02为0~255
  101. //pBuffer  :数据数组首地址
  102. //NumToRead:要读出数据的个数
  103. void AT24CXX_Read(u16 ReadAddr,u8 *pBuffer,u16 NumToRead)
  104. {
  105.         while(NumToRead)
  106.         {
  107.                 *pBuffer++=AT24CXX_ReadOneByte(ReadAddr++);        
  108.                 NumToRead--;
  109.         }
  110. }  
  111. //在AT24CXX里面的指定地址开始写入指定个数的数据
  112. //WriteAddr :开始写入的地址 对24c02为0~255
  113. //pBuffer   :数据数组首地址
  114. //NumToWrite:要写入数据的个数
  115. void AT24CXX_Write(u16 WriteAddr,u8 *pBuffer,u16 NumToWrite)
  116. {
  117.         while(NumToWrite--)
  118.         {
  119.                 AT24CXX_WriteOneByte(WriteAddr,*pBuffer);
  120.                 WriteAddr++;
  121.                 pBuffer++;
  122.         }
  123. }
复制代码
  1. 24cxx.h:
  2. #ifndef __24CXX_H
  3. #define __24CXX_H
  4. #include "myiic.h"   

  5. #define AT24C01                127
  6. #define AT24C02                255
  7. #define AT24C04                511
  8. #define AT24C08                1023
  9. #define AT24C16                2047
  10. #define AT24C32                4095
  11. #define AT24C64            8191
  12. #define AT24C128        16383
  13. #define AT24C256        32767  
  14. //Mini STM32开发板使用的是24c02,所以定义EE_TYPE为AT24C02
  15. #define EE_TYPE AT24C02
  16.                                           
  17. u8 AT24CXX_ReadOneByte(u16 ReadAddr);                                                        //指定地址读取一个字节
  18. void AT24CXX_WriteOneByte(u16 WriteAddr,u8 DataToWrite);                //指定地址写入一个字节
  19. void AT24CXX_WriteLenByte(u16 WriteAddr,u32 DataToWrite,u8 Len);//指定地址开始写入指定长度的数据
  20. u32 AT24CXX_ReadLenByte(u16 ReadAddr,u8 Len);                                        //指定地址开始读取指定长度数据
  21. void AT24CXX_Write(u16 WriteAddr,u8 *pBuffer,u16 NumToWrite);        //从指定地址开始写入指定长度的数据
  22. void AT24CXX_Read(u16 ReadAddr,u8 *pBuffer,u16 NumToRead);           //从指定地址开始读出指定长度的数据

  23. u8 AT24CXX_Check(void);  //检查器件
  24. void AT24CXX_Init(void); //初始化IIC
  25. #endif

  26. main.c:
  27. #include "sys.h"
  28. #include "delay.h"
  29. #include "usart.h"
  30. #include "led.h"
  31. #include "lcd.h"
  32. #include "24cxx.h"
  33. #include "key.h"  

  34. //要写入到24c02的字符串数组
  35. const u8 TEXT_Buffer[]={"Explorer STM32F4 IIC TEST"};
  36. #define SIZE sizeof(TEXT_Buffer)         
  37.         
  38. int main(void)
  39. {
  40.         u8 key;
  41.         u16 i=0;
  42.         u8 datatemp[SIZE];        
  43.         NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
  44.         delay_init(168);    //初始化延时函数
  45.         uart_init(115200);        //初始化串口波特率为115200
  46.         
  47.         LED_Init();                                        //初始化LED
  48.         KEY_Init();                                 //按键初始化  
  49.         AT24CXX_Init();                        //IIC初始化
  50.                         
  51.          while(AT24CXX_Check())//检测不到24c02
  52.         {
  53.                 printf("24C02 Check Failed!");
  54.                 delay_ms(500);
  55.                 printf("Please Check!      ");
  56.                 delay_ms(500);
  57.                 LED0=!LED0;//DS0闪烁
  58.         }
  59.         
  60.         while(1)
  61.         {
  62.                 key=KEY_Scan(0);
  63.                 if(key==KEY1_PRES)//KEY1按下,写入24C02
  64.                 {
  65.                         printf("Start Write 24C02....");   
  66.                         AT24CXX_Write(0,(u8*)TEXT_Buffer,SIZE);
  67.                         printf("24C02 Write Finished!");  //提示传送完成
  68.                 }
  69.                 if(key==KEY0_PRES)//KEY0按下,读取字符串并显示
  70.                 {
  71.                         printf("Start Read 24C02.... ");
  72.                         AT24CXX_Read(0,datatemp,SIZE);
  73.                         printf("The Data Readed Is: \r\n ");//提示传送完成
  74.                         printf("%s\r\n ",datatemp);
  75.                 }           
  76.         }            
  77. }
复制代码



收藏 评论0 发布时间:2022-5-16 11:39

举报

0个回答

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版