资讯动态

24位ADC CS5532与STM32实战:从SPI配置到软件滤波的称重数据稳定方案

发布时间:2026/10/15 16:55:52 来源:尧图企业网站定制
简介这份资源面向使用STM32微控制器驱动CS5532高精度ADC的嵌入式开发者提供一套已实际调试通过的完整程序代码可直接用于工业测量、医疗设备、音频采集等需要24位高精度数据采集的场景。压缩包共243个文件约5.17MB以h头文件、c源文件、d依赖文件、crf交叉引用文件及o目标文件为主另含少量s汇编、txt说明、hex固件、pdf文档与uvproj工程文件覆盖驱动层、外设配置与工程构建的完整链路。代码含大量注释清晰呈现STM32与CS5532之间的通信协议、寄存器配置及数据采集处理流程便于理解与二次修改。目前已有3045人学习下载适合希望快速搭建高精度ADC采集方案、减少底层调试时间的开发者参考使用。1. 24位ADC CS5532配STM32为什么你的称重数据总在末位跳如果你正在做电子秤、压力变送器、精密配料系统大概率绕不开一个组合24位高精度ADC CS5532加STM32主控。这颗ADC在工业称重圈子里口碑很稳原因不复杂——它内置低噪声斩波稳定放大器配合ΔΣ调制架构在低速采样下能跑到22位有效分辨率对微伏级称重信号来说够用。但很多人第一次上手会翻车代码烧进去读数能出来可末位数字像心跳一样跳个不停标定完过半小时又漂了。问题往往不在芯片本身而在初始化时序、通道配置和滤波参数没对齐。这篇笔记按我实际调试过的路径把CS5532配STM32的完整流程拆开讲从SPI模式选型、寄存器配置、单次与连续转换切换到数据就绪判断和软件滤波每一步都给出可复现的代码和参数依据。适合已经能跑通STM32基础工程、想把这颗24位ADC真正用稳的嵌入式工程师。2. CS5532与STM32的硬件连接和SPI模式选择2.1 为什么CS5532的SPI不能照搬Flash那套配置CS5532的串行接口虽然叫SPI但它不是标准四线全双工SPI。它只支持三线制SDI数据输入、SDO数据输出、SCLK时钟片选CS独立控制。关键区别在于CS5532在SCLK上升沿采样SDI在下降沿更新SDO。这意味着STM32的SPI必须配成CPOL0、CPHA1也就是模式1。如果你按Flash常用的模式0去配写寄存器时命令字会被错位采样读回来的数据全是0xFF或0x00。我一般会先确认STM32的SPI时钟不超过2MHz。CS5532在3.3V供电下SCLK高电平最小脉宽要求约100ns对应上限约5MHz但实际调试中超过2MHz后长杜邦线或排线会引入振铃导致偶发读写失败。稳妥做法是先用1MHz跑通再根据板子布线质量往上调。硬件连接上CS5532的CS引脚不要直接接地。有些开发者图省事把CS拉低靠SCLK空闲来区分帧这在单从机时勉强能用但CS5532内部状态机会在SCLK意外翻转时误判命令。正确做法是用STM32的一个GPIO控制CS每次读写前拉低、完成后拉高。2.2 初始化GPIO和SPI的完整代码下面这段代码基于STM32 HAL库SPI1配置为模式1、8位数据、MSB先行、时钟分频到1MHz左右。CS引脚用PA4手动控制。// cs5532_spi.c #include stm32f1xx_hal.h SPI_HandleTypeDef hspi1; void CS5532_SPI_Init(void) { // SPI1时钟使能 __HAL_RCC_SPI1_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); // PA5SCK, PA6MISO, PA7MOSI GPIO_InitTypeDef gpio {0}; gpio.Pin GPIO_PIN_5 | GPIO_PIN_7; gpio.Mode GPIO_MODE_AF_PP; // 复用推挽 gpio.Speed GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOA, gpio); gpio.Pin GPIO_PIN_6; gpio.Mode GPIO_MODE_INPUT; // MISO浮空输入 HAL_GPIO_Init(GPIOA, gpio); // PA4作为CS手动控制 gpio.Pin GPIO_PIN_4; gpio.Mode GPIO_MODE_OUTPUT_PP; gpio.Pull GPIO_NOPULL; HAL_GPIO_Init(GPIOA, gpio); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET); // 空闲拉高 // SPI1参数模式18位MSB分频到约1.125MHz hspi1.Instance SPI1; hspi1.Init.Mode SPI_MODE_MASTER; hspi1.Init.Direction SPI_DIRECTION_2LINES; hspi1.Init.DataSize SPI_DATASIZE_8BIT; hspi1.Init.CLKPolarity SPI_POLARITY_LOW; // CPOL0 hspi1.Init.CLKPhase SPI_PHASE_2EDGE; // CPHA1 hspi1.Init.NSS SPI_NSS_SOFT; hspi1.Init.BaudRatePrescaler SPI_BAUDRATEPRESCALER_64; // 72M/64≈1.125M hspi1.Init.FirstBit SPI_FIRSTBIT_MSB; hspi1.Init.TIMode SPI_TIMODE_DISABLE; hspi1.Init.CRCCalculation SPI_CRCCALCULATION_DISABLE; HAL_SPI_Init(hspi1); }逻辑说明CS5532的SDI在SCLK上升沿被采样所以CPHA必须为1让数据在第一个时钟边沿之前就稳定。分频系数64是在72MHz主频下得到的如果你用的是STM32F4系列主频168MHz需要改成256才能落到相近频率。CS引脚初始拉高保证上电时ADC不误触发。参数说明SPI_BAUDRATEPRESCALER_64对应72MHz/641.125MHz这是保守值。如果PCB走线短且干净可以降到32或16但建议先用64跑通全流程。SPI_PHASE_2EDGE就是CPHA1别配错。2.3 复位和配置寄存器的写入时序CS5532上电后必须执行一次软件复位否则内部寄存器状态不确定。复位序列是拉低CS发送0xFF至少15个连续1然后发送0xFE再拉高CS。之后等待约1ms让内部振荡器稳定。配置寄存器写入用命令字节0x03开头后跟4个配置字节。配置字节1控制省电模式、复位、VRS等配置字节2设置字速率、滤波器配置字节3和4设置增益和偏移/增益寄存器校准。// 写配置寄存器 void CS5532_WriteConfig(uint8_t *cfg) { HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET); HAL_Delay(1); uint8_t cmd 0x03; // 写配置寄存器命令 HAL_SPI_Transmit(hspi1, cmd, 1, 100); HAL_SPI_Transmit(hspi1, cfg, 4, 100); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET); HAL_Delay(1); } // 软件复位 void CS5532_Reset(void) { HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET); HAL_Delay(1); uint8_t buf[2] {0xFF, 0xFE}; HAL_SPI_Transmit(hspi1, buf, 2, 100); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET); HAL_Delay(10); // 等待内部稳定 }逻辑说明复位序列中0xFF是连续时钟让内部状态机同步0xFE是复位命令。配置写入时CS必须在整个4字节传输期间保持低电平中间不能拉高否则配置只写入部分字节。参数说明HAL_Delay(1)是保守等待实际CS5532在8MHz内部时钟下配置写入后约需几十微秒生效但上电复位后建议等10ms。配置字节的具体值取决于你的传感器类型和采样率下一章展开。3. 寄存器配置与单次/连续转换模式落地3.1 配置字节逐位拆解增益、字速率和滤波器怎么选CS5532的配置寄存器1到4每个字节都有明确分工。配置字节1的bit7是PSS省电模式bit6是PDW掉电bit5是RS复位bit4是RV复位有效bit3是IS输入短路bit2是VRS电压参考选择bit1和bit0保留。正常工作时PSS0、PDW0、RS0、RV0、IS0。VRS根据你的参考电压接法如果VREF和VREF-接外部基准VRS0如果VREF-接地VRS1。配置字节2控制字速率和滤波器。bit7是WOR写偏移寄存器bit6到bit3是WRS字速率选择bit2到bit0是保留。字速率决定输出数据率对应关系是WRS0000时字速率3200SPS滤波陷波在50/60HzWRS0001时1600SPSWRS0010时800SPSWRS0011时400SPSWRS0100时200SPSWRS0101时100SPSWRS0110时50SPSWRS0111时25SPSWRS1000时12.5SPSWRS1001时6.25SPS。称重场景常用25SPS或12.5SPS因为低速下噪声更低有效分辨率更高。配置字节3设置增益。bit7到bit4是G1增益粗调bit3到bit0是G2增益细调。CS5532的增益范围是1到64通过G1和G2组合实现。常用增益64对应G10101、G20000。如果你用2mV/V的称重传感器满量程输出20mV配合64倍增益和2.5V基准能充分利用ADC量程。配置字节4设置偏移和增益校准寄存器访问。正常转换时设为0x00。3.2 单次转换与连续转换的代码实现CS5532支持单次转换和连续转换。单次转换适合低功耗场景每次需要时触发一次连续转换适合实时称重数据就绪后直接读。单次转换命令是0x00连续转换命令是0x01。读数据用0x0A命令读回来4个字节第一个字节是状态字节后三个字节是24位数据。// 单次转换并读取 int32_t CS5532_ReadSingle(void) { uint8_t cmd; uint8_t rx[4]; int32_t val; // 触发单次转换 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET); cmd 0x00; HAL_SPI_Transmit(hspi1, cmd, 1, 100); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET); // 等待转换完成查询SDO引脚或延时 HAL_Delay(100); // 25SPS下约40ms留余量 // 读数据 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET); cmd 0x0A; HAL_SPI_Transmit(hspi1, cmd, 1, 100); HAL_SPI_Receive(hspi1, rx, 4, 100); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET); // 拼接24位有符号数 val ((int32_t)rx[1] 16) | ((int32_t)rx[2] 8) | rx[3]; if (val 0x00800000) { // 符号位扩展 val | 0xFF000000; } return val; }逻辑说明单次转换触发后CS5532需要一定时间完成转换。25SPS对应40ms代码里延时100ms是保守值。读数据时先发0x0A命令然后连续读4个字节。第一个字节是状态字节bit7是OD数据就绪bit6是OF溢出bit5是CO校准bit4到bit0保留。实际使用时可以先读状态字节判断OD位再决定是否读取数据。参数说明HAL_Delay(100)在25SPS下足够如果你用3200SPS延时可以缩短到1ms以内。符号位扩展是必须的因为CS5532输出24位二进制补码不扩展的话负数会变成大正数。连续转换模式下触发一次0x01后ADC会按设定字速率持续转换。每次读数据前检查SDO引脚是否拉低表示数据就绪或者读状态字节判断OD位。// 连续转换模式初始化 void CS5532_StartContinuous(void) { uint8_t cmd 0x01; HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET); HAL_SPI_Transmit(hspi1, cmd, 1, 100); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET); } // 检查数据就绪并读取 int32_t CS5532_ReadContinuous(void) { uint8_t cmd 0x0A; uint8_t rx[4]; int32_t val; HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET); HAL_SPI_Transmit(hspi1, cmd, 1, 100); HAL_SPI_Receive(hspi1, rx, 4, 100); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET); if (rx[0] 0x80) { // OD位为1表示新数据就绪 val ((int32_t)rx[1] 16) | ((int32_t)rx[2] 8) | rx[3]; if (val 0x00800000) { val | 0xFF000000; } return val; } return 0x7FFFFFFF; // 无效标记 }逻辑说明连续转换下每次读数据前不需要重新触发转换但需要判断OD位。如果OD为0说明数据还没更新这次读到的可能是旧值。返回0x7FFFFFFF作为无效标记上层可以丢弃。参数说明连续转换适合配合定时器中断按字速率周期读取。比如25SPS下每40ms读一次用STM32的TIM2定时中断触发。3.3 校准寄存器的读写操作CS5532内部有偏移校准寄存器和增益校准寄存器用于系统级校准。写偏移校准用命令0x05写增益校准用命令0x06读偏移用0x07读增益用0x08。校准值也是24位。// 写偏移校准寄存器 void CS5532_WriteOffset(int32_t offset) { uint8_t cmd 0x05; uint8_t buf[3]; buf[0] (offset 16) 0xFF; buf[1] (offset 8) 0xFF; buf[2] offset 0xFF; HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET); HAL_SPI_Transmit(hspi1, cmd, 1, 100); HAL_SPI_Transmit(hspi1, buf, 3, 100); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET); }逻辑说明校准寄存器写入后后续转换结果会自动减去偏移值或乘以增益系数。实际产线校准流程是空载时读一组数据取平均写入偏移寄存器加载标准砝码后读一组数据计算增益系数写入增益寄存器。参数说明偏移值是24位有符号数范围-8388608到8388607。增益校准值也是24位但通常用定点数表示具体格式参考CS5532数据手册的校准章节。4. 数据就绪判断与软件滤波的工程实现4.1 查询SDO引脚还是读状态字节两种就绪判断的取舍CS5532的数据就绪有两种判断方式。第一种是硬件方式转换完成后SDO引脚会从高阻变为低电平STM32可以用外部中断或轮询检测。第二种是软件方式读数据时第一个字节的bit7就是OD位。硬件方式响应更快适合高速采样。但SDO引脚在CS拉高后状态不确定需要配合CS时序。我一般用软件方式因为读数据命令本身就会返回状态字节不需要额外引脚。但在连续转换模式下如果读数据太频繁可能会读到OD0的旧数据。解决办法是读之前先等一个转换周期或者用定时器严格同步。// 轮询方式等待数据就绪 int32_t CS5532_WaitAndRead(uint32_t timeout_ms) { uint32_t tick HAL_GetTick(); uint8_t cmd 0x0A; uint8_t rx[4]; int32_t val; while (1) { HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET); HAL_SPI_Transmit(hspi1, cmd, 1, 100); HAL_SPI_Receive(hspi1, rx, 4, 100); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET); if (rx[0] 0x80) { val ((int32_t)rx[1] 16) | ((int32_t)rx[2] 8) | rx[3]; if (val 0x00800000) { val | 0xFF000000; } return val; } if ((HAL_GetTick() - tick) timeout_ms) { return 0x7FFFFFFF; // 超时 } HAL_Delay(1); } }逻辑说明每次读数据都检查OD位只有OD1才返回有效值。超时机制防止死循环。HAL_Delay(1)让出CPU避免频繁SPI操作。参数说明timeout_ms根据字速率设置25SPS下建议至少100ms。如果超时频繁检查字速率配置和SPI通信是否正常。4.2 滑动平均加中值滤波的代码实现CS5532原始数据即使配置正确末位仍会有几个字的跳动。这是ΔΣ ADC的正常噪声不是故障。工程上需要软件滤波。我常用滑动平均加中值滤波的组合先取N个样本去掉最大最小再平均。#define FILTER_N 9 int32_t CS5532_Filter(int32_t new_val) { static int32_t buf[FILTER_N] {0}; static uint8_t idx 0; static uint8_t cnt 0; int32_t temp[FILTER_N]; int32_t sum 0; uint8_t i, j; buf[idx] new_val; idx (idx 1) % FILTER_N; if (cnt FILTER_N) cnt; // 复制到临时数组排序 for (i 0; i cnt; i) { temp[i] buf[i]; } // 冒泡排序 for (i 0; i cnt - 1; i) { for (j 0; j cnt - 1 - i; j) { if (temp[j] temp[j 1]) { int32_t t temp[j]; temp[j] temp[j 1]; temp[j 1] t; } } } // 去掉最大最小各一个剩余平均 for (i 1; i cnt - 1; i) { sum temp[i]; } return sum / (cnt - 2); }逻辑说明FILTER_N9时每次去掉最大最小各一个剩下7个平均。这样既能抑制突发尖峰又能平滑随机噪声。排序用冒泡数据量小开销可接受。参数说明FILTER_N越大平滑效果越好但响应越慢。称重场景9到15比较合适。如果用于动态称重需要减小到5以下。4.3 采样率与滤波窗口的匹配关系字速率和滤波窗口要匹配。25SPS下9点滤波需要9×40ms360ms稳定时间。如果用于静态称重这个时间可以接受。如果用于配料系统可能需要12.5SPS配合15点滤波稳定时间1.2秒但分辨率更高。我一般会先确定系统对稳定时间和分辨率的要求再反推字速率和滤波点数。公式是稳定时间≈滤波点数/字速率。分辨率方面25SPS下CS5532有效位数约21位12.5SPS下约22位。注意不要为了追求分辨率把字速率降到6.25SPS以下因为内部振荡器低频下更容易受温度影响长期稳定性反而下降。5. 避坑与常见问题排查5.1 读回数据全是0xFF或0x00现象SPI能收发但读配置寄存器或数据寄存器全是固定值。原因SPI模式配错。CS5532要求CPOL0、CPHA1如果配成模式0命令字在第一个边沿被采样数据错位。解决检查CLKPhase是否为SPI_PHASE_2EDGE。用逻辑分析仪抓SCLK和SDI确认数据在上升沿稳定。5.2 数据末位跳动超过10个码现象空载时读数在±20个码内波动。原因参考电压噪声或模拟电源纹波。CS5532对VREF的噪声非常敏感如果直接用STM32的3.3V供电做参考开关电源纹波会直接耦合进转换结果。解决VREF用独立低噪声LDO供电并联10uF钽电容和0.1uF陶瓷电容。模拟地和数字地单点连接。字速率降到25SPS或12.5SPS。5.3 连续转换模式下读数据总是旧值现象OD位一直为0读回的数据不变。原因读数据命令发送太快CS5532还没完成一次转换。或者连续转换命令没有正确发送。解决确认连续转换命令0x01已发送。读数据前用定时器等待一个转换周期。25SPS下等待40ms以上。5.4 校准后零点漂移现象写入偏移校准后过一段时间零点又偏了。原因校准时的温度和实际工作温度不同CS5532内部偏移有温漂。或者校准数据没有正确写入。解决校准前让板子预热15分钟。偏移校准值写入后回读验证。如果温漂要求高需要定期自动校准比如每次开机空载时重新校准。5.5 SPI通信偶发失败现象大部分时间正常偶尔读回错误数据。原因CS信号在SPI传输中间被拉高或者SCLK频率过高导致时序余量不足。解决CS控制用GPIO手动操作确保整个命令加数据期间保持低电平。SCLK降到1MHz以下。SPI传输加超时和重试机制。6. 从能读到能用标定流程与长期稳定性验证标定是CS5532从“能读数据”到“能用数据”的关键一步。我一般分两步零点标定和量程标定。零点标定时传感器空载连续读100个样本去掉最大最小后取平均把这个值写入偏移校准寄存器。量程标定时加载接近满量程的标准砝码再读100个样本取平均计算增益系数。增益系数的计算方法是理论满量程码值除以实际码值差。CS5532在增益64、基准2.5V下满量程输入±20mV对应码值±8388608。实际加载砝码后码值差如果只有理论值的90%增益系数就是1/0.9≈1.111写入增益校准寄存器。// 标定流程示例 void CS5532_Calibrate(void) { int32_t zero_sum 0, span_sum 0; int32_t zero_avg, span_avg; int32_t offset, gain; int i; // 零点标定空载读100次 for (i 0; i 100; i) { zero_sum CS5532_WaitAndRead(200); HAL_Delay(50); } zero_avg zero_sum / 100; CS5532_WriteOffset(zero_avg); // 量程标定加载标准砝码后读100次 for (i 0; i 100; i) { span_sum CS5532_WaitAndRead(200); HAL_Delay(50); } span_avg span_sum / 100; // 计算增益系数简化示例实际用定点数 // 假设理论码值差为8000000实际差为span_avg - zero_avg // gain 8000000 / (span_avg - zero_avg) // 写入增益寄存器... }逻辑说明标定时样本数取100是为了平均掉随机噪声。每次读取间隔50ms保证CS5532完成转换。零点标定后立即写入偏移寄存器量程标定后计算增益。参数说明标准砝码选择满量程的80%到100%之间太小会放大增益误差。标定环境温度变化不超过±2℃。长期稳定性验证方面我习惯做24小时连续采样每小时记录一次零点和满量程读数。如果零点漂移超过50个码需要检查参考电压和电源。如果满量程漂移超过0.01%检查传感器和机械结构。提示CS5532的校准寄存器写入次数有限不要频繁标定。产线标定一次后现场用软件零点跟踪即可。最后一个技巧如果系统对成本不敏感给CS5532的模拟电源加一级LC滤波电感用10uH电容用100uF加0.1uF。这个改动对末位跳动的改善立竿见影。我早期做的一款配料控制器加了LC滤波后空载跳动从±30码降到±5码标定周期从每周一次延长到每月一次。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑