你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

STM32——IIC通讯应用

[复制链接]
gaosmile 发布时间:2020-6-4 15:27
  说到IIC(也叫I2C,其实都是一样的)通讯,是一种最简单的通讯协议。在学习STM32时第一个接触的就是串口USART通讯协议,接下来就是IIC通讯协议了还有的就是SPI协议,SPI我们下一章再说,这一章就说说IIC吧。很多模块都用到过IIC通讯,最常见的就是4针的0.96寸OLED显示屏,当然啦在学习STM32是我们一般最先接触到就是通过IIC来与EEPROM进行通讯,但是本章我们只讲协议本身。

一、 IIC 简介


  IIC(Inter-Integrated Circuit)总线是一种由PHILIPS公司开发的两线式串行总线,用于连接微控制器及其外围设备。它是由数据线 SDA 和时钟SCL构成的串行总线,可发送和接收数据。在CPU与被控IC之间、IC与 IC之间进行双向传送,高速IIC总线一般可达 400kbps 以上。
  IIC 总线在传送数据过程中共有三种类型信号, 它们分别是:开始信号、结束信号和应答信号。
  开始信号:SCL 为高电平时,SDA 由高电平向低电平跳变,开始传送数据。
  结束信号:SCL 为高电平时,SDA 由低电平向高电平跳变,结束传送数据。
  应答信号:接收数据的IC在接收到8bit数据后,向发送数据的IC发出特定的低电平脉冲,表示已收到数据。CPU向受控单元发出一个信号后,等待受控单元发出一个应答信号,CPU接收到应答信号后,根据实际情况作出是否继续传递信号的判断。若未收到应答信号,由判断为受控单元出现故障。这些信号中,起始信号是必需的,结束信号和应答信号都可以不要。

微信图片_20200604152455.png

  IIC使用 SDA信号线来传输数据,使用 SCL信号线进行数据同步。SDA数据线在 SCL的每个时钟周期传输一位数据。传输时,SCL为高电平的时候 SDA 表示的数据有效,即此时的 SDA 为高电平时表示数据“1”,为低电平时表示数据“0”。当 SCL为低电平时,SDA的数据无效,一般在这个时候SDA进行电平切换,为下一次表示数据做好准备。每次数据传输都以字节为单位,每次传输的字节数不受限制。
  如果我们直接控制STM32的两个GPIO 引脚,分别用作 SCL和SDA,按照上述信号的时序要求,直接像控制 LED 灯那样控制引脚的输出(若是接收数据时则读取 SDA电平),就可以实现 IIC通讯。同样假如我们按照 USART的要求去控制引脚,也能实现 USART通讯。所以只要遵守协议,就是标准的通讯,不管您如何实现它,不管是ST生产的控制器还是ATMEL生产的存储器, 都能按通讯标准交互。
  由于直接控制 GPIO 引脚电平产生通讯时序时,需要由 CPU 控制每个时刻的引脚状态,所以称之为“软件模拟协议”方式。相对地,还有“硬件协议”方式,STM32 的 IIC片上外设专门负责实现IIC通讯协议,只要配置好该外设,它就会自动根据协议要求产生通讯信号,收发数据并缓存起来,CPU只要检测该外设的状态和访问数据寄存器,就能完成数据收发。这种由硬件外设处理IIC协议的方式减轻了 CPU 的工作,且使软件设计更加简单。

二、 软件模拟协议


1.IIC初始化函数


功能:配置IIC的时钟线和数据线

void IIC_Init(void)
{                        
    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOC, ENABLE);         
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11|GPIO_Pin_12;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOC, &GPIO_InitStructure);
    IIC_SCL=1;
    IIC_SDA=1;
}


微信图片_20200604152459.png
  
       因为是软件模拟IIC那么我们选择IIC通讯的引脚就相对来说说比较随意,具体使用的引脚可查阅《STM32F1xx 规格书》,以它为准。这里我们就选择PC11、PC12作为IIC的数据和时钟引脚。设置为推挽输出即可。


2.起始信号


功能:CPU 发起 IIC总线启动信号void IIC_Start(void)

{
    IIC_SDA=1;        
    IIC_SCL=1;
    delay_us(4);
    IIC_SDA=0;//START:当 CLK 为高电平时,DATA 从高到低改变
    delay_us(4);   
    IIC_SCL=0;//钳住I2C总线,准备发送或接收数据
    delay_us(4);
}

  起始信号产生后,所有从机设备就开始等待STM32紧接下来的从机地址信号。在IIC总线上,每个设备的地址都是唯一的,当主机广播的地址与某个设备地址相同时,这个设备就被选中了,没被选中的设备将会忽略之后的数据信号。根据IIC协议,从机地址可以是 7位或10位。在地址位之后,是传输方向的选择位,该位为 0时,表示后面的数据传输方向是由主机传输至从机,即主机向从机写数据。该位为 1时,则相反,即主机由从机读数据


3.等待应答信号


功能:CPU 产生一个时钟,并读取器件的 ACK 应答信号//返回值:1,接收应答失败  0,接收应答成功

u8 IIC_Wait_Ack(void)
{
    u8 re;
    IIC_SDA=1;delay_us(1);//CPU释放SDA总线     
    IIC_SCL=1;delay_us(1);//CPU驱动SCL=1,此时器件会返回ACK应答
    if(READ_SDA){//CPU读取SDA口线状态
       re=1;
    }else{
      re=0;
    }   
    IIC_SCL=0;//时钟输出0      
    return re;  
}

  该函数用于 STM32 作为发送方时,等待及处理接收方传来的响应或非响应信号, 即一般调用前面的 IIC_SendByte 函数后,再调用本函数检测响应。
  STM32控制 SDA 信号线输出高阻态,释放它对 SDA的控制权,由接收方控制;控制 SCL 信号线切换高低电平,产生一个时钟信号,根据IIC协议,此时接收方若把 SDA 设置为低电平,就表示返回一个“应答”信号,若 SDA 保持为高电平,则表示返回一个“非应答 ”信号;在 SCL 切换高低电平之间,有个延时确保给予了足够的时间让接收方返回应答信号,延时后使用宏READ_SDA读取 SDA 线的电平,根据电平值赋予 re 变量的值;函数的最后返回 re的值,接收到响应时返回 0,未接收到响应时返回 1。当 STM32 作为数据接收端,调用 IIC_ReadByte函数后,需要给发送端返回应答或非应答信号,此时可使用 IIC_Ack及 IIC_Nack 函数处理,该处理与 IIC_Wait_Ack函数相反,此时 SDA线也由 STM32控制。


4.应答信号


功能: CPU 产生一个 ACK 信号//CPU产生一个ACK信号

void IIC_Ack(void)
{
    IIC_SDA=0;//CPU驱动SDA=0
    delay_us(2);
    IIC_SCL=1;//CPU产生一个时钟
    delay_us(2);
    IIC_SCL=0;
    delay_us(2);
    IIC_SDA=1;//CPU释放SDA总线
}
//CPU产生1个NACK信号
void IIC_Nack (void)
{
    IIC_SDA=1();//CPU驱动SDA=1
    delay_us(2);
    IIC_SDA=1;//CPU产生1个时钟
    delay_us(2);
    IIC_SCL=0;
    delay_us(2);   
}

  IIC的数据和地址传输都带响应。响应包括“应答(ACK)”和“非应答(NACK)”两种信号。作为数据接收端时,当设备接收到 IIC 传输的一个字节数据或地址后,若希望对方继续发送数据,则需要向对方发送“应答(ACK)”信号,发送方会继续发送下一个数据;若接收端希望结束数据传输,则向对方发送“非应答(NACK)”信号,发送方接收到该信号后会产生一个停止信号,结束信号传输。
  代码的具体流程就是:根据要返回“应答”还是“非应答”信号,先准备好 SDA 线的电平,IIC_Ack函数中把 SDA 线设置为低电平,表示“应答”信号,IIC_Nack 函数中把 SDA 线设置为高电平,表示“非应答”信号;控制 SCL 线进行高低电平切换,产生一个时钟信号,在 SCL 线的高低电平之间加入一个延时,确保有足够的时间让通讯的另一方接收到 SDA信号线的电平;在 IIC_Ack 函数的末尾,响应信号发送结束后,重新把 SDA 线设置为高电平以释放总线的控制权,方便后续的通讯。


5.停止信号


功能:CPU 发起IIC总线停止信号{   

    IIC_SDA=0;//STOP:当 CLK 为高电平时候, SDA出现一个上调表示IIC总线停止信号
    IIC_SCL=1;
    delay_us(4);
    IIC_SDA=1;//发送IIC总线结束信号                        
}

  停止信号直接看是时序图就可以搞定了,在SCL和SDA都为低电平的情况下,首先把时钟线SCL拉高,再把数据线SDA拉高,IIC就会结束传输了。
  以上就是软件模拟IIC协议了,在平时的应用中我们实际上不需要掌握这些具体的代码,只要知道IIC协议的过程原理就行了,为一般来说我们用的都是别人写好的代码,我们只需要会用就可以了,如果你的代码和我这些有出入也没有关系,只要能正常通讯即可,当然如果你的设计在过程中出现了一些问题,或者显示不正常,我们首先考虑的也不是底层协议的问题,而是你代码的其他问题。


6.IIC发送字节


功能: CPU向IIC总线设备发送8bit数据void IIC_SendByte(u8 Byte)

{
    u8 i;
    /* 先发送字节的高位bit7 */
    for (i = 0; i < 8; i++)
    {      
        if (Byte & 0x80)
        {
          IIC_SDA=1;
        }
        else
        {
            IIC_SDA=0;
        }
        delay_us(2);
        IIC_SCL=1;
        delay_us(2);
        IIC_SCL=0;
        if (i == 7)
        {
             IIC_SDA=1;// 释放总线
        }
        Byte <<= 1; //左移一个bit
        delay_us(2);
    }
}

  该函数以其输入参数作为要使用IIC协议输出的数据,该数据大小为一字节。函数的主体是一个8次的 for 循环,循环体执行一次将会对外发送一个数据位,循环结束时刚好发送完该字节数据。步骤分解如下:
  首先程序对输入参数Byte和 0x80“与”运算,判断其最高位的逻辑值,为 1 时控制 SDA输出高电平,为 0则控制 SDA输出低电平;接下来延时,以此保证 SDA 线输出的电平已稳定,再进行后续操作;之后控制 SCL线产生高低电平跳变,也就是产生 IIC协议中 SCL线的通讯时钟;在 SCL线高低电平之间有个延时,该延时期间 SCL线维持高电平,根据 IIC协议,此时数据有效,通讯的另一方会在此时读取 SDA 线的电平逻辑,高电平时接收到该位为数据 1,否则为 0;就这样一次循环体执行结束,Byte 左移一位以便下次循环发送下一位的数据;如次循环 8 次,把Byte 中的 8 位个数据位发送完毕,在最后一位发送完成后(此时循环计数器 i=7),控制 SDA 线输出 1(即高阻态),也就是说发送方释放 SDA总线,等待接收方的应答信号。


7.IIC读取字节


功能: CPU从IIC总线设备读取8bit数据u8_t IIC_ReadByte(void)

{
    u8 i;
    u8 value;
    //读到第1个bit为数据的bit7
    value = 0;
    for (i = 0; i < 8; i++)
    {
        value <<= 1;
        IIC_SCL=1;
        delay_us(2);
        if (SDA_READ)
        {
            value++;
        }
        IIC_SCL=0;
        delay_us(2);
    }
    return value;
}

  IIC_ReadByte 函数也是以 for 循环为主体,循环体会被执行 8次,执行完毕后将会接收到一个字节的数据,循环体接收数据的流程如下:
  首先使用一个变量 value 暂存要接收的数据,每次循环开始前先对 value 的值左移 1 位,以给 value 变量的 bit0 腾出空间,bit0 将用于缓存最新接收到的数据位,一位一位地接收并移位,最后拼出完整的 8位数据;然后控制 SCL线进行高低电平切换,输出 IIC 协议通讯用的时钟;在SCL线高低电平切换之间,有个延时,该延时确保给予了足够的时间让数据发送方进行处理,即发送方在 SCL 时钟驱动下通过 SDA 信号线发出电平逻辑信号,而这个延时之后,作为数据接收端的 STM32 使用宏SDA_READ读取 SDA信号线的电平,若信号线为 1,则 value++,即把它的 bit0置 1,否则不操作(这样该位将保持为 0),这样就读取到了一位的数据;接下来SCL线切换成低电平后,加入延时,以便接收端根据需要切换 SDA 线输出数据;直到循环结束后,value 变量中包含有 1 个字节数据,使用 return 把它作为函数返回值返回。


三、 硬件协议


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

  STM32的IIC外设可用作通讯的主机或从机,支持 100Kbit/s 和 400Kbit/s 的速率,支持 7位、10位设备地址,支持 DMA数据传输,并具有数据校验功能。它的IIC外设还支持 SMBus2.0协,SMBus 协议与IIC类似,主要应用于笔记本电脑的电池管理中。
  STM32 芯片有多个IIC外设,它们的IIC通讯信号引出到不同的 GPIO 引脚上,使用时必须配置到这些指定的引脚,GPIO引脚的复用功能,可查阅《STM32F1xx 规格书》,以它为准。

微信图片_20200604152503.png


IIC初始化函数


void IIC_init(void)

{
    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;// 开漏输出
    GPIO_Init(GPIOA, &GPIO_InitStructure);  
    IIC_SCL=1;
    IIC_SDA=1;//给一个停止信号, 复位I2C总线上的所有设备到待机模式
}

  因为是硬件IIC直接使用外设来控制引脚,那么我们选择IIC通讯的引脚就比较固定,具体使用的引脚可查阅《STM32F1xx 规格书》,以它为准。可以看到PB6和PB7两个引脚可以作为IIC的通讯引脚,而且PB6为SCL时钟线,而PB7则为SDA数据线,并设置为开漏输出。
  这里为啥设置为开漏输出的方式呢?
  这是由于使用的是软件模拟IIC方式,而IIC协议的 GPIO 必须的开漏输出模式,开漏输出模式在输出高电平时实际输出高阻态,当IIC该总线上所有设备都输出高阻态时,由外部的上拉电阻上拉为高电平。另外当 STM32 的 GPIO 配置成开漏输出模式时,它仍然可以通过读取GPIO 的输入数据寄存器获取外部对引脚的输入电平,也就是说它同时具有浮空输入模式的功能,因此在后面控制 SDA线对外输出电平或读取 SDA线的电平信号时不需要切换 GPIO的模式。
  另外在硬件IIC协议之下,它的起始信号、等待应答信号、应答信号、停止信号都与软件模拟IIC协议之下的函数相同,在这里我就不重复说明了。
      总结:IIC通讯协议很简单,在实际项目中我们不需要掌握具体的IIC协议代码,只要会用即可,作为最常见且常用的协议,我们最好能够背下来或者有所了解。现在IIC通讯不陌生了吧!

收藏 1 评论1 发布时间:2020-6-4 15:27

举报

1个回答
likang1202 回答时间:2020-6-5 08:47:26
谢谢分享

所属标签

STM32团队

意法半导体微控制器和微处理器拥有广泛的产品线,包含低成本的8位单片机和基于ARM® Cortex®-M0、M0+、M3、M4、M33、M7及A7内核并具备丰富外设选择的32位微控制器及微处理器


最新内容

相似技术帖

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
微信公众号二维码 微信公众号
手机版二维码 手机版