動AD7124實戰(zhàn):從SPI配置到電壓采集的完整指南)
簡介這份資源是面向STM32開發(fā)者與嵌入式新手的AD7124高精度ADC驅(qū)動代碼基于STM32F103平臺采用模擬SPI方式實現(xiàn)工程基于Keil5搭建并已完整調(diào)通。代碼聚焦6通道、雙極性、全功率采樣與外部參考電壓配置未開啟多余功能便于讀者直接移植到自己的采集項目中也適合作為學習24位ADC寄存器配置與通信時序的參考。壓縮包共215個文件約6.18MB包含35個c源文件、38個h頭文件以及大量編譯中間產(chǎn)物d、o、crf文件、Keil工程文件uvprojx、uvoptx、map映射文件與hex固件等覆蓋從源碼到可執(zhí)行文件的完整工程結(jié)構(gòu)。目前已有6215人學習下載。作者在描述中坦言調(diào)試過程曲折因此代碼經(jīng)過實際驗證新手無需懷疑其可用性可直接對照工程理解AD7124初始化、通道切換與數(shù)據(jù)讀取流程并在此基礎(chǔ)上擴展硬件SPI或更多通道應用。1. 拿到一份調(diào)通的 AD7124 驅(qū)動先別急著往板子上懟如果你手頭正捏著一塊 STM32F103 的板子旁邊還躺著一顆 AD7124 這顆 24 位 Σ-Δ 型 ADC那你大概率已經(jīng)體會過那種“手冊翻爛了、寄存器配了半天、讀回來全是 0xFFFFFF”的絕望。這份資源就是沖這個場景來的一套基于 STM32F103 的 AD7124 完整驅(qū)動代碼作者標注“已全部調(diào)通”意味著 SPI 時序、寄存器配置、通道切換、數(shù)據(jù)讀取這些最容易翻車的地方已經(jīng)有人替你趟過一遍了。它適合兩類人一類是剛接手高精度采集項目、想先跑通再研究的嵌入式新手另一類是手上有現(xiàn)成硬件、懶得從零啃 80 頁寄存器手冊、想直接拿一份能跑的參考實現(xiàn)來改的熟手。關(guān)鍵詞 ADC、AD7124、STM32F 驅(qū)動基本就是這份代碼的全部標簽。2. 先看清這套驅(qū)動到底封裝了什么從 SPI 到通道掃描的完整鏈路2.1 硬件接口層SPI 時序和片選是怎么接的AD7124 和 STM32F103 之間走的是標準 4 線 SPI但有幾個細節(jié)如果沒處理好讀出來的數(shù)據(jù)就是玄學。常見做法是STM32 做 SPI 主機AD7124 做從機SCLK 空閑電平根據(jù) CPOL/CPHA 配置通常用 Mode 3CPOL1CPHA1或者 Mode 1具體看你的時序圖。片選 CS 建議用普通 GPIO 手動拉低拉高而不是用硬件 NSS因為 AD7124 的寄存器讀寫需要嚴格的幀邊界硬件 NSS 有時候會在字節(jié)間隙抖動導致命令解析錯位。下面這段是 SPI 初始化和字節(jié)收發(fā)的基礎(chǔ)封裝基于 STM32 標準外設(shè)庫的寫法// spi.c - AD7124 SPI 底層收發(fā) #include spi.h void AD7124_SPI_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; SPI_InitTypeDef SPI_InitStructure; // 使能 SPI 和 GPIO 時鐘 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_SPI1, ENABLE); // SCLK(PA5) / MOSI(PA7) 復用推挽輸出 GPIO_InitStructure.GPIO_Pin GPIO_Pin_5 | GPIO_Pin_7; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA, GPIO_InitStructure); // MISO(PA6) 浮空輸入 GPIO_InitStructure.GPIO_Pin GPIO_Pin_6; GPIO_InitStructure.GPIO_Mode GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, GPIO_InitStructure); // CS 用 PA4 普通推挽輸出手動控制 GPIO_InitStructure.GPIO_Pin GPIO_Pin_4; GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_PP; GPIO_Init(GPIOA, GPIO_InitStructure); GPIO_SetBits(GPIOA, GPIO_Pin_4); // 默認拉高 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_High; // 空閑高 SPI_InitStructure.SPI_CPHA SPI_CPHA_2Edge; // 第二邊沿采樣 SPI_InitStructure.SPI_NSS SPI_NSS_Soft; SPI_InitStructure.SPI_BaudRatePrescaler SPI_BaudRatePrescaler_64; // 約 1.1MHz SPI_InitStructure.SPI_FirstBit SPI_FirstBit_MSB; SPI_InitStructure.SPI_CRCPolynomial 7; SPI_Init(SPI1, SPI_InitStructure); SPI_Cmd(SPI1, ENABLE); } // 收發(fā)一個字節(jié)返回讀到的數(shù)據(jù) uint8_t AD7124_SPI_ReadWriteByte(uint8_t txData) { while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) RESET); SPI_I2S_SendData(SPI1, txData); while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) RESET); return SPI_I2S_ReceiveData(SPI1); }邏輯說明SPI 速率這里用了 64 分頻STM32F103 主頻 72MHz 時 SCLK 約 1.1MHzAD7124 手冊允許的最高 SCLK 通常遠高于此但低速更穩(wěn)適合長線或干擾環(huán)境。CPOL/CPHA 選的是 Mode 3如果你發(fā)現(xiàn)讀回全 0 或全 1第一個要改的就是這兩個參數(shù)。CS 手動控制意味著每次讀寫寄存器前要拉低 PA4結(jié)束后拉高這個動作在寄存器操作函數(shù)里統(tǒng)一做。2.2 寄存器讀寫24 位數(shù)據(jù)怎么拆成 3 個字節(jié)AD7124 的寄存器是 8 位地址 24 位數(shù)據(jù)的結(jié)構(gòu)通信寄存器決定下一步是讀還是寫、操作哪個寄存器。常見做法是封裝兩個函數(shù)寫寄存器和讀寄存器內(nèi)部處理 CS 拉低、發(fā)命令、收發(fā)數(shù)據(jù)、CS 拉高。// ad7124_reg.c - 寄存器讀寫 #include ad7124.h // 寫寄存器addr 為寄存器地址data 為 24 位數(shù)據(jù) void AD7124_WriteRegister(uint8_t addr, uint32_t data) { AD7124_CS_LOW(); // 通信寄存器寫操作地址 addr AD7124_SPI_ReadWriteByte(AD7124_COMM_REG_WR | addr); // 依次發(fā) 24 位數(shù)據(jù)高字節(jié)在前 AD7124_SPI_ReadWriteByte((data 16) 0xFF); AD7124_SPI_ReadWriteByte((data 8) 0xFF); AD7124_SPI_ReadWriteByte(data 0xFF); AD7124_CS_HIGH(); } // 讀寄存器addr 為寄存器地址返回 24 位數(shù)據(jù) uint32_t AD7124_ReadRegister(uint8_t addr) { uint32_t data 0; AD7124_CS_LOW(); // 通信寄存器讀操作地址 addr AD7124_SPI_ReadWriteByte(AD7124_COMM_REG_RD | addr); // 讀回 24 位 data | (uint32_t)AD7124_SPI_ReadWriteByte(0xFF) 16; data | (uint32_t)AD7124_SPI_ReadWriteByte(0xFF) 8; data | (uint32_t)AD7124_SPI_ReadWriteByte(0xFF); AD7124_CS_HIGH(); return data; }參數(shù)說明AD7124_COMM_REG_WR和AD7124_COMM_REG_RD是通信寄存器的讀寫標志位通常是 0x00 和 0x40具體值要對照手冊確認。addr是寄存器地址比如配置寄存器 0x01、數(shù)據(jù)寄存器 0x02 等。注意每次讀寫之間 CS 必須拉高否則 AD7124 會把下一次通信當成當前幀的延續(xù)導致命令錯位。這個坑我在早期調(diào)試時踩過現(xiàn)象是讀配置寄存器返回的值總是偏移一個字節(jié)。2.3 通道配置與掃描怎么讓 AD7124 輪流采多路信號AD7124 有多路模擬輸入可以通過配置寄存器設(shè)置每個通道的使能、增益、濾波等參數(shù)。常見做法是初始化時把用到的通道寫進配置寄存器然后設(shè)置 ADC 控制寄存器為連續(xù)轉(zhuǎn)換模式數(shù)據(jù)寄存器會自動更新。// ad7124_init.c - 通道配置示例 void AD7124_SetupChannels(void) { // 配置寄存器 0使能通道 0增益 1單極性 // 具體位定義參考手冊這里只寫關(guān)鍵字段 uint32_t cfg0 0; cfg0 | (0 12); // 增益 1 cfg0 | (0 11); // 單極性 cfg0 | (0 10); // 使能緩沖 AD7124_WriteRegister(AD7124_CFG_0_REG, cfg0); // 配置寄存器 1通道 1 uint32_t cfg1 cfg0; // 同樣參數(shù) AD7124_WriteRegister(AD7124_CFG_1_REG, cfg1); // 通道寄存器使能 AIN0/AIN1 對應通道 0AIN2/AIN3 對應通道 1 AD7124_WriteRegister(AD7124_CH_0_REG, 0x0001); // 使能正輸入 AIN0負輸入 AIN1 AD7124_WriteRegister(AD7124_CH_1_REG, 0x0043); // 使能正輸入 AIN2負輸入 AIN3 // ADC 控制寄存器連續(xù)轉(zhuǎn)換模式內(nèi)部基準 uint32_t adc_ctrl 0; adc_ctrl | (0 11); // 連續(xù)轉(zhuǎn)換 adc_ctrl | (1 6); // 內(nèi)部基準使能 AD7124_WriteRegister(AD7124_ADC_CTRL_REG, adc_ctrl); }邏輯說明通道寄存器的低 4 位是正輸入選擇接著 4 位是負輸入選擇最高位是使能位。上面0x0001表示通道 0 使能正輸入 AIN0負輸入 AIN10x0043表示通道 1 使能正輸入 AIN2負輸入 AIN3。實際項目中通道號對應的引腳要查原理圖別照抄。ADC 控制寄存器里連續(xù)轉(zhuǎn)換模式讓芯片自己輪詢使能的通道數(shù)據(jù)寄存器會依次更新你只需要在需要的時候讀數(shù)據(jù)寄存器即可。2.4 數(shù)據(jù)讀取與轉(zhuǎn)換24 位原始碼怎么變成電壓值讀數(shù)據(jù)寄存器得到的是 24 位原始碼要轉(zhuǎn)成電壓需要知道基準電壓和增益。常見做法是用內(nèi)部基準 2.5V增益為 1 時電壓 (原始碼 / 2^23) * 2.5V單極性。如果是雙極性公式略有不同。// ad7124_read.c - 讀取并轉(zhuǎn)換 float AD7124_ReadVoltage(void) { uint32_t raw AD7124_ReadRegister(AD7124_DATA_REG); // 單極性內(nèi)部基準 2.5V增益 1 float voltage ((float)raw / 8388608.0f) * 2.5f; // 2^23 8388608 return voltage; }參數(shù)說明8388608.0f是 2 的 23 次方對應 24 位 ADC 單極性滿量程。如果增益不是 1分母要乘增益如果基準是外部 5V把 2.5f 換成 5.0f。注意讀數(shù)據(jù)寄存器之前最好檢查狀態(tài)寄存器的 RDY 位確保數(shù)據(jù)已更新否則可能讀到舊值。這個細節(jié)在連續(xù)轉(zhuǎn)換模式下尤其重要我一般會在讀之前加一個超時等待。3. 把代碼跑起來從工程搭建到第一個電壓讀數(shù)3.1 工程目錄怎么組織別把所有文件堆在 main.c 里拿到一份驅(qū)動代碼第一件事是看目錄結(jié)構(gòu)。這套代碼常見做法是分成三層底層 SPI 驅(qū)動、AD7124 寄存器操作層、應用層。目錄大概長這樣project/ ├── CMSIS/ // STM32 內(nèi)核文件 ├── STM32F10x_StdPeriph_Driver/ // 標準外設(shè)庫 ├── User/ │ ├── main.c // 主循環(huán)初始化 讀數(shù)據(jù) │ ├── spi.c / spi.h // SPI 底層 │ ├── ad7124.c / ad7124.h // AD7124 驅(qū)動 │ └── delay.c / delay.h // 延時函數(shù) └── Project.uvprojx // Keil 工程文件如果你用的是 HAL 庫把 spi.c 換成 HAL 的 SPI 初始化即可寄存器操作層不用大改。關(guān)鍵是別把 AD7124 的寄存器配置直接寫在 main 里否則換一顆 ADC 或者改通道時你會想哭。3.2 初始化順序先 SPI 再 AD7124 最后開轉(zhuǎn)換初始化順序錯了后面全是白費。常見做法是系統(tǒng)時鐘配置STM32F103 通常 72MHz延時函數(shù)初始化SPI 初始化AD7124 復位拉低 RESET 引腳或者發(fā)復位命令寫配置寄存器、通道寄存器寫 ADC 控制寄存器啟動連續(xù)轉(zhuǎn)換等待 RDY 位讀數(shù)據(jù)// main.c - 初始化流程 int main(void) { Delay_Init(); AD7124_SPI_Init(); AD7124_Reset(); // 硬件復位或軟件復位 AD7124_SetupChannels(); // 配置通道和 ADC AD7124_StartConversion(); // 啟動連續(xù)轉(zhuǎn)換 while (1) { if (AD7124_DataReady()) // 檢查 RDY { float v AD7124_ReadVoltage(); printf(Voltage: %.6f V\r\n, v); } } }邏輯說明AD7124_Reset()通常是把 RESET 引腳拉低一段時間再拉高或者寫通信寄存器的復位命令。AD7124_DataReady()讀狀態(tài)寄存器的 RDY 位為 0 表示數(shù)據(jù)就緒。主循環(huán)里不要用死延時等數(shù)據(jù)用 RDY 查詢或者 DRDY 引腳中斷更靠譜。3.3 用串口打印驗證第一個讀數(shù)出來之前別急著接傳感器代碼跑起來后先別接傳感器把 AD7124 的輸入短接到地或者基準看讀數(shù)是否接近 0 或滿量程。常見做法是短接到地讀數(shù)應該接近 0x000000電壓接近 0V短接到基準讀數(shù)應該接近 0x7FFFFF單極性電壓接近 2.5V如果讀數(shù)跳得厲害檢查基準電壓是否穩(wěn)定、SPI 速率是否過高、電源是否干凈串口打印用 printf 重定向到 USART1波特率 115200。如果打印出來全是 0 或者 0xFFFFFF先查 SPI 模式再查 CS 時序最后查寄存器地址有沒有寫錯。4. 避坑與排查那些讓讀數(shù)變成玄學的瞬間4.1 讀回全 0 或全 1SPI 模式或 CS 時序錯了現(xiàn)象讀任何寄存器都返回 0x000000 或 0xFFFFFF。原因SPI 的 CPOL/CPHA 和 AD7124 不匹配或者 CS 在字節(jié)之間被拉高導致幀斷裂。解決把 CPOL/CPHA 改成 Mode 1 或 Mode 3 試一遍CS 確保在整個讀寫過程中保持低電平讀寫完成后再拉高。4.2 數(shù)據(jù)跳動大基準不穩(wěn)或濾波沒配現(xiàn)象電壓讀數(shù)跳動幾十毫伏甚至上百毫伏。原因內(nèi)部基準還沒穩(wěn)定就啟動轉(zhuǎn)換或者濾波寄存器配置的采樣率太高。解決啟動轉(zhuǎn)換前加 10ms 以上延時等基準穩(wěn)定把濾波寄存器配成較低的輸出速率比如 10SPS犧牲速度換穩(wěn)定。4.3 通道切換后讀到舊值沒等 RDY 就讀現(xiàn)象切換通道后第一次讀到的還是上一個通道的值。原因連續(xù)轉(zhuǎn)換模式下通道切換后需要等一個完整的轉(zhuǎn)換周期數(shù)據(jù)寄存器才會更新。解決每次讀之前檢查 RDY 位或者用 DRDY 引腳中斷觸發(fā)讀取。4.4 通信完全沒反應RESET 引腳沒接或沒拉高現(xiàn)象SPI 發(fā)什么都是 0AD7124 像死了一樣。原因RESET 引腳懸空或者一直拉低芯片處于復位狀態(tài)。解決RESET 引腳接 GPIO初始化時拉低至少 100ns 再拉高或者直接接 3.3V 上拉。4.5 讀數(shù)據(jù)寄存器返回 0x800000溢出或負滿量程現(xiàn)象讀數(shù)據(jù)寄存器得到 0x800000。原因輸入電壓超出量程或者雙極性模式下負滿量程。解決檢查輸入電壓是否在基準范圍內(nèi)確認配置寄存器的極性設(shè)置和增益是否匹配實際信號。5. 二次開發(fā)怎么改換通道、換基準、換 MCU 的實操思路拿到這份代碼你大概率不會原封不動用總得改點東西。我一般會從三個方向下手換通道、換基準、換 MCU。換通道最簡單改AD7124_SetupChannels()里的通道寄存器值就行。比如你要用 AIN4 和 AIN5 作為差分輸入就把通道寄存器的正輸入改成 4負輸入改成 5。注意通道號對應的物理引腳要查原理圖別照著代碼里的注釋抄。改完通道后配置寄存器也要對應改增益和極性否則讀數(shù)會差一個系數(shù)。換基準分兩種情況用內(nèi)部基準還是外部基準。內(nèi)部基準 2.5V 在 ADC 控制寄存器里使能代碼里已經(jīng)寫了。如果你用外部基準比如 REFIN1 接 5V那要把 ADC 控制寄存器的內(nèi)部基準關(guān)掉同時電壓換算公式里的 2.5f 改成 5.0f。外部基準的好處是精度更高但布線要講究基準芯片的退耦電容不能省。換 MCU 是改動最大的。如果你從 STM32F103 換到 F4 或者 G0SPI 底層要重寫但 AD7124 寄存器操作層幾乎不用動。我的習慣是把 SPI 收發(fā)封裝成兩個函數(shù)指針初始化時根據(jù) MCU 類型賦值這樣上層驅(qū)動完全不用改。下面是一個簡單的函數(shù)指針封裝示例// ad7124_port.h - 移植層接口 typedef struct { void (*cs_low)(void); void (*cs_high)(void); uint8_t (*spi_rw)(uint8_t tx); void (*delay_ms)(uint32_t ms); } AD7124_Port_t; extern AD7124_Port_t ad7124_port; // 在 main.c 里根據(jù) MCU 填充 AD7124_Port_t ad7124_port { .cs_low AD7124_CS_LOW, .cs_high AD7124_CS_HIGH, .spi_rw AD7124_SPI_ReadWriteByte, .delay_ms Delay_ms };這樣換 MCU 時只需要重新實現(xiàn)這幾個函數(shù)AD7124 的寄存器讀寫和通道配置代碼原封不動。驗證方法也簡單短接到地讀 0短接到基準讀滿量程中間接一個分壓電阻看讀數(shù)是否線性。我一般會拿一個 1.5V 電池串兩個等值電阻分壓理論值 0.75V實測偏差在 1mV 以內(nèi)就算通過。從那以后我每次拿到新的 ADC 驅(qū)動都強制先跑一遍“短接到地、短接到基準、分壓驗證”這三步確認底層通了再往上疊業(yè)務邏輯。希望這份代碼幫你省下啃手冊的那幾天。本文還有配套的精品資源點擊獲取