8.1 實(shí)驗(yàn)內(nèi)容
通過本實(shí)驗(yàn)主要學(xué)習(xí)以下內(nèi)容:
? ADC的簡介
? GD32F470 ADC工作原理
? DMA原理
? 規(guī)則組多通道循環(huán)采樣
8.2 實(shí)驗(yàn)原理
8.2.1 ADC原理
我們知道,自然界中有非常多的模擬信號(hào),比如光照強(qiáng)度,還有其他的例如溫度、聲音等等,那么人們是怎么來衡量一個(gè)模擬信號(hào)的呢?
我們通常會(huì)說今天光照度達(dá)到了3萬Lux(照度單位),現(xiàn)在測量到的體溫是36.5℃,我們所處的環(huán)境是40分貝,沒錯(cuò),人們就是通過將這些模擬信號(hào)數(shù)字化,從而達(dá)到衡量這些模擬信號(hào)的目的。那對于MCU來說,如果要測量一個(gè)模擬量,可以通過自帶的ADC(Analog-to-Digital converters)模塊,即模-數(shù)轉(zhuǎn)換器將模擬量轉(zhuǎn)化為可以被MCU讀取到的數(shù)字量。
8.2.2 GD32F470 ADC工作原理
GD32F470有3個(gè)12位逐次逼近型ADC(SAR ADC),這三個(gè)ADC可以獨(dú)立工作,也可以工作在同步模式下。有最多24個(gè)外部ADC引腳可用于將連接到這些引腳的電壓值轉(zhuǎn)換為數(shù)字量,這些引腳號(hào)可以通過Datasheet獲得。
表中ADC012_INx的意思是:該IO口可以作為通道x用于ADC0、ADC1和ADC2。如ADC012_IN0,表示PA0可以用于ADC0的通道0使用,也可以作為ADC1和ADC2的通道0使用。但要注意:不能在同一個(gè)時(shí)刻讓不同的ADC去轉(zhuǎn)換同一個(gè)通道,否則會(huì)有無法預(yù)料的結(jié)果
以下總結(jié)了GD32F470 ADC的特性:
? 高性能:
– ADC采樣分辨率: 12位、 10位、 8位、或者6位分辨率;
– ADC采樣率: 12位分辨率為2.6 MSPs, 10位分辨率為3.0 MSPs。分辨率越低,轉(zhuǎn)換越快;
– 自校準(zhǔn)時(shí)間: 131個(gè)ADC時(shí)鐘周期;
– 可編程采樣時(shí)間;
– 數(shù)據(jù)存儲(chǔ)模式:最高有效位對齊和最低有效位對齊;
– DMA請求。
? 模擬輸入通道:
– 16個(gè)外部模擬輸入通道;
– 1個(gè)內(nèi)部溫度傳感通道(VSENSE);
– 1個(gè)內(nèi)部參考電壓輸入通道(VREFINT);
– 1個(gè)外部監(jiān)測電池VBAT供電引腳輸入通道。
? 轉(zhuǎn)換開始的發(fā)起:
– 軟件觸發(fā);
– 硬件觸發(fā)。
? 運(yùn)行模式:
– 轉(zhuǎn)換單個(gè)通道,或者掃描一序列的通道;
– 單次運(yùn)行模式,每次觸發(fā)轉(zhuǎn)換一次選擇的輸入通道;
– 連續(xù)運(yùn)行模式,連續(xù)轉(zhuǎn)換所選擇的輸入通道;
– 間斷運(yùn)行模式;
– 同步模式(適用于具有兩個(gè)或多個(gè)ADC的設(shè)備)。
? 轉(zhuǎn)換結(jié)果閾值監(jiān)測器功能: 模擬看門狗。
? 中斷產(chǎn)生:
– 常規(guī)轉(zhuǎn)換結(jié)束;
– 模擬看門狗事件;
– 溢出事件。
? 過采樣:
– 16位的數(shù)據(jù)寄存器;
– 可調(diào)整的過采樣率,從2x到256x;
– 高達(dá)8位的可編程數(shù)據(jù)移位。
? ADC供電要求:
– 2.4V到3.6V,一般供電電壓為3.3V。
? ADC輸入范圍: VREFN ≤VIN ≤VREFP 。
下面介紹下GD32F470的ADC框圖:
標(biāo)注1:輸入電壓和參考電壓
輸入電壓引腳定義如下表:
大于等于100pin的GD32F470,ADC參考電壓等于VREFP,100pin以下的GD32F470,ADC參考電壓等于VDDA
GD32F470的ADC是12bit有效位的,滿量程對應(yīng)的轉(zhuǎn)換值是4095,即當(dāng)采樣引腳上的電壓等于ADC參考電壓時(shí),得到的轉(zhuǎn)換值即為4095。故理論采樣是指可通過以下公式得到:
采樣數(shù)值=實(shí)際電壓/參考電壓*4095
標(biāo)注2:輸入通道
前面提到,ADC有最多16個(gè)外部模擬通道和3個(gè)內(nèi)部通道,外部通道號(hào)從IN0~IN15,由IO口號(hào)來決定,兩個(gè)內(nèi)部通道是IN16(溫度傳感器)和IN17(內(nèi)部Vrefint,典型值1.2V),下表給出了IO口號(hào)對應(yīng)的ADC通道:
標(biāo)注3:規(guī)則組
GD32F470的ADC轉(zhuǎn)換組稱為規(guī)則組,也叫常規(guī)序列。
規(guī)則組有兩個(gè)重要的參數(shù),其一為轉(zhuǎn)換的個(gè)數(shù),其二為轉(zhuǎn)換的序列,規(guī)定好這兩個(gè)參數(shù)后,一旦開始規(guī)則組的轉(zhuǎn)換,則ADC就按照轉(zhuǎn)換序列一個(gè)一個(gè)的進(jìn)行模-數(shù)轉(zhuǎn)換,直到達(dá)到要求的轉(zhuǎn)換個(gè)數(shù)。
規(guī)則組的轉(zhuǎn)換個(gè)數(shù)由ADC_RSQ0寄存器的RL[3:0]位規(guī)定,轉(zhuǎn)換的總數(shù)目為RL[3:0]+1,轉(zhuǎn)換總數(shù)目最大為16個(gè);轉(zhuǎn)換序列由ADC_RSQ0~ADC_RSQ2共同決定,我們來看下這幾個(gè)寄存器。
ADC_RSQ0寄存器:
ADC_RSQ1寄存器:
ADC_RSQ2寄存器:
舉個(gè)例子,現(xiàn)需要按照CH3->CH2->CH1的順序進(jìn)行規(guī)則組轉(zhuǎn)換,則設(shè)定RL[3:0] = 2,然后設(shè)定RSQ0為CH3,RSQ1為CH2,RSQ2為CH1,則當(dāng)開始規(guī)則組轉(zhuǎn)換時(shí),ADC首先進(jìn)行RSQ0規(guī)定的通道即CH3的轉(zhuǎn)換,再進(jìn)行RSQ1規(guī)定的通道即CH2的轉(zhuǎn)換,最后進(jìn)行RSQ2規(guī)定的通道即CH1轉(zhuǎn)換,當(dāng)這三個(gè)通道轉(zhuǎn)換完后,規(guī)則組轉(zhuǎn)換結(jié)束。
需要注意的是,每轉(zhuǎn)換一個(gè)規(guī)則組通道,轉(zhuǎn)換結(jié)果都會(huì)放在寄存器ADC_RDATA中,所以CPU一定要在下一個(gè)通道轉(zhuǎn)換完成前將上一個(gè)通道轉(zhuǎn)換結(jié)果讀走,否則會(huì)導(dǎo)致上一個(gè)通道數(shù)據(jù)被新的數(shù)據(jù)覆蓋。所以在多通道規(guī)則組轉(zhuǎn)換時(shí),為了保證能讀到所有通道的數(shù)據(jù),一定要使用DMA(直接存儲(chǔ)器訪問控制器),每個(gè)通道轉(zhuǎn)換結(jié)束后,都會(huì)給DMA發(fā)送請求,DMA就會(huì)將最新的ADC_RDATA中的數(shù)據(jù)搬走。關(guān)于ADC配合DMA的使用,后面會(huì)詳細(xì)介紹。
標(biāo)注4:觸發(fā)源
ADC的規(guī)則組需要選特定的觸發(fā)源用于觸發(fā)ADC轉(zhuǎn)換,注意,ADC的Enable(即ADC_CTL1寄存器的ADC_ON位置“1”)不會(huì)觸發(fā)ADC轉(zhuǎn)換,而是當(dāng)選定的觸發(fā)源來臨后ADC才開始轉(zhuǎn)換。
觸發(fā)源分為內(nèi)部觸發(fā)和外部觸發(fā),內(nèi)部觸發(fā)是指軟件觸發(fā);外部觸發(fā)源是除了內(nèi)部觸發(fā)源以外的觸發(fā)源,外部觸發(fā)源可以通過ADC_CTL1寄存器查看:
ADC_CTL1寄存器:
標(biāo)注5:規(guī)則組數(shù)據(jù)寄存器
如標(biāo)注3規(guī)則組的表述,每個(gè)ADC的規(guī)則組只有一個(gè)數(shù)據(jù)寄存器ADC_RDATA,每轉(zhuǎn)換一個(gè)通道,轉(zhuǎn)換結(jié)果放在這個(gè)寄存器中,在下一通道轉(zhuǎn)換結(jié)束前必須要將上一個(gè)通道的轉(zhuǎn)換結(jié)果取走。
標(biāo)注6:ADC中斷及標(biāo)志位
ADC的中斷總共有兩種:規(guī)則組轉(zhuǎn)換結(jié)束中斷以及模擬看門狗,可以通過將ADC_CTL0中的EOCIE和WDEIE置“1”來開啟相應(yīng)中斷。
ADC_STAT寄存器中的EOC和WDE表示相應(yīng)事件發(fā)生,EOC置“1”表示規(guī)則組的轉(zhuǎn)換已經(jīng)結(jié)束。
8.2.3 DMA原理
本實(shí)驗(yàn)中ADC通道有兩個(gè),分別為PF7和PF10,所以我們用規(guī)則組多通道采樣實(shí)現(xiàn)雙電壓讀取,從上一節(jié)內(nèi)容中可以知道,ADC規(guī)則組實(shí)現(xiàn)多通道轉(zhuǎn)換時(shí),必須要用到DMA。下面我們介紹下DMA原理。
DMA(直接存儲(chǔ)器訪問控制器)是一個(gè)非常好用的外設(shè),它提供了一種硬件的方式在外設(shè)和存儲(chǔ)器之間或者存儲(chǔ)器和存儲(chǔ)器之間傳輸數(shù)據(jù),而無需 CPU 的介入,從而使 CPU 可以專注在處理其他系統(tǒng)功能上。GD32F470有兩個(gè)DMA,其中DMA0有8個(gè)通道,DMA1也有8個(gè)通道。
GD32F470支持DMA的單一傳輸和多數(shù)據(jù)傳輸模式。這里只講解單一傳輸。DMA實(shí)現(xiàn)很簡單,只要配置好以下幾要素即可。
軟件優(yōu)先級(jí):分為4級(jí),低,中,高和極高。可以通過寄存器DMA_CHxCTL的PRIO位域來配置。
硬件優(yōu)先級(jí):當(dāng)通道具有相同的軟件優(yōu)先級(jí)時(shí),編號(hào)低的通道優(yōu)先級(jí)高。例:通道0和通道2配置為相同的軟件優(yōu)先級(jí)時(shí),通道0的優(yōu)先級(jí)高于通道2。
上面描述了DMA配置的一些要素,那么DMA是如何被觸發(fā)的呢,我們來看下DMA請求映射表:
DMA0各通道請求表:
DMA1各通道請求表:
本實(shí)驗(yàn)中是ADC配合DMA來使用,如果使用DMA去搬運(yùn)ADC0的數(shù)據(jù),從上表查詢得知需要使用DMA1的通道0,如果是搬運(yùn)ADC2的數(shù)據(jù),也可以用到DMA1的通道0。如現(xiàn)在設(shè)置DMA1的通道0去搬運(yùn)ADC2的數(shù)據(jù),當(dāng)ADC2每轉(zhuǎn)換一個(gè)通道,ADC2_RDATA會(huì)更新一次數(shù)據(jù),此時(shí)ADC2會(huì)自動(dòng)向DMA1的通道0發(fā)出一次搬運(yùn)請求,DMA收到請求后會(huì)進(jìn)行一次數(shù)據(jù)搬運(yùn)。DMA的請求和應(yīng)答方式見下圖:
8.3 硬件設(shè)計(jì)
本實(shí)驗(yàn)是使用PF7和PF10來進(jìn)行電壓采集,讀者可以采用飛線方式外接電壓到這兩個(gè)引腳進(jìn)行測試。
8.4 代碼解析
本實(shí)驗(yàn)用到兩個(gè)ADC2通道,使用ADC2規(guī)則組搭配DMA1通道0進(jìn)行數(shù)據(jù)轉(zhuǎn)換和搬運(yùn),ADC2規(guī)則組和DMA1通道0都開啟循環(huán)模式,一旦開始ADC2規(guī)則組轉(zhuǎn)換,會(huì)持續(xù)PF7和PF10上的電壓進(jìn)行轉(zhuǎn)換和數(shù)據(jù)搬運(yùn)。
8.4.1 DMA和ADC初始化
在driver_adc.c中定義driver_adc_regular_ch_dma_config函數(shù),該函數(shù)實(shí)現(xiàn)DMA和ADC的初始化。
C
void driver_adc_regular_ch_dma_config(typdef_adc_ch_general *ADC, typdef_adc_ch_parameter *ADC_CH,void *buffer)
{
dma_single_data_parameter_struct dma_single_data_parameter;
rcu_periph_clock_enable(ADC->dma_parameter.rcu_dma); ???????????????????????????????????????????????????????????????????????????????????????????????????????/*DMA時(shí)鐘開啟*/
dma_deinit(ADC->dma_parameter.dma_periph, ADC->dma_parameter.dma_channel); ???????????????/*DMA通道參數(shù)復(fù)位*/
/*DMA源地址、目標(biāo)地址、增量方式、傳輸位寬、傳輸方向、傳輸個(gè)數(shù)、優(yōu)先級(jí)設(shè)置*/
dma_single_data_parameter.periph_addr ?= (uint32_t)(&ADC_RDATA(ADC->adc_port));
dma_single_data_parameter.periph_inc ??= DMA_PERIPH_INCREASE_DISABLE;
dma_single_data_parameter.memory0_addr ?= (uint32_t)(buffer);
dma_single_data_parameter.memory_inc ??= DMA_MEMORY_INCREASE_ENABLE;
if(ADC->adc_mode == ADC_DAUL_ROUTINE_PARALLEL)
{
dma_single_data_parameter.periph_memory_width = DMA_PERIPH_WIDTH_32BIT;
}
else
{
dma_single_data_parameter.periph_memory_width = DMA_PERIPH_WIDTH_16BIT;
}
dma_single_data_parameter.direction ???= DMA_PERIPH_TO_MEMORY;
dma_single_data_parameter.number ??????= ADC->dma_parameter.dma_number;
dma_single_data_parameter.priority ????= ADC->dma_parameter.dma_priority;
dma_single_data_mode_init(ADC->dma_parameter.dma_periph, ADC->dma_parameter.dma_channel, &dma_single_data_parameter);
dma_channel_subperipheral_select(ADC->dma_parameter.dma_periph, ADC->dma_parameter.dma_channel,ADC->dma_parameter.dma_subperipheral_num);
/*DMA循環(huán)模式設(shè)置*/
if(ADC->dma_parameter.dma_circulation_mode == ENABLE)
{
dma_circulation_enable(ADC->dma_parameter.dma_periph, ADC->dma_parameter.dma_channel);
}
else
{
dma_circulation_disable(ADC->dma_parameter.dma_periph, ADC->dma_parameter.dma_channel);
}
dma_channel_enable(ADC->dma_parameter.dma_periph, ADC->dma_parameter.dma_channel); ???????/* 使能DMA */
driver_adc_config(ADC,ADC_CH); ???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????/* ADC初始化 */
}
在driver_adc.h中聲明了ADC DMA的結(jié)構(gòu)體:
C
typedef struct __typdef_adc_dma_parameter
{
rcu_periph_enum rcu_dma; ???????????????//DMA時(shí)鐘
uint32_t dma_periph; ???????????????????????????????//DMA號(hào)
dma_channel_enum dma_channel;//DMA通道號(hào)
dma_subperipheral_enum dma_subperipheral_num;//外設(shè)請求號(hào)
uint32_t dma_number; ???????????????????????????????//DMA傳輸個(gè)數(shù)
uint32_t dma_priority; ???????????????????????//DMA通道優(yōu)先級(jí)
EventStatus dma_circulation_mode;//循環(huán)模式
}typdef_adc_dma_parameter;
這段代碼比較簡單,請讀者按照前面介紹的DMA原理進(jìn)行解析。
8.4.2 BSP_ADC設(shè)置所需要的參數(shù)及IO口結(jié)構(gòu)體定義
在bsp_adc.c中,對BSP_ADC設(shè)置所需要的參數(shù)及IO擴(kuò)結(jié)構(gòu)體數(shù)組進(jìn)行了定義:
C
typdef_adc_ch_general ?BSP_ADC= {
.rcu_adc = RCU_ADC2, ???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????/* ADC2的時(shí)鐘 */
.adc_psc = ADC_ADCCK_PCLK2_DIV8, ???????????????????????????????????????????????????????????????????????/* ADC2設(shè)置為APB2 8分頻 */
.adc_port = ADC2, ???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????/* ADC口為ADC2 */
.adc_mode = ADC_SYNC_MODE_INDEPENDENT, ???????????????????????????????????????????????/* ADC模式為獨(dú)立模式 */
.adc_channel_group = ADC_ROUTINE_CHANNEL, ???????????????????????????????????????/* 使用規(guī)則組 */
.adc_scan_function = ENABLE, ???????????????????????????????????????????????????????????????????????????????????????/* 開啟掃描模式 */
.adc_continuous_function = ENABLE, ???????????????????????????????????????????????????????????????/* 開啟循環(huán)模式 */
.ch_count = 2, ???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????/* 轉(zhuǎn)換長度為2 */
.adc_external_trigger_mode = EXTERNAL_TRIGGER_DISABLE,
.dma_parameter =
{
.rcu_dma = RCU_DMA1, ???????????????????????????????????????????????????????????????????????????????????????????????????????????????/* DMA1的時(shí)鐘 */
.dma_periph = DMA1, ???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????/* 使用DMA1 */
.dma_channel = DMA_CH0, ???????????????????????????????????????????????????????????????????????????????????????????????????????/* 使用通道4 */
.dma_number = 2, ???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????/* DMA傳輸長度為2 */
.dma_subperipheral_num = DMA_SUBPERI2,
.dma_priority = DMA_PRIORITY_HIGH, ???????????????????????????????????????????????????????/* DMA通道優(yōu)先級(jí) */
.dma_circulation_mode = ENABLE ???????????????????????????????????????????????????????????????????????/* DMA循環(huán)模式打開 */
},
.trigger_source = ADC_EXTTRIG_ROUTINE_T0_CH0, ???????/* ADC觸發(fā)源選擇為軟件觸發(fā) */
.DMA_mode = ENABLE ???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????/* 使用DMA */
};
typdef_adc_ch_parameter BSP_ADC_ch[2] =
{
{
.rcu_port = RCU_GPIOF, ???????????????????????????????????????????????????????????????????????????????????????????????????????????????/* GPIOF時(shí)鐘 */
.port = GPIOF, ???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????/* GPIO port */
.pin = GPIO_PIN_7, ???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????/* PF7 */
.gpio_speed = GPIO_OSPEED_2MHZ, ???????????????????????????????????????????????????????????????????????????????/* PF7速度設(shè)置為10MHz */
.adc_channel = ADC_CHANNEL_5, ???????????????????????????????????????????????????????????????????????????????????????/* PF7是ADC2的通道5 */
.sample_time = ADC_SAMPLETIME_144 ???????????????????????????????????????????????/* 設(shè)置采樣周期為55.5 */
}
,
{
.rcu_port = RCU_GPIOF, ???????????????????????????????????????????????????????????????????????????????????????????????????????????????/* GPIOF時(shí)鐘 */
.port = GPIOF, ???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????/* GPIO port */
.pin = GPIO_PIN_10, ???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????/* PF8 */
.gpio_speed = GPIO_OSPEED_2MHZ, ???????????????????????????????????????????????????????????????????????????????/* PF8速度設(shè)置為10MHz */
.adc_channel = ADC_CHANNEL_8, ???????????????????????????????????????????????????????????????????????????????????????/* PF8是ADC2的通道10 */
.sample_time = ADC_SAMPLETIME_144 ???????????????????????????????????????????????/* 設(shè)置采樣周期為55.5 */
}
};/* ADC通道參數(shù)配置,包括IO口,和對應(yīng)通道以及采樣周期 */
8.4.3 BSP_ ADC初始化和觸發(fā)ADC轉(zhuǎn)換的具體實(shí)現(xiàn)函數(shù)
在bsp_adc.c中定義了 DMA和ADC初始化和觸發(fā)ADC轉(zhuǎn)換的函數(shù):
C
uint16_t BSP_ADC_data[2] ;
void bsp_ADC_config()
{
driver_adc_regular_ch_dma_config(&BSP_ADC,BSP_ADC_ch,(uint16_t*)BSP_ADC_data);
driver_adc_software_trigger_enable(&BSP_ADC);
}
8.4.4 main函數(shù)實(shí)現(xiàn)
C
int main(void)
{
driver_init();//延時(shí)函數(shù)初始化
bsp_uart_init(&BOARD_UART);//BOARD_UART串口初始化
bsp_ADC_config();//ADC配置
while (1)
{
delay_ms(100);//延時(shí)100ms
printf_log(" the BSP_ADC data is %d,%d \r\n", BSP_ADC_data[0],BSP_ADC_data[1]);//打印ADC數(shù)據(jù)
}
}
本例程main函數(shù)首先進(jìn)行了延時(shí)函數(shù)初始化,為了演示實(shí)驗(yàn)結(jié)果,這里初始化了BOARD_UART串口,關(guān)于串口的使用,請讀者參考串口章節(jié),然后是BSP_ADC配置。在主循環(huán)中,每100ms打印一次PF7和PF10的ADC轉(zhuǎn)換數(shù)據(jù)。
8.5 實(shí)驗(yàn)結(jié)果
如上main函數(shù)實(shí)現(xiàn)說明。
更多GD32F470紫藤派資料下載:http://www.nmjo.cn/cms/kaifaban/391.html