雙ADC基本介紹
雙 ADC 的機(jī)制就是使用兩個(gè) ADC 同時(shí)采樣一個(gè)或者多個(gè)通道。雙重ADC 模式較獨(dú)立模式一個(gè)最大的優(yōu)勢就是提高了采樣率,彌補(bǔ)了單個(gè) ADC 采樣不夠快的缺點(diǎn)。
雙ADC工作框圖
雙ADC模式
在雙ADC模式里,根據(jù)ADC1_CR1寄存器中DUALMOD[2:0]位所選的模式,轉(zhuǎn)換的啟動可以是ADC1主和ADC2從的交替觸發(fā)或同步觸發(fā)。
注意:在雙ADC模式里,當(dāng)轉(zhuǎn)換配置成由外部事件觸發(fā)時(shí),用戶必須將其設(shè)置成僅觸發(fā)主ADC,從ADC設(shè)置成軟件觸發(fā),這樣可以防止意外的觸發(fā)從轉(zhuǎn)換。但是,主和從ADC的外部觸發(fā)必須同時(shí)被激活。
注意:在雙ADC模式里,為了在主數(shù)據(jù)寄存器上讀取從轉(zhuǎn)換數(shù)據(jù),必須使能DMA位,即使不使用DMA傳輸規(guī)則通道數(shù)據(jù)。
同步規(guī)則模式
此模式在規(guī)則通道組上執(zhí)行。外部觸發(fā)來自ADC1的規(guī)則組多路開關(guān)(由ADC1_CR2寄存器的EXTSEL[2:0]選擇), 它同時(shí)給ADC2提供同步觸發(fā)。
注意: 不要在2個(gè)ADC上轉(zhuǎn)換相同的通道 ((兩個(gè)ADC在同一個(gè)通道上的采樣時(shí)間不能重疊)。
在ADC1或ADC2的轉(zhuǎn)換結(jié)束時(shí):
● 產(chǎn)生一個(gè)32位DMA傳輸請求(如果設(shè)置了DMA位), 32位的ADC1_DR寄存器內(nèi)容傳輸?shù)絊RAM中,它上半個(gè)字包含ADC2的轉(zhuǎn)換數(shù)據(jù),低半個(gè)字包含ADC1的轉(zhuǎn)換數(shù)據(jù)。
● 當(dāng)所有ADC1/ADC2規(guī)則通道都被轉(zhuǎn)換完時(shí),產(chǎn)生EOC中斷(若任一ADC接口開放了中斷)。
注:在同步規(guī)則模式中,必須轉(zhuǎn)換具有相同時(shí)間長度的序列,或保證觸發(fā)的間隔比2個(gè)序列中較長的序列長,否則當(dāng)較長序列的轉(zhuǎn)換還未完成時(shí),具有較短序列的ADC轉(zhuǎn)換可能會被重啟。
掃描模式
此模式用來掃描一組模擬通道。 掃描模式可通過設(shè)置ADC_CR1寄存器的SCAN位來選擇。一旦這個(gè)位被設(shè)置, ADC掃描所有被ADC_SQRX寄存器(對規(guī)則通道)或ADC_JSQR(對注入通道)選中的所有通道。在每個(gè)組的每個(gè)通道上執(zhí)行單次轉(zhuǎn)換。在每個(gè)轉(zhuǎn)換結(jié)束時(shí),同一組的下一個(gè)通道被自動轉(zhuǎn)換。如果設(shè)置了CONT位,轉(zhuǎn)換不會在選擇組的最后一個(gè)通道上停止,而是再次從選擇組的第一個(gè)通道繼續(xù)轉(zhuǎn)換。
如果設(shè)置了DMA位,在每次EOC后, DMA控制器把規(guī)則組通道的轉(zhuǎn)換數(shù)據(jù)傳輸?shù)絊RAM中。而 注入通道轉(zhuǎn)換的數(shù)據(jù)總是存儲在ADC_JDRx寄存器中。
連續(xù)轉(zhuǎn)換模式
*單次轉(zhuǎn)換
*
外部觸發(fā)轉(zhuǎn)換
轉(zhuǎn)換可以由外部事件觸發(fā)(例如定時(shí)器捕獲,EXTI線)。如果設(shè)置了EXTTRIG控制位,則外部事件就能夠觸發(fā)轉(zhuǎn)換。EXTSEL[2:0]和JEXTSEL2:0]控制位允許應(yīng)用程序選擇8個(gè)可能的事件中的某一個(gè),可以觸發(fā)規(guī)則和注入組的采樣。
注意:當(dāng)外部觸發(fā)信號被選為ADC規(guī)則或注入轉(zhuǎn)換時(shí),只有它的上升沿可以啟動轉(zhuǎn)換
數(shù)據(jù)對齊
ADC_CR2寄存器中的ALIGN位選擇轉(zhuǎn)換后數(shù)據(jù)儲存的對齊方式。數(shù)據(jù)可以左對齊或右對齊,如圖29和圖30所示。注入組通道轉(zhuǎn)換的數(shù)據(jù)值已經(jīng)減去了在ADC_JOFRx寄存器中定義的偏移量,因此結(jié)果可以是一個(gè)負(fù)值。SEXT位是擴(kuò)展的符號值。對于規(guī)則組通道,不需減去偏移值,因此只有12個(gè)位有效。
通道選擇
有16個(gè)多路通道??梢园艳D(zhuǎn)換組織成兩組: 規(guī)則組和注入組 。在任意多個(gè)通道上以任意順序進(jìn)行的一系列轉(zhuǎn)換構(gòu)成成組轉(zhuǎn)換。例如,可以如下順序完成轉(zhuǎn)換:通道3、通道8、通道2、通道 2、通道0、通道2、通道2、通道15。
● 規(guī)則組由多達(dá)16個(gè)轉(zhuǎn)換組成。規(guī)則通道和它們的轉(zhuǎn)換順序在ADC_SQRx寄存器中選擇。規(guī)則組中轉(zhuǎn)換的總數(shù)應(yīng)寫入ADC_SQR1寄存器的L[3:0]位中。
● 注入組由多達(dá)4個(gè)轉(zhuǎn)換組成。注入通道和它們的轉(zhuǎn)換順序在ADC_JSQR寄存器中選擇。注入組里的轉(zhuǎn)換總數(shù)目應(yīng)寫入ADC_JSQR寄存器的L[1:0]位中。
如果ADC_SQRx或ADC_JSQR寄存器在轉(zhuǎn)換期間被更改,當(dāng)前的轉(zhuǎn)換被清除,一個(gè)新的啟動脈沖將發(fā)送到ADC以轉(zhuǎn)換新選擇的組。
ADC時(shí)鐘
ADC預(yù)分頻器的ADCCLK是ADC模塊的時(shí)鐘來源。通常,由時(shí)鐘控制器提供的ADCCLK時(shí)鐘和PCLK2(APB2時(shí)鐘)同步。RCC控制器為ADC時(shí)鐘提供一個(gè)專用的可編程預(yù)分頻器。
一般情況下:不要讓ADC時(shí)鐘超過14MHz,否則可能不準(zhǔn)。
也就是說,如果按照默認(rèn)設(shè)置PCLK2為72MHz,此時(shí)應(yīng)為6分頻或者8分頻。
可編程的通道采樣時(shí)間
ADC使用若干個(gè)ADC_CLK周期對輸入電壓采樣,采樣周期數(shù)目可以通過ADC_SMPR1和ADC_SMPR2寄存器中的SMP[2:0]位更改。每個(gè)通道可以分別用不同的時(shí)間采樣。
總轉(zhuǎn)換時(shí)間如下計(jì)算:
TCONV = 采樣時(shí)間+ 12.5個(gè)周期
例如:當(dāng)ADCCLK=14MHz,采樣時(shí)間為1.5周期,TCONV = 1.5 + 12.5 = 14周期 = 1μs
校準(zhǔn)
ADC有一個(gè)內(nèi)置自校準(zhǔn)模式。校準(zhǔn)可大幅減小因內(nèi)部電容器組的變化而造成的準(zhǔn)精度誤差。在校準(zhǔn)期間,在每個(gè)電容器上都會計(jì)算出一個(gè)誤差修正碼(數(shù)字值),這個(gè)碼用于消除在隨后的轉(zhuǎn)換中每個(gè)電容器上產(chǎn)生的誤差。
通過設(shè)置 ADC_CR2 寄存器的CAL位啟動校準(zhǔn)。一旦校準(zhǔn)結(jié)束, CAL位被硬件復(fù)位,可以開始正常轉(zhuǎn)換。建議在上電時(shí)執(zhí)行一次ADC校準(zhǔn)。校準(zhǔn)階段結(jié)束后,校準(zhǔn)碼儲存在ADC_DR中。
注意:1 建議在每次上電后執(zhí)行一次校準(zhǔn)。2 啟動校準(zhǔn)前, ADC必須處于關(guān)電狀態(tài)(ADON=’0’)超過至少兩個(gè)ADC時(shí)鐘周期
ADC中斷
規(guī)則和注入組轉(zhuǎn)換結(jié)束時(shí)能產(chǎn)生中斷,當(dāng)模擬看門狗狀態(tài)位被設(shè)置時(shí)也能產(chǎn)生中斷。它們都有獨(dú)立的中斷使能位。
注:ADC1和ADC2的中斷映射在同一個(gè)中斷向量上,而ADC3的中斷有自己的中斷向量。
ADC_SR寄存器中有2個(gè)其他標(biāo)志,但是它們沒有相關(guān)聯(lián)的中斷:
● JSTRT(注入組通道轉(zhuǎn)換的啟動)
● STRT(規(guī)則組通道轉(zhuǎn)換的啟動)
ADC寄存器
ADC狀態(tài)寄存器(ADC_SR)
ADC控制寄存器(ADC_CR1)
ADC控制寄存器(ADC_CR2)
ADC采樣時(shí)間寄存器(ADC_SMPRx)
ADC規(guī)則序列寄存器(ADC_SQRx)
ADC規(guī)則數(shù)據(jù)寄存器(ADC_DR)
ADC庫函數(shù)配置
volatileuint32_t ADC_ConvertedValue[5] = {0};
void ADC_Config(void)
{
DMA_InitTypeDef DMA_InitStructure;
ADC_InitTypeDef ADC_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);//開DMA時(shí)鐘
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_ADC2, ENABLE);//開ADC1,ADC2時(shí)鐘
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOB, ENABLE);
//GPIO口配置-----------------------------------------------------------------------------
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 ;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOC, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOB, &GPIO_InitStructure);
//DMA1配置-----------------------------------------------------------------------------
DMA_DeInit(DMA1_Channel1);//復(fù)位DMA控制器
DMA_InitStructure.DMA_PeripheralBaseAddr = ( uint32_t ) ( & ( ADC1- >DR ) );//外設(shè)基址為:ADC數(shù)據(jù)寄存器地址
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)ADC_ConvertedValue;//存儲器地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;//數(shù)據(jù)源來自外設(shè)
DMA_InitStructure.DMA_BufferSize = 5;//緩沖區(qū)大小,應(yīng)該等于數(shù)據(jù)目的地的大小
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外設(shè)寄存器只有一個(gè),地址不用遞增
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //存儲器地址遞增
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;//全字(32位)
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;//全字(32位)
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //循環(huán)傳輸模式
DMA_InitStructure.DMA_Priority = DMA_Priority_High;//DMA 傳輸通道優(yōu)先級為高
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;//禁止存儲器到存儲器模式,因?yàn)槭菑耐庠O(shè)到存儲器
DMA_Init(DMA1_Channel1, &DMA_InitStructure);//初始化DMA
//ADC1配置-----------------------------------------------------------------------------
ADC_InitStructure.ADC_Mode = ADC_Mode_RegSimult;//同步規(guī)則
ADC_InitStructure.ADC_ScanConvMode = DISABLE ; //關(guān)閉掃描模式
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;//連續(xù)轉(zhuǎn)換模式
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//不用外部觸發(fā)轉(zhuǎn)換,軟件開啟即可
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//右對齊
ADC_InitStructure.ADC_NbrOfChannel = 1; //轉(zhuǎn)換通道數(shù)
ADC_Init(ADC1, &ADC_InitStructure);//初始化ADC
RCC_ADCCLKConfig(RCC_PCLK2_Div8); //配置ADC時(shí)鐘,CLK2的8分頻,即9MHz
ADC_RegularChannelConfig(ADC1, ADC_Channel_14, 1, ADC_SampleTime_239Cycles5);//配置ADC通道的轉(zhuǎn)換順序和采樣時(shí)間
ADC_DMACmd(ADC1, ENABLE); //使能DMA請求
//ADC2配置-----------------------------------------------------------------------------
ADC_InitStructure.ADC_Mode = ADC_Mode_RegSimult;//同步規(guī)則
ADC_InitStructure.ADC_ScanConvMode = DISABLE; //關(guān)閉掃描模式
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;//連續(xù)轉(zhuǎn)換模式
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//不用外部觸發(fā)轉(zhuǎn)換,軟件開啟即可
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//右對齊
ADC_InitStructure.ADC_NbrOfChannel = 1; //轉(zhuǎn)換通道數(shù)
ADC_Init(ADC2, &ADC_InitStructure);//初始化ADC
RCC_ADCCLKConfig(RCC_PCLK2_Div8); //配置ADC時(shí)鐘,CLK2的8分頻,即9MHz
ADC_RegularChannelConfig(ADC2, ADC_Channel_8, 1, ADC_SampleTime_239Cycles5);//配置ADC通道的轉(zhuǎn)換順序和采樣時(shí)間
//ADC1校準(zhǔn)-----------------------------------------------------------------------------
ADC_Cmd(ADC1, ENABLE);//使能ADC1
ADC_ResetCalibration(ADC1);//使能復(fù)位校準(zhǔn)
while(ADC_GetResetCalibrationStatus(ADC1));//等待復(fù)位校準(zhǔn)結(jié)束
ADC_StartCalibration(ADC1);//開啟AD校準(zhǔn)
while(ADC_GetCalibrationStatus(ADC1));//等待校準(zhǔn)結(jié)束
//ADC2校準(zhǔn)-----------------------------------------------------------------------------
ADC_Cmd(ADC2, ENABLE);//使能ADC2
ADC_ResetCalibration(ADC2);//使能復(fù)位校準(zhǔn)
while(ADC_GetResetCalibrationStatus(ADC2));//等待復(fù)位校準(zhǔn)結(jié)束
ADC_StartCalibration(ADC2); //開啟AD校準(zhǔn)
while(ADC_GetCalibrationStatus(ADC2));//等待校準(zhǔn)結(jié)束
DMA_Cmd(DMA1_Channel1 , ENABLE);//使能DMA1通道
ADC_ExternalTrigConvCmd(ADC2, ENABLE);//使能ADC2的外部觸發(fā)轉(zhuǎn)換
ADC_SoftwareStartConvCmd(ADC1, ENABLE);//使能軟件觸發(fā)轉(zhuǎn)換
}
//DMA1中斷服務(wù)函數(shù)
__IO uint16_t ADC_ConvertedValueLocal_R = 0;
__IO uint16_t ADC_ConvertedValueLocal_L = 0;
uint16_t ADC_ConvertedValue_R[5] = {0};
uint16_t ADC_ConvertedValue_L[5] = {0};
void DMA1_Channel1_IRQHandler(void)//電流值讀取
{
if(DMA_GetITStatus(DMA1_IT_TC1) != RESET)
{
int i = 0, j = 0, k = 0;
DMA_ClearITPendingBit(DMA1_IT_TC1);
DMA_Cmd