[015] [STM32] IIC协议详解与HAL库相关函数分析

这篇具有很好参考价值的文章主要介绍了[015] [STM32] IIC协议详解与HAL库相关函数分析。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1 IIC协议

1.1 物理层

[015] [STM32] IIC协议详解与HAL库相关函数分析

IIC(Inter Integrated Circuit)总线在物理层由SDA(Serial data, 串行数据线)、SCL(Serial clock line,串行时钟线)和上拉电阻组成。

  • 每个连接到总线的设备都有一个独立的地址,主机可以利用此地址进行不同设备之间的访问
  • 连接到相同总线的 IC 数量受到总线的最大电容 400pF 限制
  • 为了避免总线信号的混乱,要求各设备连接到总线的输出端时必须是**漏极开路(OD)输出或集电极开路(OC)**输出(IIC的空闲状态只能有外部上拉, 而此时空闲设备被拉到了高阻态,也就是相当于断路, 整个IIC总线只有开启了的设备才会正常进行通信,而不会干扰到其他设备)
  • 多个主机同时使用总线时,为了防止数据冲突,会利用仲裁方式(线与特性)决定由哪个设备占用总线
  • 主机与从机之间的数据传输只在SDA一根线上完成,不能同时发送和接收数据,所以IIC是一种半双工的通信协议

高阻态:高阻状态是三态门电路的一种状态。逻辑门的输出除有高、低电平两种状态外,还有第三种状态——高阻状态的门电路。电路分析时高阻态可做开路理解。

漏极开路/集电极开路即为高阻态,若需要产生高电平,则需使用外部上拉电阻。

SDA 和SCL通过一个电流源或上拉电阻连接到正的电源电压,当总线空闲时,这两条线都是高电平,内部电平如下图所示:

[015] [STM32] IIC协议详解与HAL库相关函数分析

1.2 协议层

相关术语:

[015] [STM32] IIC协议详解与HAL库相关函数分析

IIC协议标准规定发起通信(控制时钟线,即控制SCL的电平高低变换)的设备称为设备,主设备发起一次通信后,其它设备均为设备。

发送器与接收器的角色,与主机和从机没有关系:

  1. 假设微控制器A 要发送信息到微控制器B:
    • 微控制器A(主机)寻址微控制器B(从机)
    • 微控制器A (主机-发送器)发送数据到微控制器B(从机-接收器
    • 微控制器A 终止传输
  2. 如果微控制器A 想从微控制器B 接收信息:
    • 微控制器A(主机)寻址微控制器B(从机)
    • 微控制器A (主机-接收器)发送数据到微控制器B(从机-发送器
    • 微控制器A 终止传输

1.2.1 数据有效性

[015] [STM32] IIC协议详解与HAL库相关函数分析

  • SCL为高电平的时候SDA表示的数据有效
  • SCL低电平时SDA进行电平切换,为下一次表示数据做好准备。因此,数据在SCL上升沿到来之前要准备好,在SCL下降沿前必须保持稳定。

1.2.2 起始与停止信号

[015] [STM32] IIC协议详解与HAL库相关函数分析

  • 起始信号:当SCL为高电平时,SDA由高到底的跳变。(SDA低电平延时>4.7us后,SCL变为低电平)
  • 停止信号:当SCL为高电平时,SDA由底到高的跳变。(SDA高电平延时需>4us)
/**
  * @brief I2C起始信号
  * SDA -> Output
  */
void I2CStart(void)
{
    SDA = 1;		// 确保SCL拉高前SDA为高电平
    delay_us(5);
    SCL = 1;
    delay_us(5);
	SDA = 0;
    delay_us(5);
    SCL = 0;		// SCL变低,起始信号结束
    delay_us(5);
}
/**
  * @brief I2C停止信号
  * SDA -> Output
  */
void I2CStop(void)
{
    SCL = 0;
    SDA = 0;
    delay_us(5); 
    SCL = 1;		// SCL拉高,释放总线控制权,停止接收数据
	SDA = 1;
    delay_us(5);
}

1.2.3 设备地址与数据传输方向(R/W)

[015] [STM32] IIC协议详解与HAL库相关函数分析

主机发起通讯时(产生起始信号),首先会通过SDA线发送设备地址(7位或10位)来查找从机,LSB位用来表示数据传输方向:

  • LSB = 1:主机向从机数据
  • LSB = 0:主机向从机数据

1.2.4 数据传输

只有当SCL为高电平时才能传输数据,且SDA线上的数据必须保持稳定(不允许高低跳变),只有当SCL为低时,SDA线上的数据才允许切换状态。

数据传输时先传输MSB,输出到SDA线上的数据必须为8位(传输设备地址时,7位地址+1位表示读/写),且每个字节后面必须紧跟一位应答位(即一帧数据9位

  • 写数据

主机确定了从机的设备地址后,生成一个开始信号,然后向IIC总线上面发送设备的地址和读写方向标志。从机检测到该地址和自己设备地址相对应后,回复主机一个应答信号。主机接收到应答信号后就开始向这个设备以字节为单位发送数据,每一个字节后面都会带有从机的应答信号,直到主机发送完成最后一个数据后生成一个停止信号结束此次数据的传输。

[015] [STM32] IIC协议详解与HAL库相关函数分析

[015] [STM32] IIC协议详解与HAL库相关函数分析

  • 读数据

读操作与写操作类似。

下面为发送/接收8位数据,不包括起始/停止条件:

/**
  * @brief 发送字节数据
  * SDA -> Output
  */
void I2CSendByte(uint8_t data)
{
    uint8_t i = 8;
    while (i--)
    {
        SCL = 0;
        delay_us(2);
        SDA = !!(data & 0x80);	// 将数据转换为bool型
        data <<= 1;
        delay_us(2);
        SCL = 1;
        delay_us(2);
    }
    SCL = 0;
    delay_us(2);
}
/**
  * @brief 读取字节数据
  * SDA -> Input
  */
uint8_t I2CReadByte(uint8_t ack)
{
    uint8_t i = 8, data = 0;
    SDA_Input_Mode();
    while (i--)
    {
        SCL = 0;
        delay_us(2);
        SCL = 1;
        delay_us(1);
        data <<= 1;		// 第一次右移为0,然后依次移位7次
        data |= SDA;
    }
    SDA_Output_Mode();
    if (ack)
        I2CSendACK();	// 发送应答
    else
        I2CSendNACK();	// 发送非应答
    return data;
}

1.2.5 响应 NACK/ACK

[015] [STM32] IIC协议详解与HAL库相关函数分析
当数据发送端传送8位数据结束后,在第9个时钟时,数据发送端会将SDA线拉高(释放SDA的控制权),防止数据冲突,由数据接收端控制SDA,此时:

  • SDA为高电平,表示非应答信号(NACK),说明数据接收端已成功地接收了该字节
  • SDA为低电平,表示应答信号(ACK),说明数据接收端接收该字节未成功
/**
  * @brief 发送应答信号
  * SDA -> Output
  */
void I2CSendACK(void)
{
    SCL = 0;
    SDA = 0;		//拉低SDA,产生应答信号
    delay_us(2);
    SCL = 1;
    delay_us(5);
    SCL = 0;
}
/**
  * @brief 发送非应答信号
  * SDA -> Output
  */
void I2CSendNACK(void)
{
    SCL = 0;
    SDA = 1;		//拉高SDA,不产生应答信号
    delay_us(2);
    SCL = 1;
    delay_us(5);
    SCL = 0;
}

当 发送器 需要等待并接收 接收器 的应答信号时,需要将发生器SDA数据线由输出模式修改为输入模式:

/**
  * @brief 等待应答信号
  * SDA -> Input
  */
int I2CSendACK(void)
{
    uint8_t timeout = 5;
    SDA_Input_Mode();  // 将主机SDA引脚GPIO变为输入模式
    delay_us(2);
    SCL = 1;
    delay_us(2);
    while (SDA)			// 读取SDA总线电平, 若接收器应答则会低电平退出循环;否则将超时错误返回
    {
        timeout--;
        delay_us(1);
        if (0 == timeout)
        {
            SDA_Output_Mode();	// 将主机SDA引脚GPIO变为输出模式
            I2CStop();
            return ERROR;
        }
    }
    SDA_Output_Mode();
    SCL = 0;
    delay_us(2);
    return SUCCESS;
}

1.2.6 仲裁

  • 时钟同步仲裁

[015] [STM32] IIC协议详解与HAL库相关函数分析

IIC的仲裁机制得益于其开漏的输入输出结构。当SCL线上挂载的多个设备,其中的MCU2的SCL输出低电平,那么这条IIC总线SCL就会被MCU2拉低,体现线与特性。

如下图所示,CLK1和CLK2都是连接在一条SCL线上的设备同时产生的时钟信号,由于IIC总线存在“线与”的特性,同为高电平才能输出高电平,有1个为低电平则全部为低电平,因此同一条SCL总线上面的时钟都是相同的。
[015] [STM32] IIC协议详解与HAL库相关函数分析

由此可知:产生的同步SCL 时钟的低电平周期由低电平时钟周期最长的器件决定,而高电平周期由高电平时钟周期最短的器件决定。

  • 数据传输仲裁

SDA仲裁也是基于“线与”的特性,因为SCL高电平时才能传输数据,所以SCL高电平期间开始仲裁。

下图显示了两个主机的仲裁过程,在第1个和第2个周期内DATA1和DATA2的数据都是相同的,当在第2个时钟周期时DATA1与SDA的数据不一致,这个时候设备1就会停止发送数据,转而启动接收模式(变为高电平)。这样SDA的数据就会与DATA2的数据保持一致,并且设备1停止发送数据也不会影响SDA的数据。

[015] [STM32] IIC协议详解与HAL库相关函数分析

注意:在串行传输时当重复起始条件或停止条件发送到I2C 总线的时侯仲裁过程仍在进行,即一帧数据完全相同,此时相关主机必须发送重复起始条件或停止条件,因此仲裁在不能下面情况进行:

  • 重复起始条件和数据位
  • 停止条件和数据位
  • 重复起始条件和停止条件

从机不参与上述仲裁。

2 STM32硬件IIC

2.1 硬件IIC框架

[015] [STM32] IIC协议详解与HAL库相关函数分析

2.2 主要寄存器

[015] [STM32] IIC协议详解与HAL库相关函数分析

2.3 CubeMx配置

[015] [STM32] IIC协议详解与HAL库相关函数分析

时间配置:

  • I2C Speed Mode:
    • Standard Mode标准模式(100K)
    • Fast Mode快速模式(400K)
    • Fast Plus Mode高速模式(1000K)
  • Rise time:增加SDA和SCL升沿延时时间 ns(I2C->TIMINGR bit[23:20] SCLDEL[3:0]位)
  • Fall time:增加SDA和SCL升沿延时时间 ns(I2C->TIMINGR bit[19:16] SDADEL[3:0]位) — 需关闭时钟拉伸才生效
  • Coefficient of Digital Filter:配置模拟噪声滤波器系数,在SDA和SCL输入端有一个模拟噪声滤波器,要求在快模式和快模式Plus中抑制脉冲宽度高达50 ns的尖峰。通过I2C->CR1ANFOFF位使能/禁用模拟噪声滤波器,通过I2C->CR1DNF[3:0]位选择数字滤波器系数。
  • Analog Filter:即为模拟噪声滤波器使能/禁用,注意复位默认使能,即ANFOFF位为0表示使能

从机配置:

  • Clock No Stretch Mode:配置时钟拉伸模式(I2C->CR1 bit17 NOSTRETCH位),clock stretching通过将SCL线拉低来暂停一个传输,直到释放SCL线为高电平,传输才继续进行,一般不用。
  • General Call Address Detection:通用呼叫地址检测。在Slave模式下,接口能够识别自己的地址(7位或10位)和通用呼叫地址
  • Primary Address Length selection: 从设备地址长度 一般为7位,通讯时7位地址+1位读写做开头
  • Dual Address Acknowledged: 双地址确认,当主地址是7位长度时,可以有一个双地址
  • Primary slave address:从设备初始地址(地址值从0到127,且生成的地址值左移1位,因为LSB需表示R/W)

2.4 HAL库函数

HAL函数模型有轮询、中断、DMA三种,下面仅分析轮询模式函数

2.4.1 主机写/读数据

  • 写数据
/**
  * @brief  在主机模式下以阻塞模式传输数据
  * @param  hi2c 
  * @param  DevAddress  目标设备地址: 7位地址, 必须向左移1位!!!
  * @param  pData 		指针数据缓冲区的指针(写入过程中指针会根据字字数自增)
  * @param  Size 		要发送数据的字节数
  * @param  Timeout 	超时时间
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_I2C_Master_Transmit(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData,
                                          uint16_t Size, uint32_t Timeout)
  • 读数据
/**
  * @brief  在主机模式下以阻塞模式接收数据
  * @param  hi2c
  * @param  DevAddress  目标设备地址: 7位地址, 必须向左移1位!!!
  * @param  pData 		指针数据缓冲区的指针(读取过程中指针会根据字字数自增)
  * @param  Size        要接收数据的字节数
  * @param  Timeout 	超时时间	
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_I2C_Master_Receive(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData,
                                         uint16_t Size, uint32_t Timeout)

2.4.2 从机写/读数据

  • 写数据
/**
  * @brief  在从机模式下以阻塞模式接收数据
  * @param  hi2c
  * @param  pData 		指针数据缓冲区的指针
  * @param  Size        要接收数据的字节数
  * @param  Timeout 	超时时间	
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_I2C_Slave_Transmit(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t Size,
                                         uint32_t Timeout)

HAL_I2C_Slave_Receive读数据与之类似。和主机相比,少了设备地址参数。

2.4.3 向从机特定的内存地址写/读入数据

  • 写数据
/**
  * @brief  在阻塞模式下向从机特定的内存地址写/读入数据
  * @param  hi2c 
  * @param  DevAddress  目标设备地址: 7位地址, 必须向左移1位!!!
  * @param  MemAddress 	从机寄存器地址(写入过程中会自加)
  * @param  MemAddSize  从机寄存器地址的大小(8位或16位)
  * @param  pData 		指针数据缓冲区的指针
  * @param  Size        要接收数据的字节数
  * @param  Timeout 	超时时间
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_I2C_Mem_Write(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress,
                                    uint16_t MemAddSize, uint8_t *pData, uint16_t Size, uint32_t Timeout)

该函数适用于IIC外设里面还有子地址寄存器的设备,如AT24CXX E2PROM存储器,除了设备地址,每个存储字节都有其对应的地址。

其中MemAddSize可选宏:

#define I2C_MEMADD_SIZE_8BIT            (0x00000001U)
#define I2C_MEMADD_SIZE_16BIT           (0x00000002U)

使用**HAL_I2C_Mem_Write等于先使用HAL_I2C_Master_Transmit传输第一个寄存器地址,再用HAL_I2C_Master_Transmit**传输写入第一个寄存器的数据:

uint8_t Cmd_Code[2] = {0x00, 0x00};
uint8_t Data_Code[2] = {0x40, 0x00};
extern I2C_HandleTypeDef hi2c3;
void OLED_Write(uint8_t type, uint8_t data)
{
    if (type == TYPE_COMMAND)
    {
        Cmd_Code[1] = data;
        // HAL_I2C_Master_Transmit(&hi2c3, 0x78, Cmd_Code, 2, 100);
        HAL_I2C_Mem_Write(&hi2c3, 0x78, 0x00, I2C_MEMADD_SIZE_8BIT, &data,  1, 100);
    }
    else
    {
        Data_Code[1] = data;
        // HAL_I2C_Master_Transmit(&hi2c3, 0x78, Data_Code, 2, 100);
        HAL_I2C_Mem_Write(&hi2c3, 0x78, 0x40, I2C_MEMADD_SIZE_8BIT, &data,  1, 100);
    }
}

HAL_I2C_Mem_Read读数据与写数据函数类似。


参考:

  • IIC原理超详细讲解—值得一看

END文章来源地址https://www.toymoban.com/news/detail-405316.html

到了这里,关于[015] [STM32] IIC协议详解与HAL库相关函数分析的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • STM32(HAL库)软件IIC驱动OLED

    目录 1、简介 2、CubeMX初始化配置 2.1 基础配置 2.1.1 SYS配置  2.1.2 RCC配置 2.2 软件IIC引脚配置 2.3 项目生成  3、KEIL端程序整合 3.1 OLED驱动添加 3.3 主函数代 3.4 效果展示 本文通过STM32F103C8T6单片机(HAL库)通过软件IIC方式对OLED进行驱动。 2.1.1 SYS配置  2.1.2 RCC配置 首先在建立Ha

    2024年02月14日
    浏览(15)
  • STM32 HAL库函数——HAL_UART_Receive_IT()详解

    huart :UART句柄,指向要使用的UART外设。 pData :指向数据缓冲区的指针,用于存储接收到的数据。 Size :要接收的数据元素(u8或u16)的数量。 返回值类型为 HAL_StatusTypeDef ,表示函数执行的状态。可能的返回值有: HAL_OK :函数执行成功。 HAL_ERROR :函数执行过程中发生错误

    2024年02月08日
    浏览(21)
  • STM32 HAL库函数——HAL_UART_Transmit_IT()详解

    HAL_UART_Transmit_IT 函数的用法如下: 输入参数: huart :指向UART句柄结构体的指针,用于指定要使用的UART外设。 pData :指向要发送数据缓冲区的指针,可以是 uint8_t 类型或 uint16_t 类型的数据。 Size :要发送的数据大小,以数据元素( uint8_t 或 uint16_t )的数量表示。 返回值:

    2024年02月08日
    浏览(17)
  • STM32 HAL库函数——HAL_TIM_Base_Start_IT()详解

    以STM32G030C8T6中的HAL_TIM_Base_Start_IT()函数为例,进行解释; 函数原型: 函数原型: 该函数用于 启动定时器的中断模式 。在使用该函数之前,需要先初始化好定时器的配置,并将相关的中断处理函数注册到对应的中断向量中。 htim:指向TIM_HandleTypeDef结构体的指针,包含了定时

    2024年02月04日
    浏览(17)
  • 【STM32+HAL库】---- 硬件IIC驱动0.96OLED

    代码借鉴学习于以下文章: STM32 使用硬件IIC驱动0.96寸4针IOLED显示器(HAL库) 略... 点击查看代码 首先在 main.c 中引入头文件 随即根据要求在 main函数 中添加对应功能函数即可 【 注意 】使用时应将编译器编码选项选择为 GBK系列 ,否则编译报错,尤其注意通过keil修改后,再

    2024年02月19日
    浏览(17)
  • STM32CubeMX-HAL库-UART串口接收中断回调函数代码分析

            CubeMx中HAL库函数的调用不同于库函数调用,在学习CubeMx串口通信时,不理解HAL库中的回调函数是怎么被调用的,于是查看每个的定义,参考其他人写的博客,总算弄明白了HAL库中断调用与库函数不同之处。写下这篇博客一是加深自己的理解,二是希望对不理解HA

    2024年02月02日
    浏览(28)
  • 【正点原子STM32】RS485串行通信标准(串口基础协议 和 MODBUS协议、总线连接、通信电路、通信波形图、RS485相关HAL库驱动、RS485配置步骤、)

    一、RS485介绍 二、RS485相关HAL库驱动介绍 三、RS485配置步骤 四、编程实战 五、总结 串口、UART、TTL、RS232、RS422和RS485之间的关系可以如此理解: 串口 :是一个广义术语,通常指的是采用串行通信协议的接口,它可以包括多种具体的物理接口标准和逻辑电平标准。 UART (通用

    2024年04月13日
    浏览(29)
  • 0.96OLED 4针IIC STM32-HAL库版本(附源码)

    OLED源码放在文章末,有需要自己下滑取用即可。关于如何移植到自己定义的引脚上也做了说明。 另外,本人在代码中封装了一个OLED显示的接口,方便开发者对字符显示位置的快速定位,以及像C语言printf()函数一样在屏幕上进行int,float,char等变量的格式化输出。非常方便实用

    2023年04月26日
    浏览(69)
  • STM32 Cube MX 之hal库软件模拟IIC 可直接移植使用

    此为软件模拟IIC,可以直接移植到HAL库使用。.h文件需要自己做函数声明这里就不再放出,如有问题大家可以讨论。 使用的时候只需要更改SDA 和SCL引脚的宏定义就可以移植使用,当然IIC协议其实就是根据IIC的时序图编写代码,主要内容就是包括开始信号,停止信号以及发送数

    2024年02月15日
    浏览(22)
  • stm32之IIC协议

    主要通过两个层面来讲: 物理层、协议层。 I IC 是一个同步半双工串行总线协议。 一、物理层(通信模型) 1、最早是 飞利浦 公司开发的这个协议,最早应用到其产品上去。 2、两线制(两根信号线) 其中SCL为时钟线,SDA为数据线。   3、挂载在IIC总线上的设备有主从之分

    2024年02月10日
    浏览(16)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包