第三章 STM32 IIC驱动

news/2025/2/24 3:52:13

1、IIC的速度:标准模式100Kbit/s、快速模式下400Kbit/s、高速模式下3.4Mbit/s
2、理论上IIC地址是8位,其中1位广播地址,7位地址,2^7=128,理论上IIC可以挂载128个器件。但IIC总线上可挂接的设备数量受总线的最大电容400pF限制,(管脚都是有输入电容、PCB也会有寄生电容)所有在实际设计中不超过8个器件。

1、 IIC的概念

IIC协议是一种用于电子设备之间进行通信和数据交互的串行通信协议。

IIC协议采用双线结构传输数据,一根数据线SDA,一根时钟线SCL。其中SDA线用于双向数据传输,SCL线则用于同步数据传输的时钟信号。通信始终由主设备控制,从设备被动接收和回应。

2、 IIC详解

  • 主机有权发起和结束一次通信,从机只能被动呼叫

  • 当总线上有多个主机同时启用总线时,IIC也具备冲突检测和仲裁的功能来防止错误产生

  • 每个连接到IIC总线上的器件都有一个唯一的地址(7bit),且每个器件都可以作为主机也可以作为从机(但同一时刻只能一个主机),总线上的器件增加和删除不影响其它器件正常工作

  • IIC总线在通信时总线上发出数据的器件为发送器,接收数据的器件为接收器。

IIC总线可以通过外部连线进行检测,便于系统故障诊断和调试,故障可以立即被寻址,软件也有利于标准化和模块化,缩短开发时间。

理论上IIC地址是8位,其中1位广播地址,7位地址,2^7=128,理论上IIC可以挂载128个器件。但IIC总线上可挂接的设备数量受总线的最大电容400pF限制,(管脚都是有输入电容、PCB也会有寄生电容)所有在实际设计中不超过8个器件。

串行的8位双向数据传输速率在标准模式下可达100Kbit/s,快速模式下可达400Kbit/s,高速模式下可达3Mbit/s

3、 IIC协议

IIC时序包括起始信号、停止信号、应答信号和数据传输。

3.1 信号的时序

3.1.1 起始信号

起始信号就是在SCL时钟信号为高电平时,SDA数据信号出现下降沿就表示为起始信号。

3.1.2 停止信号

停止信号是SCL为高电平时,SDA出现上升沿表示停止信号

3.1.3 数据传输

SDA上数据的变化,只能在SCL为低电平的时候,SCL为高电平时候,SDA上的数据不变。

3.1.4 应答信号

应答信号是主机发送完8位数据后,等待从机应答,也就是等待从机告诉主机它接收到了数据,应答信号由从机发送,主机提供应答信号所需的时钟,主机只需要让SCL为高电平,检测SDA是高电平或是低电平以及持续时间,即刻知道应答信号是否有效。

3.2 IIC设备地址格式

3.2.1 写数据操作

  • 主机发送起始信号

  • 发送器件地址+读操作(0):8位

  • 从机发送应答信号

  • 主机发送写入寄存器地址

  • 从机发送应答

  • 主机发送写入寄存器数据

  • 从机发送应答

  • 主机发送停止

3.2.2 读数据操作

IIC读时许操作要比写时许操作要多一点步骤,读时许分为四个步骤,第一步发送设备地址,第二步发送要读取的寄存器地址,第三步从新发送设备地址,最后一步从器件输出要读取的寄存器值。

  • 主机发送起始信号
  • 主机发送器件地址+写操作(0):要先告诉设备我是要操作这个寄存器,要先写入寄存器地址
  • 从机发送应答信号
  • 主机从新发送起始信号
  • 主机发送要读取的寄存器地址+读操作(1):已经告诉过设备我需要操作的寄存器,这里就可以来读取寄存器中数据
  • 从机发送应答信号
  • 从IIC器件里面读取到数据
  • 主机发出NACK信号,表示读取完毕,不需要从机再发送ACK信号
  • 主机发送停止信号

3.2.3 单个和多个写/读数据

写入单个字节:
开始信号+设备地址(7bit)写(1bit)+等待从机应答+寄存器地址(8bit)+等待从机应答+写入的数据(8bit)+等待从机应答+停止位
写入多个字节:
开始信号+设备地址(7bit)写(1bit)+等待从机应答+寄存器地址(8bit)+等待从机应答+写入数据1(8bit)+等待从机应答+写入数据2(8bit)+等待从机应答+……+停止位

读取一个字节:
开始信号+设备地址(7bit)写(1bit)+等待从机应答+寄存器地址(8bit)+等待从机应答开始信号+设备地址(7bit)读(1bit)+等待从机应答+读取寄存器数据(8bit)+主机应答+停止位
读取多个字节:
开始信号+设备地址(7bit)写(1bit)+等待从机应答+寄存器地址(8bit)+等待从机应答开始信号+设备地址(7bit)读(1bit)+等待从机应答+读取寄存器数据1(8bit)+主机应答+读取寄存器数据2(8bit)+主机应答+……+停止位

4、 STM32的软件IIC

根据IIC时许可知,IIC时许的模拟需要起始信号、停止信号、应答和非应答信号,以及读写字节操作。

4.1 起始信号

根据IIC时许可知,IIC时许的模拟需要起始信号、停止信号、应答和非应答信号,以及读写字节操作。

void IIC_Start(void)
{
    SDA_OUT();     //sda线输出
    IIC_SDA=1;            
    IIC_SCL=1;
    delay_us(4);
     IIC_SDA=0;//START:when CLK is high,DATA change form high to low 
    delay_us(4);
    IIC_SCL=0;//钳住I2C总线,准备发送或接收数据 
}  

SDA配置为输出模式,SDA为高电平,SCL(推挽输出)为高电平,一个延时然后让SDA产生下降沿,表示起始信号。这里把SCL时钟线拉低的原因是SCL为低电平时,SDA上的数据变化才能被接收到,SCL为高电平,是不允许SDA上数据发生变化的,为后续的发送或接收数据做准备。

4.2 停止信号

void IIC_Stop(void)
{
    SDA_OUT();//sda线输出
    IIC_SCL=0;
    IIC_SDA=0;
     delay_us(4);
    IIC_SCL=1; 
    IIC_SDA=1;//发送I2C总线结束信号
    delay_us(4);                                   
}

SCL从为高电平,SDA产生一个上升沿表示结束信号。代码中SCL、SDA均为低电平,短暂延时后拉高SCL,再拉高SDA让SDA再SCL在高电平时候产生一个上升沿信号表示结束。

IIC信号在数据传输过程中,当SCL=1高电平时,数据线SDA必须保持稳定状态,不允许有电平跳变,只有在时钟线上的信号为低电平期间,数据线上的高电平或低电平状态才允许变化。
SCL=1时 数据线SDA的任何电平变换会看做是总线的起始信号或者停止信号。

4.3 应答、非应答信号及等待应答

void IIC_Ack(void)
{
    IIC_SCL=0;
    SDA_OUT();
    IIC_SDA=0;
    delay_us(2);
    IIC_SCL=1;
    delay_us(2);
    IIC_SCL=0;
}

应答信号,SCL从低电平变化为高电平时,持续4us时间SDA为低电平,然后SCL从高电平变化为低电平。

void IIC_NAck(void)
{
    IIC_SCL=0;
    SDA_OUT();
    IIC_SDA=1;
    delay_us(2);
    IIC_SCL=1;
    delay_us(2);
    IIC_SCL=0;
}        

非应答信号,SCL从低电平变为高电平时,持续4us时间SDA为高电平,然后SCL从高电平变化为低电平。

u8 IIC_Wait_Ack(void)
{
    u8 ucErrTime=0;
    SDA_IN();      //SDA设置为输入  
    IIC_SDA=1;delay_us(1);       
    IIC_SCL=1;delay_us(1);     
    while(READ_SDA)
    {
        ucErrTime++;
        if(ucErrTime>250)
        {
            IIC_Stop();
            return 1;
        }
    }
    IIC_SCL=0;//时钟输出0        
    return 0;  

等待从机给出应答,在主机向从机读取数据时,读取成功需要从机返回一个标志,这个标志就是需要取读取SDA数据线,在SCL=1时,SDA是否由设备端拉低为0。

4.4 发送一个字节

void IIC_Send_Byte(u8 txd)
{                        
    u8 t;   
    SDA_OUT();         
    IIC_SCL=0;//拉低时钟开始数据传输
    for(t=0;t<8;t++)
    {              
        IIC_SDA=(txd&0x80)>>7;
        txd<<=1;       
        delay_us(2);
        IIC_SCL=1;
        delay_us(2); 
        IIC_SCL=0;    
        delay_us(2);
    }     

SCL为低电平的时候,将要发送的数据给到SDA数据线上(这个时候可以给),SCL为高电平时候,就会产生一个脉冲信号将SDA上面的数据发送给从机设备。SCL为高电平时,SDA上面的数据不能发生变化(即使变化也是无效的,会记录SDA产生上升沿前的数据)。

IIC发送数据是从最高位开始,然后一步一步的将8bit数据发送到SDA上。

4.5 读取一个字节

u8 IIC_Read_Byte(unsigned char ack)
{
    unsigned char i,receive=0;
    SDA_IN();//SDA设置为输入
    for(i=0;i<8;i++ )
    {
        IIC_SCL=0; 
        delay_us(2);
        IIC_SCL=1;
        receive<<=1;
        if(READ_SDA)receive++;   
        delay_us(1); 
    }                     
    if (!ack)
        IIC_NAck();//发送nACK
    else
        IIC_Ack(); //发送ACK   
    return receive;
}

读取数据也是在SCL从低电平变化为高电平后,获取SDA上面发送的数据。获取完8bit数据后,主机发送应答信号给从机。

4.6 模拟IIC获取AT24C02

4.6.1 读取数据

发送起始信号,发送设备地址+写命令,等待从机应答,发送设备寄存器地址,等待应答,从新发送起始信号,发送设备寄存器地址,等待应答,读数据,发送停止信号。

u8 AT24CXX_ReadOneByte(u16 ReadAddr)
{                  
    u8 temp=0;                                                                                   
    IIC_Start();  
    IIC_Send_Byte(0XA0);       //发送写命令
    IIC_Wait_Ack();
    IIC_Send_Byte(ReadAddr>>8);//发送高地址 
    IIC_Wait_Ack(); 
    IIC_Send_Byte(ReadAddr%256);   //发送低地址
    IIC_Wait_Ack();        
    IIC_Start();              
    IIC_Send_Byte(0XA1);           //进入接收模式               
    IIC_Wait_Ack();
    temp=IIC_Read_Byte(0);
    IIC_Stop();//产生一个停止条件        
    return temp;
}

//在AT24CXX里面的指定地址开始读出长度为Len的数据
//该函数用于读出16bit或者32bit的数据.
//ReadAddr   :开始读出的地址 
//返回值     :数据
//Len        :要读出数据的长度2,4
u32 AT24CXX_ReadLenByte(u16 ReadAddr,u8 Len)
{      
    u8 t;
    u32 temp=0;
    for(t=0;t<Len;t++)
    {
        temp<<=8;
        temp+=AT24CXX_ReadOneByte(ReadAddr+Len-t-1);                  
    }
    return temp;                                                    
}

指定开始地址,读多少个数据
void AT24CXX_Read(u16 ReadAddr,u8 *pBuffer,u16 NumToRead)
{
    while(NumToRead)
    {
        *pBuffer++=AT24CXX_ReadOneByte(ReadAddr++);    
        NumToRead--;
    }
}  

4.6.2 写数据

void AT24CXX_WriteOneByte(u16 WriteAddr,u8 DataToWrite)
{                                                                             
    IIC_Start();  
    IIC_Send_Byte(0XA0);        //发送写命令
    IIC_Wait_Ack();
    IIC_Send_Byte(WriteAddr>>8);//发送高地址     
    IIC_Wait_Ack();       
    IIC_Send_Byte(WriteAddr%256);   //发送低地址
    IIC_Wait_Ack();                                                    
    IIC_Send_Byte(DataToWrite);     //发送字节                               
    IIC_Wait_Ack();                     
    IIC_Stop();//产生一个停止条件 
    delay_ms(10);     
}
void AT24CXX_WriteLenByte(u16 WriteAddr,u32 DataToWrite,u8 Len)
{      
    u8 t;
    for(t=0;t<Len;t++)
    {
        AT24CXX_WriteOneByte(WriteAddr+t,(DataToWrite>>(8*t))&0xff);
    }                                                    
}

指定地址写入多少个数据
void AT24CXX_Write(u16 WriteAddr,u8 *pBuffer,u16 NumToWrite)
{
    while(NumToWrite--)
    {
        AT24CXX_WriteOneByte(WriteAddr,*pBuffer);
        WriteAddr++;
        pBuffer++;
    }
}

4.6.3 检测AT24C02是否正常

//检查AT24CXX是否正常
//这里用了24XX的最后一个地址(255)来存储标志字.
//如果用其他24C系列,这个地址要修改
//返回1:检测失败
//返回0:检测成功
u8 AT24CXX_Check(void)
{
    u8 temp;
    temp=AT24CXX_ReadOneByte(255);//避免每次开机都写AT24CXX               
    if(temp==0X55)return 0;           
    else//排除第一次初始化的情况
    {
        AT24CXX_WriteOneByte(255,0X55);
        temp=AT24CXX_ReadOneByte(255);      
        if(temp==0X55)return 0;
    }
    return 1;                                              
}

5、 STM32的硬件IIC

STM32控制器内置硬件IIC模块,支持配置IIC主机和从机模式。

硬件IIC发送数据,不会被中断所打断,因为硬件IIC发送数据是由硬件完成的,只受外部晶振时钟影响。

主模式时, I2C接口启动数据传输并产生时钟信号。串行数据传输总是以起始条件开始并以停止
条件结束。起始条件和停止条件都是在主模式下由软件控制产生。

从模式时, I2C接口能识别它自己的地址(7位或10位)和广播呼叫地址。软件能够控制开启或禁止广播呼叫地址的识别。

数据和地址按8位/字节进行传输,高位在前。跟在起始条件后的1或2个字节是地址(7位模式为1
个字节, 10位模式为2个字节)。地址只在主模式发送。

在一个字节传输的8个时钟后的第9个时钟期间,接收器必须回送一个应答位(ACK)给发送器。

硬件IIC:利用STM32芯片中的硬件IIC外设,和USART串口外设类似,只需要配置好对应的寄存器,外设就会产生标准串口协议的时许。

5.1 硬件IIC

5.1.1 时钟控制

时钟控制寄存器(I2C_CCR)用来控制在主模式下,输出时钟频率(标准模式、快速模式)。

Thigh:高电平的时间
Tlow: 低电平的时间    
Tpclk1:1/36MHz
时钟周期SCL:高电平时间+低电平时间

5.1.2 数据控制

数据控制寄存器(I2C_DR)

  • 当向外发送数据的时候,数据移位寄存器以“数据寄存器”为数据源,把数据一位一位地通过SDA信号线发送出去。(从数据寄存器中发)

  • 当从外部接收数据的时候,数据移位寄存器把SDA信号线采样到的数据一位一位地存储到“数据寄存器”中。(从数据寄存器中拿)

CPU或DMA从DATA REGISTER(数据寄存器)中获取数据。这个里面可以是外设返回的数据,也可以是CPU向外设发送的数据。

如果IIC配置在从机模式,就需要设置自身的地址,在寄存器I2C_OAR1和I2C_OAR2中

5.1.3 逻辑控制

前面看了IIC时钟信号的配置、数据的配置,逻辑控制就是描述:IIC起始信号、停止信号、应答信号等配置。

控制寄存器(I2C_CR1)

在I2C_SR1寄存器中,只要看bit10、bit7、bit6、bit3、bit2、bit1这几个位的信息。

5.2 硬件IIC软件配置

5.2.1 IIC初始化

I2C_InitStuctrue.I2C_Ack=I2C_Ack_Enable;                              // 设置ACK应答
I2C_InitStuctrue.I2C_AcknowledgedAddress=I2C_AcknowledgedAddress_7bit;// 设置设备地址位数
I2C_InitStuctrue.I2C_ClockSpeed=400000;                                 // 设置IIC通信时钟速度
I2C_InitStuctrue.I2C_DutyCycle=I2C_DutyCycle_2;                    // 设置时钟信号占空比时间
I2C_InitStuctrue.I2C_Mode=I2C_Mode_I2C;                            // 设置为IIC通信模式
I2C_InitStuctrue.I2C_OwnAddress1=STM32_I2C_OWN_ADDR;            // 设置IIC控制器地址
I2C_Init(EEPROM_I2C,&I2C_InitStuctrue);
I2C_Cmd(EEPROM_I2C,ENABLE);

5.2.2 写入一个字节

// 发送起始信号
I2C_GenerateSTART(I2C1, ENABLE);
// 检测EVS事件   模式选择
while( I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_MODE_SELECT)==ERROR);
// 发送设备写地址
I2C_Send7bitAddress(EEPROM_I2C,EEPROM_I2C_Address,I2C_Direction_Transmitter);
// 检测EV事件
while( I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_MODE_SELECT)==ERROR);
// 发送要操作设备内部的地址
I2C_SendData(EEPROM_I2C,addr);
// 检测字节发送
while( I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_BYTE_TRANSMITTING )==ERROR);
  I2C_SendData(EEPROM_I2C,data);
// 检测EV事件
while( I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_BYTE_TRANSMITTED )==ERROR);
// 发送停止信号
I2C_GenerateSTOP(EEPROM_I2C,ENABLE);

5.1.3 读取一个字节

// 判断IIC总线是否忙碌
while(I2C_GetFlagStatus(EEPROM_I2C, I2C_FLAG_BUSY))  {;}
// 发送起始信号
I2C_GenerateSTART(I2C1, ENABLE);
// 等待事件
while( I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_MODE_SELECT)==ERROR);
// 发送设备地址写数据
I2C_Send7bitAddress(EEPROM_I2C,EEPROM_I2C_Address,I2C_Direction_Transmitter);
// 等待事件
while( I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED )==ERROR);
// 发送要操作设备内部寄存器地址
I2C_SendData(EEPROM_I2C,addr);
// 检测事件
while( I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_BYTE_TRANSMITTING )==ERROR);
// 发送起始信号
I2C_GenerateSTART(EEPROM_I2C,ENABLE);
// 检测事件
while( I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_MODE_SELECT )==ERROR);
// 发送设备读地址操作
I2C_Send7bitAddress(EEPROM_I2C,EEPROM_I2C_Address,I2C_Direction_Receiver);
// 检测
while( I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED )==ERROR);
// 读取数据
 *data=I2C_ReceiveData(EEPROM_I2C);
// 发送非应答信号
I2C_AcknowledgeConfig(EEPROM_I2C,DISABLE);
// 发送停止信号
I2C_GenerateSTOP(EEPROM_I2C,ENABLE);


    http://www.niftyadmin.cn/n/5862740.html

    相关文章

    C++ Primer string流

    欢迎阅读我的 【CPrimer】专栏 专栏简介&#xff1a;本专栏主要面向C初学者&#xff0c;解释C的一些基本概念和基础语言特性&#xff0c;涉及C标准库的用法&#xff0c;面向对象特性&#xff0c;泛型特性高级用法。通过使用标准库中定义的抽象设施&#xff0c;使你更加适应高级…

    资本资产定价模型(CAPM, Capital Asset Pricing Model)通俗解析

    现代资产定价理论&#xff1a;CAPM模型通俗解析 &#x1f4c9;&#x1f4ca;&#x1f4a1; 在金融领域&#xff0c;如何定价一个资产&#xff08;如股票、债券等&#xff09;是一个至关重要的问题。而 资本资产定价模型&#xff08;CAPM, Capital Asset Pricing Model&#xf…

    leetcode_位运算 191.位1的个数

    191. 位1的个数 给定一个正整数 n&#xff0c;编写一个函数&#xff0c;获取一个正整数的二进制形式并返回其二进制表达式中设置位 (set bit&#xff0c;指在某数的二进制表示中值为 1 的二进制位)的个数&#xff08;也被称为汉明重量&#xff09;。 1. 字符串 列表 class …

    工业路由器和工业交换机,打造高效稳定的工业网络?

    工业路由器和工业交换机各有千秋&#xff0c;但如何将它们完美结合&#xff0c;构建稳定高效的工业网络&#xff1f;答案就在这里&#xff01; 工业物联网&#xff08;IIoT&#xff09;是高效、稳定的工业网络成为智慧工厂、工业自动化和远程监控等场景的基础支撑。工业路由器…

    STM32MP157A单片机移植Linux驱动深入版

    需求整理 在Linux设备树中新增leds节点&#xff0c;其有3个gpio属性&#xff0c;分别表示PE10对应led1&#xff0c;PF10对应led2&#xff0c;PE8对应led3&#xff0c;设备树键值对如下&#xff1a; leds { led1-gpio <&gpioe 10 0>; led2-gpio &l…

    matlab 轮边驱动系统汽车垂向动力学分析

    1、内容简介 matlab154-轮边驱动系统汽车垂向动力学分析 可以交流、咨询、答疑 2、内容说明 略 随着经济和科技的快速发展&#xff0c;汽车正呈现出蒸蒸日上的发展态势。但是日益严重的资源问题和能源问题对汽车行业的发展带来了很大的挑战&#xff0c;电动汽车将成为解决这…

    Java数据结构---栈

    目录 一、栈的概念 二、栈的基本方法 三、栈的模拟实现 四、栈的练习 1、括号匹配 2、出栈入栈次序匹配 一、栈的概念 栈是一种特殊的线性表&#xff0c;其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶&#xff0c;另一端称为栈底…

    python小项目编程-中级(1、图像处理)

    目录 图像处理 实现 测试 unittest pytest 图像处理 实现界面化操作&#xff0c;使用PIL库实现简单的图像处理功能&#xff0c;如缩放&#xff08;设置缩放比例&#xff09;、旋转和滤镜、对比度调整、亮度调整、灰度图、二值化图&#xff08;二值图如果使用的是彩色图片需…