zoukankan      html  css  js  c++  java
  • 基于STM8的IIC协议通信

    一、 综述

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

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

           在往后的模块调试中也经常涉及,是一个很常见并且很好用的协议。

    二、STM8S103中手册对I2C简介

     看完中文资料手册,个人觉得比较浅显,具体使用在后面我会贴出来。

    三、 I2C详细解析

    I2C总共由五个核心函数,分别为:

    ①起始信号

    ②停止信号

    ③应答信号

    ④发送数据

    ⑤接收数据

    通过这五个核心基本函数就能于大多数的传感进行通信了。

    以下对各个部分进行详细介绍,附上部分主要代码,各位可以参考一下。

    3.1 起始信号

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

    void Start_Signal_IIC_(void){
      
      //起始信号:
      GPIO_WriteHigh(GPIOD, GPIO_PIN_2);//数据线
      IIC_Delay_4us();
      GPIO_WriteHigh(GPIOD, GPIO_PIN_3);//时钟线
      IIC_Delay_4us();
      GPIO_WriteLow(GPIOD, GPIO_PIN_2); //数据线
      IIC_Delay_4us();
      GPIO_WriteLow(GPIOD, GPIO_PIN_3); //时钟线
      IIC_Delay_4us();
    }
    

      

    3.2 停止信号

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

    void End_Data_IIC_()
      {
        GPIO_WriteLow(GPIOD, GPIO_PIN_2); //数据线拉低
        IIC_Delay_4us();
        GPIO_WriteHigh(GPIOD, GPIO_PIN_3);//时钟线拉高
        IIC_Delay_4us();
        GPIO_WriteHigh(GPIOD, GPIO_PIN_2);//数据线拉高
    }

      

    3.3 应答信号

    应答信号有两种:分别是主动应答信号和主动不应答信号

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

     I2C总线的数据都是以字节(8位)的方式传送的,发送器件每发送一个字节之后,在时钟的第9个脉冲期间释放数据总线,由接收器发送一个  ACK(把数据总线的电平拉低)来表示数据成功接收。

    //主动应答信号
    void vIIC_Ack() { GPIO_WriteLow(GPIOD, GPIO_PIN_2); IIC_Delay_4us(); GPIO_WriteHigh(GPIOD, GPIO_PIN_3); IIC_Delay_4us(); GPIO_WriteLow(GPIOD, GPIO_PIN_3); IIC_Delay_4us(); }

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

    在时钟的第9个脉冲期间发送器释放数据总线,接收器不拉低数据总线表示一个NACK,NACK有两种用途:

    a. 一般表示接收器未成功接收数据字节;

    b. 当接收器是主控器时,它收到最后一个字节后,应发送一个NACK信号,以通知被控发送器结束数据发送,并释放总线,以便主控接收器发送一个停止信号STOP。

    //主动不应答
    void vIIC_NAck()
    {
      GPIO_WriteHigh(GPIOD, GPIO_PIN_2);
      IIC_Delay_4us();
      GPIO_WriteHigh(GPIOD, GPIO_PIN_3);
      IIC_Delay_4us();
      GPIO_WriteLow(GPIOD, GPIO_PIN_3);
      IIC_Delay_4us();
    }
    

     ③ReadAck(等待应答信号)

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

     u8 bIIC_ReadACK()
      {
        GPIO_Init(GPIOD, GPIO_PIN_2, GPIO_MODE_IN_PU_IT);//将SDA改为输入模式。
        GPIO_WriteHigh(GPIOD, GPIO_PIN_3);               //拉高时钟线。
        IIC_Delay_4us();  
        if(IIC_SDA_R != 0)
           {
             //低 有应答
             GPIO_WriteLow(GPIOD, GPIO_PIN_3);
             IIC_Delay_4us();
             GPIO_Init(GPIOD, GPIO_PIN_2, GPIO_MODE_OUT_PP_HIGH_FAST);//SDA
             return 1;
           }
         else
            //高 无应答
           {
            GPIO_WriteLow(GPIOD, GPIO_PIN_3);
            IIC_Delay_4us();
            GPIO_Init(GPIOD, GPIO_PIN_2, GPIO_MODE_OUT_PP_HIGH_FAST);//SDA
            return 0;
           }
      }
    

      

    3.4 发送数据

    在发送起始信号后开始通信,主机发送一个8位数据。然后,主机释放SDA线并等待从从机发出得确认信号(ACK)。

    void Send_Data_IIC_(uint8_t Data){
        int i;
    //拉低数据线和时钟线 GPIO_WriteLow(GPIOD, GPIO_PIN_3); GPIO_WriteLow(GPIOD, GPIO_PIN_2); for(i=0;i<8;i++) { if(Data&0x80) GPIO_WriteHigh(GPIOD, GPIO_PIN_2); else GPIO_WriteLow(GPIOD, GPIO_PIN_2); Data= Data<<1; IIC_Delay_2us(); GPIO_WriteHigh(GPIOD, GPIO_PIN_3); IIC_Delay_4us(); GPIO_WriteLow(GPIOD, GPIO_PIN_3); IIC_Delay_2us(); } }

      

    3.5 接收数据

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

    //接收函数
    uint8_t uIIC_RecvByte()
    {
      uint8_t i,uReceiveByte = 0;
      GPIO_Init(GPIOD, GPIO_PIN_2, GPIO_MODE_IN_PU_IT);
      for(i=0;i<8;i++)
      {
      
        uReceiveByte <<= 1;
        IIC_Delay_4us();
        GPIO_WriteHigh(GPIOD, GPIO_PIN_3);//高时钟线时读取数据电平
        IIC_Delay_4us();
        if(IIC_SDA_R !=0 )
        {
          uReceiveByte|=0x01;
        }
        IIC_Delay_4us();
        GPIO_WriteLow(GPIOD, GPIO_PIN_3);
        IIC_Delay_4us();
      }
      
       GPIO_Init(GPIOD, GPIO_PIN_2, GPIO_MODE_OUT_PP_HIGH_FAST);
       return uReceiveByte;
    }
    

      

    3.6 I2C通信总过程

    以下图完美地诠释了iic从接收到发送全过程中,SDA和SCL线的变化线。综合上面的解释和过程代码,这张图可以帮助记忆和理解。

    四、例程

    4.1 编译环境:

            这里用的是IAR进行编译,较为好用,后期使用STM32的开发板可以推荐使用CUBE直接生成初始化函数,与Keil5相互搭配使用,很是方便。

    4.2 主芯片:

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

    4.3 代码&解析

           iic代码可以驱动几乎市面上的所有时钟模块,所以这里的代码可以与时钟模块的代码相互调用。每个函数我都有加以解释,可以详细了解一下。

          看了上面的代码也可以知道,这个协议是由数据线和时钟线,数据发送接收要求拉高数据线或拉低时钟线。所以这里推荐直接使用库函数的拉高低,如果要方便的话,再加个宏定义可以更加直观方便。

    五、结尾

    以上是iic的核心函数,对于每个函数我已经写的很清楚,下一篇博客我会将基于iic的时钟模块的传感通信博客贴出来,各位可以继续阅读下一篇博客做一下参考。

  • 相关阅读:
    UnxUtils让windows下的dos命令变为linux下的命令
    Python多线程&进程
    Web前端工程师-优秀简历汇总
    最详细的Vuex教程
    Vue2.0 探索之路——生命周期和钩子函数的一些理解
    理解 $nextTick 的作用
    使用git rebase合并多次commit
    vim 退出命令(保存、放弃保存)
    Vue获取DOM元素样式 && 样式更改
    Vue Router的配置
  • 原文地址:https://www.cnblogs.com/shasha2019/p/12171728.html
Copyright © 2011-2022 走看看