找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 6914|回复: 3
打印 上一主题 下一主题
收起左侧

IIC通信协议分析--STM32F4驱动温湿度传感器SHT20为例

[复制链接]
跳转到指定楼层
楼主
IIC总线
1       IIC总线概述(文档有图)
I2C总线两线制包括:串行数据SDA(Serial Data)、串行时钟SCL(Serial Clock)。总线必须由主机(通常为微控制器)控制,主机产生串行时钟(SCL)控制总线的传输方向,并产生起始和停止条件。
IIC总线特征:同步串行半双工(同一时刻只能是一种身份)
2       IIC总线拓扑图
                              
SDA:双向串行数据线,数据是一位一位传输,既可以从主机发送到从机,也可以从从机发送到主机
SCL:时钟线(单向),驱动数据线SDA的信号由时钟线SCL提供,只能由主机发送,从机接收
主机:主机产生串行时钟(SCL)控制总线的传输方向,并产生起始条件(占用总线)和停止条件(释放总线)
从机:从机也能发送数据给主机,但是从机永远不会主动给主机发送数据。
主机是主宰
主机是如何找到从机来进行通信的呢?
2.1   主从设备通信
主机如何能找到对应的从机与其进行通信?
每个从机都有一个唯一的器件地址,主机就是通过这个器件地址去找到对应的从机与其通信。
器件地址谁分配?如何分配?(具体查看模块手册)
在IIC总线上,从机的器件地址可以为7位或者10位,一般情况下都是7位器件地址。
在器件地址中包含了固定地址(在高位,不可变)和可编程地址(在低位,可变)
器件地址的位数是由厂家决定
固定地址的位数和内容也是由厂家决定
可编程地址的位数由厂家决定
可编程地址的内容由使用者决定
3       IIC数据帧
UART数据帧格式:起始位+数据位(5~8)+校验位+停止位。
IC数据帧格式:起始条件+数据位(8位)+应答位+停止条件
起始条件:一次通信的开始(主机占用总线)
数据位:从发送器到接收器,连续的8位数据
应答位:当接收器成功接收到发送器的8位数据后,必须应答。0代表应答,1代表非应答。
停止条件:一次通信的结束(主机释放总线,双线电平拉高)
4       标准IIC协议
空闲状态
开始信号
停止信号
应答信号
数据的有效性
数据传输
4.1   空闲状态(都拉高,数据比时钟快)
空闲状态:此时各个器件的输出级场效应管均处在截止状态,即释放总线,由两条信号线各自的上拉电阻把电平拉高。




4.2   起始条件(CD变低)和停止信号(CD变高、都高)
  
起始信号:当SCL为高期间,SDA由高到低的跳变;启动信号是一种电平跳变时序信号,而不是一个电平信号。
停止信号:当SCL为高期间,SDA由低到高的跳变;停止信号也是一种电平跳变时序信号,而不是一个电平信号。
起始条件伪代码
  
SCL=1
  
SDA=1    //起始前都是高电平
  
//延时,起始条件建立时间
  
SDA=0//SDA变低,产生起始条件
  
//延时,起始条件的保持时间
  
SCL=0//一个周期的结束
  
停止条件伪代码
  
SCL=1
  
SDA=0//低
  
//延时,停止条件建立时间
  
SDA=1//SDA变高 产生停止条件
  
//延时,本次停止条件到下一个起始条件的时间间隔
  
4.3   位传输(CD准备数据,C拉高读取D)
SCL串行时钟的配合下,在SDA上逐位地串行传送每一位数据。数据的准备是在SCL的低电平,数据位的传输是上边沿触发。
拉低准备数据,拉高采集数据
主机发送一位数据给从机:(主机输出)
  
SCL=0//主机拉低时钟线
  
SDA=0/1//主机在总线上准备数据
  
//延时,让数据稳定在数据线上
  
SCL=1//主机拉高时钟线
  
从机在时钟上升沿从总线上采集数据
  
//延时,给时间从机采集数据
  
  
主机读取从机发送的一位数据:(主机输入)
  
SCL=0//主机拉低时钟线
  
从机在总线上准备数据(从机自动进行,主机不动作)
  
//延时,让数据稳定在数据线上
  
SCL=1//主机拉高时钟线
  
主机读取SDA//主机在时钟上升沿从总线上采集数据
  
//延时,给时间主机机采集数据
  
  
4.4   应答位(第九位发送高(不应答)低(应答))
发送器每发送一个字节,就在时钟脉冲9期间释放数据线,由接收器反馈一个应答信号。
应答信号为低电平时,规定为有效应答位(ACK简称应答位),表示接收器已经成功地接收了该字节;
应答信号为高电平时,规定为非应答位(NACK),一般表示接收器接收该字节没有成功
主机读取从机的应答:(主机读取一位数据)
主机每发送1个字节给从机后,都必须通过这个应答位查看从机是否能正常收到,如果一旦读到的是非应答信号(‘1’),表明没有正常接收到当前字节数据,通信就要终止(主机发送停止信号)
  
SCL=0//主机拉低时钟线            (还是拉低给数据,拉高采集数据)
  
从机根据自己接受的情况,给不给主机应答信号
  
//延时,让数据稳定在数据线上
  
SCL=1//主机拉高时钟线
  
主机读取SDA//主机在时钟上升沿从总线上采集应答信号
  
//延时,给时间主机机采集数据
  
如果采集到的0,表示有应答,如果采集到的是1,表示非应答
  
  
主机发送一个应答给从机:(主机发送一位数据)
主机每读取完从机发送过来的一个字节数据后,都必须给从机一个应答信号。如果主机读取完当前字节后还想从机继续给它发下一个字节数据,就要给从机应答(‘0’),如果主机读取完当前字节后不想从机再给它发数据,那么主机发送非应答信号(‘1’)给从机。
  
SCL=0//主机拉低时钟线
  
SDA=0/1//主机根据自己的情况,决定给不给应答从机
  
//延时,让数据稳定在数据线上
  
SCL=1//主机拉高时钟线
  
从机在时钟上升沿从总线上采集应答位
  
//延时,给时间从机采集数据
  
5       IIC的寻址方式  
器件地址(8位)组成:7位从设备地址+1位方向位
从设备地址包含了固定地址和可编程地址
方向位决定有效数据位的传输方向,主机---》从机(主机写) 还是 从机----》主机(主机读)
5.1   IIC一次完整通信
1.      主机发送起始条件(主机占用总线,唤醒总线所有的从机)
2.      主机发送器件地址(总线上所有的从机就会拿这个器件跟自身进行比较,匹配成功的那个从机就会回复                 一个应答信号给主机,并根据方向位来决定数据传输方向)
3.      进行有效数据交流(每传输完一个直接数据都要给应答)
4.      主机发送停止信号(释放总线,结束本次通信)
5.2   IIC三种通信方式
只读主机只读取数据
只写:主机仅发送数据
有读有写
6       GPIO口模拟IIC通信协议
GPIO初始化
作为SCL的GPIO口:时钟线SCL只能由主机(MCU)发出,SCL既有低电平也有高电平,所以这个GPIO口可以配置成推挽输出,另外总线结构本来就有上拉电阻,所以也可以配置成开漏输出。
作为SDA的GPIO口:数据线SDA是双向的,有时候需要从MCU发送,有时候又要输入到MCU里。刚好,在M4里面,当GPIO口配置成输出模式时,输入电路并没有被关闭。但是,当在采集输入信号的时候,IO口的输出电路就很有可能会影响到输入信号的采集,所以必须要配置成开漏输出,在读取输入信号前输出‘1’,目的是让输出电路从IO口中断开。
准备数据要延时,读取数据也要延时
起始条件
  
void IIC_Start(void)
  
{
  
         IIC_SCL=1;
  
         IIC_SDA_OUT=1;
  
         Systick_Delay_us(1);//延时,起始条件建立时间
  
         IIC_SDA_OUT=0;//产生起始条件
  
         Systick_Delay_us(2);//延时,起始条件的保持时间
  
         
  
         IIC_SCL=0;//一个周期的结束
  
}
  
停止条件
  
void IIC_Stop(void)
  
{
  
         IIC_SCL=1;
  
         IIC_SDA_OUT=0;
  
         Systick_Delay_us(1);//延时,停止条件建立时间
  
         IIC_SDA_OUT=1;//产生停止条件
  
  
         Systick_Delay_us(1);//延时,本次停止条件到下一个起始条件的时间间隔
  
}
  
主机发送应答
  
void IIC_Send_ACK(u8 ack)
  
{
  
         IIC_SCL=0;//主机拉低时钟线
  
         if(ack)//主机根据自己的情况,决定给不给应答从机
  
         {
  
                  IIC_SDA_OUT=1;
  
         }
  
         else
  
         {
  
                  IIC_SDA_OUT=0;
  
         }
  
         Systick_Delay_us(2);//延时,让数据稳定在数据线上
  
         IIC_SCL=1;//主机拉高时钟线,从机在时钟上升沿从总线上采集应答位
  
         Systick_Delay_us(1);//延时,给时间从机采集数据
  
         
  
}
  
主机读取应答
  
u8 IIC_Revice_Ack(void)
  
{
  
         u8 ack=0;
  
         
  
         IIC_SCL=0;//主机拉低时钟线
  
         IIC_SDA_OUT=1;//切换成读模式---让输出电路从IO口断开*************************
  
         //从机根据自己接受的情况,给不给主机应答信号
  
         Systick_Delay_us(2);//延时,让数据稳定在数据线上
  
         IIC_SCL=1;//主机拉高时钟线
  
         if(IIC_SDA_IN)//主机在时钟上升沿从总线上采集应答信号
  
         {
  
                  ack=1;
  
         }
  
         Systick_Delay_us(1);//延时,给时间主机机采集数据
  
         
  
         IIC_SCL=0;//完整周期   
  
         return ack;
  
  
}
  
主机发送一个字节数据给从机
  
u8 IIC_Send_Byte(u8 data)  
  
{
  
         u8 i;
  
         
  
         for(i=0;i<8;i++)
  
         {
  
                  IIC_SCL=0;//主机拉低时钟线
  
                  //主机在总线上准备数据
  
                  if(data&0x80)  IIC_SDA_OUT=1;
  
                  else  IIC_SDA_OUT=0;
  
                  Systick_Delay_us(2);//延时,让数据稳定在数据线上
  
                  IIC_SCL=1;//主机拉高时钟线
  
                  //从机在时钟上升沿从总线上采集数据
  
                  Systick_Delay_us(1);//延时,给时间从机采集数据
  
                  
  
                  data<<=1;//让次高位成为最高位
  
         }
  
         
  
         return IIC_Revice_Ack(  );
  
}
  
主机读取从机的一个字节数据
  
u8 IIC_Revice_Byte(u8 ack)
  
{
  
         u8 i;
  
         u8 data=0;
  
         
  
         for(i=0;i<8;i++)
  
         {
  
                  IIC_SCL=0;//主机拉低时钟线
  
                  IIC_SDA_OUT=1;//切换成读模式---让输出电路从IO口中断开***************************
  
                  //从机在总线上准备数据
  
                  Systick_Delay_us(2);//延时,让数据稳定在数据线上
  
                  IIC_SCL=1;//主机拉高时钟线
  
                  data<<=1;//空出最低位来接受数据
  
                  //主机在时钟上升沿从总线上采集数据   
  
                  if(IIC_SDA_IN)
  
                  {
  
                          data  |=1;
  
                  }                        
  
                  Systick_Delay_us(1);//延时,给时间主机机采集数据
  
                  
  
         }
  
         
  
         IIC_Send_ACK(ack);
  
         
  
         return data;
  
}
  
7       IIC驱动温湿度传感器
7.1   SHT20命令
7.2   SHT20测量时序
7.3   示例代码
  
float Read_SHT20_Data(u8 cmd)
  
{
  
         u8 ack;
  
         u16 data=0;
  
         float DATA;
  
         IIC_Start( );//起始信号
  
         ack =  IIC_Send_Byte(SHT20_ADDR&0XFE);//发送器件地址+写方向
  
         
  
         if(ack)//没有应答
  
         {
  
                  IIC_Stop(  );
  
                  return -1;
  
         }      
  
         ack =  IIC_Send_Byte(cmd);//发送测量命令
  
         if(ack)//没有应答,等待从机应答
  
         {
  
                  IIC_Stop(  );
  
                  return -1;
  
         }      
  
         do
  
         {
  
                  Delay_ms(10);//给时间测量
  
                  IIC_Start(  ); //开始信号,测量中
  
                  ack =  IIC_Send_Byte(SHT20_ADDR | 0x01);//发送器件地址+读方向
  
         }while(ack);//没有应答则继续询问,知道有应答,表明测量结束
  
         
  
         data |=  IIC_Revice_Byte(0) <<8;//高位结果
  
         data |=  IIC_Revice_Byte(1) ;   //低位结果
  
         
  
         IIC_Stop( );
  
         
  
         //------数字信号转换成模拟信号
  
         data &=0xFFFC;//清除两位状态位
  
         
  
         if(cmd==T_MEASURE)
  
         {
  
                  DATA=-46.85+175.72*data/65536.0;
  
         }
  
         else  if(cmd==RH_MEASURE)
  
         {
  
                  DATA=-6.0+125.0*data/65536.0;
  
         }
  
         
  
         return DATA;
  
}
  

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏5 分享淘帖 顶1 踩
回复

使用道具 举报

沙发
ID:237239 发表于 2018-10-27 10:51 | 只看该作者
PDF文档密码:20171027

拓扑图.png (55.77 KB, 下载次数: 59)

拓扑图.png

IIC总线.pdf

966.81 KB, 下载次数: 45, 下载积分: 黑币 -5

密码:20181027

回复

使用道具 举报

板凳
ID:441532 发表于 2020-2-28 18:18 | 只看该作者
楼主,这个有stm32F4的完成代码吗?我用F103的代码移植到407上,不好使了,不知道啥问题。
回复

使用道具 举报

地板
ID:286233 发表于 2020-6-29 22:59 | 只看该作者
非常感谢,最近在做一个相关的项目,遇到问题,能不能冒昧请教下??
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表