STM32与ADS1256的SPI通信实战:从寄存器配置到串口数据可视化

STM32与ADS1256的SPI通信实战:从寄存器配置到串口数据可视化 1. 硬件准备与电路连接第一次接触ADS1256这块24位ADC芯片时我被它的精度吓到了——理论上能分辨出0.000000119V的电压变化不过要让STM32和它正常对话硬件连接是第一个门槛。我用的STM32F103C8T6最小系统板和ADS1256模块之间需要接7根线这里最容易出错的是SPI引脚分配。记得有次调试时发现数据全是乱码折腾半天才发现把MOSI和MISO接反了。正确的接法应该是PB13接SCLK时钟线PB14接DOUTMISO主设备输入PB15接DINMOSI主设备输出PB12接CS片选PB11接DRDY数据就绪中断电源部分要特别注意ADS1256的模拟供电最好用LDO稳压我用的AMS1117-5.0给模块供电。如果采集的信号有高频噪声建议在AIN引脚加RC滤波比如100Ω电阻串联104瓷片电容到地。2. SPI初始化与时序调试SPI配置是第一个软件难关ADS1256的SPI时序比较特殊时钟极性要设低电平空闲CPOL0数据在第二个边沿采样CPHA1必须使用软件控制片选void SPI2_Init(void) { SPI_InitTypeDef SPI_InitStructure; GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE); // PB13(SCK), PB14(MISO), PB15(MOSI) GPIO_InitStructure.GPIO_Pin GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOB, GPIO_InitStructure); SPI_InitStructure.SPI_Direction SPI_Direction_2Lines_FullDuplex; SPI_InitStructure.SPI_Mode SPI_Mode_Master; SPI_InitStructure.SPI_DataSize SPI_DataSize_8b; SPI_InitStructure.SPI_CPOL SPI_CPOL_Low; // CPOL0 SPI_InitStructure.SPI_CPHA SPI_CPHA_2Edge; // CPHA1 SPI_InitStructure.SPI_NSS SPI_NSS_Soft; SPI_InitStructure.SPI_BaudRatePrescaler SPI_BaudRatePrescaler_32; SPI_InitStructure.SPI_FirstBit SPI_FirstBit_MSB; SPI_Init(SPI2, SPI_InitStructure); SPI_Cmd(SPI2, ENABLE); }调试时建议用逻辑分析仪抓SPI波形重点看CS拉低后是否有时钟信号数据线变化是否发生在时钟边沿DRDY信号变化是否正常3. 寄存器配置详解ADS1256有11个可配置寄存器最关键的三个是寄存器地址功能说明STATUS0x00控制数据顺序、自动校准ADCON0x02设置PGA增益和传感器检测DRATE0x03配置数据输出速率比如要设置10SPS采样率、增益为1的配置代码void ADS1256_Config(void) { // 高位在前启用输入缓冲 ADS1256_WriteReg(ADS1256_STATUS, 0x06); // 增益设为1关闭传感器检测 ADS1256_WriteReg(ADS1256_ADCON, 0x00); // 10SPS采样率 ADS1256_WriteReg(ADS1256_DRATE, 0xF0); // 执行自校准 while(ADS1256_DRDY_PIN); // 等待DRDY变低 ADS1256_CS_LOW(); SPI_SendByte(ADS1256_CMD_SELFCAL); while(ADS1256_DRDY_PIN); // 等待校准完成 ADS1256_CS_HIGH(); }特别注意写寄存器时序先发送写寄存器命令(0x50|寄存器地址)接着发送要写入的数值最后需要执行WAKEUP命令(0x00)4. 数据采集与电压转换读取24位AD值是个精细活要注意符号位处理int32_t ADS1256_ReadADC(uint8_t channel) { uint32_t adc_value 0; // 设置输入通道 (单端模式示例) ADS1256_WriteReg(ADS1256_MUX, (channel4) | 0x08); // 发送同步命令 ADS1256_CS_LOW(); SPI_SendByte(ADS1256_CMD_SYNC); SPI_SendByte(ADS1256_CMD_WAKEUP); // 请求读取数据 SPI_SendByte(ADS1256_CMD_RDATA); Delay_us(10); // 等待转换完成 // 读取24位数据 adc_value | (SPI_ReceiveByte() 16); adc_value | (SPI_ReceiveByte() 8); adc_value | SPI_ReceiveByte(); ADS1256_CS_HIGH(); // 处理负数 (24位有符号数转换) if(adc_value 0x800000) { adc_value | 0xFF000000; } return (int32_t)adc_value; }电压转换公式需要根据参考电压计算float VREF 5.0f; // 假设使用内部5V参考 float voltage (adc_value * VREF) / 8388608.0f; // 83886082^23实际测试中发现接地通道会有约0.001V的底噪这是正常现象。如果要更高精度可以采集10次取平均值使用外部精密基准源在代码里做软件滤波5. 串口输出与数据可视化最后把采集到的数据通过串口发送到上位机我用的是最简协议void Send_ADC_Values(void) { char buffer[64]; for(uint8_t ch0; ch8; ch) { int32_t raw ADS1256_ReadADC(ch); float voltage (raw * 5.0f) / 8388608.0f; sprintf(buffer, CH%d: %.6fV\r\n, ch, voltage); USART_SendString(USART1, buffer); } }在PC端可以用串口助手或者自己用Python写个简单的接收程序import serial import matplotlib.pyplot as plt ser serial.Serial(COM3, 115200) voltages [[] for _ in range(8)] while True: line ser.readline().decode().strip() if line.startswith(CH): ch int(line[2]) v float(line.split(:)[1].replace(V,)) voltages[ch].append(v) # 简单绘图 plt.clf() for i in range(8): plt.plot(voltages[i][-100:], labelfCH{i}) plt.legend() plt.pause(0.01)调试时遇到过串口数据错位的问题后来发现是printf浮点打印占用了太多时间导致SPI时序错乱。解决方法是用sprintf先格式化好字符串再一次性发送。