基于STM8的IIC协议---STM8第五章

释放双眼,带上耳机,听听看~!

1. 综述

  I2C(IIC,Inter-Integrated Circuit),两线式串行总线,由PHILIPS公司开发用于连接微控制器及其外围设备。

  它是由数据线SDA和时钟SCL构成的串行总线,可发送和接收数据。在CPU和被控IC之间、IC与IC之间进行双向传送,高速IIC总线一般可达400kbps以上。但在STM8中,400kHZ已经是最快速度了。

 

2.关于STM8S103手册的TIM简介

基于STM8的IIC协议---STM8第五章基于STM8的IIC协议---STM8第五章

芯片手册中只对I2C的特点进行了简单的讲解,但并未深入解析其中的过程。

 

3. I2C详细解析

  I2C总共由五个核心函数,分别为:①起始信号②停止信号③应答信号④发送数据⑤接收数据,通过这五个核心基本函数就能于大多数的传感进行通信了。

3.1 起始信号

  当SCL为高电平期间,SDA由高电平到低电平的跳变过程;起始信号是一种电平跳变时序信号,而不是一个电平信号,如图虚线框所示。

 

3.2 停止信号

  当SCL为高电平期间,SDA由低电平到高电平的跳变过程;停止信号也是一种电平跳变时序信号,而不是一个电平信号,如图虚线框所示。

基于STM8的IIC协议---STM8第五章

 

 3.3 应答信号

  I2C的数据字节定义为8位长,对于发送端每发送1个字节后,需要将数据线(SDA)释放,由接收端反馈一个应答信号(ACK)。应答信号为低电平时,则将其规定为有效信号(ACK简称应答位),表示接收端已经成功接收了该字节;应答位为高电平时,规定为非应答位(NACK),一般表示接收端没有成功接收该字节。

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

基于STM8的IIC协议---STM8第五章

 

3.4 发送数据

  在发送起始信号后开始通信,主机发送一个8位数据。然后,主机释放SDA线并等待从从机发出得确认信号(ACK)。详细过程请看4.3.7代码示例。

 

3.5 接收数据

  在发送起始信号后开始通信,主机发送一个8位数据。然后,从机收到数据返回一个确认信号(ACK)给主机,这时候主机才开始接收数据,待主机接收数据完成后,发送一个NACK信号给从机,以通知接收端结束数据接收。详细过程请看4.3.8代码示例。

 

3.6 数据有效性

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

 

3.7 I2C通信总过程 

 基于STM8的IIC协议---STM8第五章

 

4. 例程

 

4.1 编译环境:

  我的编译环境是IAR,这款软件是现在STM8的主流平台,比较推荐。不过我打算等到STCubeMX更新出比较方便的版本后再去使用Keil5,因为我在用STM32的时候就是利用Keil5,的确很方便,你们也可以学着用一下。

 

4.2 主芯片:

  我的主芯片是STM8S系列中的103,其中STM8S的003、005、和103、105,配置一样(外设和CPU频率,FLASH),在代码相同的情况下均可进行烧写。

 

4.3 代码&解析

  I2C的基本函数代码我已经和传感的代码区隔开来,可以移植,几乎适用于市面上使用I2C驱动的传感器。

4.3.1 GPIO设置

  为了方便我们对SDA、SCL引角的GPIO进行一个调用,我将它们的GPIO写成结构体变量,调用结构体的参数便可完成操作,再对它们的电平进行宏定义。

  结构体参数解析:可见分为了SCL和SDA两个大类,我们用一个例子来解释可以更好的理解参数的意思,如单片机上的一个引角PB3,则Port即为B,Pin即为3。而uSDA_Mode_Pin_Position的话,我在下面再详细解释。

  宏定义解析:_HANDLE_即为所要创建的结构体变量,而后面对GPIO的寄存器操作暂不解释,详情可以察看相对应芯片的寄存器手册。STM8S103中的则在6.2小节中就有介绍。这里其实也可以使用库函数的GPIO读写函数去对SDA、SCL的引角进行一个控制,不过效率不太高,而直接操作寄存器的话可以减少一些不必要的麻烦,效率也就更高。

 1 /* Struct --------------------------------------------------------------------*/
 2 
 3 typedef struct iic
 4 {
 5   //具体信息:引脚 
 6   GPIO_TypeDef * pSCL_Port;             //SCL Gpio
 7   uint8_t        uSCL_Pin;              //SCL Pin
 8   GPIO_TypeDef * pSDA_Port;              //SDA Gpio
 9   uint8_t        uSDA_Pin;               //SDA Pin
10   
11   uint8_t        uSDA_Mode_Pin_Position;  //SDA Mode
12 
13 }IIC_HandleTypedef;
14 
15 
16 
17 /* Define --------------------------------------------------------------------*/
18 
19 #define IIC_SCL_1(_HANDLE_)  ( (_HANDLE_)->pSCL_Port->ODR |= ( (uint8_t)(_HANDLE_)->uSCL_Pin))
20 #define IIC_SCL_0(_HANDLE_)  ( (_HANDLE_)->pSCL_Port->ODR &= (~(uint8_t)(_HANDLE_)->uSCL_Pin))
21                                   
22 #define IIC_SDA_1(_HANDLE_)  ( (_HANDLE_)->pSDA_Port->ODR |= ( (uint8_t)(_HANDLE_)->uSDA_Pin))
23 #define IIC_SDA_0(_HANDLE_)  ( (_HANDLE_)->pSDA_Port->ODR &= (~(uint8_t)(_HANDLE_)->uSDA_Pin))
24 #define IIC_SDA_R(_HANDLE_)  ( (BitStatus)(_HANDLE_)->pSDA_Port->IDR &  (_HANDLE_)->uSDA_Pin)
25 
26 #define IIC_GPIO_MODE_Opt(_HANDLE_)  (_HANDLE_)->pSDA_Port->ODR |=    (uint8_t)1<<(_HANDLE_)->uSDA_Mode_Pin_Position
27 #define IIC_GPIO_MODE_Ipt(_HANDLE_)  (_HANDLE_)->pSDA_Port->ODR &=  ~((uint8_t)1<<(_HANDLE_)->uSDA_Mode_Pin_Position)

 

 4.3.2 延时函数

  延时函数顾名思义,就单纯的延时,延时时间可以根据芯片的速率调整,具体时间通过示波器或者可以观察到脉冲的仪器进行测量即可。

  这里定义了两个延时函数目的是在SCL低电平期间先提前改变SDA的电平,待到SDA电平稳定时,再将SCL电平改变进行读取。

 1 /*******************************************************************************
 2 * Function Name  : vIIC_Delay_4us
 3 * Description    : 示波器实测4.02us
 4 * Input          : None
 5 * Output         : None
 6 * Return         : None
 7 ****************************************************************************** */
 8 void vIIC_Delay_4us(void)    
 9 {
10   uint8_t i=3;
11   while(i--)
12   {
13     asm(\" NOP\");asm(\" NOP\");asm(\" NOP\");asm(\" NOP\");
14   }
15     
16 }
17 
18 
19 /*******************************************************************************
20 * Function Name  : vIIC_Delay_2us
21 * Description    : 示波器实测2.08us
22 * Input          : None
23 * Output         : None
24 * Return         : None
25 ****************************************************************************** */
26 void vIIC_Delay_2us(void)    
27 {
28   asm(\" NOP\");asm(\" NOP\");asm(\" NOP\");
29 }

 

4.3.3 I2C结构体初始化

  在这个函数里,我将结构体变量传进来,对该结构体的参数进行一个赋值。

 1 /*******************************************************************************
 2 * Function Name  : vIIC_Handle_Init
 3 * Description    : Initialization handle
 4 * Input          : *hIICx,*pSCL_Port,uSCL_Pin,*pSDA_Port,uSDA_Pin
5 * Output : None 6 * Return : None 7 ****************************************************************************** */ 8 void vIIC_Handle_Init(IIC_HandleTypedef * hIICx,GPIO_TypeDef * pSCL_Port,uint8_t uSCL_Pin,GPIO_TypeDef * pSDA_Port,uint8_t uSDA_Pin) 9 { 10 //引脚 Pin: 11 hIICx->pSCL_Port = pSCL_Port; 12 hIICx->uSCL_Pin = uSCL_Pin ; 13 hIICx->pSDA_Port = pSDA_Port; 14 hIICx->uSDA_Pin = uSDA_Pin ; 15 16 17 switch(uSDA_Pin) 18 { 19 case GPIO_PIN_0 : hIICx->uSDA_Mode_Pin_Position = 0 ;break; 20 case GPIO_PIN_1 : hIICx->uSDA_Mode_Pin_Position = 2 ;break; 21 case GPIO_PIN_2 : hIICx->uSDA_Mode_Pin_Position = 4 ;break; 22 case GPIO_PIN_3 : hIICx->uSDA_Mode_Pin_Position = 6 ;break; 23 case GPIO_PIN_4 : hIICx->uSDA_Mode_Pin_Position = 8 ;break; 24 case GPIO_PIN_5 : hIICx->uSDA_Mode_Pin_Position = 10;break; 25 case GPIO_PIN_6 : hIICx->uSDA_Mode_Pin_Position = 12;break; 26 case GPIO_PIN_7 : hIICx->uSDA_Mode_Pin_Position = 14;break; 27 28 } 29 }

  该函数的调用方式为:先在主函数里定义一个结构体对象如 IIC_HandleTypedef  hIIC_Test;

  则直接调用即可,如 vIIC_Handle_Init(&hIIC_Test, pSCL_Port, uSCL_Pin, pSDA_Port, uSDA_Pin);  

  而下半部分的switch选择则是通过判断SDA的Pin是哪一位,而通过通过操作的ODR,是按位操作的,具体如图,如想了解更多请到官方察看手册,。

基于STM8的IIC协议---STM8第五章

  (这里就不帮你们翻译了,看不懂就去翻译或者直接找官方手册翻了)

  官方手册网址:https://www.st.com/content/ccc/resource/technical/document/reference_manual/9a/1b/85/07/ca/eb/4f/dd/CD00190271.pdf/files

 

4.3.4 起始信号

  这里与3.1讲解的操作有点不同,就是3.1中最后没有将SCL拉低包括在内,而为了发送数据的方便,我也将SCL在此函数中拉低了。

 1 /*******************************************************************************
 2 * Function Name  : vIIC_Start_Signal
 3 * Description    : Master Send Start Signal
 4 * Input          : None
 5 * Output         : None
 6 * Return         : None
 7 ****************************************************************************** */
 8 void vIIC_Start_Signal(IIC_HandleTypedef * hIICx)
 9 {
10     
11   IIC_SDA_1        (hIICx);                        //拉高数据线    
12   IIC_SCL_1        (hIICx);                        //拉高时钟线
13   vIIC_Delay_4us   (     );                        //延时
14   IIC_SDA_0        (hIICx);                        //拉低数据线
15   vIIC_Delay_4us   (     );                        //延时
16   IIC_SCL_0        (hIICx);                        //拉低时钟线
17   vIIC_Delay_4us   (     );                        //延时
18     
19 }

 基于STM8的IIC协议---STM8第五章

4.3.5 结束信号

  这里与3.2讲解的操作也有所不同,因为在数据接收完或者是发送完成后,SDA的电平不能确定,有可能是高也有可能是低电平,但在结束信号的时候,SDA需要是低电平时候拉低SCL才能作为结束信号的开始。

 1 /*******************************************************************************
 2 * Function Name  : vIIC_Stop_Signal
 3 * Description    : Master Send Stop Signal
 4 * Input          : None
 5 * Output         : None
 6 * Return         : None
 7 ****************************************************************************** */
 8 void vIIC_Stop_Signal(IIC_HandleTypedef * hIICx)
 9 {
10 
11   IIC_SDA_0        (hIICx);                        //拉低数据线
12   vIIC_Delay_4us   (     );                        //延时
13   IIC_SCL_1        (hIICx);                        //拉高时钟线
14   vIIC_Delay_4us   (     );                        //延时
15   IIC_SDA_1        (hIICx);                        //拉高数据线
16   vIIC_Delay_4us   (     );                        //延时
17     
18 }

 基于STM8的IIC协议---STM8第五章基于STM8的IIC协议---STM8第五章

4.3.6 应答信号(ACK)

  由于因为发送端和操作的不同,这里需要将ACK分成三种,①Ack(主动拉低SDA形成应答信号)  ②NAck(主动不拉低SDA不形成应答信号)  ③ReadAck(等待应答信号)。

  ①Ack(主动拉低SDA形成应答信号)

  该信号在你没有读取到最后一个数据时由主机发送,使从机继续发送数据。

 1 /*******************************************************************************
 2 * Function Name  : vIIC_Ack
 3 * Description    : Master Send Acknowledge Single
 4 * Input          : None
 5 * Output         : None
 6 * Return         : None
 7 ****************************************************************************** */
 8 void vIIC_Ack(IIC_HandleTypedef * hIICx)
 9 {
10 
11   IIC_SDA_0        (hIICx);                        //拉低数据位
12   vIIC_Delay_2us   (     );                        //延时    
13   IIC_SCL_1        (hIICx);                        //拉高时钟位
14   vIIC_Delay_4us   (     );                        //延时
15   IIC_SCL_0        (hIICx);                        //拉低时钟位
16   vIIC_Delay_2us   (     );                        //延时
17     
18 }

  ②NAck(主动不拉低SDA不形成应答信号) 

  该信号在你读取完最后一个数据时由主机发送,使从机停止发送数据。

 1 /*******************************************************************************
 2 * Function Name  : vProto_IIC_NAck
 3 * Description    : Master Not Send Acknowledge Single 
 4 * Input          : None
 5 * Output         : None
 6 * Return         : None
 7 ****************************************************************************** */
 8 void vIIC_NAck(IIC_HandleTypedef * hIICx)
 9 {    
10     
11   IIC_SDA_1        (hIICx);                        //SDA拉高 不应答对方
12   vIIC_Delay_2us   (     );               //延时  13   IIC_SCL_1        (hIICx);               //拉高时钟位
14 vIIC_Delay_4us ( );               //延时
15 IIC_SCL_0 (hIICx);             //拉低时钟线 16 vIIC_Delay_2us ( );               //延时 17 18 }

  ③ReadAck(等待应答信号)

  该信号在主机发送完数据后等待从机应答时候使用。

 1 /*******************************************************************************
 2 * Function Name  : bIIC_ReadACK
 3 * Description    : Master Reserive Slave Acknowledge Single
 4 * Input          : None
 5 * Output         : None
 6 * Return         : None
 7 ****************************************************************************** */
 8 bool bIIC_ReadACK(IIC_HandleTypedef * hIICx) //返回为:=1有ACK,=0无ACK
 9 {                
10   IIC_GPIO_SDA_MODE_Ipt  (hIICx);                 //将SDA的模式改成输入模式
11   IIC_SDA_1            (hIICx);                 //拉高数据线
12   vIIC_Delay_2us         (     );                 //延时
13   IIC_SCL_1            (hIICx);                 //拉高时钟线
14   vIIC_Delay_2us         (     );                 //延时
15   
16   if(IIC_SDA_R(hIICx))                            //判断是否成功接收应答,如‘有’返回0,‘没有’则返回1
17   {
18     IIC_SCL_0            (hIICx);                 //拉低时钟线
19     vIIC_Delay_2us       (     );                 //延时
20     IIC_GPIO_SDA_MODE_Opt(hIICx);                 //接收完应答后,将SDA的模式改回输出模式
21     return FALSE;                                 //没有应答
22   }
23   else
24   {
25     IIC_SCL_0            (hIICx);                 //拉低时钟线
26     vIIC_Delay_2us       (     );                 //延时
27     IIC_GPIO_SDA_MODE_Opt(hIICx);                 //接收完应答后,将SDA的模式改回输出模式
28     return TRUE;                                  //产生应答
29   }    
30                   
31 }

 基于STM8的IIC协议---STM8第五章基于STM8的IIC协议---STM8第五章

4.3.7 发送数据

  所要发送的数据为8位,学过串口协议的应该知道按位发送,我们这里将要发送的数据进行由高到低位的一个顺序发送,具体操作如下,不懂的朋友可以将一下代码画出来,以方便理解。

 1 /*******************************************************************************
 2 * Function Name  : vIIC_SendByte
 3 * Description    : Master Send a Byte to Slave
 4 * Input          : Will Send Date
 5 * Output         : None
 6 * Return         : None
 7 ****************************************************************************** */
 8 void vIIC_SendByte(IIC_HandleTypedef * hIICx,uint8_t uSendByte)
 9 {    
10     
11   uint8_t i;
12     
13   for (i=0; i<8; i++)                                //循环8次
14   {
15     if(uSendByte & 0X80)                             //将发送的数据最高位与1相与,若发送的数据最高位为1,则将SDA拉高,否则拉低
16        IIC_SDA_1      (hIICx);
17     else
18        IIC_SDA_0      (hIICx);
19     uSendByte <<= 1;                                 //数据左移1位
20     vIIC_Delay_2us    (     );                       //延时
21     IIC_SCL_1         (hIICx);                       //时钟线拉高
22     vIIC_Delay_4us    (     );                       //延时
23     IIC_SCL_0         (hIICx);                       //时钟线拉低
24     vIIC_Delay_2us    (     );                       //延时
25                 
26     }
27         
28 }

 

4.3.8 数据接收

  具体操作都写在注释部分,在SCL高电平时候去读取SDA的电平。

 1 /*******************************************************************************
 2 * Function Name  : uIIC_RecvByte
 3 * Description    : Master Receive a Byte From Slave
 4 * Input          : None
 5 * Output         : None
 6 * Return         : Date From Slave 
 7 ****************************************************************************** */
 8 uint8_t uIIC_RecvByte(IIC_HandleTypedef * hIICx)
 9 {
10   uint8_t i,uReceiveByte = 0;                                   
11     
12   IIC_GPIO_SDA_MODE_Ipt (hIICx);                     //将SDA的模式设置为输入模式
13   IIC_SDA_1             (hIICx);                     //拉高数据线
14   for(i=0;i<8;i++)                                   //进行8次的循环
15   {   
16     uReceiveByte <<= 1;                              //将接收到的数据左移
17         
18     vIIC_Delay_2us      (     );                     //延时
19     IIC_SCL_1           (hIICx);                     //拉高时钟线
20     vIIC_Delay_2us      (     );                     //延时
21         
22     if(IIC_SDA_R        (hIICx))                     //读取SDA电平
23     {
24         uReceiveByte |=0x01;                         //若SDA电平为高则将数据的最低位或上1,即为加1;若SDA电平为低,不进行该操作,则数据最低位为0
25     }
26          
27     vIIC_Delay_2us      (     );                     //延时
28     IIC_SCL_0           (hIICx);                     //拉低时钟线
29     vIIC_Delay_2us      (     );                     //延时
30   }
31   IIC_GPIO_SDA_MODE_Opt (hIICx);                     //将SDA的模式设置为输出模式
32     
33   return uReceiveByte;
34     
35 }

 

5.结尾 

  I2C协议核心基本函数为以上,将所有的核心函数结合起来便可与传感器设备进行通信了,但本博客只是单纯讲解了I2C协议,并未与传感器进行通信,若理解完I2C协议后可前往下一章博客进行与传感器通信的实践。    

  对STM8的I2C协议讲解到这里结束,感谢各位看官的点击。

  如果觉得有所收获请点下推荐,若认为该博客中存在错误的说明或者对博客中某方面有疑问请留言。

 

 

作 者:浩宇99✌
出 处:https://www.cnblogs.com/zhenghaoyu/p/10719233.html
版权声明:本文原创发表于 博客园,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则视为侵权。

 

给TA打赏
共{{data.count}}人
人已打赏
随笔日记

asp.net core系列 57 IS4 使用混合流(OIDC+OAuth2.0)添加API访问

2020-11-9 4:27:43

随笔日记

vue axios封装以及API统一管理

2020-11-9 4:27:45

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索