單芯片解決方案,開啟全新體驗——W55MH32 高性能以太網單片機
W55MH32是WIZnet重磅推出的高性能以太網單片機,它為用戶帶來前所未有的集成化體驗。這顆芯片將強大的組件集于一身,具體來說,一顆W55MH32內置高性能Arm? Cortex-M3核心,其主頻最高可達216MHz;配備1024KB FLASH與96KB SRAM,滿足存儲與數據處理需求;集成TOE引擎,包含WIZnet全硬件TCP/IP協議棧、內置MAC以及PHY,擁有獨立的32KB以太網收發緩存,可供8個獨立硬件socket使用。如此配置,真正實現了All-in-One解決方案,為開發者提供極大便利。
在封裝規格上,W55MH32 提供了兩種選擇:QFN100和QFN68。
W55MH32L采用QFN100封裝版本,尺寸為12x12mm,其資源豐富,專為各種復雜工控場景設計。它擁有66個GPIO、3個ADC、12通道DMA、17個定時器、2個I2C、5個串口、2個SPI接口(其中1個帶I2S接口復用)、1個CAN、1個USB2.0以及1個SDIO接口。如此豐富的外設資源,能夠輕松應對工業控制中多樣化的連接需求,無論是與各類傳感器、執行器的通信,還是對復雜工業協議的支持,都能游刃有余,成為復雜工控領域的理想選擇。 同系列還有QFN68封裝的W55MH32Q版本,該版本體積更小,僅為8x8mm,成本低,適合集成度高的網關模組等場景,軟件使用方法一致。更多信息和資料請進入網站或者私信獲取。
此外,本W55MH32支持硬件加密算法單元,WIZnet還推出TOE+SSL應用,涵蓋TCP SSL、HTTP SSL以及 MQTT SSL等,為網絡通信安全再添保障。
為助力開發者快速上手與深入開發,基于W55MH32L這顆芯片,WIZnet精心打造了配套開發板。開發板集成WIZ-Link芯片,借助一根USB C口數據線,就能輕松實現調試、下載以及串口打印日志等功能。開發板將所有外設全部引出,拓展功能也大幅提升,便于開發者全面評估芯片性能。
若您想獲取芯片和開發板的更多詳細信息,包括產品特性、技術參數以及價格等,歡迎訪問官方網頁,我們期待與您共同探索W55MH32的無限可能。
第十九章 ADC——電壓采集
本章參考資料:《W55MH32參考手冊》ADC章節。學習本章時, 配合《W55MH32參考手冊》ADC章節一起閱讀,效果會更佳,特別是涉及到寄存器說明的部分。
1 ADC簡介
12 位 ADC 是一種逐次逼近型模擬數字轉換器。它有多達 18 個通道,可測量 16 個外部和 2 個內部信號源。各通道的 A/D 轉換可以單次、連續、掃描或間斷模式執行。ADC 的結果可以左對齊或右對齊方式存儲在 16 位數據寄存器中。模擬看門狗特性允許應用程序檢測輸入電壓是否超出用戶定義的高/低閥值。
ADC 的輸入時鐘不得超過 14MHz,它是由 PCLK2 經分頻產生。
2 ADC功能框圖剖析
ADC功能框圖如下:
ADC3 的規則轉換和注入轉換觸發與 ADC1 和 ADC2 的不同。
名稱 | 信號類型 | 注解 |
VREF+ | 輸入,模擬參考正極 | ADC 使用的高端 / 正極參考電壓,2.4V≤VREF+≤VDDA |
VDDA?1? | 輸入,模擬電源 | 等效于 VDD 的模擬電源且:2.4V≤VDDA≤VDD (3.6V) |
VREF- | 輸入,模擬參考負極 | ADC 使用的低端 / 負極參考電壓,VREF-=VSSA |
VSSA?1? | 輸入,模擬電源地 | 等效于 VSS 的模擬電源地 |
ADCx_IN[15:0] | 模擬輸入信號 | 16 個模擬輸入通道 |
VDDA 和 VSSA 應該分別連接到 VDD 和 VSS。
2.1 電壓輸入范圍
ADC輸入范圍為:VREF- ≤ VIN ≤ VREF+。由VREF-、 VREF+ 、VDDA 、VSSA、這四個外部引腳決定。
我們在設計原理圖的時候一般把VSSA和VREF-接地, 把VREF+和VDDA 接3V3,得到ADC的輸入電壓范圍為:0~3.3V。
如果我們想讓輸入的電壓范圍變寬,去到可以測試負電壓或者更高的正電壓,我們可以在外部加一個電壓調理電路, 把需要轉換的電壓抬升或者降壓到0~3.3V,這樣ADC就可以測量。
2.2 輸入通道
我們確定好ADC輸入電壓之后,那么電壓怎么輸入到ADC?這里我們引入通道的概念,W55MH32的ADC多達18個通道, 其中外部的16個通道就是框圖中的ADCx_IN0、ADCx_IN1…ADCx_IN5。這16個通道對應著不同的IO口,具體是哪一個IO口可以從手冊查詢到。 其中ADC1/2/3還有內部通道:ADC1的通道16連接到了芯片內部的溫度傳感器,Vrefint連接到了通道17。 ADC2的模擬通道16和17連接到了內部的VSS。ADC3的模擬通道9、14、15、16和17連接到了內部的VSS。
ADC1 | IO | ADC2 | IO | ADC3 | IO |
通道 0 | PA0 | 通道 0 | PA0 | 通道 0 | PA0 |
通道 1 | PA1 | 通道 1 | PA1 | 通道 1 | PA1 |
通道 2 | PA2 | 通道 2 | PA2 | 通道 2 | PA2 |
通道 3 | PA3 | 通道 3 | PA3 | 通道 3 | PA3 |
通道 4 | PA4 | 通道 4 | PA4 | 通道 4 | 沒有通道 4 |
通道 5 | PA5 | 通道 5 | PA5 | 通道 5 | 沒有通道 5 |
通道 6 | PA6 | 通道 6 | PA6 | 通道 6 | 沒有通道 6 |
通道 7 | PA7 | 通道 7 | PA7 | 通道 7 | 沒有通道 7 |
通道 8 | PB0 | 通道 8 | PB0 | 通道 8 | 沒有通道 8 |
通道 9 | PB1 | 通道 9 | PB1 | 通道 9 | 連接內部 VSS |
通道 10 | PC0 | 通道 10 | PC0 | 通道 10 | PC0 |
通道 11 | PC1 | 通道 11 | PC1 | 通道 11 | PC1 |
通道 12 | PC2 | 通道 12 | PC2 | 通道 12 | PC2 |
通道 13 | PC3 | 通道 13 | PC3 | 通道 13 | PC3 |
通道 14 | PC4 | 通道 14 | PC4 | 通道 14 | 連接內部 VSS |
通道 15 | PC5 | 通道 15 | PC5 | 通道 15 | 連接內部 VSS |
通道 16 | 連接內部溫度傳感器 | 通道 16 | 連接內部 VSS | 通道 16 | 連接內部 VSS |
通道 17 | 連接內部 Vrefint | 通道 17 | 連接內部 VSS | 通道 17 | 連接內部 VSS |
外部的16個通道在轉換的時候又分為規則通道和注入通道,其中規則通道最多有16路,注入通道最多有4路。那這兩個通道有什么區別?在什么時候使用?
規則通道
規則通道:顧名思意,規則通道就是很規矩的意思,我們平時一般使用的就是這個通道,或者應該說我們用到的都是這個通道,沒有什么特別要注意的可講。
注入通道
注入,可以理解為插入,插隊的意思,是一種不安分的通道。它是一種在規則通道轉換的時候強行插入要轉換的一種通道。 如果在規則通道轉換過程中,有注入通道插隊,那么就要先轉換完注入通道,等注入通道轉換完成后,再回到規則通道的轉換流程。 這點跟中斷程序很像,都是不安分的主。所以,注入通道只有在規則通道存在時才會出現。
2.3 轉換順序
規則序列
規則序列寄存器有3個,分別為SQR3、SQR2、SQR1。SQR3控制著規則序列中的第一個到第六個轉換, 對應的位為:SQ1[4:0]~SQ6[4:0],第一次轉換的是位4:0 SQ1[4:0],如果通道16想第一次轉換,那么在SQ1[4:0]寫16即可。 SQR2控制著規則序列中的第7到第12個轉換, 對應的位為:SQ7[4:0]~SQ12[4:0],如果通道1想第8個轉換,則SQ8[4:0]寫1即可。SQR1控制著規則序列中的第13到第16個轉換, 對應位為:SQ13[4:0]~SQ16[4:0],如果通道6想第10個轉換,則SQ10[4:0]寫6即可。 具體使用多少個通道,由SQR1的位L[3:0]決定,最多16個通道。
寄存器 | 寄存器位 | 功能 | 取值 |
SQR3 | SQ1[4:0] | 設置第 1 個轉換的通道 | 通道 1~16 |
SQR3 | SQ2[4:0] | 設置第 2 個轉換的通道 | 通道 1~16 |
SQR3 | SQ3[4:0] | 設置第 3 個轉換的通道 | 通道 1~16 |
SQR3 | SQ4[4:0] | 設置第 4 個轉換的通道 | 通道 1~16 |
SQR3 | SQ5[4:0] | 設置第 5 個轉換的通道 | 通道 1~16 |
SQR3 | SQ6[4:0] | 設置第 6 個轉換的通道 | 通道 1~16 |
SQR3 | SQ7[4:0] | 設置第 7 個轉換的通道 | 通道 1~16 |
SQR3 | SQ8[4:0] | 設置第 8 個轉換的通道 | 通道 1~16 |
SQR2 | SQ9[4:0] | 設置第 9 個轉換的通道 | 通道 1~16 |
SQR2 | SQ10[4:0] | 設置第 10 個轉換的通道 | 通道 1~16 |
SQR2 | SQ11[4:0] | 設置第 11 個轉換的通道 | 通道 1~16 |
SQR2 | SQ12[4:0] | 設置第 12 個轉換的通道 | 通道 1~16 |
SQR1 | SQ13[4:0] | 設置第 13 個轉換的通道 | 通道 1~16 |
SQR1 | SQ14[4:0] | 設置第 14 個轉換的通道 | 通道 1~16 |
SQR1 | SQ15[4:0] | 設置第 15 個轉換的通道 | 通道 1~16 |
SQR1 | SQ16[4:0] | 設置第 16 個轉換的通道 | 通道 1~16 |
SQR1 | LQL[3:0] | 需要轉換多少個通道 | 1~16 |
注入序列
注入序列寄存器JSQR只有一個,最多支持4個通道,具體多少個由JSQR的JL[1:0]決定。如果JL的 值小于4的話, 則JSQR跟SQR決定轉換順序的設置不一樣,第一次轉換的不是JSQR1[4:0],而是JCQRx[4:0] ,x = 4-JL),跟SQR剛好相反。 如果JL=00(1個轉換),那么轉換的順序是從JSQR4[4:0]開始,而不是從JSQR1[4:0]開始,這個要注意,編程的時候不要搞錯。當JL等于4時,跟SQR一樣。
寄存器 | 寄存器位 | 功能 | 取值 |
JSQR | JSQ1[4:0] | 設置第 1 個轉換的通道 | 通道 1~4 |
JSQR | JSQ2[4:0] | 設置第 2 個轉換的通道 | 通道 1~4 |
JSQR | JSQ3[4:0] | 設置第 3 個轉換的通道 | 通道 1~4 |
JSQR | JSQ4[4:0] | 設置第 4 個轉換的通道 | 通道 1~4 |
JSQR | JLQ[4:0] | 需要轉換多少個通道 | 1~4 |
2.4 觸發源
通道選好了,轉換的順序也設置好了,那接下來就該開始轉換了。ADC轉換可以由ADC控制寄存器2: ADC_CR2的ADON這個位來控制, 寫1的時候開始轉換,寫0的時候停止轉換,這個是最簡單也是最好理解的開啟ADC轉換的控制方式。
除了上述的控制方法,ADC還支持觸發轉換,這個觸發包括內部定時器觸發和外部IO觸發。觸發源有很多,具體選擇哪一種觸發源, 由ADC控制寄存器2:ADC_CR2的EXTSEL[2:0]和JEXTSEL[2:0]位來控制。EXTSEL[2:0]用于選擇規則通道的觸發源, JEXTSEL[2:0]用于選擇注入通道的觸發源。選定好觸發源之后,觸發源是否要激活,則由ADC控制寄存器2:ADC_CR2的EXTTRIG和JEXTTRIG這兩位來激活。
2.5 轉換時間
ADC時鐘
ADC輸入時鐘ADC_CLK由PCLK2經過分頻產生,最大是14M,分頻因子由RCC時鐘配置寄存器RCC_CFGR的位15:14 ADCPRE[1:0]設置, 可以是2/4/6/8分頻,注意這里沒有1分頻。一般我們設置PCLK2=HCLK=72M。
采樣時間
ADC使用若干個ADC_CLK周期對輸入的電壓進行采樣, 采樣的周期數可通過ADC 采樣時間寄存器ADC_SMPR1和ADC_SMPR2中的SMP[2:0]位設置,ADC_SMPR2控制的是通道0~9,ADC_SMPR1控制的是通道10~17。每個通道可以分別用不同的時間采樣。其中采樣周期最小是1.5個, 即如果我們要達到最快的采樣,那么應該設置采樣周期為1.5個周期,這里說的周期就是1/ADC_CLK。
ADC的轉換時間跟ADC的輸入時鐘和采樣時間有關,公式為:Tconv = 采樣時間 + 12.5個周期。當ADCLK = 14MHZ (最高), 采樣時間設置為1.5周期(最快),那么總的轉換時間(最短)Tconv = 1.5周期 + 12.5周期 = 14周期 = 1us。
一般我們設置PCLK2=72M,經過ADC預分頻器能分頻到最大的時鐘只能是12M,采樣周期設置為1.5個周期, 算出最短的轉換時間為1.17us,這個才是最常用的。
2.6 數據寄存器
一切準備就緒后,ADC轉換后的數據根據轉換組的不同,規則組的數據放在ADC_DR寄存器,注入組的數據放在JDRx。
規則數據寄存器
ADC規則組數據寄存器ADC_DR只有一個,是一個32位的寄存器,低16位在單ADC時使用,高16位是在ADC1中雙模式下保存ADC2轉換的規則數據, 雙模式就是ADC1和ADC2同時使用。在單模式下,ADC1/2/3都不使用高16位。因為ADC的精度是12位,無論ADC_DR的高16或者低16位都放不滿, 只能左對齊或者右對齊,具體是以哪一種方式存放,由ADC_CR2的11位ALIGN設置。
規則通道可以有16個這么多,可規則數據寄存器只有一個,如果使用多通道轉換,那轉換的數據就全部都擠在了DR里面,前一個時間點轉換的通道數據, 就會被下一個時間點的另外一個通道轉換的數據覆蓋掉,所以當通道轉換完成后就應該把數據取走,或者開啟DMA模式,把數據傳輸到內存里面, 不然就會造成數據的覆蓋。最常用的做法就是開啟DMA傳輸。
注入數據寄存器
ADC注入組最多有4個通道,剛好注入數據寄存器也有4個,每個通道對應著自己的寄存器,不會跟規則寄存器那樣產生數據覆蓋的問題。 ADC_JDRx是32位的,低16位有效,高16位保留,數據同樣分為左對齊和右對齊,具體是以哪一種方式存放,由ADC_CR2的11位ALIGN設置。
2.7 中斷
轉換結束中斷
數據轉換結束后,可以產生中斷,中斷分為三種:規則通道轉換結束中斷,注入轉換通道轉換結束中斷,模擬看門狗中斷。 其中轉換結束中斷很好理解,跟我們平時接觸的中斷一樣,有相應的中斷標志位和中斷使能位,我們還可以根據中斷類型寫相應配套的中斷服務程序。
模擬看門狗中斷
當被ADC轉換的模擬電壓低于低閾值或者高于高閾值時,就會產生中斷,前提是我們開啟了模擬看門狗中斷, 其中低閾值和高閾值由ADC_LTR和ADC_HTR設置。例如我們設置高閾值是2.5V,那么模擬電壓超過2.5V的時候,就會產生模擬看門狗中斷,反之低閾值也一樣。
DMA請求
規則和注入通道轉換結束后,除了產生中斷外,還可以產生DMA請求,把轉換好的數據直接存儲在內存里面。 要注意的是只有ADC1和ADC3可以產生DMA請求。有關DMA請求需要配合《W55MH32參考手冊》DMA控制器這一章節來學習。 一般我們在使用ADC的時候都會開啟DMA傳輸。
2.8 電壓轉換
模擬電壓經過ADC轉換后,是一個12位的數字值,如果通過串口以16進制打印出來的話,可讀性比較差,那么有時候我們就需要把數字電壓轉換成模擬電壓, 也可以跟實際的模擬電壓(用萬用表測)對比,看看轉換是否準確。
我們一般在設計原理圖的時候會把ADC的輸入電壓范圍設定在:0~3.3v,因為ADC是12位的,那么12位滿量程對應的就是3.3V, 12位滿量程對應的數字值是:2^12。數值0對應的就是0V。如果轉換后的數值為 X ,X對應的模擬電壓為Y, 那么會有這么一個等式成立: 2^12 / 3.3 = X/ Y,=> Y = (3.3 * X ) / 2^12。
3 ADC初始化結構體詳解
標準庫函數對每個外設都建立了一個初始化結構體xxx_InitTypeDef(xxx為外設名稱),結構體成員用于設置外設工作參數, 并由標準庫函數xxx_Init()調用這些設定參數進入設置外設相應的寄存器,達到配置外設工作環境的目的。
結構體xxx_InitTypeDef與庫函數xxx_Init()的協同工作機制體現了模塊化設計的核心思想。結構體xxx_InitTypeDef定義在w55mh32_xxx.h文件中,庫函數xxx_Init()定義在w55mh32_xxx.c文件中,編程時我們可以結合這兩個文件內注釋使用。
ADC_InitTypeDef結構體
ADC_InitTypeDef結構體定義在w55mh32_adc.h文件內,具體定義如下:
typedef struct { uint32_t ADC_Mode; // ADC 工作模式選擇 FunctionalState ADC_ScanConvMode; /* ADC 掃描(多通道) 或者單次(單通道)模式選擇 */ FunctionalState ADC_ContinuousConvMode; // ADC 單次轉換或者連續轉換選擇 uint32_t ADC_ExternalTrigConv; // ADC 轉換觸發信號選擇 uint32_t ADC_DataAlign; // ADC 數據寄存器對齊格式 uint8_t ADC_NbrOfChannel; // ADC 采集通道數 } ADC_InitTypeDef;
ADC_Mode:配置ADC的模式,當使用一個ADC時是獨立模式,使用兩個ADC時是雙模式,在雙模式下還有很多細分模式可選,我們一般使用一個ADC的獨立模式。
ScanConvMode:可選參數為ENABLE和DISABLE,配置是否使用掃描。如果是單通道AD轉換使用DISABLE,如果是多通道AD轉換使用ENABLE。
ADC_ContinuousConvMode:可選參數為ENABLE和DISABLE,配置是啟動自動連續轉換還是單次轉換。使用ENABLE配置為使能自動連續轉換; 使用DISABLE配置為單次轉換,轉換一次后停止需要手動控制才重新啟動轉換。一般設置為連續轉換。
ADC_ExternalTrigConv:參數用于配置模數轉換器的外部觸發源選擇機制。該配置項提供多種可編程觸發模式,包括定時器輸出信號、外部引腳事件等硬件觸發源,以及軟件自主觸發模式。在實際工程應用中,基于簡化系統設計和提高觸發精度的考量,通常采用軟件自動觸發(SWSTART)方式實現轉換控制,該模式通過直接調用庫函數即可精準啟動轉換過程,有效規避外部信號干擾風險。
ADC_DataAlign:轉換結果數據對齊模式,可選右對齊ADC_DataAlign_Right或者左對齊ADC_DataAlign_Left。一般我們選擇右對齊模式。
ADC_NbrOfChannel:AD轉換通道數目,根據實際設置即可。
4 單通道 ADC 轉換
4.1 代碼分析
1. 頭文件和宏定義
#include #include #include #include "delay.h" #include "w55mh32.h" #include "math.h" #define CONV_CHANNEL_NUM 1 #define VREF (3300) #define ADC_TEST_CHANNEL_PIN (GPIO_Pin_6) uint8_t ADC_CovChannel[1] = {ADC_Channel_6}; uint8_t ADC_SampleTIME[1] = {ADC_SampleTime_239Cycles5}; uint32_t DAM_ADC_Value[1];
頭文件:引入了標準庫頭文件和自定義頭文件,像stdlib.h、string.h、stdio.h這類標準庫,delay.h和w55mh32.h屬于自定義頭文件,math.h是數學庫頭文件。
宏定義:
CONV_CHANNEL_NUM:定義了 ADC 轉換的通道數量,這里是 1 個通道。
VREF:參考電壓值,單位為 mV,這里是 3300mV。
ADC_TEST_CHANNEL_PIN:定義了 ADC 測試通道對應的 GPIO 引腳,為GPIO_Pin_6。
數組:
ADC_CovChannel:存放要轉換的 ADC 通道,這里是ADC_Channel_6。
ADC_SampleTIME:存放每個通道的采樣時間,這里是ADC_SampleTime_239Cycles5。
DAM_ADC_Value:用于存儲 DMA 搬運過來的 ADC 轉換結果。
2. 函數聲明
void UART_Configuration(void); void ADC_Configuration(void); void DMA_Configuration(void);
聲明了用于配置串口、ADC 和 DMA 的函數。
3. main()函數
int main(void) { RCC_ClocksTypeDef clocks; delay_init(); UART_Configuration(); RCC_GetClocksFreq(&clocks); printf("n"); printf("SYSCLK: %3.1fMhz, HCLK: %3.1fMhz, PCLK1: %3.1fMhz, PCLK2: %3.1fMhz, ADCCLK: %3.1fMhzn", (float)clocks.SYSCLK_Frequency / 1000000, (float)clocks.HCLK_Frequency / 1000000, (float)clocks.PCLK1_Frequency / 1000000, (float)clocks.PCLK2_Frequency / 1000000, (float)clocks.ADCCLK_Frequency / 1000000); printf("ADC Single Testn"); ADC_Configuration(); while (1) { ADC_SoftwareStartConvCmd(ADC1, ENABLE); delay_ms(1000); } }
初始化延時函數delay_init()。
調用UART_Configuration()配置串口通信。
獲取系統時鐘頻率并通過串口輸出。
調用ADC_Configuration()配置 ADC。
進入無限循環,每秒通過軟件觸發一次 ADC1 的轉換。
4. GetCmd()函數
uint8_t GetCmd(void) { uint8_t tmp = 0; if (USART_GetFlagStatus(USART1, USART_FLAG_RXNE)) { tmp = USART_ReceiveData(USART1); } return tmp; }
此函數用于從串口接收數據,若串口 1 接收到數據,就將其讀取并返回。
5. UART_Configuration()函數
void UART_Configuration(void) { //GPIO port settings GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_InitStructure); USART_InitStructure.USART_BaudRate = 115200; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; USART_Init(USART1, &USART_InitStructure); USART_Cmd(USART1, ENABLE); }
使能 USART1 和 GPIOA 的時鐘。
配置 GPIOA 的Pin_9為復用推挽輸出,用于串口發送;Pin_10為浮空輸入,用于串口接收。
配置 USART1 的波特率、數據位、停止位、校驗位等參數,并使能 USART1。
6. ADC_Configuration()函數
void ADC_Configuration(void) { uint32_t i; ADC_InitTypeDef ADC_InitStructure; GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1, ENABLE); GPIO_InitStructure.GPIO_Pin = ADC_TEST_CHANNEL_PIN; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; GPIO_Init(GPIOA, &GPIO_InitStructure); RCC_ADCCLKConfig(RCC_PCLK2_Div8); ADC_DeInit(ADC1); ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; ADC_InitStructure.ADC_ScanConvMode = ENABLE; ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; ADC_InitStructure.ADC_NbrOfChannel = CONV_CHANNEL_NUM; ADC_Init(ADC1, &ADC_InitStructure); for (i = 0; i < CONV_CHANNEL_NUM; i++) { ADC_RegularChannelConfig(ADC1, ADC_CovChannel[i], i + 1, ADC_SampleTIME[i]); } ADC_SoftwareStartConvCmd(ADC1, ENABLE); ADC_Cmd(ADC1, ENABLE); ADC_ResetCalibration(ADC1); while (ADC_GetResetCalibrationStatus(ADC1)); ADC_StartCalibration(ADC1); while (ADC_GetCalibrationStatus(ADC1)); DMA_Configuration(); }
使能 GPIOA 和 ADC1 的時鐘。
配置GPIOA_Pin_6為模擬輸入模式。
配置 ADC1 的時鐘,復位 ADC1。
配置 ADC1 的工作模式、掃描模式、連續轉換模式等參數。
配置 ADC1 的規則通道。
啟動一次 ADC 轉換,使能 ADC1。
對 ADC1 進行校準操作。
調用DMA_Configuration()配置DMA。
7.DMA_Configuration()函數
void DMA_Configuration(void) { NVIC_InitTypeDef NVIC_InitStructure; DMA_InitTypeDef DMA_InitStructure; RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); DMA_DeInit(DMA1_Channel1); DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR; DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)DAM_ADC_Value; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; DMA_InitStructure.DMA_BufferSize = CONV_CHANNEL_NUM; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word; DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; DMA_InitStructure.DMA_Priority = DMA_Priority_High; DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; DMA_Init(DMA1_Channel1, &DMA_InitStructure); DMA_Cmd(DMA1_Channel1, ENABLE); ADC_DMACmd(ADC1, ENABLE); NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel1_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); DMA_ITConfig(DMA1_Channel1, DMA1_IT_TC1, ENABLE); }
使能 DMA1 的時鐘,復位 DMA1 通道 1。
配置 DMA 的源地址(ADC1 的數據寄存器)、目標地址(DAM_ADC_Value數組)、傳輸方向、緩沖區大小等參數。
使能 DMA1 通道 1 和 ADC1 的 DMA 功能。
配置 DMA1 通道 1 的中斷優先級并使能中斷。
8. DMA1_Channel1_IRQHandler()函數
void DMA1_Channel1_IRQHandler(void) { if (DMA_GetITStatus(DMA1_IT_TC1) != RESET) { DMA_ClearITPendingBit(DMA1_IT_TC1); DMA_ClearFlag(DMA1_FLAG_TC1); printf("Code Value = %d ,voltage value = %2.4fn", DAM_ADC_Value[0], (float)VREF * DAM_ADC_Value[0] / 4095 / 1000); } }
當 DMA1 通道 1 傳輸完成中斷發生時,清除中斷標志和標志位。
通過串口輸出 ADC 轉換后的數字值和對應的電壓值。
9. SER_PutChar()和fputc()函數
//Retarget Printf int SER_PutChar(int ch) { while (!USART_GetFlagStatus(USART1, USART_FLAG_TC)); USART_SendData(USART1, (uint8_t)ch); return ch; } int fputc(int c, FILE *f) { /* Place your implementation of fputc here */ /* e.g. write a character to the USART */ if (c == 'n') { SER_PutChar('r'); } return (SER_PutChar(c)); }
這兩個函數用于重定向printf(),讓printf的輸出通過串口發送出去。
4.2 下載驗證
這段代碼實現了對ADC_Channel_6的單通道模擬信號采集,利用 DMA 將 ADC 轉換結果搬運到指定數組,通過串口輸出轉換后的數字值和對應的電壓值,每秒進行一次轉換,以下是轉換結果:
5 雙重ADC同步規則模式采集實驗
AD轉換包括采樣階段和轉換階段,在采樣階段才對通道數據進行采集;而在轉換階段只是將采集到的數據進行轉換為數字量輸出,此刻通道數據變化不會改變轉換結果。
獨立模式的ADC采集需要在一個通道采集并且轉換完成后才會進行下一個通道的采集。而雙重ADC的機制就是使用兩個ADC同時采樣一個或者多個通道。 雙重ADC模式較獨立模式一個最大的優勢就是提高了采樣率,彌補了單個ADC采樣不夠快的缺點。
啟用雙ADC模式的時候,通過配置ADC_CR1寄存器的DUALMOD[3:0]位,可以有幾種不同的模式, 具體見如下表格,雙ADC模式的各種模式匯總 :
模式 | 簡要說明 |
同步注入模式 | ADC1 和 ADC2 同時轉換一個注入通道組,其中 ADC1 為主,ADC2 為從。轉換的數據存儲在每個 ADC 接口的 ADC_JDRx 寄存器中。 |
同步規則模式 | ADC1 和 ADC2 同時轉換一個規則通道組,其中 ADC1 為主,ADC2 為從。ADC1 轉換的結果放在 ADC1_DR 的低 16 位,ADC2 轉換的結果放在 ADC1_DR 的高十六位。 |
快速交叉模式 | ADC1 和 ADC2 交替采集一個規則通道組(通常為一個通道)。當 ADC2 觸發之后,ADC1 需要等待 7 個 ADCCLK 之后才能觸發。 |
慢速交叉模式 | ADC1 和 ADC2 交替采集一個規則通道組(只能為一個通道)。當 ADC2 觸發之后,ADC1 需要等待 14 個 ADCCLK 之后才能觸發。 |
交替觸發模式 | ADC1 和 ADC2 輪流采集注入通道組,當 ADC1 所有通道采集完畢之后再采集 ADC2 的通道,如此循環。跟交叉采集不一樣。 |
混合的規則 / 注入同步模式 | 規則組同步轉換被中斷,以啟動注入組的同步轉換。分開兩個模式來理解即可,區別是注入組可中斷規則組的轉換。 |
混合的同步規則 + 交替觸發模式 | 規則組同步轉換被中斷,以啟動注入組交替觸發轉換。分開兩個模式理解即可,區別是注入組可中斷規則組的轉換。 |
混合同步注入 + 交叉模式 | 交叉轉換可被同步注入模式中斷,此時交叉轉換中斷,注入轉換啟動。 |
這里我們選取同步規則模式來作為實驗講解。同步規則模式是ADC1和ADC2同時轉換一個規則通道組,ADC1是主,ADC2是從, ADC1轉換的結果放在ADC1_DR的低16位,ADC2轉換的結果放在ADC1_DR的高十六位。并且必須開啟DMA功能。
外部觸發來自ADC1的規則組多路開關(由ADC1_CR2寄存器的EXTSEL[2:0]選擇), 它同時給ADC2提供同步觸發。 為了簡單起見,ADC1跟ADC2我們選擇軟件觸發。
為了實驗的簡單起見,實驗中我們選取ADC1和ADC2各采集一個通道 :
5.1 代碼分析
1. 頭文件和宏定義
#include #include #include #include "delay.h" #include "w55mh32.h" #include "math.h" #define CONV_CHANNEL_NUM 1 #define VREF (3300)
頭文件:
引入了標準 C 庫的頭文件以及自定義的頭文件,用于實現基本的庫函數調用、延時功能等。
宏定義:
CONV_CHANNEL_NUM:定義了 ADC 轉換的通道數量,這里設置為 1,表示每個 ADC 使用一個通道進行轉換。
VREF:定義了參考電壓值,單位為 mV,這里設置為 3300mV,用于后續將 ADC 轉換的數字值轉換為實際的電壓值。
函數聲明
void UART_Configuration(void); void ADC_Configuration(void); void DMA_Configuration(void);
聲明了三個函數,分別用于配置串口通信、ADC 模塊和 DMA 模塊。
3. 全局變量
uint32_t DAM_ADC_Value[1];
定義了一個全局數組DAM_ADC_Value,用于存儲 DMA 從 ADC1 的數據寄存器搬運過來的轉換結果。
4. main()函數
int main(void) { RCC_ClocksTypeDef clocks; delay_init(); UART_Configuration(); RCC_GetClocksFreq(&clocks); printf("n"); printf("SYSCLK: %3.1fMhz, HCLK: %3.1fMhz, PCLK1: %3.1fMhz, PCLK2: %3.1fMhz, ADCCLK: %3.1fMhzn", (float)clocks.SYSCLK_Frequency / 1000000, (float)clocks.HCLK_Frequency / 1000000, (float)clocks.PCLK1_Frequency / 1000000, (float)clocks.PCLK2_Frequency / 1000000, (float)clocks.ADCCLK_Frequency / 1000000); printf("ADC Double Test(ADC1 & ADC2)n"); ADC_Configuration(); while (1) { ADC_SoftwareStartConvCmd(ADC1, ENABLE); delay_ms(1000); } }
初始化:
初始化延時函數delay_init(),用于后續的延時操作。
調用UART_Configuration()函數配置串口通信,以便后續通過串口輸出信息。
獲取系統時鐘頻率信息并通過串口輸出,方便調試和查看系統時鐘配置。
輸出提示信息,表示開始進行 ADC 雙模式測試。
調用ADC_Configuration()函數配置 ADC 模塊。
主循環:
通過軟件觸發 ADC1 開始轉換操作。
延時 1 秒,控制 ADC 轉換的頻率。
5. GetCmd()函數
uint8_t GetCmd(void) { uint8_t tmp = 0; if (USART_GetFlagStatus(USART1, USART_FLAG_RXNE)) { tmp = USART_ReceiveData(USART1); } return tmp; }
該函數用于從串口接收數據。如果串口 1 接收到數據(USART_FLAG_RXNE標志置位),則讀取該數據并返回。
6. UART_Configuration()函數
void UART_Configuration(void) { GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_InitStructure); USART_InitStructure.USART_BaudRate = 115200; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; USART_Init(USART1, &USART_InitStructure); USART_Cmd(USART1, ENABLE); }
時鐘使能:使能 USART1 和 GPIOA 的時鐘,因為 USART1 使用 GPIOA 的引腳進行通信。
GPIO 配置:
配置 GPIOA 的 Pin 9 為復用推挽輸出模式,用于 USART1 的發送功能。
配置 GPIOA 的 Pin 10 為浮空輸入模式,用于 USART1 的接收功能。
USART 配置:
設置 USART1 的波特率為 115200,數據位為 8 位,停止位為 1 位,無校驗位,無硬件流控制,支持收發模式。
初始化 USART1 并使能該模塊。
7. ADC_Configuration()函數
void ADC_Configuration(void) { ADC_InitTypeDef ADC_InitStructure; GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1 | RCC_APB2Periph_ADC2, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; GPIO_Init(GPIOA, &GPIO_InitStructure); RCC_ADCCLKConfig(RCC_PCLK2_Div8); ADC_DeInit(ADC1); ADC_DeInit(ADC2); ADC_InitStructure.ADC_Mode = ADC_Mode_RegSimult; //synchronization rule mode ADC_InitStructure.ADC_ScanConvMode = DISABLE; ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; ADC_InitStructure.ADC_NbrOfChannel = 1; ADC_Init(ADC1, &ADC_InitStructure); ADC_Init(ADC2, &ADC_InitStructure); /* channel configuration */ ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_239Cycles5); ADC_RegularChannelConfig(ADC2, ADC_Channel_1, 1, ADC_SampleTime_239Cycles5); /* module enablement */ ADC_Cmd(ADC1, ENABLE); ADC_Cmd(ADC2, ENABLE); /* adc calibration */ ADC_ResetCalibration(ADC1); while (ADC_GetResetCalibrationStatus(ADC1)); ADC_StartCalibration(ADC1); while (ADC_GetCalibrationStatus(ADC1)); ADC_ResetCalibration(ADC2); while (ADC_GetResetCalibrationStatus(ADC2)); ADC_StartCalibration(ADC2); while (ADC_GetCalibrationStatus(ADC2)); DMA_Configuration(); }
時鐘使能:使能 GPIOA、ADC1 和 ADC2 的時鐘。
GPIO 配置:配置 GPIOA 的 Pin 0 和 Pin 1 為模擬輸入模式,分別用于 ADC1 的通道 0 和 ADC2 的通道 1。
ADC 時鐘配置和復位:配置 ADC 的時鐘,將 PCLK2 分頻 8 作為 ADC 的時鐘源。復位 ADC1 和 ADC2,將它們的寄存器恢復到默認狀態。
ADC 初始化:
設置 ADC 工作模式為ADC_Mode_RegSimult,即規則同步模式,使 ADC1 和 ADC2 可以同時對各自的通道進行轉換。
禁用掃描模式和連續轉換模式,采用軟件觸發轉換。
數據右對齊,每個 ADC 使用 1 個通道進行轉換。
分別初始化 ADC1 和 ADC2。
通道配置:
配置 ADC1 的規則通道為通道 0,轉換順序為 1,采樣時間為 239.5 個周期。
配置 ADC2 的規則通道為通道 1,轉換順序為 1,采樣時間為 239.5 個周期。
使能 ADC 模塊:使能 ADC1 和 ADC2。
ADC 校準:對 ADC1 和 ADC2 分別進行復位校準和開始校準操作,并等待校準完成。
調用 DMA 配置函數:調用DMA_Configuration()函數配置 DMA 模塊。
8.DMA_Configuration()函數
void DMA_Configuration(void) { NVIC_InitTypeDef NVIC_InitStructure; DMA_InitTypeDef DMA_InitStructure; RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); DMA_DeInit(DMA1_Channel1); DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR; DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)DAM_ADC_Value; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; DMA_InitStructure.DMA_BufferSize = CONV_CHANNEL_NUM; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word; DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; DMA_InitStructure.DMA_Priority = DMA_Priority_High; DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; DMA_Init(DMA1_Channel1, &DMA_InitStructure); DMA_Cmd(DMA1_Channel1, ENABLE); ADC_DMACmd(ADC1, ENABLE); NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel1_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); DMA_ITConfig(DMA1_Channel1, DMA1_IT_TC1, ENABLE); }
時鐘使能和 DMA 復位:
使能 DMA1 的時鐘。
復位 DMA1 的通道 1。
DMA 初始化:
設置 DMA 的源地址為 ADC1 的數據寄存器ADC1->DR。
設置 DMA 的目標地址為DAM_ADC_Value數組。
數據傳輸方向為從外設(ADC1 的數據寄存器)到內存。
緩沖區大小為CONV_CHANNEL_NUM,即 1。
外設地址不遞增,內存地址遞增。
數據傳輸大小為字(32 位)。
DMA 工作模式為循環模式,即完成一次傳輸后自動重新開始。
設置 DMA 優先級為高。
禁用內存到內存的傳輸模式。
初始化 DMA1 的通道 1 并使能該通道。
使能 ADC1 的 DMA 功能。
NVIC 配置:
配置 DMA1 通道 1 的中斷優先級,搶占優先級和子優先級都設置為 0。
使能 DMA1 通道 1 的中斷。
使能 DMA1 通道 1 的傳輸完成中斷。
9. DMA1_Channel1_IRQHandler()函數
void DMA1_Channel1_IRQHandler(void) { if (DMA_GetITStatus(DMA1_IT_TC1) != RESET) { DMA_ClearITPendingBit(DMA1_IT_TC1); DMA_ClearFlag(DMA1_FLAG_TC1); printf("ADC1 Code Value = %d ,voltage value = %2.4fn", DAM_ADC_Value[0] & 0xFFFF, (float)VREF * (DAM_ADC_Value[0] & 0xFFFF) / 4095 / 1000); printf("ADC2 Code Value = %d ,voltage value = %2.4fn", (DAM_ADC_Value[0] >> 16) & 0xFFFF, (float)VREF * ((DAM_ADC_Value[0] >> 16) & 0xFFFF) / 4095 / 1000); } }
中斷處理:
當 DMA1 通道 1 的傳輸完成中斷發生時,清除中斷標志和標志位。
數據處理和輸出:
從DAM_ADC_Value[0]中提取 ADC1 和 ADC2 的轉換結果。ADC1 的結果存儲在低 16 位,ADC2 的結果存儲在高 16 位。
將提取的數字值轉換為對應的電壓值,并通過串口輸出 ADC1 和 ADC2 的數字值和電壓值。
10. SER_PutChar和fputc()函數
//Retarget Printf int SER_PutChar(int ch) { while (!USART_GetFlagStatus(USART1, USART_FLAG_TC)); USART_SendData(USART1, (uint8_t)ch); return ch; } int fputc(int c, FILE *f) { if (c == 'n') { SER_PutChar('r'); } return (SER_PutChar(c)); }
SER_PutChar()函數:將一個字符發送到 USART1,并等待發送完成。
fputc()函數:重定向printf()函數的輸出到 USART1。當輸出換行符n時,先發送回車符r,以確保在終端上正確顯示換行。
這段代碼通過配置 ADC1 和 ADC2 在規則同步模式下同時對不同通道進行模擬信號采集,利用 DMA 將轉換結果傳輸到內存,最后通過串口輸出轉換后的數字值和對應的電壓值,實現了 ADC 雙模式測試的功能。
5.2 下載驗證
6 ADC 模擬看門狗功能
6.1 代碼解析
1. 頭文件與全局聲明
w55mh32.h:包含 STM32 外設寄存器定義(如 ADC1、USART1)。
delay.h:提供毫秒級延時函數:delay_ms()。
2. main()函數:初始化與主循環
int main(void) { // 初始化 delay_init(); // 延時初始化 UART_Configuration(); // 串口配置(115200波特率) ADC_Configuration(); // ADC配置(含模擬看門狗) // 打印系統時鐘 printf("SYSCLK: %3.1fMhz...n", (float)clocks.SYSCLK_Frequency / 1000000); // 主循環:周期性觸發ADC轉換 while (1) { ADC_SoftwareStartConvCmd(ADC1, ENABLE); // 啟動軟件觸發轉換 delay_ms(200); // 延時200ms } }
3. ADC_Configuration:ADC 與模擬看門狗配置
void ADC_Configuration(void) { // 使能時鐘:ADC1、GPIOA RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1, ENABLE); // ADC時鐘:PCLK2/8(假設PCLK2=72MHz → ADCCLK=9MHz) RCC_ADCCLKConfig(RCC_PCLK2_Div8); // ADC初始化:獨立模式,單通道,單次轉換 ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; ADC_Init(ADC1, &ADC_InitStructure); ADC_Cmd(ADC1, ENABLE); // 使能ADC // 中斷配置:ADC1_2_IRQn(模擬看門狗中斷) NVIC_Init(&NVIC_InitStructure); // 配置通道10:采樣時間239.5周期 ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 1, ADC_SampleTime_239Cycles5); // 模擬看門狗配置: ADC_AnalogWatchdogThresholdsConfig(ADC1, 2048, 1024); // 閾值:1024(下限)~2048(上限) ADC_AnalogWatchdogSingleChannelConfig(ADC1, ADC_Channel_10); // 監控通道10 ADC_AnalogWatchdogCmd(ADC1, ADC_AnalogWatchdog_SingleRegEnable); // 使能單通道看門狗 // 校準ADC(必須步驟) ADC_ResetCalibration(ADC1); ADC_StartCalibration(ADC1); }
4. 中斷處理函數:ADC1_2_IRQHandler()
void ADC1_2_IRQHandler(void) { ADC_ITConfig(ADC1, ADC_IT_AWD, DISABLE); // 禁用中斷,避免嵌套 if (ADC_GetFlagStatus(ADC1, ADC_FLAG_AWD)) { // 檢測看門狗標志 ADC_ClearFlag(ADC1, ADC_FLAG_AWD); // 清除標志 printf("ADC Awd is Happened. Code Value = %d rn", ADC1->DR); // 輸出ADC值 } ADC_ITConfig(ADC1, ADC_IT_AWD, ENABLE); // 重新使能中斷 }
5. printf重定向:串口輸出
該代碼實現了W55MH32的 ADC 模擬看門狗功能,通過串口輸出系統信息和電壓越界報警。核心流程:配置 ADC 和串口 → 周期性觸發 ADC 轉換 → 監控電壓閾值 → 中斷響應。適用于需要實時監控模擬信號的場景(如電池電壓監測、傳感器輸入保護)。
6.2 下載驗證
7 ADC_VrefintTemper
7.1 代碼解析
1. 全局定義與變量
#define CONV_CHANNEL_NUM 2 // 轉換通道數(2個內部通道) #define VREF (3300) // 參考電壓(3.3V,單位:mV) uint8_t ADC_CovChannel[2] = {ADC_Channel_16, ADC_Channel_17}; // 通道16(溫度)、17(VREFINT) uint32_t DAM_ADC_Value[2]; // DMA存儲數組(2個通道數據)
2. main()函數:初始化與主循環
int main(void) { delay_init(); // 延時初始化 UART_Configuration(); // 串口配置(115200波特率) ADC_Configuration(); // ADC+DMA配置 // 打印系統時鐘 printf("SYSCLK: %3.1fMhz...n", (float)clocks.SYSCLK_Frequency / 1000000); // 主循環:周期性觸發ADC轉換(軟件觸發) while (1) { ADC_SoftwareStartConvCmd(ADC1, ENABLE); // 啟動轉換 delay_ms(1000); // 每秒觸發一次 } }
3. ADC_Configuration:ADC 初始化
void ADC_Configuration(void) { // 使能ADC1、GPIOA時鐘 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1, ENABLE); // ADC時鐘:PCLK2/4(假設PCLK2=72MHz → ADCCLK=18MHz) RCC_ADCCLKConfig(RCC_PCLK2_Div4); // ADC配置:獨立模式、掃描模式(多通道)、單次轉換 ADC_InitStructure.ADC_ScanConvMode = ENABLE; // 掃描模式(多通道) ADC_InitStructure.ADC_NbrOfChannel = CONV_CHANNEL_NUM; // 2通道 ADC_Init(ADC1, &ADC_InitStructure); // 配置通道16(溫度)、17(VREFINT) for (i=0; i
4. DMA_Configuration():DMA 初始化
void DMA_Configuration(void) { // 配置DMA1通道1:ADC_DR → DAM_ADC_Value數組 DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR; // 外設地址(ADC數據寄存器) DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)DAM_ADC_Value; // 內存地址(存儲數組) DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; // 外設到內存 DMA_InitStructure.DMA_BufferSize = CONV_CHANNEL_NUM; // 傳輸數據量(2個通道) DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; // 循環模式(連續傳輸) DMA_Init(DMA1_Channel1, &DMA_InitStructure); DMA_Cmd(DMA1_Channel1, ENABLE); // 使能ADC的DMA請求 ADC_DMACmd(ADC1, ENABLE); // 配置DMA中斷(傳輸完成觸發) NVIC_Init(&NVIC_InitStructure); DMA_ITConfig(DMA1_Channel1, DMA1_IT_TC1, ENABLE); // 使能傳輸完成中斷 }
5. DMA 中斷處理函數:DMA1_Channel1_IRQHandler()
void DMA1_Channel1_IRQHandler(void) { if (DMA_GetITStatus(DMA1_IT_TC1)) { // 檢測傳輸完成中斷 DMA_ClearITPendingBit(DMA1_IT_TC1); // 計算并打印結果(單位:mV) printf("temperature AD: %d → %.4fVn", DAM_ADC_Value[0], (float)VREF * DAM_ADC_Value[0] / 4095 / 1000); printf("VREFINT AD: %d → %.4fVn", DAM_ADC_Value[1], (float)VREF * DAM_ADC_Value[1] / 4095 / 1000); } }
該代碼利用 W55MH32 的內部 ADC 通道(溫度傳感器和 VREFINT),結合 DMA 實現高效數據采集,通過串口輸出實時電壓值。適用于需要監測內部傳感器或校準 ADC 的場景(如工業控制、嵌入式系統診斷)。核心優勢:DMA 減輕 CPU 負載、內部傳感器免外部接線、循環模式連續采集。
7.2 下載驗證
WIZnet 是一家無晶圓廠半導體公司,成立于 1998 年。產品包括互聯網處理器 iMCU?,它采用 TOE(TCP/IP 卸載引擎)技術,基于獨特的專利全硬連線 TCP/IP。iMCU? 面向各種應用中的嵌入式互聯網設備。
WIZnet 在全球擁有 70 多家分銷商,在香港、韓國、美國設有辦事處,提供技術支持和產品營銷。
香港辦事處管理的區域包括:澳大利亞、印度、土耳其、亞洲(韓國和日本除外)。
-
單片機
+關注
關注
6067文章
44969瀏覽量
649526 -
寄存器
+關注
關注
31文章
5432瀏覽量
124158 -
adc
+關注
關注
99文章
6678瀏覽量
548907 -
電壓采集
+關注
關注
2文章
24瀏覽量
14456
發布評論請先 登錄
【紫光同創國產FPGA教程】【第十九章】ADDA測試例程

【MangoTree】芒果樹將參加第十九屆中國國際工業博覽會
第十九講 數據選擇器和分配器

2010年第十九屆波蘭國際建筑建材博覽會BUDMA 王薇
模擬電路網絡課件 第十九節:金屬-氧化物-半導體場效應管

第十九屆中國半導體封裝測試技術與市場年會即將召開
長電科技舉辦第十九屆中國半導體封測年會
喜!聯誠發成功入選第十九屆“深圳知名品牌”

景旺電子榮獲第十九屆“深圳知名品牌”稱號
美格智能亮相IOTE 2023第十九屆上海國際物聯網展
英飛特電子參加第十九屆中國道路照明論壇

芯茂微電子榮獲第十九屆中國芯“芯火新銳產品”大獎
第十九屆中博會圓滿閉幕,思爾芯“小巨人”展現EDA創新實力

評論