前言
7801資料讀起來不是很好理解,大概率是之前MTK的大佬寫的。在此以簡單的方式進行描述。我們做一個簡單的規則組軟件觸發Demo。因為規則組通道只有一個數據寄存器,因此還需要用上DMA方式搬運數據到內存。
AC7801的ADC簡介
7801的ADC是一種 12 位 逐次逼近型 模擬數字轉換器,擁有 12 路外部通道和 2 路內部通道,支持單次、連續、掃描或間斷轉換多種模式。模擬監控器特性允許應用程序監測輸入電壓是否超出設定的電壓范圍。
特性
? 12 位分辨率
? 通道輸入電壓范圍: AVSS < Vin < AVDD
? 最大轉換速率: 1Msps
? 14 路通道: 12 路外部通道, 1 路內部溫度傳感器( T-Sensor ), 1 路內部帶隙基準(Bandgap ),每路通道可單獨配置采樣時間
? 轉換序列分為 規則組( regular group )和注入組( injection group )
????????? 規則組:最多可配置 12 個通道
????????? 注入組:最多可配置 4 個通道
? 8 種操作模式 ( 方便起見,稱為 mode x , x=1~8)
????????? 規則組單通道單次轉換 (mode1)
????????? 規則組單通道連續轉換 (mode2)
????????? 規則組掃描 + 注入組掃描模式多通道單次轉換 (mode3 注入組掃描模式 )
????????? 規則組掃描 + 注入組間隔模式多通道單次轉換 (mode3 注入組間隔模式 )
????????? 規則組掃描 + 自動觸發注入組掃描模式多通道單次轉換 (mode4)
????????? 規則組掃描 + 注入組掃描模式多通道連續轉換 (mode5 注入組掃描模式 )
????????? 規則組掃描+注入組間隔模式多通道連續轉換(mode5 注入組間隔模式 )
????????? 規則組掃描 + 自動觸發注入組掃描模式多通道連續轉換 (mode6)
????????? 規則組子組掃描模式轉換 (mode7)
????????? 注入組子組掃描模式轉換 (mode8)
? 通過內部軟件觸發或外部硬件觸發啟動 ADC
? 模擬監控器功能:
????????? 配置為單個或所有通道電壓檢查
????????? 監控通道電壓是否低于低閾值或高于高閾值
? 中斷:
????????? 規則或注入組轉換結束 (EOC , End Of Conversion)
????????? 注入組轉換結束 (IEOC)
????????? 模擬監控器事件 (AMO)
? DMA 訪問,僅用于規則組通道
典型操作流程
ADC 首先上電,然后可以通過內部 SWSTART 或外部觸發源觸發 ADC ,該觸發來源于其它模塊。觸發后ADC 轉換器單元開始工作,并將選擇信號發送至輸入通道選擇器,根據規則或注入組通道序列逐個選擇所需的通道。在一個通道完成轉換后,轉換結果將根據當前轉換通道所屬的組存儲到 RDR 或 IDRx 中,并且產生相應的 EOC 或 IEOC 標志置位。模擬監控器工作時,如果發生相應的事件則會出現相關的狀態標志。
使用DMA
由于規則組通道只有一個數據寄存器,因此建議使用 DMA 功能 ,以避免在有多個規則組通道進行轉換時,丟失轉換結果。DMA 功能專用于規則組通道。只有規則組通道轉換結束標志才會產生 DMA 請求。只有產生了 DMA 請求, DMA 才會將轉換數據從ADC_RDR 搬運到用戶指定的目標位置。
ADC流程
經典的初始化,DMA初始化。
ADC的初始化,185/186兩行注釋看的一臉懵逼
ADC的回調
DMA的回調
業務代碼
旋轉電位器查看打印值
由單個ADC改成多個ADC
AC7801的ADC寫的注釋相對比較完整,但是沒說明軟件觸發從1個怎么改到多個。
使用時候需要注意下圖中紅框部分有些DISABLE和ABLE的參數,錯了大概率就不可能正常采樣。黃框部分就是從1個ADC改成3個需要修改的地方。
讀取時候,只需要觸發一次ADC0即可
具體代碼如下:
#include "adc_sample.h"#define Delay5us (APB_BUS_FREQ/200000-1)
#define Delay5ms (APB_BUS_FREQ/200-1)
#define Delay1s (APB_BUS_FREQ-1)uint8_t g_dmaFinish = 0; // DMA傳輸完成
uint8_t g_halfDmaFinish = 0; // DMA傳輸半完成
uint8_t g_dmaTransError = 0; // DMA傳輸錯誤
uint32_t g_ADCValueBuffer[DMA_TRANSFER_NUM + 1] = {0};
uint32_t g_timerCnt = 0;
uint16_t g_regularAverageSampleValue = 0; // 規則組采樣平均值
uint16_t g_injectAverageSampleValue = 0; // 注入組采樣平均值
uint16_t g_adcInjectValue[4];
uint8_t g_AMOFlag = 0; // 模擬看門狗事件標志
/*
注意:EOC標志寫0或讀取ADC_RDR都會清除該標志位。
在進行debug時,如果有打開memory窗口或打開ADC寄存器。
該標志會被debug清除。
*/
uint8_t g_EOCFlag = 0; // 規則組轉換結束標志。
uint8_t g_IEOCFlag = 0; // 注入組轉換結束標志。void ADC_Callback(void *device, uint32_t wpara, uint32_t lpara)
{if (wpara & ADC_STR_EOC_Msk) // 規則組中斷標志{g_EOCFlag = 1;}if (wpara & ADC_STR_AMO_Msk) // 模擬監控中斷標志{g_AMOFlag = 1;}
}void ADC_DMACallback(void *device, uint32_t wpara, uint32_t lpara)
{/*wparam為DMA通道狀態,狀態含義可參考CHANNELx_STATUS寄存器,CHANNELx_STATUS[2] 傳輸錯誤CHANNELx_STATUS[1] 半傳輸完成(相對設置的transferNum,如果半傳輸中斷有使能,transferNum設為6,則DATA_TRANS_NUM為3時產生中斷,進入回調)CHANNELx_STATUS[0] 傳輸完成*/if ((wpara & 0x01) == 0x1){g_dmaFinish = 1;}if ((wpara & 0x02) == 0x2){g_halfDmaFinish = 1;}if ((wpara & 0x04) == 0x4){g_dmaTransError = 1;}
}void ADC_DMAInit(void)
{uint32_t tmpMemStartAddr = (uint32_t)&g_ADCValueBuffer[0];uint32_t tmpMemEndAddr = (uint32_t)&g_ADCValueBuffer[DMA_TRANSFER_NUM + 1]; ///< Setting memory DMA addressDMA_ConfigType tmpDMAConfig;memset(&tmpDMAConfig, 0x00, sizeof(DMA_ConfigType));tmpDMAConfig.memStartAddr = tmpMemStartAddr; // 設置DMA開始地址tmpDMAConfig.memEndAddr = tmpMemEndAddr; // 設置DMA結束地址tmpDMAConfig.periphStartAddr = (uint32_t)(&(ADC0->RDR)); ///< Move ADC DR to memorytmpDMAConfig.channelEn = ENABLE; ///< 使能DMAx通道tmpDMAConfig.finishInterruptEn = ENABLE; ///< 使能DMA傳輸完成中斷tmpDMAConfig.halfFinishInterruptEn = DISABLE; ///< 去能DMA半傳輸完成中斷tmpDMAConfig.errorInterruptEn = ENABLE; ///< 使能DMA傳輸錯誤中斷tmpDMAConfig.channelPriority = DMA_PRIORITY_VERY_HIGH; ///< 設置DMA通道優先級,0~3 :優先級由低到高tmpDMAConfig.circular = ENABLE; ///< 使能循環模式,如果只想工作一次,設為0即可。tmpDMAConfig.direction = DMA_READ_FROM_PERIPH; ///< 0: 從外設讀取,1:從存儲器讀取tmpDMAConfig.MEM2MEM = DISABLE; ///< 0:在非存儲器與存儲器之間傳輸,1:在存儲器與存儲器之間傳輸tmpDMAConfig.memByteMode = DMA_MEM_BYTE_MODE_1TIME; ///< MEM字分割傳輸數,0:32-bit,1:16-bit[15:0]; 2:16-bit[23:16][7:0];3:8-bit。詳情可參考AC781X芯片手冊 表20-2 可編程數據寬度&數據對齊tmpDMAConfig.memIncrement = ENABLE; ///< 1:MEM地址增加tmpDMAConfig.periphIncrement = DISABLE; ///< 0:外設地址固定tmpDMAConfig.memSize = DMA_MEM_SIZE_32BIT; ///< 0:8-bit,1:16-bit,2:32-bittmpDMAConfig.periphSize = DMA_PERIPH_SIZE_16BIT; ///< 0:8-bit,1:16-bit,2:32-bittmpDMAConfig.transferNum = DMA_TRANSFER_NUM; ///< DMA通道傳輸長度tmpDMAConfig.periphSelect = DMA_PEPIRH_ADC0; // 外設選擇tmpDMAConfig.callBack = ADC_DMACallback; ///< 設置DMA中斷回調DMA_Init(DMA0_CHANNEL0, &tmpDMAConfig); ///< ADC 使用DMA1通道,每個模塊對應的DMA通道,可參考 AC781X芯片手冊 表20-1 DMA請求列表NVIC_EnableIRQ(DMA0_CHANNEL0_IRQn); ///< 使能DMA1中斷請求
}void ADC_init()
{ADC_ConfigType tempAdcConfig;ADC_ConfigType *adcConfig;adcConfig = &tempAdcConfig;// 配置PINMUXGPIO_SetFunc(GPIOA, GPIO_PIN4, GPIO_FUN2); ///< ADC_IN6 Analog function enableGPIO_SetFunc(GPIOA, GPIO_PIN3, GPIO_FUN2); ///< ADC_IN7 Analog function enableGPIO_SetFunc(GPIOA, GPIO_PIN2, GPIO_FUN2); ///< ADC_IN8 Analog function enableadcConfig->clkPsc = ADC_CLK_PRESCALER_1; ///< Set ADC Clk = 24M/2/(0+1)adcConfig->scanModeEn = ENABLE; // 掃描模式adcConfig->continousModeEn = DISABLE; // 連續模式adcConfig->regularDiscontinousModeEn = DISABLE; // 1:打開規則組間斷轉換模式adcConfig->injectDiscontinousModeEn = DISABLE; // 1:打開注入組間斷轉換模式adcConfig->injectAutoModeEn = DISABLE; // 1:自動注入模式adcConfig->intervalModeEn = DISABLE; // 1:注入組為間隔轉換模式adcConfig->regularDiscontinousNum = 0; //adcConfig->EOCInterruptEn = ENABLE; // EOC中斷使能adcConfig->IEOCInterruptEn = ENABLE; // IEOC中斷使能adcConfig->interruptEn = ENABLE; // 中斷使能adcConfig->regularDMAEn = ENABLE; // 使能ADC DMAadcConfig->regularTriggerMode = ADC_TRIGGER_INTERNAL; // ADC觸發源,內部觸發adcConfig->regularSequenceLength = 3; // 規則組長度設為3adcConfig->dataAlign = ADC_DATA_ALIGN_RIGHT; // 右對齊adcConfig->callBack = ADC_Callback; // 回調adcConfig->powerMode = ADC_POWER_ON; // 上電ADC_Init(ADC0, adcConfig); ///< ADC works Mode Config// ADC轉換率計算公式: 轉換時間= 采樣時間+轉換時間+同步時間 轉換時間= (SPT+12)/ADC模塊時鐘頻率+5/APB時鐘頻率// 備注:1.同步時間為5個APB CLK。2.ADC時鐘頻率 = APB時鐘頻率 /(分頻系數+1)// 規則組通道設置ADC_SetRegularGroupChannel(ADC0, ADC_CH_7, ADC_SPT_CLK_7, 0); // 采樣&轉換時間= (7+12)/24000000 + 5/24000000 = 1usADC_SetRegularGroupChannel(ADC0, ADC_CH_8, ADC_SPT_CLK_7, 1); // 采樣&轉換時間= (7+12)/24000000 + 5/24000000 = 1usADC_SetRegularGroupChannel(ADC0, ADC_CH_6, ADC_SPT_CLK_7, 2); // 采樣&轉換時間= (7+12)/24000000 + 5/24000000 = 1us
}void ADC_SampleSoftwareTrigerADC(void)
{ADC_init();ADC_DMAInit(); // ADC DMA初始化while (1){// 每次轉換數據清零memset(g_ADCValueBuffer, 0x00, sizeof(g_ADCValueBuffer));ADC_SoftwareStartRegularConvert(ADC0); /// 軟件觸發規則組采樣udelay(8); // 需要采樣8個通道,延時8us以保證數據采樣完成printf("%d %d %d\r\n", g_ADCValueBuffer[0],g_ADCValueBuffer[1],g_ADCValueBuffer[2]);mdelay(100);}
}