为什么买 LHA 的 MCU,我相信不必多言,但是这么好的小东西没有人维护,那就不好看了,作为民间伪官方我自然是要出来指指点点的,这次是解读的官方 demo,双 ADC 同步设计的详细细节,学会了这个,别的 MCU 也是手拿把掐的。
芯片型号: LH32M0G30X
应用场景: 双ADC同步连续采样,24位Sigma-Delta ADC、双通道同步、2.5SPS采样率

围绕 ADC 子模块展开
ADC规格:
├── 分辨率: 24位 Sigma-Delta ADC
├── 采样率: 2.5 SPS (Samples Per Second)
├── 输入模式: 差分输入/单端输入
├── 编码方式: 双极性补码 / 单极性二进制
├── 参考电压: 4.096V / 2.5V 可配置
├── 增益: PGA可编程增益放大器 (1x, 2x, 4x, 8x等)
└── 中断机制: 数据就绪中断
┌─────────────────┐
│ 时钟管理模块 │
└─────────┬───────┘
│
┌───────────────┴───────────────┐
│ │
┌───────▼──────┐ ┌──────▼──────┐
│ ADC0 │◄──────────────►│ ADC1 │
│ │ 同步控制 │ │
└──────┬───────┘ └─────┬───────┘
│ │
┌────────▼────────┐ ┌──────▼──────┐
│ AIN0 (P) / AIN1(N) │ │AVDDP/AVDDN │
│ 外部信号输入 │ │ 内部电源 │
└─────────────────┘ └─────────────┘
void RCC_init(void)
{
/* 系统时钟树配置 */
RCC_SYSCLKConfig(RCC_SW_HSI); // HSI = 32MHz
RCC_HSIDIVConfig(RCC_HSI_Div2); // HSI/2 = 16MHz
RCC_HCLKConfig(RCC_SYSCLK_Div2); // HCLK = 8MHz
RCC_PCLK1Config(RCC_HCLK_Div1); // PCLK1 = 8MHz
RCC_PCLK2Config(RCC_HCLK_Div1); // PCLK2 = 8MHz
}
// ADC子系统时钟配置
RCC_APB2PeriphClockCmd(RCC_APB2ENR_ADC0EN_Msk, ENABLE); // ADC0时钟
RCC_APB2PeriphClockCmd(RCC_APB2ENR_ADC1EN_Msk, ENABLE); // ADC1时钟
RCC_APB2PeriphClockCmd(RCC_APB2ENR_USART0EN_Msk, ENABLE); // UART0时钟
RCC_AHBPeriphClockCmd(RCC_AHBENR_IOPEN_Msk, ENABLE); // GPIO时钟
void VREF_init(void)
{
AFE_REF_CTRL(
VREFP_ENABLE, // 使能正参考电压
VREFP_4V096, // 设置为4.096V (高精度)
VDRIVE_ENABLE, // 使能驱动电压
VDRIVE_2V5, // 驱动电压2.5V
TEMP_SENSER_EN // 使能温度传感器
);
}
参考电压 | 输入范围 | 精度 | LSB值 | 应用场景 |
|---|---|---|---|---|
2.5V | ±2.5V | 298nV | 149nV | 通用测量 |
4.096V | ±4.096V | 488nV | 244nV | 高动态范围 |
void ADC0_1_SIMU_CONT_init(void)
{
ADC_InitTypeDef ADCx_InitStructure;
/* ADC0配置 - 同步模式主控制器 */
ADCx_InitStructure.ADC_Mode = ADC_SIMU_ENABLE; // 启用同步模式
ADCx_InitStructure.ADC_ConvMode = ADC_CONT_CONVERT; // 连续转换模式
ADCx_InitStructure.ADC_TrigMode = ADC_SOFT_TRIG; // 软件触发
/* 输入通道配置 */
ADCx_InitStructure.ADC_INP = ADC0_PCHAN_AIN0; // 正输入: AIN0
ADCx_InitStructure.ADC_INM = ADC0_NCHAN_AIN1; // 负输入: AIN1
/* 信号处理配置 */
ADCx_InitStructure.ADC_CODE_MODE = ADC_CODE_BIPOLAR; // 双极性编码
ADCx_InitStructure.ADC_PGA = PGA_GAIN_1; // 增益1倍
ADCx_InitStructure.ADC_DR = ADC_DR_2P5SPS; // 采样率2.5SPS
/* 参考电压缓冲器配置 */
ADCx_InitStructure.REF_BUFP_MODE = REF_BUFP_ENABLE; // 正参考缓冲使能
ADCx_InitStructure.REF_BUFN_MODE = REF_BUFN_ENABLE; // 负参考缓冲使能
ADCx_InitStructure.ADC_REF_SEL = VREF_REFP_AVSS; // 参考源选择
/* 中断配置 */
ADCx_InitStructure.ADC_RDY_INT = ADC_RDY_INT_EN; // 数据就绪中断使能
ADCx_InitStructure.ADC_RDY_CNT = 1; // 中断计数器
ADCx_init(&ADCx_InitStructure, 0); // 初始化ADC0
}
/* ADC1配置 - 同步模式从控制器 */
ADCx_InitStructure.ADC_Mode = ADC_SIMU_DISABLE; // 从模式 (受ADC0控制)
ADCx_InitStructure.ADC_ConvMode = ADC_CONT_CONVERT; // 连续转换模式
ADCx_InitStructure.ADC_TrigMode = ADC_SOFT_TRIG; // 软件触发
/* 内部电源监测 */
ADCx_InitStructure.ADC_INP = ADC1_PCHAN_AVDDP; // 正输入: 电源正极
ADCx_InitStructure.ADC_INM = ADC1_NCHAN_AVDDN; // 负输入: 电源负极
/* 其他参数与ADC0一致 */
ADCx_InitStructure.ADC_CODE_MODE = ADC_CODE_BIPOLAR;
ADCx_InitStructure.ADC_PGA = PGA_GAIN_1;
ADCx_InitStructure.ADC_DR = ADC_DR_2P5SPS;
ADCx_init(&ADCx_InitStructure, 1); // 初始化ADC1
/* 中断优先级配置 */
NVIC_EnableIRQ(ADC0_IRQn); // 使能ADC0中断
NVIC_DisableIRQ(ADC1_IRQn); // 禁用ADC1独立中断 (在ADC0中断中处理)
/* 启动转换 */
ADC0_convert_start(); // 启动ADC0, ADC1自动同步启动
void ADC0_IRQHandler(void)
{
volatile uint32_t reg_data;
uint8_t adc_code[3];
int32_t adc_hex;
float adc_voltage;
/* 步骤1: 读取状态寄存器 (清除中断标志) */
reg_data = pADC_SUBSYS_USER->ADC_STATUS_0;
/* 步骤2: 读取24位ADC原始数据 */
reg_data = pADC_SUBSYS_USER->ADC_DATA_0;
/* 步骤3: 数据字节分离 */
adc_code[2] = (reg_data >> 16) & 0xFF; // 高字节 [23:16]
adc_code[1] = (reg_data >> 8) & 0xFF; // 中字节 [15:8]
adc_code[0] = (reg_data) & 0xFF; // 低字节 [7:0]
/* 步骤4: 24位数据重组与符号扩展 */
adc_hex = (int32_t)(
((((adc_code[2] & 0x80) ? (0xFF) : (0x00)) ) << 24) | // 符号扩展
((adc_code[2] & 0xFF) << 16) | // 高字节
((adc_code[1] & 0xFF) << 8) | // 中字节
((adc_code[0] & 0xFF) << 0) // 低字节
);
}
24位数据格式 (双极性补码):
┌─────┬─────────────────────────┐
│ S │ 22位数据位 │
└─────┴─────────────────────────┘
MSB LSB
符号扩展逻辑:
if (bit[23] == 1) // 负数
result = 0xFF000000 | 24bit_data
else // 正数
result = 0x00000000 | 24bit_data
算法实现:
adc_code[2] & 0x80 // 检测符号位
? 0xFF : 0x00 // 符号扩展字节
float ADC_DataToMiniVolt_bipolar(int32_t data)
{
float mv;
/* 转换公式解析:
* mv = data × 2.0 × Vref / (2^24)
*
* 参数说明:
* - data: 24位有符号整数 (-8388608 ~ +8388607)
* - 2.0: 差分输入系数 (±Vref范围)
* - 4096.0: 参考电压 (mV) - 实际校准值
* - (1<<24): 2^24 = 16777216 (24位满量程)
*/
mv = data * 2.0 * 4096.0 / ((1<<24));
return mv;
}
float ADC_DataToMilliVolt_Unipolar(uint32_t data)
{
float mv;
/* 单极性转换公式:
* mv = data × Vref / (2^24)
*
* 输入范围: 0 ~ +Vref
* 数据范围: 0 ~ 16777215
*/
mv = (float)data * 2048.0f / 16777216.0f;
return mv;
}
参考电压 | 模式 | LSB值 | 理论精度 | 有效精度 |
|---|---|---|---|---|
4.096V | 双极性 | 488nV | ±4.096V | ±4.096V |
2.048V | 单极性 | 122nV | 0~2.048V | 0~2.048V |
void ADC0_IRQHandler(void)
{
/* 策略: 在ADC0中断中同时处理两个ADC的数据 */
// 处理ADC0数据
process_adc0_data();
// 处理ADC1数据 (同步采样)
process_adc1_data();
/* 优势:
* 1. 确保数据时间同步性
* 2. 减少中断开销
* 3. 简化同步逻辑
*/
}
void process_adc0_data(void)
{
// ADC0: 外部信号采样 (AIN0差分AIN1)
reg_data = pADC_SUBSYS_USER->ADC_STATUS_0; // 清除中断
reg_data = pADC_SUBSYS_USER->ADC_DATA_0; // 读取数据
// 数据处理...
}
void process_adc1_data(void)
{
// ADC1: 内部电源监测 (AVDDP差分AVDDN)
reg_data = pADC_SUBSYS_USER->ADC_STATUS_1; // 清除中断
reg_data = pADC_SUBSYS_USER->ADC_DATA_1; // 读取数据
// 数据处理...
}
void ADC0_IRQHandler(void)
{
/* 优化策略1: 最小化中断时间 */
GPIO_ToggleBits(pGPIO1, GPIO_Pin_0); // 调试用IO翻转
/* 优化策略2: 快速状态读取 */
volatile uint32_t status0 = pADC_SUBSYS_USER->ADC_STATUS_0;
volatile uint32_t status1 = pADC_SUBSYS_USER->ADC_STATUS_1;
/* 优化策略3: 批量数据处理 */
process_dual_adc_data();
/* 性能指标:
* - 中断响应时间: < 1μs
* - 数据处理时间: < 10μs
* - 中断频率: 2.5Hz
*/
}
void UART0_init(void)
{
/* GPIO复用功能配置 */
GPIO_PinAFConfig(pGPIO0, GPIO_PinSource2, GPIO0_2_AF_SOUT); // P0.2 → UART TX
GPIO_PinAFConfig(pGPIO0, GPIO_PinSource1, GPIO0_1_AF_SIN); // P0.1 → UART RX
/* UART参数配置 */
UART_InitTypeDef uart0_initStr;
uart0_initStr.UART_BaudRate = 115200; // 波特率
uart0_initStr.UART_WordLength = UART_WordLength_8b; // 8位数据
uart0_initStr.UART_StopBits = UART_StopBits_1; // 1位停止位
uart0_initStr.UART_Parity = UART_Parity_None; // 无校验
UART_Init(pUART0, &uart0_initStr);
UART_ITConfig(pUART0, UART_IT_ERBFI, ENABLE); // 接收中断
}
void gpio_pin_init(void)
{
GPIO_InitTypeDef gpio_initStr;
/* 调试用GPIO配置 */
gpio_initStr.GPIO_Pin = GPIO_Pin_0; // P1.0
gpio_initStr.GPIO_Mode = GPIO_Mode_OUT; // 输出模式
gpio_initStr.GPIO_Current = GPIO_4mA; // 4mA驱动能力
gpio_initStr.GPIO_OType = GPIO_OType_PP; // 推挽输出
gpio_initStr.GPIO_PuPd = GPIO_PuPd_NOPULL; // 无上下拉
GPIO_Init(pGPIO1, &gpio_initStr);
GPIO_WriteBit(pGPIO1, GPIO_Pin_0, Bit_RESET); // 初始低电平
}
输入信号路径:
AIN0 ──┐
├─► [差分放大器] ──► [PGA] ──► [Σ-Δ调制器] ──► [数字滤波器] ──► ADC_DATA_0
AIN1 ──┘
参考电压路径:
VREFP ──► [参考缓冲器] ──► [ADC核心]
VREFN ──┘
/* 参考电压缓冲器配置 */
ADCx_InitStructure.REF_BUFP_MODE = REF_BUFP_ENABLE; // 正参考缓冲
ADCx_InitStructure.REF_BUFN_MODE = REF_BUFN_ENABLE; // 负参考缓冲
/* 优势:
* 1. 降低参考电压阻抗
* 2. 提高噪声抑制能力
* 3. 改善温度漂移特性
* 4. 增强负载驱动能力
*/
应用场景1: 传感器信号采集
├── ADC0: 传感器差分信号 (AIN0-AIN1)
├── ADC1: 电源监测 (AVDDP-AVDDN)
├── 采样率: 2.5SPS (适合慢变信号)
└── 精度: 24位 (适合高精度测量)
应用场景2: 工业控制系统
├── ADC0: 过程变量监测
├── ADC1: 系统状态监测
├── 同步采样: 确保时间相关性
└── 连续模式: 实时数据流
特性 | 指标 | 说明 |
|---|---|---|
分辨率 | 24位 | 理论精度16,777,216级别 |
采样率 | 2.5SPS | 适合直流/慢变信号 |
输入范围 | ±VREF | 差分输入最大动态范围 |
噪声性能 | < 1LSB | Sigma-Delta调制器优势 |
功耗 | 低功耗 | 适合电池供电应用 |
void read_reg_print(void)
{
/* 关键寄存器状态读取 */
volatile uint32_t reg_data;
// 配置寄存器
reg_data = pADC_SUBSYS_USER->CONFIGURATION_0;
printf("CONFIGURATION_0:0x%08X\n", reg_data);
// 控制寄存器
reg_data = pADC_SUBSYS_USER->ADC_CONTROL_0;
printf("ADC_CONTROL_0:0x%08X\n", reg_data);
// 偏移校准寄存器
reg_data = pADC_SUBSYS_USER->OFFSET_1;
printf("OFFSET_1:0x%08X\n", reg_data);
// 增益校准寄存器
reg_data = pADC_SUBSYS_USER->GAIN_1;
printf("GAIN_1:0x%08X\n", reg_data);
}
/* 优化1: 中断优先级配置 */
NVIC_SetPriority(ADC0_IRQn, 1); // 高优先级
/* 优化2: DMA传输 (如果支持) */
// 减少CPU中断负载,提高系统效率
/* 优化3: 数据缓冲策略 */
#define ADC_BUFFER_SIZE 64
static volatile float adc_buffer[ADC_BUFFER_SIZE];
static volatile uint8_t buffer_index = 0;
/* 校准策略 */
void adc_calibration(void)
{
// 零点校准
int32_t zero_offset = measure_zero_point();
// 满量程校准
int32_t full_scale = measure_full_scale();
// 计算校准系数
float gain_factor = calculate_gain_correction(full_scale);
float offset_factor = calculate_offset_correction(zero_offset);
}
/* 温度补偿 */
void temperature_compensation(void)
{
float temp = read_internal_temperature();
float temp_coeff = calculate_temp_coefficient(temp);
// 应用温度补偿
}
程序启动
↓
关闭看门狗 (IWDG_Disable)
↓
硬件初始化
├── GPIO初始化 (gpio_pin_init)
├── 时钟配置 (RCC_init)
├── 串口配置 (UART0_init)
├── 参考电压初始化 (VREF_init)
└── ADC双通道初始化 (ADC0_1_SIMU_CONT_init)
↓
启动ADC转换 (ADC0_convert_start)
↓
进入主循环 (while(1))
↓
等待中断事件
ADC0中断触发 (2.5Hz)
↓
读取ADC状态寄存器 (清除中断标志)
↓
读取ADC0数据 (外部信号)
├── 数据字节分离
├── 24位重组与符号扩展
├── 电压转换计算
└── 串口输出结果
↓
读取ADC1数据 (内部电源)
├── 数据字节分离
├── 24位重组与符号扩展
├── 电压转换计算
└── 串口输出结果
↓
GPIO状态翻转 (调试指示)
↓
中断返回
/* 关键代码段分析 */
adc_hex = (int32_t)(
((((adc_code[2] & 0x80) ? (0xFF) : (0x00)) ) << 24) | // [31:24] 符号扩展
((adc_code[2] & 0xFF) << 16) | // [23:16] 高字节
((adc_code[1] & 0xFF) << 8) | // [15:8] 中字节
((adc_code[0] & 0xFF) << 0) // [7:0] 低字节
);
/* 数据完整性验证 */
数据范围检查:
- 双极性模式: -8,388,608 ~ +8,388,607
- 单极性模式: 0 ~ 16,777,215
溢出检测:
- if (adc_hex == 0x800000) // 负溢出
- if (adc_hex == 0x7FFFFF) // 正溢出
/* ADC0主控制器配置 */
ADC_Mode = ADC_SIMU_ENABLE; // 主控制器,产生同步信号
/* ADC1从控制器配置 */
ADC_Mode = ADC_SIMU_DISABLE; // 从控制器,接收同步信号
/* 同步机制:
* 1. ADC0启动转换时,自动触发ADC1开始转换
* 2. 两个ADC使用相同的时钟源和采样率
* 3. 数据就绪时间差 < 1个时钟周期
* 4. 在单一中断中处理两路数据,确保时间相关性
*/
/* 激励电流源配置示例 */
void IEXC0_1_init(void)
{
// 配置激励电流源0: 1000μA输出到AIN3
// 配置激励电流源1: 150μA输出到AIN2
AFE_IEXC_RANGE(IEXC1_150UA, IEXC0_1000UA);
AFE_IEXC_CTRL(IEXC1_EN, IEXC0_EN, IEXC1_CHAN_AIN2, IEXC0_CHAN_AIN3);
}
/* 应用场景: RTD温度测量
* AIN0-AIN1: RTD差分信号
* AIN2: 激励电流返回路径
* AIN3: 激励电流输出
*/
void VBIAS_init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2ENR_ADC0EN_Msk, ENABLE);
VABIS_channel_select(VBIAS_ENABLE_AIN0);
/* VBIAS功能:
* 为传感器提供共模偏置电压
* 改善信号完整性
* 扩大输入动态范围
*/
}
24位分辨率,理论精度达到488nV (4.096V参考);双通道同步,硬件保证的时间同步性。集成度高有片内参考、激励源、偏置电路。
2.5SPS限制了动态信号应用,其中连续模式功耗相对较高,另外Sigma-Delta需要滤波器建立时间,在使用的时候需要深入理解ADC架构
/* 精度测试代码示例 */
void accuracy_test(void)
{
float test_voltages[] = {0.0, 1.0, 2.0, 3.0, 4.0}; // mV
float measured_values[5];
float errors[5];
for(int i = 0; i < 5; i++) {
// 设置测试电压
set_test_voltage(test_voltages[i]);
delay_ms(1000); // 等待稳定
// 测量并记录
measured_values[i] = read_adc_average(100);
errors[i] = measured_values[i] - test_voltages[i];
printf("Input: %.3fmV, Measured: %.3fmV, Error: %.3fmV\n",
test_voltages[i], measured_values[i], errors[i]);
}
}
版本 | 日期 | 修订内容 | 修订人 |
|---|---|---|---|
v1.0 | 2025-09-22 | 初始版本,完整技术解析 | YUNSWJ |
本文档版权归LH32M0G30X技术团队所有,未经授权不得转载或用于商业用途。(当然了,有事去找官方🤣)