ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

STM32实战:AD7689高精度SAR ADC的SPI采集与多ADC共存

STM32实战:AD7689高精度SAR ADC的SPI采集与多ADC共存 简介这份资源是一套围绕 AD7689 高精度 16 位多通道 ADC 的嵌入式开发资料包面向从事数据采集、测试测量或医疗仪器相关开发的软硬件工程师解决芯片初始化、SPI/并行通信、通道配置及数据读取等实际问题。压缩包内含 155 个文件核心为 C 源码与 H 头文件并配有 Keil 工程文件uvproj/uvopt以及 hex、axf、map 等编译产物和 o/d/crf 中间文件可直接用 Keil 打开、编译并烧录验证也便于对照工程结构排查问题。整个压缩包约 3.01MB结构紧凑适合快速移植到现有 MCU 项目中。资源还结合 AD7705、AD9880 芯片协同开发的背景提示了构建混合 ADC 数据采集系统时的同步、滤波等关键问题。目前已有 977 人学习/下载作为包含完整工程、驱动源码和编译输出的资料对需要短时间上手 AD7689 的开发者有较强的参考价值。1. 别把 AD7689 当普通 SPI ADC 调先看懂包里的文件结构这个压缩包第一眼看上去就是一堆 Keil 工程文件和 STM32F10x 标准外设库真正值得你盯着的不是 SPI.axf而是 ad7689 源码如何被嵌进整条 SPI 采集链路前端用 16 位 ADC 采信号定时器管触发LCD 管显示Flash 和 I2C 管数据存储与配置。常见误区是一上来就照着 AD7705 的时序去调 AD7689结果在采样点、空闲电平和片选边沿上反复踩坑。这套源码适合两类人一类是刚把多通道 ADC 接到 STM32F1 上需要一套能跑通的中速采集框架另一类是已经在用 AD7705 做低频测量想对比 SAR 和 Sigma-Delta 两种架构差异的人。2. AD7689 的采样核心16 位精度、输入范围和 SPI 时序2.1 为什么是 AD7689不是 AD7705标题里同时出现 AD7689、AD7705、AD9880不代表这三颗芯片可以互换。ADC 选型首先要卡三个点采样率、输入带宽、接口形态。AD7689 属于 SAR 架构适合多通道、中高速、单次触发后立刻读码的场景AD7705 是 Sigma-Delta 架构适合直流和低频小信号AD9880 则是视频前端 ADC处理的是 RGB 和音频同步采样不能当普通数据采集 ADC 用。型号ADC 架构位数适合场景接口特点AD7689SAR16多通道中高速采集、测试测量SPI读码快适合逐次转换AD7705Sigma-Delta16低频高精度、直流信号SPI带数字滤波输出有延时AD9880视频前端14RGB 视频、音频分离/同步I2C 配置 并行数据输出在工程里最常见的搭配是 AD7689 做系统主采集通道AD7705 做辅助慢速监视AD9880 只走视频链路。这三者如果挂在同一块板子上SPI 总线上不能同时占用片选逻辑必须分开处理。2.2 输入范围与通道配置落到寄存器AD7689 的多通道不是多个独立 ADC 并行工作而是多路模拟输入由一个采样保持前端分时切换。因此配置字里需要同时写通道号、单双极性、参考电压选择等位。直接硬编码一个固定值能跑但换通道时特别容易漏改配置我一般会把它封装成宏并在每次读通道前重新发送配置。#define AD7689_CFG_CH0 (0x00) #define AD7689_CFG_CH1 (0x01) #define AD7689_CFG_UNI_0V (0x00) /* 单极性输入按数据手册编码 */ #define AD7689_CFG_BIP (0x40) /* 双极性输入具体值以手册为准 */ #define AD7689_CFG_SEQ_EN (0x80) /* 通道序列模式使能 */ uint16_t ad7689_read_channel(uint8_t ch) { uint16_t cfg AD7689_CFG_SEQ_EN | (uint16_t)(ch 0x07) | AD7689_CFG_UNI_0V; return ad7689_spi_xfer(cfg); }这段代码把配置字放在函数入口CH0 到 CH7 切换时只需要改ch。AD7689_CFG_BIP这类双极性配置必须和模拟输入硬件一致不能只在代码里改否则满量程信号会被削掉一半。单极性输入下原始码到电压的换算比较直接float ad7689_to_voltage(uint16_t raw, float vref) { return (float)raw * vref / 65536.0f; }如果输入配置成双极性就不能拿这个公式直接套。双极性下通常要判断最高位再决定是正半轴还是负半轴公式会变成((float)raw - 32768.0f) * vref / 32768.0f一类形式。具体偏移码还是补码必须对照芯片手册确认。2.3 读时序里最容易错的 4 个点第一点是 SPI 模式。AD7689 这类 PulSAR 器件对 SCK 空闲电平和采样沿很敏感常见工程默认 CPOL0、CPHA1也就是空闲低、第二个边沿采数据。但不能把 AD7705 的模式直接套过来AD7705 是 Sigma-Delta 结构寄存器字长和移位方式都不一样。第二点是 CS 覆盖范围。读 AD7689 时CS 通常要覆盖整个转换和读出窗口不能在 SCK 结束后立刻拉高。我一般会在最后一个 SCK 之后留半个周期再释放 CS否则可能丢掉最后一位数据读出的码是偶数跳变。第三点是忙状态判断。如果 SDO 同时兼作 BUSY 输出可以等它从高变低再读结果如果硬件没接 BUSY就用定时器触发加固定延时。不要为了省时间不判断转换完成高采样率下很容易读到上一通道的残留结果。第四点是字节顺序。STM32F1 的 SPI 必须设置SPI_FirstBit_MSB和 ADC 的高位先出对齐。若用 8 位帧读 16 位结果需要自己拼字节还要注意 AD7689 可能在前导位里带溢出标志或状态位不能把状态位当成数据直接参与电压换算。3. STM32F10x 工程里跑通 AD7689标准库 SPI 初始化与触发方式3.1 从包里的文件列表判断工程的真实结构这套源码里没有单独拆出一个ad7689.c驱动逻辑是散落在 SPI 工程里的。SPI.uvgui.Administrator是 Keil 的界面布局文件SPI.axf是编译产物SPI.uvgui_ZSY.bak是备份keilkilll.bat负责清理中间文件。真正需要维护的是stm32f10x_spi.c、stm32f10x_tim.c、lcd.c以及使用者自己实现的ad7689_drv.c。stm32f10x_adc.c是 STM32 片上 ADC 的驱动不是 AD7689 的驱动。不少初学者看到文件名带 adc就以为源码里已经写了 AD7689 的初始化结果在单步调试时找不到头绪。工程里如果用到stm32f10x_flash.c多半是存校准参数或最后一批采样结果stm32f10x_i2c.c则可能是给板上的传感器、EEPROM 或 AD9880 的 I2C 配置口用的。3.2 SPI 初始化参数怎么设STM32F10x 标准库的写法比较固定关键是把 SPI 设成主机、16 位帧、MSB 先发并选择合适的时钟分频。下面是一份常见的初始化配置GPIO_InitTypeDef gpio; SPI_InitTypeDef spi; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_SPI1, ENABLE); gpio.GPIO_Pin GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7; gpio.GPIO_Mode GPIO_Mode_AF_PP; gpio.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA, gpio); gpio.GPIO_Pin GPIO_Pin_4; gpio.GPIO_Mode GPIO_Mode_Out_PP; gpio.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA, gpio); spi.SPI_Direction SPI_Direction_2Lines_FullDuplex; spi.SPI_Mode SPI_Mode_Master; spi.SPI_DataSize SPI_DataSize_16b; spi.SPI_CPOL SPI_CPOL_Low; spi.SPI_CPHA SPI_CPHA_2Edge; spi.SPI_NSS SPI_NSS_Soft; spi.SPI_BaudRatePrescaler SPI_BaudRatePrescaler_8; spi.SPI_FirstBit SPI_FirstBit_MSB; SPI_Init(SPI1, spi); SPI_Cmd(SPI1, ENABLE);这段代码里最影响 AD7689 读码的是SPI_CPOL、SPI_CPHA和SPI_DataSize。如果读回来的数据全是 0x0000先查片选是否真的拉低如果读回来数据全是 0xFFFF大概率是 SDO 浮空也就是 MISO 引脚复用配置不对。SPI_BaudRatePrescaler_8在 72MHz 的 APB2 上得到 9MHz SCK对 AD7689 来说足够但若使用更长排线要降低分频否则上升沿处的信号抖动会被当成有效数据采进去。3.3 定时器触发 CNV还是软件读一次AD7689 的转换通常由 CNV 引脚边沿启动。软件触发只适合低速调试真正的采集系统会用一个定时器 PWM 输出连接 CNV让采样率由硬件决定。工程里的stm32f10x_tim.c大概率就是干这件事的。TIM_TimeBaseInitTypeDef tb; TIM_OCInitTypeDef oc; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); tb.TIM_Prescaler 71; /* 72MHz / 72 1MHz */ tb.TIM_Period 1000; /* PWM 频率 1kHz */ tb.TIM_CounterMode TIM_CounterMode_Up; TIM_TimeBaseInit(TIM2, tb); oc.TIM_OCMode TIM_OCMode_PWM1; oc.TIM_OutputState TIM_OutputState_Enable; oc.TIM_Pulse 1; oc.TIM_OCPolarity TIM_OCPolarity_High; TIM_OC4Init(TIM2, oc); TIM_Cmd(TIM2, ENABLE);TIM_Prescaler71先得到 1MHz 计数时钟TIM_Period1000让计数周期为 1ms所以输出 PWM 就是 1kHz 采样率。把TIM_Pulse设成 1 是让 CNV 引脚产生一个很窄的高脉冲转换由上升沿启动。改采样率时只需要调TIM_Prescaler或TIM_Period不要动 PWM1 的极性否则触发边沿会反掉读到的数据会慢一拍。3.4 读取结果与 DMA 取舍固定通道连续采集时可以用 DMA 把 SPI 接收寄存器搬到内存数组里省掉主循环里的轮询。前提是 CS 由独立 GPIO 控制并且每一帧不需要动态改通道配置。DMA_InitTypeDef dma; RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); dma.DMA_PeripheralBaseAddr (uint32_t)SPI1-DR; dma.DMA_MemoryBaseAddr (uint32_t)adc_buf; dma.DMA_DIR DMA_DIR_PeripheralSRC; dma.DMA_BufferSize 1024; dma.DMA_PeripheralInc DMA_PeripheralInc_Disable; dma.DMA_MemoryInc DMA_MemoryInc_Enable; dma.DMA_PeripheralDataSize DMA_PeripheralDataSize_HalfWord; dma.DMA_MemoryDataSize DMA_MemoryDataSize_HalfWord; dma.DMA_Mode DMA_Mode_Circular; dma.DMA_Priority DMA_Priority_High; dma.DMA_M2M DMA_M2M_Disable; DMA_Init(DMA1_Channel2, dma); DMA_Cmd(DMA1_Channel2, ENABLE); SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_RX, ENABLE);DMA 模式下每次 SPI 时钟都需要主机主动发送因此必须在 SPI 发送端也产生足够的 SCK否则接收端不会收到数据。比较实用的做法是发送一个固定值比如 0x0000让 SPI 主机持续产生时钟DMA 只搬运 MISO 上的有效结果。这样做有个限制通道配置不能放在 DMA 搬运的字节里否则每个周期都要重新初始化 DMA。4. 多 ADC 共存的信号链AD7705、AD9880 和 AD7689 怎么各干各的4.1 三颗 ADC 的分工一块复杂的采集板上出现多颗 ADC 很常见。AD7689 负责中速多通道采集AD7705 负责慢速高精度监视AD9880 负责视频信号链路。三者的数据率和接口特性差异很大不能混用同一套驱动。器件接口典型控制信号数据输出AD7689SPICS、CNV、SDO16 位原始码AD7705SPICS、SCLK、DIN、DOUT、DRDY16 位寄存器值AD9880I2C 并行I2C 配置、HS、VS、CLK14 位 RGB 数据如果板子上 AD7705 和 AD7689 共用同一根 SPI重点不是改波特率而是片选互斥。AD7705 的 DRDY 线用来通知数据准备好AD7689 用 CNV 触发配合 SDO 读码两者不能简单并联。4.2 同一根 SPI 总线上的片选隔离共享 SPI 总线时先全部拉高片选再选中目标器件能避免两个芯片同时驱动 MISO。下面是一个简单的总线选择逻辑typedef enum { ADC_DEV_AD7689 0, ADC_DEV_AD7705 } adc_dev_t; static void adc_spi_select(adc_dev_t dev) { GPIO_SetBits(CS_AD7689_PORT, CS_AD7689_PIN); GPIO_SetBits(CS_AD7705_PORT, CS_AD7705_PIN); switch (dev) { case ADC_DEV_AD7689: GPIO_ResetBits(CS_AD7689_PORT, CS_AD7689_PIN); break; case ADC_DEV_AD7705: GPIO_ResetBits(CS_AD7705_PORT, CS_AD7705_PIN); break; } }这段代码的核心是“先高后低”。如果不先拉高旧片选两条 CS 之间可能出现短暂同时为低的窗口两个 ADC 会把 MISO 线拉成未知电平读回的数据可能是两个器件的混合输出。每次切换器件后还要等一个短延时让 SDO 和 SCK 线恢复空闲电平。4.3 采样时钟和触发边沿的同步问题AD7689 和 AD7705 的采样时钟不能直接并联因为 AD7705 内部有数字滤波数据输出天然滞后于输入信号。若系统要求同一时刻测量多个物理量应该给每个 ADC 配独立的触发源然后在软件里给 AD7705 的结果打时间戳。AD9880 和这两颗芯片的同步问题更明显。AD9880 的输出时钟来自视频源和 STM32 的 SPI 时钟无关因此它的数据不能直接丢给 SPI 外设读取。工程里如果出现stm32f10x_i2c.c通常就是给 AD9880 写内部配置寄存器用的。AD9880 的 14 位数据要丢给带时序解析的接口模块或者直接进 LCD 控制器不能和 AD7689 混在同一个 DMA 里。5. 用一块已知电压把 AD7689 源码调到 16 位该有的样子5.1 先用直流回读判断接线和相位把 ADC 输入引脚接到一个干净的直流电压源先不用接复杂传感器。分别输入 0V、0.5 倍参考电压、接近满量程的电压然后读 AD7689 原始码。如果三个点的读数都接近 0x0000说明 SPI 时钟或片选信号根本没到器件如果读数都在 0xFFFF 附近常见原因是 MISO 悬空或者 CS 没有真正覆盖读出窗口如果读数有值但偏差超过几十 LSB则优先怀疑参考电压去耦不足以及 SPI 线太长造成的读码抖动。5.2 用两次校准代替幻想中的 16 位精度16 位 ADC 不等于直接就有 16 位精度系统里还有基准误差、运放失调、PCB 压降。用一个电压源做两点校准能快速修正大部分固定误差输入电压理论码实测码0.100V对应理论码第一次实测码4.000V对应理论码第二次实测码得到两组数据后用线性映射公式把原始码转成校准后的工程值。写入stm32f10x_flash.c里的应当是校准比例和偏移而不是只存最终电压值这样更换量程时不需要重新标定。连续采样时先做 1000 次采集去掉最大最小值后再平均这个操作能滤掉由 SPI 时钟抖动和参考噪声带来的偶发毛刺。最后把平均码拿去查漏如果连续两次校准结果相差超过 2 LSB先查 VREF 引脚旁边的 10uF 和 100nF 电容是否靠近芯片放置再查 AGND 和数字地是否被信号线切断。AD7689 源码里的读码函数本身只是第一步真正决定精度的往往是这些供电和接地细节。本文还有配套的精品资源点击获取
返回列表