程序项目代做,有需求私信(小程序、网站、爬虫、电路板设计、驱动、应用程序开发、毕设疑难问题处理等)

STM32F103 I2C软件模拟(AT24C02)

有关I2C通信协议我们在《通信协议-I2C》已经进行了详细的介绍,因此这一节不再重复介绍。

一、软件/硬件I2C

想要控制STM32产生I2C方式的通讯,可以采用软件模拟或硬件I2C这两种方式。

1.1 软件模拟

所谓软件模拟,即直接使用CPU内核按照I2C协议的要求控制GPIO输出高低电平。

如控制产生I2C的起始信号时, 先控制作为SCL线的GPIO引脚输出高电平, 然后控制作为SDA线的GPIO引脚在此期间完成由高电平至低电平的切换,最后再控制SCL线切换为低电平,这样就输出了一个标准的I2C起始信号。

1.2 硬件I2C

硬件I2C是指直接利用STM32芯片中的硬件I2C外设,该硬件I2C外设跟USART串口外设类似,只要配置好对应的寄存器, 外设就会产生标准串口协议的时序。

使用它的I2C外设则可以方便地通过外设寄存器产生I2C协议方式的通讯,如初始化好I2C外设后, 只需要把某寄存器位置1,那么外设就会控制对应的SCLSDA线自动产生I2C起始信号,而不需要内核直接控制引脚的电平。

相对来说,硬件I2C直接使用外设来控制引脚,可以减轻CPU的负担。不过使用硬件I2C时必须使用某些固定的引脚作为SCLSDA, 软件模拟I2C则可以使用任意GPIO引脚,相对比较灵活。

在本开发板中,由于STM32F103RCT6芯片引脚较少,资源比较紧张, 在设计硬件时不方便使用硬件I2C指定的引脚连接外部设备,所以在控制程序上使用软件模拟I2C的方式。

二、AT24C02

2.1 回顾

有关AT24C02)可以参考《通信协议-I2C》小节中的介绍,在该篇文章中我们,介绍了AT24C0x的读写命令。

后续我们又在《linux驱动移植-I2C驱动移植(OLED SSD1306》中介绍了I2C设备驱动的编写,并以OLED SSD1306作为实现设备。

2.2 硬件接线

我所使用的STM32F103RTC6开发板上有一块数据存储器,采用的是AT24C02芯片;

AT24C02是一个2K位串行可擦可编程只读存储器(EEPRAM),内部含有2568位字节,AT24C02有一个16字节页写缓冲区,该器件通过I2C总线接口进行操作,有一个专门的写保护功能。

其中I2C_SCL连接到处理器的PC12引脚,I2C_SDA连接到处理器的PC11引脚。

三、AT24C02源码实现

3.1 I2C软件模拟

3.1.1 I2C初始化

配置PC11~PC12引脚,均配置为通用推挽输出,最大速度50MHZ,输出高电平;

//宏定义I2C端口***********************************************
#define I2C_SCL     PCout(12)
#define I2C_SDA     PCout(11)

//宏定义SDA数据方向********************************************
#define SDA_OUT()   gpio_init(PC11,GPO_PUSH_PULL_50,HIGH)  
#define SCL_OUT()   gpio_init(PC12,GPO_PUSH_PULL_50,HIGH)

//宏定义读取SDA数据*******************************************
#define Read_SDA    PCin(11)

void SDA_IN(void)   
{  
   gpio_init(PC11, GPI_DOWN,HIGH); // 输入下拉
   PCout(11)=0;
}
/**********************************************************************************
 *
 *		Description:I2C初始化
 *
 **********************************************************************************/
void I2C_Init(void)			  //I2C初始化
{
  SDA_OUT();          //SDA设置为输出       
  SCL_OUT();		  //SCL设置为输出
  I2C_SCL=1;
  I2C_SDA=1;  
}
3.1.2 起始信号

I2C通信的起始信号由主设备发起,SCL保持高电平,SDA由高电平变为低电平;只有在起始信号发送之后,其它数据才有效;

/**********************************************************************************
 *
 *		Description:I2C起始信号
 *                   SCL=1;  SDA:1->0   
 *
 **********************************************************************************/
void  I2C_Start(void)		 //I2C起始信号
{
   SDA_OUT();
   I2C_SDA =1;				   
   delay_us(5);
   I2C_SCL =1;
   delay_us(5);
   I2C_SDA =0;
   delay_us(5);
   I2C_SCL =0;
}
3.1.3 终止信号

I2C通信的终止信号由主设备发起,SCL保持高电平,SDA由低电平变为高低电平;随着终止信号的出现,所有外部操作就结束;

/**********************************************************************************
 *
 *		Description:I2C终止信号
 *                  SCL=1;  SDA:0->1   
 *
 **********************************************************************************/
void I2C_Stop(void)		   //I2C终止信号
{
   SDA_OUT();
   I2C_SDA =0;
   delay_us(5);
   I2C_SCL =1;
   delay_us(5);
   I2C_SDA =1;
   delay_us(5);
   I2C_SCL =0;
}
3.1.4 应答信号

I2C总线在进行数据传送时,传送的字节数没有限制,但是每个字节长度必须为8位。

数据传送过程中,先传送最高位(MSB),接收端在收到有效数据后向对方相应的信号,发送端每发送一个字节数据(8位),在第9个始终周期释放数据线去接收对方的应答;因此一帧数据共有9位;

  • SDA位低电平位有效应答(ACK),表示接收端已经接收到数据;
  • SDA是高电平位无效应答(NAK),表示接收端没有接收成功;

主设备发送完数据需要等待从设备的应答,GPIO模拟:

/**********************************************************************************
 *
 *		Description:I2C(主机)等待来自从机的应答信号
 *        parameter:返回0:接受失败   1:接收成功  
 *
 **********************************************************************************/
u8 I2C_WaitAck(void)		    //等待来自从机的应答信号
{
  u16 time;
  SDA_IN();
  I2C_SCL =0;
  delay_us(5);
  I2C_SDA =1;
  delay_us(5);
  I2C_SCL =1;
  delay_us(5);
  while(Read_SDA)
  {
    time++;
	if(time>=2500)
	{
	  I2C_Stop();
	  return 0;  
	}
  }
  I2C_SCL =0;
  return 1;
}

主设备接收到从设备发送的数据后,需要向从设备发送方发送应答,GPIO模拟:

/**********************************************************************************
 *
 *		Description:I2C(主机)产生应答信号
 *        parameter: ack:0不应答  1:应答        
 *
 **********************************************************************************/
void I2C_Ack(u8 ack)			//产生应答信号
{
   SDA_OUT();
   I2C_SCL =0;
   delay_us(5);
   if(ack)
      I2C_SDA =0;
   else
      I2C_SDA =1;
   I2C_SCL =1;
   delay_us(5);
   I2C_SCL =0;
   delay_us(5);
}
3.1.5 有效数据

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

因为当SCL是高电平时,数据线SDA的变化被规定为控制命令(也就是前面的起始信号和终止信号)。

主设备向从设备发送发送一个字节数据,GPIO模型模拟:

/**********************************************************************************
 *
 *		Description:I2C写一个字节数据
 *             byte:一个字节的数据   
 *
 **********************************************************************************/
void I2C_WriteData(u8 byte)			 //I2C写一个字节数据
{
  u8 i=0;
  SDA_OUT();
  I2C_SCL =0;
  for(i=0;i<8;i++)
  {
    I2C_SDA =(byte&0x80)>>7;
	byte <<=1;
	delay_us(5);
	I2C_SCL =1;
	delay_us(5);
	I2C_SCL =0;
	delay_us(5);
  }
}

主设备从从设备读取一个字节数据,GPIO模拟:

/**********************************************************************************
 *
 *		Description:I2C(主机)读取一个数据 
 *        parameter:返回读取的数据  
 *
 **********************************************************************************/
u8 I2C_ReadData(void)			   //I2C读取一个数据 
{
  u8 i=0;
  u8 data=0;
  SDA_IN();
  I2C_SCL =0;
  for(i=0;i<8;i++)
  {
	I2C_SCL =1;
	data <<=1;
	data |=(u8)Read_SDA;
	delay_us(5);
	I2C_SCL =0;
	delay_us(5);
  }
  return data;
}

3.2 AT24C02

3.2.1 按字节写
/**********************************************************************************************
 *
 *   function:向24c02的address地址中写入一字节数据  
 *    address:0~0xFF        
 *
 **********************************************************************************************/
void Write24c02(u8 address,u8 byte)
{
   I2C_Start();
   I2C_WriteData(0xA0);
   I2C_WaitAck();
   I2C_WriteData(address);
   I2C_WaitAck();
   I2C_WriteData(byte);
   I2C_WaitAck();
   I2C_Stop();
   delay_ms(10);       //这个延时一定要足够长,否则会出错。因为24c02在从sda上取得数据后,还需要一定时间的烧录过程。
}

亲爱的读者和支持者们,自动博客加入了打赏功能,陆陆续续收到了各位老铁的打赏。在此,我想由衷地感谢每一位对我们博客的支持和打赏。你们的慷慨与支持,是我们前行的动力与源泉。

日期姓名金额
2023-09-06*源19
2023-09-11*朝科88
2023-09-21*号5
2023-09-16*真60
2023-10-26*通9.9
2023-11-04*慎0.66
2023-11-24*恩0.01
2023-12-30I*B1
2024-01-28*兴20
2024-02-01QYing20
2024-02-11*督6
2024-02-18一*x1
2024-02-20c*l18.88
2024-01-01*I5
2024-04-08*程150
2024-04-18*超20
2024-04-26.*V30
2024-05-08D*W5
2024-05-29*辉20
2024-05-30*雄10
2024-06-08*:10
2024-06-23小狮子666
2024-06-28*s6.66
2024-06-29*炼1
2024-06-30*!1
2024-07-08*方20
2024-07-18A*16.66
2024-07-31*北12
2024-08-13*基1
2024-08-23n*s2
2024-09-02*源50
2024-09-04*J2
2024-09-06*强8.8
2024-09-09*波1
2024-09-10*口1
2024-09-10*波1
2024-09-12*波10
2024-09-18*明1.68
2024-09-26B*h10
2024-09-3010
2024-10-02M*i1
2024-10-14*朋10
2024-10-22*海10
2024-10-23*南10
2024-10-26*节6.66
2024-10-27*o5
2024-10-28W*F6.66
2024-10-29R*n6.66
2024-11-02*球6
2024-11-021*鑫6.66
2024-11-25*沙5
2024-11-29C*n2.88
posted @   大奥特曼打小怪兽  阅读(451)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
如果有任何技术小问题,欢迎大家交流沟通,共同进步

公告 & 打赏

>>

欢迎打赏支持我 ^_^

最新公告

程序项目代做,有需求私信(小程序、网站、爬虫、电路板设计、驱动、应用程序开发、毕设疑难问题处理等)。

了解更多

点击右上角即可分享
微信分享提示