打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
STM32的I2C的原理与使用(24C02附代码)
userphoto

2022.12.06 浙江

关注

一、IIC的定义

    I2C(IIC,Inter-Integrated Circuit),两线式串行总线,由PHILIPS开发用于连接微控制器及其外围设备。IIC是一种多向控制总线,就是说多个芯片可以连接到同一总线结构下,同时每个芯片都可以作为实施数据传输的控制源。这种方式简化信号传输总线。

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

IIC是半双工通信方式。


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

2)多设备的使用举例

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


三、I2C协议

  • 空闲状态
  • 开始信号
  • 停止信号
  • 应答信号
  • 数据的有效性
  • 数据传输

我们以斗地主为例:

1)空闲状态

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

2)起始信号与停止信号

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

3)应答信号ACK

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

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

4)数据有效性

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

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

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. //产生IIC停止信号
  31. void IIC_Stop(void)
  32. {
  33. SDA_OUT();//sda线输出
  34. IIC_SCL=0;
  35. IIC_SDA=0;//STOP:when CLK is high DATA change form low to high
  36. delay_us(4);
  37. IIC_SCL=1;
  38. IIC_SDA=1;//发送I2C总线结束信号
  39. delay_us(4);
  40. }
  41. //等待应答信号到来
  42. //返回值:1,接收应答失败
  43. // 0,接收应答成功
  44. u8 IIC_Wait_Ack(void)
  45. {
  46. u8 ucErrTime=0;
  47. SDA_IN(); //SDA设置为输入
  48. IIC_SDA=1;delay_us(1);
  49. IIC_SCL=1;delay_us(1);
  50. while(READ_SDA)
  51. {
  52. ucErrTime++;
  53. if(ucErrTime>250)
  54. {
  55. IIC_Stop();
  56. return 1;
  57. }
  58. }
  59. IIC_SCL=0;//时钟输出0
  60. return 0;
  61. }
  62. //产生ACK应答
  63. void IIC_Ack(void)
  64. {
  65. IIC_SCL=0;
  66. SDA_OUT();
  67. IIC_SDA=0;
  68. delay_us(2);
  69. IIC_SCL=1;
  70. delay_us(2);
  71. IIC_SCL=0;
  72. }
  73. //不产生ACK应答
  74. void IIC_NAck(void)
  75. {
  76. IIC_SCL=0;
  77. SDA_OUT();
  78. IIC_SDA=1;
  79. delay_us(2);
  80. IIC_SCL=1;
  81. delay_us(2);
  82. IIC_SCL=0;
  83. }
  84. //IIC发送一个字节
  85. //返回从机有无应答
  86. //1,有应答
  87. //0,无应答
  88. void IIC_Send_Byte(u8 txd)
  89. {
  90. u8 t;
  91. SDA_OUT();
  92. IIC_SCL=0;//拉低时钟开始数据传输
  93. for(t=0;t<8;t++)
  94. {
  95. IIC_SDA=(txd&0x80)>>7;
  96. txd<<=1;
  97. delay_us(2); //对TEA5767这三个延时都是必须的
  98. IIC_SCL=1;
  99. delay_us(2);
  100. IIC_SCL=0;
  101. delay_us(2);
  102. }
  103. }
  104. //读1个字节,ack=1时,发送ACK,ack=0,发送nACK
  105. u8 IIC_Read_Byte(unsigned char ack)
  106. {
  107. unsigned char i,receive=0;
  108. SDA_IN();//SDA设置为输入
  109. for(i=0;i<8;i++ )
  110. {
  111. IIC_SCL=0;
  112. delay_us(2);
  113. IIC_SCL=1;
  114. receive<<=1;
  115. if(READ_SDA)receive++;
  116. delay_us(1);
  117. }
  118. if (!ack)
  119. IIC_NAck();//发送nACK
  120. else
  121. IIC_Ack(); //发送ACK
  122. return receive;
  123. }
  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

芯片图:

这里的A0=A1=A2=0

 

24C02读写字节时序

写:

读:

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. u8 AT24CXX_ReadOneByte(u16 ReadAddr); //指定地址读取一个字节
  17. void AT24CXX_WriteOneByte(u16 WriteAddr,u8 DataToWrite); //指定地址写入一个字节
  18. void AT24CXX_WriteLenByte(u16 WriteAddr,u32 DataToWrite,u8 Len);//指定地址开始写入指定长度的数据
  19. u32 AT24CXX_ReadLenByte(u16 ReadAddr,u8 Len); //指定地址开始读取指定长度数据
  20. void AT24CXX_Write(u16 WriteAddr,u8 *pBuffer,u16 NumToWrite); //从指定地址开始写入指定长度的数据
  21. void AT24CXX_Read(u16 ReadAddr,u8 *pBuffer,u16 NumToRead); //从指定地址开始读出指定长度的数据
  22. u8 AT24CXX_Check(void); //检查器件
  23. void AT24CXX_Init(void); //初始化IIC
  24. #endif

  1. main.c:
  2. #include "sys.h"
  3. #include "delay.h"
  4. #include "usart.h"
  5. #include "led.h"
  6. #include "lcd.h"
  7. #include "24cxx.h"
  8. #include "key.h"
  9. //要写入到24c02的字符串数组
  10. const u8 TEXT_Buffer[]={"Explorer STM32F4 IIC TEST"};
  11. #define SIZE sizeof(TEXT_Buffer)
  12. int main(void)
  13. {
  14. u8 key;
  15. u16 i=0;
  16. u8 datatemp[SIZE];
  17. NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
  18. delay_init(168); //初始化延时函数
  19. uart_init(115200); //初始化串口波特率为115200
  20. LED_Init(); //初始化LED
  21. KEY_Init(); //按键初始化
  22. AT24CXX_Init(); //IIC初始化
  23. while(AT24CXX_Check())//检测不到24c02
  24. {
  25. printf("24C02 Check Failed!");
  26. delay_ms(500);
  27. printf("Please Check! ");
  28. delay_ms(500);
  29. LED0=!LED0;//DS0闪烁
  30. }
  31. while(1)
  32. {
  33. key=KEY_Scan(0);
  34. if(key==KEY1_PRES)//KEY1按下,写入24C02
  35. {
  36. printf("Start Write 24C02....");
  37. AT24CXX_Write(0,(u8*)TEXT_Buffer,SIZE);
  38. printf("24C02 Write Finished!"); //提示传送完成
  39. }
  40. if(key==KEY0_PRES)//KEY0按下,读取字符串并显示
  41. {
  42. printf("Start Read 24C02.... ");
  43. AT24CXX_Read(0,datatemp,SIZE);
  44. printf("The Data Readed Is: \r\n ");//提示传送完成
  45. printf("%s\r\n ",datatemp);
  46. }
  47. }
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
C语言使用面向对象实现IIC驱动
IIC通信协议代码分享及调试过程中的一些小误区分享
SHT30使用的学习过程2 SHT30驱动程序
IIC详解,包括原理、过程,最后一步步教你实现IIC
MSP430F149模拟IIC读写24C02程序
linux e2prom 驱动代码
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服