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