2025年STM32通信硬件 I2C

STM32通信硬件 I2C20 1 关于 I2C STM32F103 系列的 I C 控制器 可作为通信主机或从机 因此有四种工作模式可选择 主机发送模式 主机接收模式 从机发送模式 从机接收模式 传输速度上 支持标准模式 Standard mode 最高速度 100kHz 和快速模式 Fast

大家好,我是讯享网,很高兴认识大家。

20.1关于 I2C

STM32F103系列的I²C控制器,可作为通信主机或从机,因此有四种工作模式可选择:主机发送模式、主机接收模式、从机发送模式、从机接收模式。

传输速度上,支持标准模式(Standard mode,最高速度100kHz)和快速模式(Fast mode,最高速度400kHz)。同时,还支持SMBus2.0(System Management Bus,系统管理总线)和PMBus (Power Management Bus,电源管理总线)。

I²C控制器结构如图 20.1.1 所示,可以看作四部分组成。

  1. 引脚:I²C协议只需要两个引脚(SDA和SCL),SMBA引脚仅用于SMBus模式的Alert引脚,通常不用管。
  2. 数据收发:主要涉及到数据寄存器(Data Register,DR)和数据移位寄存器(Data Shift Register,DSR)。
    当发送数据时,将发送的字节写入DR寄存器,硬件会把DR中的字节搬到DSR中,然后在时钟信号的配 合下,把DSR最高位的数据放到数据线SDA上,并对DSR进行移位操作。
    当接收数据时,数据控制器(Data Control)根据时钟信号,把SDA线上的高低电平转换为“1”或“0”的
    数据,写到DSR的最低位,同时DSR移位操作,当接收完一个字节的8位数据后,把DSR中的数据搬到DR寄存器中。
  3. 时钟信号:时钟控制器(Clock Control)用于驱动同步时钟信号线SCL。通过配置时钟控制寄存器(ClockControl Register,CCR),可以调整SCL的频率。
  4. 控制逻辑:有两个控制寄存器(Control Register 1,CR1)和(Control Register 2,CR2)用于控制逻辑。通过它们可以触发起始和停止信号,做出ACK响应,配置外设时钟频率,开启DMA和中断的功能。同时控制逻辑的状态会反馈到(Status Register 1,SR1)和(Status Register 2,SR2)两个状态寄存器上,根据它们可以知道当前总线是否被占用,本机是主设备还是从设备,数据是否发送完毕等。
    在这里插入图片描述
    讯享网

20.1.2 AP3426 介绍

AP3426芯片集成了光强传感器(ALS:AmbientLight Sensor)、接近传感器(PS: Proximity Sensor)、红 外LED(IR LED),最常见的应用就是手机。当我们接听电话时,耳朵靠近手机前置扬声器附近,也就靠近了该传感器,此时距离传感器就告诉CPU可以关闭屏幕显示,以防误触。光强传感器能识别当前环境光,告诉CPU对应调节屏幕亮度,手机部分传感器如图 20.1.2 所示。

AP3426的结构如图 20.1.3 所示,左边两个光电二极管采集光照的强度,右边一个发光二极管发射940nm的红外光。

由图 20.1.4 可知,两个光电二极管的频谱响应,ALS光电二极管对450nm-700nm波长光有响应,PS光电二极管对850nm-1000nm波长的光有响应。

由图 20.1.5 可知,450nm-700nm波长的光在可见光范围内,而850nm-1000nm波长的光属于红外线。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在明亮环境中,环境光直接照射在ALS和PS上,当物体遮住AP3426,光电二极管的光照强度则会降低,即可判断物体接遮住。

在黑暗环境中,AP3426发出红外线照射在靠近物体上,反射到PS光电二极管上,当物体遮住AP3426, PS光电二极管的光照强度则会降低,即可判断物体遮住。
在这里插入图片描述

20.2 硬件设计

如图 20.2.1 为开发AP3426部分的原理图,U5为AP3426芯片。不同于AT24CXX可以电路设置设备地址,AP3426的设备地址是固定的,由芯片手册可以知为0x1E。

I²C1的SCL使用的PB6引脚,SDA使用的PB7引脚,此外,AP3426的中断引脚连接的PE5。
在这里插入图片描述

20.3 软件设计

20.3.1 软件设计思路

实验目的:本实验通过使用MCU的硬件I2C,获取AP3426的数据。

  1. 初始化I2C协议相关参数:设置速度、寻址长度模式等;
  2. 初始化I2C硬件相关参数:I2C时钟使能、GPIO端口时钟使能、GPIO引脚设置为I2C复用;
  3. 使用HAL提供的I2C对AP3426读写,封装AP3426初始化函数、数据读取函数;
  4. 主函数编写控制逻辑:按下按键KEY1(KEY_U),读取一次AP3426数据,并将数据通过串口打印;

本实验配套代码位于“5_程序源码\12_通信—硬件I2C\”。

20.3.2 软件设计讲解

  1. GPIO选择与接口定义
    首先定义使用的哪一个I2C、SCL和SDA引脚,如代码段 20.3.1 所示。
    代码段 20.3.1 模拟 I2C 引脚相关定义(driver_i2c.h)
/* I2C 硬件相关定义 */ #define I2Cx I2C1 #define I2Cx_CLK_EN() __HAL_RCC_I2C1_CLK_ENABLE() #define I2Cx_ClockSpeed () #define I2Cx_FORCE_RESET() __HAL_RCC_I2C1_FORCE_RESET() #define I2Cx_RELEASE_RESET() __HAL_RCC_I2C1_RELEASE_RESET() #define SCL_PIN GPIO_PIN_6 #define SCL_PORT GPIOB #define SCL_PIN_CLK_EN() __HAL_RCC_GPIOB_CLK_ENABLE() #define SDA_PIN GPIO_PIN_7 #define SDA_PORT GPIOB #define SDA_PIN_CLK_EN() __HAL_RCC_GPIOB_CLK_ENABLE() /* I2C 硬件相关定义结束 */ 

讯享网
  1. 初始化I2C I2C初始化和前面UART初始化类似,包含两部分:协议部分和硬件部分。
    协议部分初始化如代码段 20.3.2 所示。
    代码段 20.3.2 I2C 协议初始化(driver_i2c.c)
讯享网I2C_HandleTypeDef hi2c; /* * 函数名:void I2C_Init(void) * 输入参数: * 输出参数:无 * * 返回值:无 * 函数作用:初始化 I2C 速率和地址格式 */ void I2C_Init(void) { 
    hi2c.Instance = I2Cx; hi2c.Init.ClockSpeed = I2Cx_ClockSpeed; // 设置 SCL 时钟频率(最高 ) hi2c.Init.DutyCycle = I2C_DUTYCYCLE_2; // 设置 I2C 的 SCL 时钟的占空比(都可以) hi2c.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE; // 设置广播呼叫模式(关闭) hi2c.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE; // 设置禁止时钟延长模式(关闭) hi2c.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; // 设置 I2C 寻址长度模式(通常 7bit) hi2c.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE; // 是否使用两个 STM32 的设备地址(关闭) hi2c.Init.OwnAddress1 = 0x0A; // STM32 的设备地址 1(支持 7bit 或 10bit) hi2c.Init.OwnAddress2 = 0; // STM32 的设备地址 2(只支持 7bit) if(HAL_I2C_Init(&hi2c) != HAL_OK) { 
    Error_Handler(); } } 
  • 14~21行:设置I2C协议参数;
    – 14行:设置I2C的传输速率,最高不超过400kHz;
    – 15行:设置SCL时钟的占空比,即低电平时间比高电平时间,可设置有I2C_DutyCycle_2(2:1)和
    I2C_DutyCycle_16_9(16:9),一般要求不高,任意即可;
    – 16行:I2C作为从机模式时,广播呼叫模式设置,通常用不上,关闭即可;
    – 17行:I2C作为从机模式时,禁止时钟延长,通常用不上,关闭即可;
    – 18行:设置I2C寻址长度模式,需要根据所接设备的地址长度决定,通常为7bit;
    – 19行:STM32作为从机模式时,支持同时对两个设备地址作出响应,这里作为主机,关闭即可;
    – 20行:设置STM32的设备地址1,这里作为主机,只要设备地址不与从机一样即可;
    – 21行:设置STM32的设备地址2,没用到,不需要设置;
  • 23行:使用“HAL_I2C_Init()”初始化前面的“hi2c”,“HAL_I2C_Init()”会调用“HAL_I2C_MspInit()”
    进行硬件相关初始化,“HAL_I2C_MspInit()”的内容需要自己编写,如代码段 20.3.3 所示;

代码段 20.3.3 I2C 硬件初始化(driver_i2c.c)

/* * 函数名:void HAL_I2C_MspInit(I2C_HandleTypeDef *hi2c) * 输入参数:hi2c-I2C 句柄 * 输出参数:无 * 返回值:无 * 函数作用:使能 I2C 的时钟,使能引脚时钟,并配置引脚的复用功能 */ void HAL_I2C_MspInit(I2C_HandleTypeDef *hi2c) { 
    GPIO_InitTypeDef GPIO_InitStruct; if(hi2c->Instance==I2Cx) { 
    I2Cx_CLK_EN(); SCL_PIN_CLK_EN(); SDA_PIN_CLK_EN(); GPIO_InitStruct.Pin = SCL_PIN; GPIO_InitStruct.Mode = GPIO_MODE_AF_OD; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(SCL_PORT, &GPIO_InitStruct); GPIO_InitStruct.Pin = SDA_PIN; HAL_GPIO_Init(SDA_PORT, &GPIO_InitStruct); I2Cx_FORCE_RESET(); // 强制复位 I2Cx_RELEASE_RESET(); // 释放复位 } } 

I2C硬件初始化的内容比较简单,依旧是先使能时钟,然后设置引脚复用,最后还需要复位下I2C。初始化后,便可使用HAL库提供的I2C发送/接收函数,HAL提供三种主机收发函数:

  • HAL_I2C_Master_Receive()/HAL_I2C_Master_Transmit():I2C收发数据,使用超时管理模式;
  • HAL_I2C_Master_Receive_IT()/HAL_I2C_Master_Transmit_IT():I2C收发数据,使用中断模式;
  • HAL_I2C_Master_Receive_DMA()/HAL_I2C_Master_Transmit_DMA():I2C收发数据,使用DMA模式;这里三种收发函数都可满足需求,这里简单处理,没有使用中断和DMA,因此使用超时管理模式。
  1. 初始化和读写AP3426
    由AP3426数据手册,可知AP3426写时序如图 20.3.1 所示,首先发送设备地址,其次是命令代码(寄存
    器地址),最后是数据内容。根据时序,编写代码如代码段 20.3.4 所示。
    在这里插入图片描述
    代码段 20.3.4 AP3426 写时序(driver_ap3426.c)
讯享网/* * 函数名:void AP3426_WriteOneByte(uint8_t reg, uint8_t data) * 输入参数:reg 待写 AP3426 寄存器地址 data 待写数据 * 输出参数:无 * * 返回值:无 * 函数作用:写 AP3426 一字节数据 */ void AP3426_WriteOneByte(uint8_t reg, uint8_t data) { 
    uint16_t write_data = reg | (data<<8); if(HAL_I2C_Master_Transmit(&hi2c, (AP3426_ADDR << 1) | AP3426_W , (uint8_t*)&write_data, 2, 300) != HAL_OK) { 
    Error_Handler(); } while (HAL_I2C_GetState(&hi2c) != HAL_I2C_STATE_READY); } 
  • 10行:将寄存器地址和待发送的数据,组成一个数据;
  • 12行:使用“HAL_I2C_Master_Transmit()”发送数据,该函数需要五个参数:
    – 第一个:指定哪一个I2C;
    – 第二个:指定设备地址,最后一位为读/写位;
    – 第三个:待传输的数据所指向的指针;
    – 第四个:指定数据大小,前面将寄存器地址和待发送数据组在了一次,因此这里数据长度为2;
    – 第五个:指定超时时间,多长时间没有收到响应信号则表示传输失败;
  • 16行:等待正常传输完成;

由AP3426数据手册,可知AP3426读时序如图 20.3.1 所示,首先发送设备地址,其次是命令代码(寄存器地址),然后重新启动,发送设备地址,最后读取数据内容。根据时序,编写代码如图 20.3.2 所示。
在这里插入图片描述
代码段 20.3.5 AP3426 读时序(driver_ap3426.c)

/* * 函数名:uint8_t AP3426_ReadOneByte(uint8_t reg) * 输入参数:reg 待读 AP3426 寄存器地址 * 输出参数:无 * 返回值:读取的 AP3426 数据 * 函数作用:读 AP3426 一字节数据 */ uint8_t AP3426_ReadOneByte(uint8_t reg) { 
    uint8_t read_data = 0; if(HAL_I2C_Master_Transmit(&hi2c, (AP3426_ADDR << 1) | AP3426_W , (uint8_t*)&reg, 1, 300) != HAL_OK) { 
    Error_Handler(); } while (HAL_I2C_GetState(&hi2c) != HAL_I2C_STATE_READY); if(HAL_I2C_Master_Receive(&hi2c, (AP3426_ADDR << 1) | AP3426_R , (uint8_t*)&read_data, 1, 300) != HAL_OK) { 
    Error_Handler(); } return read_data; } 
  • 12行:使用“HAL_I2C_Master_Transmit()”发送寄存器地址;
  • 18行:使用“HAL_I2C_Master_Receive ()”读取寄存器数据;

AP3426的读写时序,分别与前面AT24Cxx随机读、字节写时序非常类似。对于其它I2C设备/模块,也差不多。

有了读写AP3426的函数,便可以操作寄存器,初始化和读取AP3426数据。AP3426的初始化比较简单,流程如下:

  1. 复位:设置System Control寄存器(地址:0x00)为“SW reset”(值:0x04);
  2. 设置工作模式:设置System Control寄存器(地址:0x00)为“ALS and PS+IR functions active”(值:
    0x03),即IR+PS+ALS三个都激活使用;
  3. 设置中断(这里没使用中断);
    在这里插入图片描述
    代码段 20.3.6 AP3426 初始化(driver_ap3426.c)
讯享网/* * 函数名:uint8_t AP3426_Init(void) * 输入参数:无 * 输出参数:无 * 返回值:0 成功 1 失败 * 函数作用:初始化 AP3426 */ uint8_t AP3426_Init(void) { 
    uint8_t ret_value = 0; AP3426_WriteOneByte(SYS_CONFIG_ADDR, SYS_SW_RESET); // 系统软件复位 HAL_Delay(50); AP3426_WriteOneByte(SYS_CONFIG_ADDR, SYS_ALS_PS_IR_ACT); // IR+PS+ALS 三个都激活使用 HAL_Delay(50); ret_value = AP3426_ReadOneByte(SYS_CONFIG_ADDR); // 读取配置寄存器值 if(ret_value != SYS_ALS_PS_IR_ACT) // 判断是否与设置的一致 { 
    return 1; } printf("I2C 配置寄存器:0x%x\n\r", SYS_CONFIG_ADDR); printf("I2C 配置值为:0x%x\n\r", SYS_ALS_PS_IR_ACT); printf("I2C 读取到的配置值:0x%x\n\r", ret_value); return 0; } 
  • 11行:复位AP3426;
  • 14行:设置AP3426的IR(环境红外光)、PS(距离感应)、ALS(光照强度)都激活使用;
  • 16~20行:读取System Control寄存器的值,以便确认是否设置正确;
  • 21~23行:打印调试信息;

初始化配置完后,就可以读取AP3426寄存器数据,如图 20.3.4 所示,分别是IR、ALS、PS的数据寄存器,每个数据占据相邻两位。以IR为例,分别读取IR_DATA_LOW(0x0A)的8位和IR_DATA_HIGH(0x0B)寄存器的低两位,再合并就得到IR的数据,同理可以得到ALS、PS的数据,如代码段 20.3.7 所示。
在这里插入图片描述
代码段 20.3.7 获取 AP3426 数据(driver_ap3426.c)

/* * 函数名:void AP3426_Read_IR_Data(uint16_t *pIR) * 输入参数:无 * 输出参数:IR 数据 * 返回值:无 * 函数作用:读 AP3426 IR(环境红外光)数据 */ void AP3426_Read_IR_Data(uint16_t *pIR) { 
    uint8_t ir_l = 0, ir_h = 0; ir_l = AP3426_ReadOneByte(IR_DATA_LOW); ir_h = AP3426_ReadOneByte(IR_DATA_HIGH); if( (ir_l&0x80) == 0x80) // Invalid IR and PS data { 
    *pIR = 0; } else // ir_l Bit[1:0] is data, ps_l bits[3:0] ans ps_h bits[5:0] is data { 
    *pIR = (ir_h<<8) | (ir_l&0x03); } } /* * 函数名:void AP3426_Read_PS_Data(uint16_t *pPS) * 输入参数:无 * 输出参数:PS 数据 * 返回值:无 * 函数作用:读 AP3426 PS(距离)数据 */ void AP3426_Read_PS_Data(uint16_t *pPS) { 
    uint8_t ps_l = 0, ps_h = 0; ps_l = AP3426_ReadOneByte(PS_DATA_LOW); ps_h = AP3426_ReadOneByte(PS_DATA_HIGH); if( (ps_l&0x40)==0x40) // Invalid IR and PS data { 
    *pPS = 0; } else // ir_l Bit[1:0] is data, ps_l bits[3:0] ans ps_h bits[5:0] is data { 
    *pPS = ((ps_h&0x1F)<<8) | (ps_l&0x0F); } } /* * 函数名:vvoid AP3426_Read_ALS_Data(uint16_t *pALS) * 输入参数:无 * 输出参数:ALS 数据 * 返回值:无 * 函数作用:读 AP3426 ALS(光照)数据 */ void AP3426_Read_ALS_Data(uint16_t *pALS) { 
    uint8_t als_l = 0, als_h = 0; als_l = AP3426_ReadOneByte(ALS_DATA_LOW); als_h = AP3426_ReadOneByte(ALS_DATA_HIGH); *pALS = (als_h<<8) | (als_l); } 
讯享网/* * 函数名:void AP3426_ReadDataTest(void) * 输入参数:无 * 输出参数:无 * 返回值:无 * 函数作用:测试读取 AP3426 所有数据 */ void AP3426_ReadDataTest(void) { 
    uint16_t ir = 0, ps = 0, als = 0; AP3426_Read_IR_Data(&ir); AP3426_Read_PS_Data(&ps); AP3426_Read_ALS_Data(&als); printf("\n\r"); printf("AP3426 IR = 0x%x\n\r", ir); printf("AP3426 PS = 0x%x\n\r", ps); printf("AP3426 ALS = 0x%x\n\r", als); } 

最后还需设置调试串口和用户按键,相关代码这里不在赘述。

  1. 主函数控制逻辑
    在主函数里,每按一下按键,调用“AP3426_ReadDataTest()”获取一次AP3426数据,如代码段 20.3.9所示。
    代码段 20.3.9 主函数控制逻辑(main.c)
// 初始化按键 KeyInit(); // 在 windows 下字符串\n\r 表示回车 // 如果工程在编译下面这句中文的时候报错,请在“Option for target”->"C/C++"->"Misc Controls"添加“ --locale=english” printf("\n\r"); printf("-->百问科技:www.100ask.net\n\r"); printf("-->硬件 I2C 读取 AP3426 实验\n\r"); printf("\n\r"); // 初始化 I2C I2C_Init(); // 初始化 AP3426 AP3426_Init(); while(1) { 
    if(key_flag) // 按键按下 { 
    key_flag = 0; AP3426_ReadDataTest(); // 读取 AP3426 数据 } } 

20.4 实验效果

本实验对应配套资料的“5_程序源码\ 12_通信—硬件I2C\”。打开工程后,编译,下载,按下按键KEY1(KEY_U)即可获取一次AP3426数据并在串口打印。

AP3426在图 3.3.1 中编号37处。分别在正常情况、手电筒照射AP3426、遮住AP3426的情况下,获取数据如图 20.4.1 所示。

在这里插入图片描述


百问网开发板:
淘宝:https://100ask.taobao.com/
天猫:https://weidongshan.tmall.com/

小讯
上一篇 2025-03-15 07:36
下一篇 2025-03-29 13:59

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/62989.html