STM32与ADS1110通过I2C模拟通信的驱动实现
简介:本文围绕STM32微控制器与ADS1110高精度ADC芯片之间的I2C通信展开,介绍了如何通过STM32配置I2C外设实现对ADS1110的模拟驱动。内容涵盖GPIO配置、I2C协议解析、寄存器设置、数据读取及错误处理等关键步骤,旨在帮助开发者构建高效可靠的嵌入式信号采集系统。配套的驱动代码可直接用于项目实践,提升开发效率。 
1. STM32与ADS1110高精度ADC采集系统概述
在嵌入式系统中,ADC(模数转换器)是实现模拟信号数字化处理的关键组件。随着工业控制、传感器网络与智能设备对测量精度要求的不断提升,高精度ADC的应用变得尤为重要。ADS1110是一款内置PGA(可编程增益放大器)的12位ADC芯片,具备I2C数字接口,适用于与STM32系列微控制器进行高效通信。STM32凭借其强大的处理能力和丰富的外设资源,成为本系统的核心控制器。本文将围绕STM32与ADS1110构建的高精度ADC采集系统展开,详细介绍其硬件配置、驱动开发与数据采集优化策略,旨在为工业测量、环境监测和智能传感器网络提供可靠的技术实现方案。
2. STM32 I2C总线通信基础
2.1 I2C总线协议的基本原理
2.1.1 I2C的物理层结构
I2C(Inter-Integrated Circuit)总线是一种广泛应用于嵌入式系统中的双线串行通信协议。它由Philips公司(现NXP)于1980年代提出,专为在单个电路板上的集成电路之间进行短距离通信而设计。其物理层由两根信号线组成:
- SCL(Serial Clock) :串行时钟线,由主设备生成,用于同步数据传输。
- SDA(Serial Data) :串行数据线,用于传输数据位。
这两根线均为开漏输出结构,通常需要外部上拉电阻连接到电源,以确保高电平状态的稳定。这种设计允许多个设备共享同一总线,无需复杂的总线仲裁机制。
| 信号线 | 类型 | 功能描述 |
|---|---|---|
| SCL | 输出 | 时钟信号,由主设备驱动 |
| SDA | 输入/输出 | 数据信号,主从设备均可读写 |
2.1.2 主从设备通信机制
I2C通信采用主从结构,总线上的设备分为 主设备 (Master)和 从设备 (Slave)。主设备负责发起通信、生成时钟信号(SCL),并决定数据的传输方向(读或写)。
每个从设备都有一个 唯一的7位或10位地址 ,主设备通过发送该地址来选择与哪个从设备通信。一次完整的I2C通信通常包括以下步骤:
- 起始信号(START) :主设备将SDA从高拉低,同时SCL保持高电平,表示通信开始。
- 地址帧(Address) :主设备发送7位从设备地址 + 1位读写标志(R/W)。
- 数据帧(Data) :主设备和从设备之间交换数据,每次传输8位数据。
- 应答信号(ACK/NACK) :每传输完一个字节后,接收方必须发送一个应答位(ACK = 0 表示接收成功,NACK = 1 表示接收失败)。
- 停止信号(STOP) :主设备将SDA从低拉高,同时SCL保持高电平,表示通信结束。
2.1.3 数据传输的同步机制
I2C协议采用 同步串行通信 方式,所有数据传输都由SCL时钟信号控制。数据在SCL上升沿被采样,下降沿期间允许SDA状态改变。因此,SDA的变化必须发生在SCL为低电平时,否则可能被误认为是起始或停止信号。
下面是一个典型的I2C通信时序图(使用Mermaid格式):
sequenceDiagram
participant Master
participant Slave
Master->>Slave: START
Master->>Slave: 7位地址 + R/W
Slave-->>Master: ACK
Master->>Slave: Data Byte 1
Slave-->>Master: ACK
Master->>Slave: Data Byte 2
Slave-->>Master: NACK
Master->>Slave: STOP
在这个流程中,主设备发送了两个数据字节,第二个字节接收方返回NACK,表明不再接收更多数据。
2.2 STM32中的I2C外设功能
2.2.1 支持的I2C模式(标准/快速/高速)
STM32系列MCU的I2C外设支持多种通信速率模式,满足不同应用场景的需求:
| 模式 | 通信速率 | 说明 |
|---|---|---|
| 标准模式(Standard Mode) | 最高100 kbps | 基础模式,广泛兼容 |
| 快速模式(Fast Mode) | 最高400 kbps | 提高通信速度 |
| 高速模式(Fast Mode Plus) | 最高1 Mbps | 需要外部上拉,部分引脚支持 |
在STM32CubeMX配置中,可以设置I2C接口的工作模式。例如,对于I2C1:
hi2c1.Instance = I2C1;
hi2c1.Init.ClockSpeed = 400000; // 快速模式
hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;
hi2c1.Init.OwnAddress1 = 0;
hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
hi2c1.Init.OwnAddress2 = 0;
hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
代码逻辑分析:
ClockSpeed:设置为400000即400kHz,为快速模式。DutyCycle:决定SCL的占空比,I2C_DUTYCYCLE_2表示低电平时间是高电平的2倍。AddressingMode:设置为7位地址模式。DualAddressMode:是否启用双地址识别,这里关闭。GeneralCallMode:是否响应通用地址,关闭。NoStretchMode:是否禁用时钟拉伸,关闭。
2.2.2 I2C寄存器结构与功能
STM32的I2C模块通过多个寄存器控制通信流程。关键寄存器包括:
| 寄存器名 | 功能描述 |
|---|---|
| CR1 | 控制寄存器1,控制使能、ACK、SMBus等 |
| CR2 | 控制寄存器2,设置时钟频率、起始/停止信号等 |
| OAR1/OAR2 | 从设备地址寄存器 |
| DR | 数据寄存器,用于读写数据 |
| SR1/SR2 | 状态寄存器,指示通信状态(如ACK失败、总线忙等) |
例如,在发送数据时,流程如下:
// 启动I2C传输
HAL_I2C_Master_Transmit(&hi2c1, DevAddress, pData, Size, Timeout);
底层操作:
- 设置CR2寄存器中的从设备地址和传输长度。
- 触发起始信号(START)。
- 依次将pData中的每个字节写入DR寄存器。
- 检查SR1寄存器中的TXE(发送寄存器空)标志。
- 收到ACK后继续发送下一个字节。
- 所有字节发送完成后,发送STOP信号。
2.2.3 中断与DMA机制支持
STM32的I2C模块支持中断和DMA机制,以提高通信效率并减少CPU占用。
- 中断方式 :适用于小数据量通信,便于实时响应。
- DMA方式 :适用于大数据量传输,自动搬运数据,减少CPU干预。
例如,使用DMA接收数据的配置如下:
// 启用DMA接收
HAL_I2C_Master_Receive_DMA(&hi2c1, DevAddress, rxBuffer, bufferSize);
DMA流程分析:
- 主设备发送从设备地址 + 写标志。
- 从设备发送ACK。
- 开始接收数据,DMA控制器自动将DR寄存器的数据搬运到内存缓冲区rxBuffer。
- 数据接收完成后触发DMA中断。
- 在DMA中断处理函数中调用HAL_I2C_MemRxCpltCallback()回调函数。
2.3 I2C通信在嵌入式开发中的典型应用
2.3.1 传感器数据采集
I2C常用于连接各种传感器,如温度传感器(LM75)、加速度计(MPU6050)、ADC芯片(ADS1110)等。以ADS1110为例,读取ADC转换结果的流程如下:
uint8_t reg = 0x00; // CONVERSION寄存器地址
uint8_t data[2];
// 发送寄存器地址
HAL_I2C_Master_Transmit(&hi2c1, ADS1110_ADDRESS, ®, 1, HAL_MAX_DELAY);
// 读取2字节数据
HAL_I2C_Master_Receive(&hi2c1, ADS1110_ADDRESS, data, 2, HAL_MAX_DELAY);
// 组合为12位结果
uint16_t adcValue = (data[0] << 8) | data[1];
逐行分析:
reg = 0x00:指定要读取CONVERSION寄存器。HAL_I2C_Master_Transmit(...):发送寄存器地址。HAL_I2C_Master_Receive(...):读取寄存器内容。adcValue:将两个字节组合为16位数据,实际有效位为12位。
2.3.2 多设备通信拓扑
I2C支持多主/多从结构,允许一个主设备控制多个从设备。典型的拓扑结构如下:
graph TD
A[STM32主设备] -->|SCL| B(I2C总线)
A -->|SDA| B
B --> C(ADS1110)
B --> D(MPU6050)
B --> E(EEPROM)
关键点:
- 每个从设备有独立地址。
- 所有设备共享SCL和SDA信号线。
- 需要合理配置上拉电阻,防止信号衰减。
2.3.3 通信速率与稳定性考量
在设计I2C通信系统时,需考虑以下因素以确保稳定性和可靠性:
| 因素 | 影响 | 优化建议 |
|---|---|---|
| 上拉电阻 | 影响上升时间 | 通常使用4.7kΩ~10kΩ |
| 时钟频率 | 影响通信速度 | 根据从设备最大支持频率设置 |
| 总线长度 | 影响信号完整性 | 不宜过长,建议<30cm |
| 噪声干扰 | 可能导致通信错误 | 使用屏蔽线或降低频率 |
| 时钟拉伸 | 从设备延缓主设备 | 需在代码中处理 |
例如,STM32中可以通过设置 I2C_TIMINGR 寄存器来优化时序:
hi2c1.Instance->TIMINGR = 0x20404786; // 自定义时序参数
该寄存器控制SCL的高/低电平时间、上升沿/下降沿斜率等,适合复杂环境下定制通信时序。
总结:
本章深入讲解了I2C总线协议的基本原理、STM32中I2C外设的硬件结构与配置方法,以及I2C在嵌入式系统中的典型应用。通过代码示例、流程图与表格的结合,帮助读者全面掌握STM32与I2C通信的实现机制与优化策略,为后续ADS1110的集成与高精度ADC采集打下坚实基础。
3. ADS1110 ADC芯片功能特性
ADS1110是一款专为高精度模拟信号采集设计的12位ADC芯片,广泛应用于嵌入式系统中对传感器信号的采集与处理。其内置的PGA(可编程增益放大器)、多种工作模式、以及标准I2C接口,使其在与STM32等微控制器进行通信时具有高度灵活性与易用性。本章将深入探讨ADS1110的核心功能特性、工作模式配置、以及其与I2C总线的连接方式。
3.1 ADS1110芯片概述
ADS1110是一款高精度、低功耗的12位ADC芯片,适用于对电压信号进行高精度测量的场景,如温度传感器、压力传感器、电池电压监测等应用。
3.1.1 高精度12位ADC性能
ADS1110的分辨率高达12位,意味着它可以将输入电压范围划分为4096个等级。以2.048V参考电压为例,其最小分辨电压为:
\text{LSB} = \frac{V_{ref}}{2^{12}} = \frac{2.048}{4096} = 0.5mV
这使得ADS1110在测量微弱信号时具有较高的精度,适用于工业控制、医疗仪器等对精度要求较高的场景。
3.1.2 内置PGA与输入通道配置
ADS1110集成了一个可编程增益放大器(PGA),增益可设置为1、2、4、8或更高的值,使得它可以处理不同幅度的输入信号。例如,当PGA设置为8倍增益时,输入电压范围可缩小为±256mV,从而提高了小信号的测量精度。
该芯片支持差分输入,提供两个可选的差分输入通道(A0-A1 或 A0-GND),允许用户根据应用需求选择合适的输入模式。
| PGA增益 | 输入范围(V) | 分辨率(LSB) |
|---|---|---|
| 1 | ±2.048 | 1mV |
| 2 | ±1.024 | 0.5mV |
| 4 | ±512mV | 0.25mV |
| 8 | ±256mV | 0.125mV |
3.1.3 工作电压与封装形式
ADS1110的工作电压范围为2.7V至5.5V,适应多种供电环境。其采用小型SOT23-6封装,适合在空间受限的嵌入式设备中使用。
3.2 ADS1110的工作模式与配置
ADS1110提供了多种工作模式,包括单次转换模式、连续转换模式、比较器模式等,满足不同应用场景下的数据采集需求。
3.2.1 单次转换与连续转换模式
- 单次转换模式 :适用于需要按需采集数据的场景。每次采集前需向配置寄存器写入启动位,采集完成后进入低功耗状态。
- 连续转换模式 :适用于实时监控应用,芯片会持续进行ADC转换,输出最新数据。
切换模式可通过修改配置寄存器(CONFIG)中的 MODE 位实现。例如,设置 MODE=0 为连续模式, MODE=1 为单次模式。
3.2.2 数据输出率与精度调整
ADS1110支持不同的数据输出速率(Data Rate),从8SPS(每秒采样数)到3300SPS不等。数据速率越高,采样速度越快,但噪声抑制能力下降。用户可以根据系统对精度和速度的需求进行权衡设置。
| 数据速率(SPS) | 等效噪声带宽(Hz) |
|---|---|
| 8 | 6.5 |
| 16 | 13 |
| 32 | 27 |
| 240 | 190 |
| 3300 | 2600 |
3.2.3 比较器模式与阈值检测
ADS1110还支持比较器模式,用户可以设置一个阈值电压,当输入电压超过该阈值时,芯片将触发中断信号。该功能非常适合用于过压检测、电池低电量提醒等应用场景。
设置比较器模式需要配置 CONFIG 寄存器中的 COMP_MODE 和 COMP_POL 位,并通过 HI_THRESH 和 LO_THRESH 寄存器设置上下限阈值。
3.3 ADS1110与I2C接口的连接方式
ADS1110通过标准I2C总线与主控制器(如STM32)通信,其通信接口包括SCL(时钟线)和SDA(数据线)。
3.3.1 SCL/SDA引脚功能说明
- SCL :I2C时钟线,由主设备控制,用于同步数据传输。
- SDA :I2C数据线,双向传输数据,支持开漏输出。
在硬件设计中,SCL和SDA引脚通常需要外接上拉电阻(通常为4.7kΩ),以确保信号的完整性。ADS1110支持I2C总线的400kHz标准速率,也兼容100kHz的低速模式。
3.3.2 地址选择与多设备共用I2C总线
ADS1110的I2C地址由 ADDR 引脚决定,该引脚可以接GND、VDD、SDA或SCL,从而实现四种不同的地址配置:
| ADDR引脚连接 | I2C地址(7位) |
|---|---|
| GND | 0x48 |
| VDD | 0x49 |
| SDA | 0x4A |
| SCL | 0x4B |
这种设计允许在同一I2C总线上挂载多个ADS1110芯片,适用于多通道ADC采集系统。
3.3.3 上拉电阻与信号完整性设计
为了确保I2C通信的稳定性,必须在SCL和SDA线上添加上拉电阻。上拉电阻的值取决于总线电容和通信速率。通常使用4.7kΩ的上拉电阻即可满足大多数应用需求。
此外,建议在PCB布局时将ADS1110靠近主控芯片,并使用短而直的走线以减少寄生电感和电容对信号完整性的影响。
graph TD
A[STM32] -->|SCL| B[ADS1110]
A -->|SDA| B
A -->|ADDR| B
A -->|VDD| B
A -->|GND| B
A -->|INT| B
如上图所示,典型的ADS1110与STM32之间的连接包括SCL、SDA、电源、地以及可选的中断引脚(INT),用于触发比较器事件。
本章从ADS1110的硬件特性、工作模式配置到I2C接口设计进行了详细分析。ADS1110凭借其高精度、低功耗、多通道支持以及灵活的I2C通信能力,成为嵌入式系统中理想的ADC采集解决方案。下一章将介绍如何在STM32平台上进行I2C总线的初始化与配置,为ADS1110的通信做好准备。
4. STM32 I2C驱动的硬件配置与初始化
在嵌入式系统开发中,I2C总线是一种常用的串行通信接口,尤其适用于连接低速外设如传感器、EEPROM、ADC等模块。STM32系列微控制器集成了I2C硬件外设,支持标准模式(100kHz)、快速模式(400kHz)甚至高速模式(3.4MHz),为开发者提供了高效、稳定的通信方式。本章将深入探讨STM32中I2C通信的硬件配置与初始化流程,涵盖GPIO引脚配置、外设初始化参数设置以及HAL/LL库的驱动实现。
4.1 STM32 GPIO引脚配置(SCL/SDA)
在使用STM32的I2C功能之前,必须对SCL(串行时钟线)和SDA(串行数据线)引脚进行正确的配置。这一步是I2C通信成功的基础。
4.1.1 推挽输出与开漏模式选择
I2C总线在物理层上采用开漏输出结构,因为多个设备可以共享同一总线,因此必须使用外部上拉电阻。在STM32中,SCL和SDA引脚应配置为 复用开漏输出模式 (GPIO_MODE_OUTPUT_OD)。
GPIO_InitStruct.Pin = GPIO_PIN_6 | GPIO_PIN_7; // 假设使用I2C1的SCL和SDA
GPIO_InitStruct.Mode = GPIO_MODE_AF_OD; // 复用开漏模式
GPIO_InitStruct.Alternate = GPIO_AF4_I2C1; // 设置复用功能为I2C1
GPIO_InitStruct.Pull = GPIO_NOPULL; // 不使用内部上下拉
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
逻辑分析与参数说明:
GPIO_MODE_AF_OD:表示该引脚用于复用功能,并以开漏方式输出。GPIO_AF4_I2C1:指定引脚的复用功能为I2C1。GPIO_NOPULL:I2C总线需外部上拉电阻,因此不使用内部上下拉。GPIO_SPEED_FREQ_VERY_HIGH:虽然I2C是低速通信,但引脚速度应设置为高速以保证时序准确。
4.1.2 引脚复用功能配置
STM32的每个GPIO引脚可以配置为多种复用功能。使用I2C时,必须将引脚映射到对应的I2C外设功能上。
示例:使用I2C1的SCL/SDA引脚
| 引脚名称 | 默认复用功能 |
|---|---|
| PB6 | I2C1_SCL |
| PB7 | I2C1_SDA |
若使用重映射引脚,例如I2C1的SDA映射到PB9、SCL映射到PB8,则需启用重映射功能。
4.1.3 上拉电阻与信号完整性设计
由于I2C使用开漏输出,SCL和SDA信号线必须通过上拉电阻接到VCC。典型值为4.7kΩ或10kΩ,取决于总线速率和电容负载。
表:不同速率下推荐的上拉电阻值
| 通信速率 | 推荐上拉电阻 |
|---|---|
| 标准模式(100kHz) | 4.7kΩ |
| 快速模式(400kHz) | 2.2kΩ |
| 高速模式(3.4MHz) | 1kΩ |
设计注意事项:
- 总线上的电容不能超过400pF;
- 若连接多个设备,需注意电容叠加;
- 使用低ESR电容滤波电源,确保VCC稳定。
4.2 STM32 I2C外设初始化流程
在完成GPIO配置后,接下来是I2C外设本身的初始化。这包括时钟源选择、波特率设置以及中断和DMA的配置。
4.2.1 I2C时钟源配置
STM32的I2C外设支持多种时钟源,通常使用系统时钟(SYSCLK)作为输入。在初始化结构体中需指定时钟源。
hi2c1.Instance = I2C1;
hi2c1.Init.ClockSpeed = 100000; // 100kHz
hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;
hi2c1.Init.OwnAddress1 = 0;
hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
hi2c1.Init.DualAddress = I2C_DUALADDRESS_DISABLE;
hi2c1.Init.OwnAddress2 = 0;
hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
逻辑分析与参数说明:
ClockSpeed:设置I2C通信速率;DutyCycle:决定SCL高电平时间,标准模式通常使用I2C_DUTYCYCLE_2;AddressingMode:选择7位或10位地址模式;OwnAddress1:主设备无需设置,设为0;GeneralCallMode:是否启用广播地址;NoStretchMode:是否禁用时钟拉伸。
4.2.2 波特率与时序参数设置
I2C的波特率计算依赖于系统时钟和时钟预分频器。公式如下:
ClockSpeed = F_SCL = F_PCLK / (CCR * (1 + DUTY))
其中:
- F_PCLK:I2C外设的时钟源频率;
- CCR:时钟控制寄存器;
- DUTY:占空比(2或16/9)。
例如:若F_PCLK=80MHz,希望F_SCL=100kHz,可设置CCR=400。
4.2.3 中断与DMA通道配置
为了提高I2C通信效率,可以启用中断或DMA方式。
使用中断方式初始化:
HAL_NVIC_SetPriority(I2C1_EV_IRQn, 0, 1);
HAL_NVIC_EnableIRQ(I2C1_EV_IRQn);
使用DMA方式初始化:
hdma_i2c1_tx.Instance = DMA1_Stream6;
hdma_i2c1_tx.Init.Channel = DMA_CHANNEL_1;
hdma_i2c1_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
hdma_i2c1_tx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_i2c1_tx.Init.MemInc = DMA_MINC_ENABLE;
hdma_i2c1_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_i2c1_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_i2c1_tx.Init.Mode = DMA_NORMAL;
hdma_i2c1_tx.Init.Priority = DMA_PRIORITY_HIGH;
hdma_i2c1_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
逻辑分析:
DMA_DIRECTION:内存到外设传输,适用于发送数据;MemInc = DMA_MINC_ENABLE:内存地址递增;PeriphDataAlignment:按字节对齐;DMA_PRIORITY_HIGH:确保I2C传输优先级较高;FIFOMode:关闭FIFO,适用于简单传输。
4.3 STM32 HAL/LL库中I2C驱动实现
STM32提供了HAL库和LL库两种方式操作I2C。HAL库封装度高,适合快速开发;LL库更贴近寄存器,适合性能优化。
4.3.1 HAL库中的I2C初始化函数
使用HAL库时,通常调用 HAL_I2C_Init() 函数完成初始化。
if (HAL_I2C_Init(&hi2c1) != HAL_OK)
{
Error_Handler();
}
HAL库内部会调用 I2C_Init() 函数,设置寄存器CR1、CR2、CCR、TRISE等,完成I2C外设配置。
4.3.2 LL库的底层寄存器操作
使用LL库可以直接操作寄存器,提高执行效率。
LL_I2C_InitTypeDef I2C_InitStruct = {0};
I2C_InitStruct.PeripheralMode = LL_I2C_MODE_I2C;
I2C_InitStruct.ClockSpeed = 100000;
I2C_InitStruct.DutyCycle = LL_I2C_DUTYCYCLE_2;
I2C_InitStruct.OwnAddress1 = 0;
I2C_InitStruct.TypeAcknowledge = LL_I2C_ACK;
I2C_InitStruct.OwnAddrSize = LL_I2C_OWNADDRESS1_7BIT;
LL_I2C_Init(I2C1, &I2C_InitStruct);
LL_I2C_Enable(I2C1);
逻辑分析:
PeripheralMode:设置为I2C模式;ClockSpeed:波特率;TypeAcknowledge:是否启用应答;OwnAddrSize:7位地址格式;LL_I2C_Enable():启用I2C外设。
4.3.3 数据发送与接收函数实现
HAL库提供了如下发送与接收函数:
// 发送数据
HAL_I2C_Master_Transmit(&hi2c1, dev_addr, tx_data, size, HAL_MAX_DELAY);
// 接收数据
HAL_I2C_Master_Receive(&hi2c1, dev_addr, rx_data, size, HAL_MAX_DELAY);
LL库中需手动控制状态机:
LL_I2C_GenerateStartCondition(I2C1);
while (!LL_I2C_IsActiveFlag_SB(I2C1));
LL_I2C_TransmitData8(I2C1, dev_addr << 1);
while (!LL_I2C_IsActiveFlag_ADDR(I2C1));
LL_I2C_ClearFlag_ADDR(I2C1);
for (int i = 0; i < size; i++) {
LL_I2C_TransmitData8(I2C1, tx_data[i]);
while (!LL_I2C_IsActiveFlag_TXE(I2C1));
}
LL_I2C_GenerateStopCondition(I2C1);
状态机流程图(mermaid):
graph TD
A[Start Condition] --> B[发送设备地址]
B --> C{地址应答?}
C -->|Yes| D[发送数据]
D --> E[等待发送完成]
E --> F[Stop Condition]
C -->|No| G[错误处理]
本章从GPIO引脚配置入手,逐步介绍了STM32 I2C外设的初始化流程,并对比了HAL库与LL库的实现方式。下一章将继续深入I2C协议本身,分析其通信流程与错误处理机制。
5. I2C通信协议详解与实现
在嵌入式系统中,I2C(Inter-Integrated Circuit)协议因其结构简洁、通信稳定、支持多主多从架构等优点,被广泛应用于芯片间的短距离通信。STM32系列微控制器集成了I2C硬件接口,同时也支持通过GPIO模拟实现I2C通信。本章将深入剖析I2C协议的基本流程,探讨STM32模拟I2C通信的实现方法,并介绍常见的错误检测与恢复机制。
5.1 I2C通信协议基本流程
I2C总线是一种同步串行通信接口,由Philips(现NXP)开发。其核心特点包括双线制(SCL为时钟线,SDA为数据线)、支持多主设备和多从设备、数据传输速率可调(标准模式100kbps、快速模式400kbps、高速模式3.4Mbps)。
5.1.1 起始信号与停止信号
I2C通信的起始(START)和停止(STOP)信号由主设备发起,用于标识一次通信的开始与结束。
- 起始信号(START) :SCL为高电平时,SDA由高变低。
- 停止信号(STOP) :SCL为高电平时,SDA由低变高。
这些信号是I2C通信的基础,确保从设备能够识别主设备的通信请求。
5.1.2 地址帧与数据帧格式
每次I2C通信开始后,主设备会发送一个地址帧,用于选择目标从设备。地址帧为8位,其中前7位为从设备地址,最后1位为读写标志(0表示写,1表示读)。
随后是数据帧的传输,每个数据帧为8位,高位(MSB)先传。每发送一个字节后,接收方需要发送一个应答信号(ACK),表示接收成功。
| 字段 | 长度 | 说明 |
|---|---|---|
| 设备地址 | 7位 | 从设备的唯一标识 |
| R/W位 | 1位 | 0表示写,1表示读 |
| 数据字节 | 8位 | 每次传输的数据 |
| ACK/NACK | 1位 | 应答/非应答信号 |
5.1.3 应答机制与数据有效性
在每个字节传输后,接收设备需返回一个ACK或NACK信号:
- ACK(应答) :SDA在第9个时钟周期为低电平。
- NACK(非应答) :SDA在第9个时钟周期为高电平。
这为通信的可靠性和错误检测提供了基础。主设备在接收到NACK时,通常表示从设备未准备好或地址无效。
5.2 STM32模拟I2C通信实现
虽然STM32具有硬件I2C模块,但在某些应用场景中(如硬件模块被占用或调试需求),开发者可能需要使用GPIO模拟I2C通信。这种方式虽然效率略低,但具有高度灵活性。
5.2.1 模拟I2C的时序控制方法
模拟I2C的核心是通过控制GPIO引脚模拟SCL和SDA信号的时序。主要步骤包括:
- 起始信号生成 :SCL高电平时,SDA下降沿。
- 地址/数据发送 :逐位发送数据,高位先传。
- 应答信号检测 :等待从设备发送ACK/NACK。
- 停止信号生成 :SCL高电平时,SDA上升沿。
以下是一个简单的模拟I2C起始信号函数示例:
void I2C_Start(void) {
SDA_HIGH(); // 初始化SDA为高
SCL_HIGH(); // SCL拉高
Delay_us(1); // 延时以满足建立时间
SDA_LOW(); // SDA下降沿表示起始信号
Delay_us(1);
SCL_LOW(); // SCL拉低准备发送数据
}
逻辑分析 :
-SDA_HIGH()和SCL_HIGH()设置GPIO为高电平。
-Delay_us(1)用于满足I2C时序要求(建立时间和保持时间)。
-SDA_LOW()产生起始信号。
- 最后将SCL拉低,进入数据发送阶段。
5.2.2 模拟I2C的GPIO操作函数
除了起始信号外,还需要实现以下基本操作函数:
void I2C_WriteByte(uint8_t data) {
for (int i = 0; i < 8; i++) {
if (data & 0x80) {
SDA_HIGH(); // 发送高位
} else {
SDA_LOW();
}
SCL_HIGH(); // 拉高SCL
Delay_us(1);
SCL_LOW(); // 拉低SCL
data <<= 1; // 左移准备发送下一位
}
// 等待ACK
SDA_HIGH(); // 释放SDA,准备读取ACK
SCL_HIGH();
Delay_us(1);
if (SDA_READ()) {
// NACK
}
SCL_LOW();
}
参数说明 :
-data:要发送的8位数据。
-SDA_READ():读取SDA引脚电平,判断是否收到ACK。
- 该函数逐位发送数据,并在最后检测应答信号。
5.2.3 模拟I2C的通信速率调整
模拟I2C的通信速率受限于GPIO操作速度和延时函数。可以通过调整 Delay_us() 的时间来控制SCL频率,从而调节通信速率。
例如,若希望实现100kHz的速率,则每个SCL周期为10μs。通过控制SCL高/低电平时间,可以实现不同速率的通信。
| 通信速率 | 周期时间 | 高/低时间(各) |
|---|---|---|
| 100kHz | 10μs | 5μs |
| 400kHz | 2.5μs | 1.25μs |
优化建议 :
- 使用硬件定时器实现更精确的延时。
- 若系统资源允许,考虑使用硬件I2C模块。
5.3 I2C通信错误检测与处理
I2C通信中可能出现多种错误类型,如仲裁丢失、总线繁忙、应答失败、通信超时等。良好的错误处理机制是系统稳定性的关键。
5.3.1 常见错误类型与判断方法
| 错误类型 | 原因 | 检测方法 |
|---|---|---|
| 应答失败(NACK) | 从设备不存在或未准备好 | 在发送完地址/数据后未收到ACK |
| 总线繁忙 | 其他主设备正在通信 | 检测SCL或SDA是否被占用 |
| 仲裁丢失 | 多主竞争导致主设备失去控制权 | 硬件I2C中断标志位判断 |
| 通信超时 | 未在规定时间内收到应答或响应 | 设置超时计数器 |
5.3.2 错误恢复机制设计
当检测到错误时,应采取相应的恢复措施,例如:
- 重发机制 :在NACK后重新发送起始信号和地址。
- 总线复位 :若总线被锁死,可通过拉高SCL多次并释放SDA来恢复。
- 中断处理 :在硬件I2C中,利用中断机制快速响应错误。
以下是一个I2C总线复位的示例函数:
void I2C_BusReset(void) {
SCL_HIGH();
SDA_HIGH();
Delay_us(5);
for (int i = 0; i < 9; i++) {
SCL_LOW();
Delay_us(1);
SCL_HIGH();
Delay_us(1);
}
I2C_Start(); // 重新发起起始信号
}
逻辑分析 :
- 拉高SCL和SDA,确保总线空闲。
- 连续发送9个SCL脉冲,强制从设备释放SDA。
- 最后发送起始信号,重新初始化通信。
5.3.3 通信超时与重试机制
为了避免通信卡死,应设置通信超时和重试机制。例如:
#define I2C_TIMEOUT 1000
uint8_t I2C_Write(uint8_t address, uint8_t* data, uint16_t length) {
uint32_t timeout = I2C_TIMEOUT;
I2C_Start();
I2C_WriteByte(address); // 写地址
if (!WaitForAck()) { // 等待ACK
return I2C_ERROR_NACK;
}
for (int i = 0; i < length; i++) {
I2C_WriteByte(data[i]);
if (!WaitForAck()) {
return I2C_ERROR_NACK;
}
}
I2C_Stop();
return I2C_OK;
}
参数说明 :
-address:从设备地址。
-data:要发送的数据缓冲区。
-length:数据长度。
-WaitForAck():等待应答信号,返回是否收到ACK。优化建议 :
- 可引入重试机制,例如失败后重试3次。
- 在RTOS环境中,使用超时任务或事件标志组进行控制。
5.4 小结与拓展
本章详细解析了I2C通信协议的核心流程,包括起始/停止信号、地址/数据帧格式、应答机制等。并深入探讨了如何在STM32上通过GPIO模拟实现I2C通信,提供了完整的代码示例及参数说明。最后,介绍了常见的I2C通信错误类型、判断方法以及恢复机制,确保系统通信的可靠性。
在后续章节中,我们将基于本章内容,结合ADS1110的I2C接口,进一步探讨其寄存器配置与ADC数据采集流程,实现完整的高精度ADC采集系统。
6. ADS1110寄存器配置与ADC转换控制
ADS1110是一款高精度、12位分辨率的I2C接口模数转换器(ADC),广泛用于高精度传感器数据采集系统中。要充分发挥其性能,必须深入了解其寄存器结构和配置方法。本章将深入解析ADS1110的寄存器配置流程,包括配置寄存器(CONFIG)、转换结果寄存器(CONVERSION)的功能与使用方法,并详细说明如何通过STM32控制ADS1110进行单次与连续ADC转换操作。
6.1 ADS1110寄存器结构与功能
ADS1110的寄存器结构相对简洁,主要包含两个寄存器: 配置寄存器(CONFIG) 和 转换结果寄存器(CONVERSION) 。
6.1.1 寄存器地址映射
ADS1110的寄存器地址映射如下:
| 寄存器地址 | 寄存器名称 | 功能描述 |
|---|---|---|
| 0x00 | CONFIG | 控制ADC工作模式、通道选择等配置 |
| 0x01 | CONVERSION | 存储最近一次转换的结果 |
ADS1110的I2C地址为固定的 0x48 (7位地址),可以通过ADDR引脚配置多个设备共用I2C总线。
6.1.2 配置寄存器(CONFIG)详解
配置寄存器(CONFIG)为16位寄存器,其结构如下(MSB到LSB):
| 位号 | 字段名称 | 描述 |
|---|---|---|
| 15 | OS | 单次转换启动位(1: 启动单次转换) |
| 14-12 | MUX | 输入通道选择(000-101:通道0~5,110:温度传感器,111:GND) |
| 11-9 | PGA | 增益设置(001: 1x, 010: 2x, 011: 4x, 100: 8x, 101: 16x) |
| 8 | MODE | 工作模式(0: 连续模式,1: 单次模式) |
| 7-5 | DR | 数据输出速率(001: 128SPS, 010: 250SPS, 011: 490SPS 等) |
| 4 | COMP_MODE | 比较器模式(0: 传统比较器,1: 窗口比较器) |
| 3 | COMP_POL | 比较器输出极性(0: 低有效,1: 高有效) |
| 2 | COMP_LAT | 比较器锁存使能(0: 非锁存,1: 锁存) |
| 1 | COMP_QUE | 比较器队列配置(00: 禁用,01: 1次,10: 2次,11: 4次) |
| 0 | 保留 | 保留位,必须为0 |
6.1.3 转换结果寄存器(CONVERSION)读取
转换结果寄存器(CONVERSION)是一个16位寄存器,存储最新的ADC转换结果。其数据格式如下:
- 12位ADC结果 :占据高位12位(bit15~bit4),为补码格式。
- 低4位(bit3~bit0) :保留位,读出为0。
例如,当输入电压为0V时,读出结果为0x0000;当输入为满量程时(根据增益不同),读出结果为0x7FF(正满量程)或0x800(负满量程)。
6.2 ADS1110寄存器配置方法
正确配置ADS1110是实现高精度ADC采集的前提。本节将介绍初始化配置流程、输入通道与增益设置、以及转换模式与数据速率选择。
6.2.1 初始化配置流程
初始化ADS1110的典型流程如下:
- 设置输入通道(MUX)
- 配置增益(PGA)
- 选择转换模式(单次或连续)
- 设定数据输出速率(DR)
- 写入配置寄存器(CONFIG)
6.2.2 输入通道与增益设置
ADS1110支持6个输入通道,包括5个外部通道和1个内部温度传感器通道。以下为MUX字段设置对照表:
| MUX 值 | 输入通道配置 |
|---|---|
| 000 | AIN0 |
| 001 | AIN1 |
| 010 | AIN2 |
| 011 | AIN3 |
| 100 | AIN0 - AIN1 |
| 101 | AIN2 - AIN3 |
| 110 | 内部温度传感器 |
| 111 | GND |
PGA字段用于设置增益,从而调整输入电压范围。例如:
| PGA 值 | 增益倍数 | 输入范围(Vref=2.048V) |
|---|---|---|
| 001 | 1x | ±2.048V |
| 010 | 2x | ±1.024V |
| 011 | 4x | ±0.512V |
| 100 | 8x | ±0.256V |
| 101 | 16x | ±0.128V |
6.2.3 转换模式与数据速率选择
ADS1110支持两种转换模式:
- 单次转换模式(MODE = 1) :每次转换后自动进入低功耗状态。
- 连续转换模式(MODE = 0) :持续进行转换,适用于需要高频率采样的场景。
数据输出速率(DR)字段控制采样率,常见设置如下:
| DR 值 | 数据输出速率(SPS) |
|---|---|
| 001 | 128 |
| 010 | 250 |
| 011 | 490 |
| 100 | 920 |
6.3 启动ADC转换与状态等待机制
在完成配置后,需要根据工作模式启动ADC转换,并等待结果。
6.3.1 启动单次转换的方法
在单次转换模式下,需要手动启动每次转换。具体步骤如下:
- 设置OS位为1(写入CONFIG寄存器)
- 等待转换完成(通过状态位或延时)
- 读取CONVERSION寄存器获取结果
uint16_t config = 0x8583; // 示例配置:AIN0, 1x增益, 单次模式, 128SPS
HAL_I2C_Mem_Write(&hi2c1, ADS1110_ADDRESS << 1, ADS1110_REG_CONFIG, 1, (uint8_t*)&config, 2, HAL_MAX_DELAY);
逐行解释:
0x8583是配置值,对应:- OS = 1 (启动单次转换)
- MUX = 000 (选择AIN0)
- PGA = 001 (1x增益)
- MODE = 1 (单次模式)
- DR = 001 (128SPS)
HAL_I2C_Mem_Write():通过I2C总线向ADS1110的CONFIG寄存器写入配置值
6.3.2 连续转换的配置与控制
在连续转换模式下,ADC会持续进行转换,适合需要高频率采样的应用场景。配置步骤如下:
- 设置MODE位为0
- 设置OS位为1(仅首次启动)
- 循环读取CONVERSION寄存器获取数据
uint16_t config = 0x0583; // MODE=0: 连续模式
HAL_I2C_Mem_Write(&hi2c1, ADS1110_ADDRESS << 1, ADS1110_REG_CONFIG, 1, (uint8_t*)&config, 2, HAL_MAX_DELAY);
逐行解释:
- MODE位设为0表示进入连续模式
- OS位设为1启动首次转换,后续自动连续运行
6.3.3 状态寄存器查询与中断响应
ADS1110没有专用的状态寄存器,但可以通过以下方式判断转换是否完成:
- 延时等待法 :适用于单次模式,已知转换时间
- 查询CONVERSION寄存器 :在连续模式中,读取新值即可
此外,ADS1110提供一个ALERT引脚,可用于比较器触发中断。通过配置COMP_MODE和阈值寄存器,可以实现超出范围中断通知。
graph TD
A[开始ADC采集] --> B{是否单次模式?}
B -->|是| C[启动单次转换]
B -->|否| D[进入连续模式]
C --> E[等待转换完成]
D --> F[循环读取转换结果]
E --> G[读取CONVERSION寄存器]
F --> G
G --> H[处理ADC数据]
小结
本章深入解析了ADS1110的寄存器结构及其配置方法,重点介绍了如何通过STM32控制其进行ADC转换。通过对配置寄存器(CONFIG)的各个字段进行解析,读者可以灵活配置输入通道、增益、转换模式与采样率。此外,还详细说明了单次与连续转换的启动方式,以及如何读取转换结果并设计中断响应机制。
下一章将继续深入探讨如何将ADC数据转化为实际的物理量(如电压、温度等),并实现完整的采集与应用系统设计。
7. 高精度ADC数据采集与应用设计实践
7.1 读取12位ADC转换结果
ADS1110作为一款12位高精度ADC芯片,其输出结果为12位二进制数,范围为0~4095(对应于输入电压范围),需要根据参考电压(VREF)进行物理量的转换。
7.1.1 数据格式与转换公式
ADS1110的转换结果存储在 CONVERSION 寄存器中(地址为0x00),是一个16位寄存器,其中高4位为符号位(用于表示负电压,但ADS1110仅支持单端输入,通常为正),低12位为有效数据。
// 读取转换结果(假设使用HAL库)
uint16_t read_adc_value(I2C_HandleTypeDef *hi2c, uint8_t dev_addr) {
uint8_t reg = 0x00; // CONVERSION寄存器地址
uint8_t data[2];
// 发送寄存器地址
HAL_I2C_Master_Transmit(hi2c, dev_addr, ®, 1, HAL_MAX_DELAY);
// 读取16位数据
HAL_I2C_Master_Receive(hi2c, dev_addr, data, 2, HAL_MAX_DELAY);
// 合并高位与低位
uint16_t adc_value = (data[0] << 8) | data[1];
return adc_value;
}
7.1.2 温度、电压等物理量的转换方法
以测量电压为例,假设使用1倍增益(PGA=1),则输入电压范围为0~2.048V,对应的ADC值为0~4095。
计算公式如下:
V_{in} = \frac{ADC_{value}}{4095} \times V_{REF}
例如,若测得ADC值为2048,VREF=2.048V,则:
V_{in} = \frac{2048}{4095} \times 2.048 ≈ 1.024V
7.1.3 数据滤波与校准处理
为了提升采集精度,通常采用以下几种方法:
- 滑动平均滤波 :取最近N次采样值的平均值。
- 中值滤波 :剔除异常值后再取平均。
- 软件校准 :通过标准电压进行系统校准,计算偏移量与增益误差。
// 滑动平均滤波示例
#define SAMPLE_BUFFER_SIZE 10
uint16_t buffer[SAMPLE_BUFFER_SIZE];
uint8_t buffer_index = 0;
uint16_t sliding_average_filter(uint16_t new_value) {
buffer[buffer_index++] = new_value;
if (buffer_index >= SAMPLE_BUFFER_SIZE) buffer_index = 0;
uint32_t sum = 0;
for (int i = 0; i < SAMPLE_BUFFER_SIZE; i++) {
sum += buffer[i];
}
return sum / SAMPLE_BUFFER_SIZE;
}
7.2 STM32与ADS1110的完整通信流程
7.2.1 初始化流程与通信顺序
整个通信流程包括:
- 配置STM32的I2C外设(SCL/SDA引脚、时钟源、波特率等)。
- 初始化ADS1110的配置寄存器(CONFIG),设置输入通道、增益、模式等。
- 启动一次ADC转换(单次模式)或设置为连续模式。
- 读取转换结果并进行处理。
7.2.2 数据采集与传输的时序控制
在STM32中使用HAL库进行I2C通信时,必须注意通信时序。ADS1110的转换时间约为1ms(128SPS),因此读取前需等待转换完成,或查询状态位。
graph TD
A[初始化I2C外设] --> B[写入ADS1110配置寄存器]
B --> C{是否为单次转换模式?}
C -->|是| D[发送启动转换命令]
C -->|否| E[设置为连续转换模式]
D --> F[等待转换完成]
E --> G[定时读取转换结果]
F --> H[读取CONVERSION寄存器]
G --> H
H --> I[数据处理与滤波]
7.2.3 完整代码示例与调试方法
void ads1110_init(I2C_HandleTypeDef *hi2c, uint8_t dev_addr) {
uint8_t config[3];
config[0] = 0x01; // CONFIG寄存器地址
config[1] = 0x84; // 单次转换模式,通道0,增益1x
config[2] = 0x83; // 数据速率128SPS,比较器模式关
HAL_I2C_Master_Transmit(hi2c, dev_addr, config, 3, HAL_MAX_DELAY);
}
uint16_t get_adc_value(I2C_HandleTypeDef *hi2c, uint8_t dev_addr) {
uint8_t reg = 0x00;
uint8_t data[2];
HAL_I2C_Master_Transmit(hi2c, dev_addr, ®, 1, HAL_MAX_DELAY);
HAL_I2C_Master_Receive(hi2c, dev_addr, data, 2, HAL_MAX_DELAY);
return (data[0] << 8) | data[1];
}
调试时可使用逻辑分析仪或串口打印转换结果进行验证。
7.3 嵌入式系统中高精度ADC采集应用设计
7.3.1 在传感器系统中的实际应用
ADS1110可用于高精度传感器数据采集,如:
- 压力传感器 :通过放大器将压力信号转换为电压后接入ADS1110。
- 温度传感器 :如PT100搭配恒流源,转换为电压信号后测量。
- pH值检测 :利用差分电压采集pH探头的输出信号。
7.3.2 多通道数据采集与处理
ADS1110支持4个单端输入通道(AIN0~AIN3),可以通过配置寄存器切换通道进行多通道采集:
// 选择AIN1通道
config[1] = 0xA4; // 通道1,增益1x
多通道采集时,建议采用轮询方式依次读取各通道值,并进行滤波处理。
7.3.3 实时性与稳定性优化策略
为提升采集系统的稳定性与实时性,可采取以下措施:
- 中断触发采集 :结合ADS1110的比较器中断功能,实现阈值触发采集。
- DMA传输 :使用DMA提升I2C通信效率,减少CPU占用。
- 硬件滤波 :在ADS1110输入端加RC低通滤波电路。
- 电源去耦 :为ADS1110提供稳定的参考电压,并在VDD加10uF和0.1uF电容。
// 示例:使用中断方式触发采集
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
if (GPIO_Pin == ADS1110_INT_PIN) {
adc_value = get_adc_value(&hi2c1, ADS1110_ADDRESS);
process_adc_data(adc_value);
}
}
简介:本文围绕STM32微控制器与ADS1110高精度ADC芯片之间的I2C通信展开,介绍了如何通过STM32配置I2C外设实现对ADS1110的模拟驱动。内容涵盖GPIO配置、I2C协议解析、寄存器设置、数据读取及错误处理等关键步骤,旨在帮助开发者构建高效可靠的嵌入式信号采集系统。配套的驱动代码可直接用于项目实践,提升开发效率。
openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。
更多推荐



所有评论(0)