[MSPM0開發]之九 MSPM0G3507的ADC
- 一、 MSPM0G3507 ADC概述
- 二、 MSPM0G3507 ADC系統框圖
- 2.1 電壓基準
- 2.2 分辨率
- 2.3 硬件均值計算
- 2.4 采樣觸發源和采樣模式
- 2.5 轉換模式
- 2.6 轉換結果數據格式
- 2.7 高級特性
- 2.7.1 非FIFO模式下的ADC操作(單次轉換和重復單次轉換)
- 2.7.2 非FIFO模式下的ADC操作(序列轉換和重復序列轉換)
- 2.7.3 FIFO模式下的ADC操作(單次轉換和重復單次轉換)
- 2.7.4 FIFO模式下的ADC操作(序列轉換)
- 三、 基于driverLib的例程
- 四、 牛刀小試1 非FIFO模式單通道連續轉換+轉換完成中斷
- 五、 牛刀小試2 非FIFO模式連續序列轉換模式+轉換完成中斷
一、 MSPM0G3507 ADC概述
MSPM0G3507的ADC 是一款高性能逐次逼近型寄存器 (SAR) 模數轉換器
ADC 支持快速的 12 位、 10 位和 8 位模數轉換, 具有 12 位 SAR 內核、采樣和轉換模式控制功能和多達 12 個獨立的轉換和控制緩沖器。此 ADC 允許在無需 CPU 干預的情況下, 轉換和存儲多達 12 個獨立的模數轉換器(ADC) 樣本。
ADC 模塊特性包括:
? 12 位分辨率下的轉換率為 4Msps
? 集成硬件過采樣, 平均處理多達 128 個樣本
? 滿量程 ADC 工作電壓范圍
? 由軟件或定時器控制的可編程采樣周期的采樣保持
? 兩個采樣觸發源: 軟件觸發和事件觸發
? 可通過軟件選擇 1.4V 或 2.5V 片上基準電壓
? 可配置 ADC 基準源: VDD、內部基準 (VREF) 或外部基準( VREF+ 和 VREF-)
? 多達 16 個可單獨配置的外部輸入通道
? 具有用于溫度檢測、電源監控和模擬信號鏈的內部轉換通道( 請參閱器件特定的數據表, 了解可用性和通道映射)
? 可配置 ADC 時鐘源
? 單通道、重復單通道、序列( 自動掃描) 和重復序列( 重復自動掃描) 轉換模式
? 12 個轉換結果存儲寄存器 (MEMRES0:11)
? 使用窗口比較器對來自轉換結果寄存器的輸入信號進行低功耗監控
? DMA 支持在傳輸完成時生成中斷事件
? 在 RUN、 SLEEP 和 STOP 模式下運行
? 可以在除 SHUTDOWN 之外的任何工作模式下觸發
? 針對低功耗工作模式的自動電源、基準和時鐘控制
? 支持并行的兩個 ADC 同時和同步運行( 采樣和轉換):需要多個ADC外設的芯片才支持
? CDAC 修整值半自動校準
MSPM0G3507支持兩個具有總計多達 17 個外部通道的 12 位 4Msps 同步采樣模數轉換器 (ADC); 硬件均值計算可在 250ksps 下實現 14 位有效分辨率。
二、 MSPM0G3507 ADC系統框圖
采樣時鐘 (SAMPCLK) 以 ULPCLK、 SYSOSC 或 HFCLK 為時鐘源, 轉換時鐘 (CONVCLK) 以 ADC IP內的 80Mhz 振蕩器為時鐘源。 轉換時鐘80MHz
關于ADC轉換結果的訪問:
2.1 電壓基準
2.2 分辨率
ADC 支持在 12 位( 默認) 、 10 位和 8 位分辨率模式下運行。分辨率模式通過 CTL2 寄存器中的 RES 位進行配置。
? 當選擇 12 位模式時, 轉換階段總共需要 14 個轉換時鐘周期
? 當選擇 10 位模式時, 轉換階段總共需要 12 個轉換時鐘周期
? 當選擇 8 位模式時, 轉換階段總共需要 9 個轉換時鐘周期
2.3 硬件均值計算
ADC 在硬件中實現數字樣本均值計算( 硬件均值計算), 無需軟件或 CPU 干預即可有效地提高 ADC 的有效分辨率。硬件均值計算功能通過 CTL1 寄存器中的 AVGN 和 AVGD 位進行配置。
? AVGN 定義為當前 MEMCTLx 累積的轉換次數
? AVGD 定義累加值使用位移所除以的值
啟用硬件均值計算后,所有的通道均有效。
2.4 采樣觸發源和采樣模式
采樣觸發器
可以通過 CTL1 寄存器中的 TRIGSRC 位選擇兩個采樣觸發源: 一個是軟件觸發器, 另一個是事件觸發器。
當選擇軟件觸發器作為觸發源時, 應用軟件可以設置 CTL1 寄存器中的 SC 位來啟動采樣階段。當選擇事件觸發器作為觸發源時, 事件管理器中所選事件的上升沿將啟動采樣階段。事件始終為邊沿觸發。
采樣模式
有兩種可用的采樣模式: AUTO 和 MANUAL, 可以通過 CTL1 寄存器中的 SAMPMODE 位進行選擇。
自動采樣模式
自動采樣模式下,可以使用采樣定時器設置采樣時間“間隔”,采樣計時器為 10 位寬, 并且有兩個采樣時間比較寄存器 (SCOMPx) ,可以使用 MEMCTL 寄存器中的== STIME== 位來選擇這兩個 SCOMP 寄存器中的一個。
手動采樣模式(僅支持單通道單次轉換)
在手動模式下, 當設置 SC 位時將生成采樣信號, 該信號可以與采樣時鐘異步。采樣窗口的持續時間由軟件通過將 SC 位保持在高電平來控制。由于事件始終是邊沿觸發的, 因此, 任何轉換模式都不支持通過事件觸發的手動模式。僅單通道單次轉換模式支持手動采樣模式的軟件觸發, 而其他三種轉換模式均不支持。
從采樣窗口結束到轉換窗口開始, 具有 2-3 個周期的同步延遲。
2.5 轉換模式
ADC 提供四種轉換模式:
1. 單通道單次轉換
? 可使用 MEMCTL 選擇通道
? 所選通道僅采樣和轉換一次
? 啟用硬件均值計算后, 執行多次轉換
2. 單通道重復轉換
? 可使用 MEMCTL 選擇通道
? 所選通道將被重復采樣和轉換, 直到 ENC 被軟件清零
– 如果設置了 TRIG 位, 那么需要一次觸發來進行下一個轉換
? 啟用硬件均值計算后, 執行多次轉換
3. 序列通道單次轉換
? 可使用 STARTADD、 ENDADD 和 MEMCTL 寄存器形成通道組
? 組中的每個通道僅采樣和轉換一次
? 啟用硬件均值計算后, 序列期間會在一個通道上執行多次轉換
? 即使 ENC 在序列中被清零, 也將完成該序列
4. 序列通道重復轉換
? 可使用 STARTADD、 ENDADD 和 MEMCTL 寄存器形成通道組
? 通道組將被重復采樣和轉換, 直到 ENC 被軟件清零
– 如果設置了 TRIG 位, 那么需要一次觸發來進行下一個轉換
? 如果 ENC 被清零, 則在最后一次轉換結束時停止操作
? 啟用均值計算后, 序列期間會在一個通道上執行多次轉換
觸發和轉換模式用法矩陣
2.6 轉換結果數據格式
ADC 支持兩種數據格式 – 無符號二進制和二進制補碼有符號二進制。無符號二進制結果右對齊存儲在 MEMRES寄存器或 FIFO 中。有符號二進制結果左對齊存儲在 MEMRES 寄存器或 FIFO 中。
2.7 高級特性
MSPM0G3507內部具有2個ADC外設,可以實現同步采樣。
同時采樣
電流和電壓檢測等一些應用需要同時在多個模擬信號中進行測量。在這些情況下, 需要在單個 MCU 上使用多個ADC 來執行同步采樣。 MSPM0xx 平臺中具有多個 ADC 外設的任何器件都支持同步采樣。
對于這個用例, 應該使用 ULPCLK 作為 ADCCLK 的源, 因為它也是 PD0 中所有外設的時鐘。這意味著它已經與總線時鐘同步, 所以可以確保采樣開始的確定性時序。除了使用 ULPCLK 作為兩個 ADC 外設的采樣時鐘源外,用戶還應選擇相同的采樣觸發器, 并在兩個 ADC 上使用相同的值對時鐘預分頻器和/或 SCOMP 進行編程。
簡單理解就是使用ULPCLK低速時鐘源,然后其它參數(采樣、觸發)保持一致。
ABOUT DMA
ADC 具有一個用于與 DMA 進行通信的專用接口。該接口可使用 DMA 自動將 ADC 結果存儲到存儲器。
“DMA 觸發計數”信號指示 DMA 在一次觸發請求時可以傳輸的樣本數。
ADC 使用“DONE 狀態”信號生成 DMA DONE 中斷, 該信號指示編程塊大小的 DMA 數據傳輸是否完成。
CTL2 寄存器中的 DMAEN 位用于使 DMA 能夠進行 ADC 數據傳輸。當 DMA 發出“DONE 狀態”信號時, ADC硬件會將 DMAEN 位清零。
ADC 還包含一個可選的先入先出緩沖區FIFO, 提供了一種方法來存儲 ADC 結果供將來使用.
2.7.1 非FIFO模式下的ADC操作(單次轉換和重復單次轉換)
– 配置 STARTADD 位以選擇所需的 MEMCTLx 寄存器
? MEMCTLx 與 MEMRESx 相關
? MEMRESx 與 MEMRESIFGx= 相關
– 配置 MEMCTL CHANSEL 位以選擇所需的 ADC 通道
– MEMRESx 中提供轉換數據
– 可以設置 MEMRESIFGx 以生成 CPU 中斷或 DMA 觸發
– 必須通過軟件將 SAMPCNT 編程為 1 以執行 DMA 操作
– 當 ADC 在 CPU 或 DMA 讀取前一個樣本之前更新 MEMRESx 時, 會設置轉換溢出標志 OVIFG(讀慢了)
– 當 CPU 或 DMA 在下一個轉換結果可用前讀取 MEMRESx 寄存器時, 會設置轉換下溢標志UVIFG==(讀快了)
2.7.2 非FIFO模式下的ADC操作(序列轉換和重復序列轉換)
– 配置 STARTADD 位以選擇序列中的第一個 MEMCTL
– 配置 ENDADD 位以選擇序列中的最后一個 MEMCTL
? MEMCTLx 與 MEMRESx 相關
? MEMRESx 與 MEMRESIFGx相關
– 配置每個 MEMCTLx CHANSEL 位以選擇所需的 ADC 通道
– MEMRESx 中提供轉換數據
– 可以設置 MEMRESIFGx 以生成 CPU 中斷或 DMA 觸發
– 必須根據軟件針對 DMA 操作進行的閾值設置, 通過軟件將 SAMPCNT 編程為一個合適的值
– 當 ADC 在 CPU 或 DMA 讀取前一個樣本之前更新 MEMRESx 時, 會設置轉換溢出標志 OVIFG(讀慢了)
– 當 CPU 或 DMA 在下一個轉換結果可用前讀取 MEMRESx 寄存器時, 會設置轉換下溢標志 UVIFG(讀快了)
2.7.3 FIFO模式下的ADC操作(單次轉換和重復單次轉換)
– 配置 STARTADD 位以選擇所需的 MEMCTLx 寄存器
? MEMCTLx 與 MEMRESx 不相關
? MEMRESx 與 MEMRESIFGx 相關
– 配置 MEMCTL CHANSEL 位以選擇所需的 ADC 通道
– 轉換數據按順序加載到 MEMRES0/1/2/…/N( FIFO 結構)
– CPU 或 DMA 必須從專用的 FIFODAT 寄存器( 而不直接從 MEMRES 寄存器) 讀取 ADC 樣本
? FIFO 中的數據始終壓縮為兩個樣本, 并在 CPU 或 DMA 讀取 FIFODAT 時作為 32 位數據提供
– MEMRESIFGx 可用作閾值條件以生成 CPU 中斷或 DMA 觸發
? 為了充分利用 FIFO,== 可以使用最后一個 MEMRESIFG==
– 必須根據針對 DMA 操作進行的閾值設置, 通過軟件將 SAMPCNT 編程為一個合適的值
– 當 ADC 在 CPU 或 DMA 讀取前一個樣本之前更新 MEMRESx 時, 會設置轉換溢出標志 OVIFG
– 當 CPU 或 DMA 在 MEMRESx 寄存器中的轉換結果可用之前讀取 FIFODAT 寄存器時, 會設置轉換下溢標志 UVIFG。
2.7.4 FIFO模式下的ADC操作(序列轉換)
– 配置 STARTADD 位以選擇序列中的第一個 MEMCTL
– 配置 ENDADD 位以選擇序列中的最后一個 MEMCTL
? MEMCTLx 與 MEMRESx 不相關
? MEMRESx 與 MEMRESIFGx 相關
– 配置每個 MEMCTLx CHANSEL 位以選擇所需的 ADC 通道
– 轉換數據按順序加載到 MEMRES0/1/2/…/N( FIFO 結構)
– CPU 或 DMA 必須從專用的 FIFODAT 寄存器( 而不直接從 MEMRES 寄存器) 讀取 ADC 樣本
? FIFO 中的數據始終壓縮為兩個樣本, 并在 CPU 或 DMA 讀取 FIFODAT 時作為 32 位數據提供
– MEMRESIFGx 可用作閾值條件以生成 CPU 中斷或 DMA 觸發
? 為了充分利用 FIFO, 可以使用最后一個 MEMRESIFG
– 必須根據針對 DMA 操作進行的閾值設置, 通過軟件將 SAMPCNT 編程為一個合適的值
– 當 ADC 在 CPU 或 DMA 讀取前一個樣本之前更新 MEMRESx 時, 會設置轉換溢出標志 OVIFG
– 當 CPU 或 DMA 在 MEMRESx 寄存器中的轉換結果可用之前讀取 FIFODAT 寄存器時, 會設置轉換下溢標志 UVIFG
-------------- 其它注意事項 ---------------
? 對于基于 CPU 或 DMA 的操作, 不建議采用啟用了 FIFO 的單次轉換模式。這將導致下溢情況, 并且必須在軟件中丟棄不需要的 16 位數據。
? 重復序列轉換模式不支持基于 DMA 的數據傳輸, 因為 DMA 不支持循環尋址模式。
? CPU 或 DMA 讀取后, 不會自動清除 FIFODAT 寄存器中的數據。新的轉換數據會覆蓋 FIFODAT 寄
存器中的先前數據。
? 為確保同步讀取存儲 16 位樣本的 32 位 FIFO 中的字節, 可以使用特定的 DMA 觸發器。尤其是,
選擇 MEMRES1、 MEMRES3、 MEMRES5、 MEMRES7、 MEMRES9 和 MEMRES11 將使 FIFO
中的字節讀取與相應的 MEMRESx 字節同步。
? 如果 ADC 在重復序列模式或正常重復模式期間被禁用, 則值得注意的是在 ADC 完全停止之前可能
會發生額外的轉換。
三、 基于driverLib的例程
四、 牛刀小試1 非FIFO模式單通道連續轉換+轉換完成中斷
時鐘選擇內部高速時鐘,32MHz
配置uart用于發送結果
ADC配置
選擇的轉換寄存器編號和通道編號可以不一致。 比如這里選擇MEMCTL2 & MEMRES2 & MEMRESIFG2,實際轉換通道為ch3(PA18)。
MEMCTLx 與 MEMRESx 相關
? MEMRESx 與 ==MEMRESIFGx= 相關
ADC初始化代碼:
/* ADC12_0 Initialization */
static const DL_ADC12_ClockConfig gADC12_0ClockConfig = {.clockSel = DL_ADC12_CLOCK_SYSOSC,.divideRatio = DL_ADC12_CLOCK_DIVIDE_1,.freqRange = DL_ADC12_CLOCK_FREQ_RANGE_24_TO_32,
};
SYSCONFIG_WEAK void SYSCFG_DL_ADC12_0_init(void)
{DL_ADC12_setClockConfig(ADC12_0_INST, (DL_ADC12_ClockConfig *) &gADC12_0ClockConfig);DL_ADC12_initSingleSample(ADC12_0_INST,DL_ADC12_REPEAT_MODE_ENABLED, DL_ADC12_SAMPLING_SOURCE_AUTO, DL_ADC12_TRIG_SRC_SOFTWARE,DL_ADC12_SAMP_CONV_RES_12_BIT, DL_ADC12_SAMP_CONV_DATA_FORMAT_UNSIGNED);DL_ADC12_setStartAddress(ADC12_0_INST, DL_ADC12_SEQ_START_ADDR_02);DL_ADC12_configConversionMem(ADC12_0_INST, ADC12_0_ADCMEM_2,DL_ADC12_INPUT_CHAN_3, DL_ADC12_REFERENCE_VOLTAGE_VDDA, DL_ADC12_SAMPLE_TIMER_SOURCE_SCOMP0, DL_ADC12_AVERAGING_MODE_DISABLED,DL_ADC12_BURN_OUT_SOURCE_DISABLED, DL_ADC12_TRIGGER_MODE_AUTO_NEXT, DL_ADC12_WINDOWS_COMP_MODE_DISABLED);DL_ADC12_setSampleTime0(ADC12_0_INST,32000);/* Enable ADC12 interrupt */DL_ADC12_clearInterruptStatus(ADC12_0_INST,(DL_ADC12_INTERRUPT_MEM2_RESULT_LOADED));DL_ADC12_enableInterrupt(ADC12_0_INST,(DL_ADC12_INTERRUPT_MEM2_RESULT_LOADED));DL_ADC12_enableConversions(ADC12_0_INST);
}
main.c部分代碼:
int main(void) {SYSCFG_DL_init();NVIC_EnableIRQ(ADC12_0_INST_INT_IRQN);gCheckADC = false;UART_printf("helloworlf\r\n");DL_ADC12_startConversion(ADC12_0_INST);while (1) {while (false == gCheckADC) {}gAdcResult = DL_ADC12_getMemResult(ADC12_0_INST, DL_ADC12_MEM_IDX_2);if (gAdcResult > 0x7ff) {DL_GPIO_clearPins(GPIO_LEDS_PORT, GPIO_LEDS_USER_LED_1_PIN);} else {DL_GPIO_setPins(GPIO_LEDS_PORT, GPIO_LEDS_USER_LED_1_PIN);}UART_printf("res = %d\r\n", gAdcResult);gCheckADC = false;}
}
void ADC12_0_INST_IRQHandler(void) {switch (DL_ADC12_getPendingInterrupt(ADC12_0_INST)) {case DL_ADC12_IIDX_MEM2_RESULT_LOADED: // 這里選擇配置的對應MEM編號gCheckADC = true;break;default:break;}
}
運行結果:
五、 牛刀小試2 非FIFO模式連續序列轉換模式+轉換完成中斷
時鐘選擇內部高速時鐘,32MHz
ADC初始化代碼:
/* ADC12_0 Initialization */
static const DL_ADC12_ClockConfig gADC12_0ClockConfig = {.clockSel = DL_ADC12_CLOCK_SYSOSC,.divideRatio = DL_ADC12_CLOCK_DIVIDE_1,.freqRange = DL_ADC12_CLOCK_FREQ_RANGE_24_TO_32,
};
SYSCONFIG_WEAK void SYSCFG_DL_ADC12_0_init(void)
{DL_ADC12_setClockConfig(ADC12_0_INST, (DL_ADC12_ClockConfig *) &gADC12_0ClockConfig);DL_ADC12_initSeqSample(ADC12_0_INST,DL_ADC12_REPEAT_MODE_ENABLED, DL_ADC12_SAMPLING_SOURCE_AUTO, DL_ADC12_TRIG_SRC_SOFTWARE,DL_ADC12_SEQ_START_ADDR_00, DL_ADC12_SEQ_END_ADDR_01, DL_ADC12_SAMP_CONV_RES_12_BIT,DL_ADC12_SAMP_CONV_DATA_FORMAT_UNSIGNED);DL_ADC12_configConversionMem(ADC12_0_INST, ADC12_0_ADCMEM_0,DL_ADC12_INPUT_CHAN_2, DL_ADC12_REFERENCE_VOLTAGE_VDDA, DL_ADC12_SAMPLE_TIMER_SOURCE_SCOMP0, DL_ADC12_AVERAGING_MODE_DISABLED,DL_ADC12_BURN_OUT_SOURCE_DISABLED, DL_ADC12_TRIGGER_MODE_AUTO_NEXT, DL_ADC12_WINDOWS_COMP_MODE_DISABLED);DL_ADC12_configConversionMem(ADC12_0_INST, ADC12_0_ADCMEM_1,DL_ADC12_INPUT_CHAN_3, DL_ADC12_REFERENCE_VOLTAGE_VDDA, DL_ADC12_SAMPLE_TIMER_SOURCE_SCOMP0, DL_ADC12_AVERAGING_MODE_DISABLED,DL_ADC12_BURN_OUT_SOURCE_DISABLED, DL_ADC12_TRIGGER_MODE_AUTO_NEXT, DL_ADC12_WINDOWS_COMP_MODE_DISABLED);DL_ADC12_setSampleTime0(ADC12_0_INST,32000);/* Enable ADC12 interrupt */DL_ADC12_clearInterruptStatus(ADC12_0_INST,(DL_ADC12_INTERRUPT_MEM1_RESULT_LOADED));DL_ADC12_enableInterrupt(ADC12_0_INST,(DL_ADC12_INTERRUPT_MEM1_RESULT_LOADED));DL_ADC12_enableConversions(ADC12_0_INST);
}
main.c主要代碼
int main(void) {/* Initialize peripherals and enable interrupts */SYSCFG_DL_init();NVIC_EnableIRQ(ADC12_0_INST_INT_IRQN);UART_printf("helloworld\r\n");gCheckADC = false;DL_ADC12_startConversion(ADC12_0_INST);while (1) {/* Wait until all data channels have been loaded. */while (gCheckADC == false) {}/* Store ADC Results into their respective buffer */gAdcResult0 = DL_ADC12_getMemResult(ADC12_0_INST, DL_ADC12_MEM_IDX_0);gAdcResult1 = DL_ADC12_getMemResult(ADC12_0_INST, DL_ADC12_MEM_IDX_1);UART_printf("res1=%4d,res2=%4d\r\n", gAdcResult0, gAdcResult1);gCheckADC = false;}
}/* Check for the last result to be loaded then change boolean */
void ADC12_0_INST_IRQHandler(void) {switch (DL_ADC12_getPendingInterrupt(ADC12_0_INST)) {case DL_ADC12_IIDX_MEM1_RESULT_LOADED:gCheckADC = true;break;default:break;}
}