jiongsheng

jiongsheng

MSPM0學習,ADC定時器觸發,DMA搬運

前言#

如果您未看过笔者之前写的 "第一步配置" 和 "MSPM0 教程之调用 SYSCONFIG 以及配置 GPIO 输出和输入",强烈建议您将这两篇先看了,以免出现配置错误,如果您已经完成了 SYSCONFIG 的配置,请继续往下看

创易栈 MSPM0 开发板参考电压选择#

image-20230606194556451

根據創易栈的用戶手冊,板載 VREF2.5V,我們把板子上的撥碼開關撥到 ON 即可

image-20230606195417792

SYSCONFIG 配置#

在 keil 內打開 empty.syscfg, 然後調用 SYSCONFIG 工具

image-20230606184519254

進入 SYSCONFIG,我們再添加 ADC,先看看他的介紹

image-20230607184911524

在基本配置中,用戶可以:

  • 配置采樣時鐘和采樣模式:這些參數影響了 ADC 的采樣率,即每秒鐘對輸入信號進行采樣的次數。
  • 配置 ADC 轉換內存:存儲從模擬信號轉換得到的數字數據。
  • 啟用 / 禁用以下功能:
    • 硬件平均模式:通過對多次采樣結果進行平均,減少隨機噪聲的影響。
    • 使用燒耗電流源:可能用於在 ADC 轉換過程中提供穩定的參考電壓。
    • 窗口比較器模式:只有當輸入信號在一個指定的範圍內時,才進行 ADC 轉換。
    • 觸發模式:可以設置 ADC 轉換只在接收到一個特定的觸發信號時開始。

在高級配置中,用戶可以:

  • 配置轉換頻率 / 分辨率:影響了 ADC 轉換的精度和速度。
  • 啟用 FIFO 模式:在這種模式下,ADC 可以連續進行多次轉換,並將結果存入一個先進先出(FIFO)的隊列中,方便後續處理。
  • 配置電源下降模式:在不需要進行 ADC 轉換時,可以降低 ADC 模塊的電源,以節省能源。
  • 指定采樣時間:對於快速變化的模擬信號,可能需要更短的采樣時間。

ADC 參數#

image-20230607185752720

如參考手冊所示,該 ADC 的在不同分辨率下的轉換周期

image-20230607190449924

如果我們使用 ULPCLK,可以繞過這個采樣延遲

勾選重複采樣模式

image-20230608203635086

在 SYSCONFIG 中配置觸發源,我設置為事件觸發,這個事件由定時器發布

image-20230607191152249

Conversion Data Format 這個選項中選擇無符號右對齊

在高級設置這裡,我們可以通過設置采集的時間來確定采樣率 (12bit 上限為 1.45M),采樣率等於 32 (時鐘頻率)/(1 / 采樣時間 + 轉換時間 (12bit 分辨率 為 14 個時鐘周期)),通過計算可以得到,將采樣時間設置為 250ns(即八個 (32M) 時鐘周期), 此時采樣率就為 32/(8+14)=1.45M

image-20230610201941717

然後我們先去配置定時器,定時器配置如圖,確保設置好對應的觸發事件

image-20230610195248506

接著再回到 ADC 配置頁,設置好訂閱事件

image-20230610195517210

然後打開 DMA 傳輸,設置好 DMA 的參數,包括觸發源,數據長度和傳輸地址模式設置以及傳輸模式

image-20230610195613677

傳輸模式定義了數據如何從源地址傳輸到目標地址,有四種模式:

  1. Single Transfer:這種模式下,每次傳輸都需要一個觸發信號。當按照 "Transfer Size" 設定的次數完成傳輸後,DMA 將被禁用。

  2. Block Transfer:這種模式下,一個觸發信號就能傳輸完整的數據塊,數據塊的大小由 "Transfer Size" 決定。每次傳輸結束後,DMA 將被禁用。

  3. Repeated Single Transfer:這種模式下,每次傳輸都需要一個觸發信號,但在所有傳輸完成後,DMA 仍然保持啟用狀態。

  4. Repeated Block Transfer:這種模式下,一個觸發信號就能傳輸完整的數據塊,數據塊的大小由 "Transfer Size" 決定。和 "Repeated Single Transfer" 一樣,所有傳輸完成後,DMA 仍然保持啟用狀態。

選擇哪種傳輸模式取決於你的具體需求。例如,如果你需要連續傳輸大量數據,可以選擇 "Block Transfer" 或 "Repeated Block Transfer" 模式;如果你需要精細地控制每次的數據傳輸,可以選擇 "Single Transfer" 或 "Repeated Single Transfer" 模式。

image-20230608204512192

點擊 FILE-save, 然後到 keil 中編譯,導入配置

main 相關代碼#

#define ADCSize 128

typedef enum ADCFlag

{

  ADCFlagDmaStart = 0,

  ADCFlagDmaDone,

  ADCFlagConversionDone,

  ADCFlagConversionStart

}ADCFlag;

unsigned short ADCResult[ADCSize];//定義ADC接收緩衝區
ADCFlag adcflag;//定義枚舉變量adcflag

void UART1SendString(char *str)
{
    while(*str != '\0')
    {
        DL_UART_transmitDataBlocking(UART1_INST, *str);
        str++;
    }
}
void UART1SendChar(char c)
{
    DL_UART_transmitDataBlocking(UART1_INST, c);
}

void strreverse(char* begin, char* end) {
   char aux;
   while(end>begin)
       aux=*end, *end--=*begin, *begin++=aux;
}
/**

 * @brief  將整數值轉換為指定基數的空終止字符串,並將結果存儲在由str參數給出的數組中。
   *

 * @param  value: 要轉換為字符串的值。

 * @param  str: 用於存儲結果的空終止字符串的數組。該數組應足夠長,以容納結果字符串(包括空字符)。

 * @param  base: 要轉換的數字的基數。它必須在[2, 36]範圍內。對於大於10的基數,此函數使用小寫字母。
   *

 * @note   如果基數為10且value為負,則結果字符串前會帶有負號(-)。對於任何其他基數,value始終被視為無符號。
   *

 * @note   如果基數不在[2, 36]範圍內,函數將不執行任何操作,str將是一個空字符串。
   *

 * @note   該函數不處理"unsigned long"數字 - 它將把它們視為"long"。因此,對於大於2147483647的值,該函數不適用於大於10的基數。
   *

 * @return 無。
   */
   void itoa(int value, char* str, int base) {
   static char num[] = "0123456789abcdefghijklmnopqrstuvwxyz";
   char* wstr = str;
   int sign;

   // Validate base
   if (base < 2 || base > 35) { *wstr = '\0'; return; }

   // Take care of sign
   if ((sign=value) < 0) value = -value;

   // Conversion. Number is reversed.
   do *wstr++ = num[value%base]; while(value/=base);

   if(sign<0) *wstr++='-';
   *wstr='\0';

   // Reverse string
   strreverse(str,wstr-1);
   }


/**

 * @brief  處理ADC結果的函數
   *
 * @param  adcflag: 指向ADC標誌的指針,當ADC完成數據轉換後,該標誌會被設置為ADCFlagDmaDone
 * @param  ADCResult: 指向存儲ADC結果的數組的指針
 * @param  resultSize: ADC結果數組的大小
   *
 * @note   當adcflag指向的值為ADCFlagDmaDone時,函數會停止定時器和ADC的數據轉換,然後遍歷ADCResult數組,
 * 並使用itoa函數將每個結果轉換為字符串,然後通過UART發送出去。在發送完所有的結果後,函數會將adcflag指向的值設置為ADCFlagConversionStart,
 * 表示ADC可以開始新一輪的數據轉換。注意,在發送字符串時,每個結果後面都會發送一個換行符,以便接收端能夠正確地識別每個結果的邊界。
    *
 * @return 無。
   */
   void processADCResults(ADCFlag* adcflag, unsigned short* ADCResult, int resultSize) 
   {
   if (*adcflag == ADCFlagDmaDone) 
   {
       DL_Timer_stopCounter(TIMER_0_INST);
       DL_ADC12_stopConversion(ADC12_0_INST);
       volatile int i;
       for ( i = 0; i < resultSize; i++) 
       {
          char buffer[12]; // Buffer for integer to string conversion, max size for int32 is 11 characters + null terminator
            itoa(ADCResult[i], buffer, 10);
            UART1SendString(buffer);
            UART1SendString("\r\n");
       }
       *adcflag = ADCFlagConversionStart;
       i = 0;
   }
   }
   void setupDMA(DMA_Regs *dma, uint8_t channelNum, unsigned int srcAddr, unsigned int destAddr, unsigned int transferSize)
   {
    DL_DMA_setSrcAddr(dma, channelNum, (unsigned int) srcAddr);
    DL_DMA_setDestAddr(dma, channelNum, (unsigned int) destAddr);
    DL_DMA_setTransferSize(dma, channelNum, transferSize);
    DL_DMA_enableChannel(dma, channelNum);
   }

int main(void)
{
    SYSCFG_DL_init();
    NVIC_EnableIRQ(UART1_INT_IRQn);
    NVIC_EnableIRQ(ADC0_INT_IRQn);
    adcflag = ADCFlagConversionStart;
    setupDMA(DMA,DMA_CH0_CHAN_ID,(unsigned int) & (ADC0->ULLMEM.MEMRES[0]),(unsigned int) &ADCResult,ADCSize);

    if(adcflag == ADCFlagConversionStart)
   {
       DL_Timer_startCounter(TIMER_0_INST);
       DL_ADC12_startConversion(ADC12_0_INST);
   }
   while (1) 
    {


   processADCResults(&adcflag, ADCResult, ADCSize);
    }
}


實際演示#

因為我每次只采 128 個點,並且不是連續采樣,所以每次重新 RST,采樣的信號肯定不和上次連續,就會出現下面這種尖點,從一次連續信號的角度來看,效果還不錯

image-20230610192450348

转载请標明出处

by <a href=https://www.cnblogs.com/jiongsheng > QDU_jiongsheng

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。