GD32E230 ADC:可調電阻 、 joystick


GD32E230 系列只有 1 路 ADC,有如下特征:,

  • 高達 2 MSPS

  • 一共 12 路模擬通道

    • 10 路外部通道 (AIN0 - AIN9)
    • 1 路內部溫度傳感器(AIN16)
    • 1 路內部參考電壓(AIN17)
  • 可選分辨率:12-bit、10-bit、8-bit or 6-bit

  • 支持模擬看門狗

轉換通道選擇 ,有 2 種方式:

  • 規則組

    規則組支持最多 16 通道,對於所使用的通道可以指定轉換順序,觸發方式可以是硬件、軟件,因為規則組數據寄存器只有一個,如果使用大於一個通道的話,就必須得配合 DMA,不然從數據寄存器讀出來得始終是最后采樣的那個通道得數據,

  • 注入組

    最多支持 4 通道,同樣支持對所使用得通道指定轉換順序,支持硬件、軟件觸發,不過注入組有 4 個數據寄存器,注入組里面得每個通道都有對應得數據寄存器,


間斷模式

規則組 跟 注入組都支持間斷模式(Discontinuous mode),如果使用了間斷模式,一次轉換不是轉換所使用的所有通道,詳情如下:

  • 規則

每次轉換所使用得通道(ADC_RSQ0~ADC_RSQ2 中配置得通道)中的 n (n<=8 )個通道,n 由DISNUM[2:0] bits 設定,然后繼續轉換規則組中的接下來的 n 個通道,直到規則組中的所有通道都轉換完,如:

ADC_RSQ0 的 RL 為 8 ,意思是規則組中有 8 個通道,DISNUM 為 3,表示每次轉換 3 個通道,沒觸發一次轉換 3 通道,直到 8 個通道都完成轉換

  • 注入

每次轉換注入組中的一個通道直到所有轉換完成


模擬看門狗

GD32E230的 ADC 還有模擬看門狗的功能,該 ADC 有低閾值和高閾值寄存器,當 ADC 采樣的值低於低閾值數值或者高於高閾值數值時,如果相應中斷使能的話,會產生中斷,


內部參考電壓

該 ADC 有個內部參考電壓,第 17 通道接到了這個內部參考電壓,這電壓是多少呢?一開始我以為是 VDD,可是我看 ADC 采集到參考電壓對於 channal 的值不對,於是我查手冊,可是,我找遍了 GD32E230 的 datasheet 跟 參考手冊 ADC 部分,都沒提到這個值是多少,直到我在 datasheet 中搜關鍵詞 reference ,發現了個線索:

然后我在 GD32E230 的參考手冊 CMP 部分找到了:

翻遍手冊,就發現這里有標明,我不知道這個參數重不重要,反正我找了下,找的好辛苦


讀取可調電阻

這個模塊有 3 個接口,一個接電源正極、一個接負極、還有一個輸出,旋轉旋鈕時,輸出口的電壓會從 電壓正極 到 電源負極改變,

如果要知道旋轉的時候,輸出時多少該怎么做呢?

這里只需要一路 ADC 通道,可是使用 ADC 的連續采集功能,也可以使用一個定時器定時觸發 ADC 采集,

具體要怎么實現呢?我看了下 SDK 給出的例程,里面有個 Timer_trigger_injected_channel 例子,從這里例子名字來看應該時滿足這個需求:

看了下源碼,里面實現了定時器 2 定時觸發一個有 4 通道的注入組,我只要把這個歷程改為 1 通道就可以滿足我的需求了

把可變電阻器兩端分別接到 電源 跟 地,輸出接到 GD32E230 的 PA0,對應 ADC 的 IN0。

為了方便查看運行結果,把這個例程往之前實現了串口輸出的工程移植,

例子中,定時器部分先不變,修改 GPIO 跟 ADC 初始化部分,改為 1 通道:

/*!
    \brief      configure the GPIO peripheral
    \param[in]  none
    \param[out] none
    \retval     none
*/
void gpio_config(void)
{
    /* config the GPIO as analog mode */
    gpio_mode_set(GPIOA, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, GPIO_PIN_0);
}
/*!
    \brief      configure the ADC peripheral
    \param[in]  none
    \param[out] none
    \retval     none
*/
void adc_config(void)
{
    /* ADC continous function enable */
    adc_special_function_config(ADC_SCAN_MODE, ENABLE);
    /* ADC trigger config */
    adc_external_trigger_source_config(ADC_INSERTED_CHANNEL, ADC_EXTTRIG_INSERTED_T2_CH3); 
    /* ADC data alignment config */
    adc_data_alignment_config(ADC_DATAALIGN_RIGHT);
    /* ADC channel length config */
    adc_channel_length_config(ADC_INSERTED_CHANNEL, 1U);
 
    /* ADC inserted channel config */
    adc_inserted_channel_config(0U, ADC_CHANNEL_0, ADC_SAMPLETIME_55POINT5);

    /* ADC external trigger enable */
    adc_external_trigger_config(ADC_INSERTED_CHANNEL, ENABLE);

    /* enable ADC interface */
    adc_enable();
    delay_1ms(1U);
    /* ADC calibration and reset calibration */
    adc_calibration_enable();
}

然后是讀取轉換結果,看了下例程,居然沒有這部分內容,

自己把 ADC 中斷部分加上,然后在中斷讀取轉成的數值,把如下添加到 ADC 初始話函數:

		adc_interrupt_enable(ADC_INT_EOC);
		nvic_irq_enable(ADC_CMP_IRQn, 0U);

然后再 中斷處理函數中讀取數值,這里只是通過串口輸出 ADC 采集到的數值:

////////////////////////////////////////////////
/*   ADC   */
/*!
    \brief      this function handles ADC exception 
    \param[in]  none
    \param[out] none
    \retval     none
*/
void ADC_CMP_IRQHandler(void)
{
    /* clear the ADC interrupt or status flag */
    adc_interrupt_flag_clear(ADC_INT_EOC);
	printf("%d\n",adc_inserted_data_read(ADC_INSERTED_CHANNEL_0));
}

運行結果為:

從結果來看好像不是太直觀。

突然想起之前使用過一個串口調試工具-- SerialPlot,支持把接收到的數據用折線圖(曲線圖)顯示出來,試了下,效果不錯:

這部分完整代碼在:main_adjust_res.c


游戲搖桿模塊 Joystick

還有這么一個東西,跟可調電阻一樣,不過這個有 2 個可調電阻組成,有 2 個模擬兩輸出,可以根據 2 個可調電阻輸出的值反映搖桿的位置。我手上的長這樣子:

這里嘗試下規則組,根據 SDK 給出的 Regular_channel_with_DMA來修改。

分別把這 2 個模擬輸出接到 PA0、PA1,對應 ADC 的 IN0、IN1,還是在之前實現了串口輸出的工程里面該,修改下 ADC 初始化函數,實現 2 通道規則組:

/*!
    \brief      configure the GPIO peripheral
    \param[in]  none
    \param[out] none
    \retval     none
*/
void gpio_config(void)
{
    /* config the GPIO as analog mode */
	gpio_mode_set(GPIOA, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, GPIO_PIN_0 | GPIO_PIN_1 );
}

/*!
    \brief      configure the ADC peripheral
    \param[in]  none
    \param[out] none
    \retval     none
*/
void adc_config(void)
{
    /* ADC contineous function enable */
    adc_special_function_config(ADC_SCAN_MODE, ENABLE);
	adc_special_function_config(ADC_CONTINUOUS_MODE, ENABLE);
    /* ADC trigger config */
    adc_external_trigger_source_config(ADC_REGULAR_CHANNEL, ADC_EXTTRIG_REGULAR_NONE); 
    /* ADC data alignment config */
    adc_data_alignment_config(ADC_DATAALIGN_RIGHT);
    /* ADC channel length config */
    adc_channel_length_config(ADC_REGULAR_CHANNEL, 2U);
 
    /* ADC regular channel config */
    adc_regular_channel_config(0U, ADC_CHANNEL_0, ADC_SAMPLETIME_55POINT5);
	adc_regular_channel_config(1U, ADC_CHANNEL_1, ADC_SAMPLETIME_55POINT5);
	
    adc_external_trigger_config(ADC_REGULAR_CHANNEL, ENABLE);

    /* enable ADC interface */
    adc_enable();
    delay_1ms(1U);
    /* ADC calibration and reset calibration */
    adc_calibration_enable();

    /* ADC DMA function enable */
    adc_dma_mode_enable();
    /* ADC software trigger enable */
    
    adc_interrupt_enable(ADC_INT_EOC);
    nvic_irq_enable(ADC_CMP_IRQn, 0U);    
    adc_software_trigger_enable(ADC_REGULAR_CHANNEL);
}

然后修改下 DMA 初始化部分:

uint16_t adc_value[2];
/*!
    \brief      configure the DMA peripheral
    \param[in]  none
    \param[out] none
    \retval     none
*/
void dma_config(void)
{
    /* ADC_DMA_channel configuration */
    dma_parameter_struct dma_data_parameter;
    
    /* ADC DMA_channel configuration */
    dma_deinit(DMA_CH0);
    
    /* initialize DMA single data mode */
    dma_data_parameter.periph_addr  = (uint32_t)(&ADC_RDATA);
    dma_data_parameter.periph_inc   = DMA_PERIPH_INCREASE_DISABLE;
    dma_data_parameter.memory_addr  = (uint32_t)(&adc_value);
    dma_data_parameter.memory_inc   = DMA_MEMORY_INCREASE_ENABLE;
    dma_data_parameter.periph_width = DMA_PERIPHERAL_WIDTH_16BIT;
    dma_data_parameter.memory_width = DMA_MEMORY_WIDTH_16BIT;  
    dma_data_parameter.direction    = DMA_PERIPHERAL_TO_MEMORY;
    dma_data_parameter.number       = 2U;
    dma_data_parameter.priority     = DMA_PRIORITY_HIGH;
    dma_init(DMA_CH0, &dma_data_parameter);

    dma_circulation_enable(DMA_CH0);
  
    /* enable DMA channel */
    dma_channel_enable(DMA_CH0);
}

定義一個 2 個 uint16_t的數組用來存儲轉換出來的數據,

例程中使用了一個通道,這里使用 2 個通道,需要對例程中的 DMA 配置進行修改,改動部分如下:

   dma_data_parameter.memory_inc   = DMA_MEMORY_INCREASE_ENABLE;
   dma_data_parameter.number       = 2U;

轉換結果還是通過串口輸出:

void ADC_CMP_IRQHandler(void)
{
    /* clear the ADC interrupt or status flag */
    adc_interrupt_flag_clear(ADC_INT_EOC);
	printf("%d,%d\n",adc_value[0],adc_value[1]);
}

轉換出來的數據在 SerialPlot 中用折線圖顯示出來為:

上圖雖然可以直觀的顯示數據的變化,可是並不能反映搖桿的變化,有沒有什么現成工具可以滿足這個需求呢?

我沒找到,不過可以自己做個,用 processing 很容易實現,這里我用 processing 實現了一個 圓形 隨着采集到 搖桿數據改變而改變位置的小程序,代碼如下,:

import processing.serial.*;

int bgcolor;           // Background color
int fgcolor;           // Fill color
Serial myPort;                       // The serial port
int[] serialInArray = new int[3];    // Where we'll put what we receive
int serialCount = 0;                 // A count of how many bytes we receive
int xpos, ypos;                 // Starting position of the ball
boolean firstContact = false;        // Whether we've heard from the microcontroller

int lf = 10;      // ASCII linefeed
String inString;  // Input string from serial port

void setup() {
  size(512, 512);  // Stage size
  noStroke();      // No border on the next thing drawn

  // Set the starting position of the ball (middle of the stage)
  xpos = width/2;
  ypos = height/2;

  // Print a list of the serial ports, for debugging purposes:
  printArray(Serial.list());

  String portName = Serial.list()[0];
  myPort = new Serial(this, portName, 115200);
  myPort.bufferUntil(lf);

}

void draw() {
  background(255,255,255);
  fill(255,0,0);
  ellipse(xpos, ypos, 20, 20);
}

void serialEvent(Serial p) {
  inString = p.readString();
  //print(inString);
  String[] list = split(inString, ',');

  if(list.length >= 3)
  {
    for(int i=0;i<list.length;i++)
  //  print("["+ i + ":" + list[i] + "],");
    xpos = int(list[0]) / 8;
    ypos = int(list[1]) / 8;
    println(xpos + "," + ypos);
    
  // Draw the shape
  
  }
}

運行結果為:

是不是很直觀啊,使用不到 100 行的代碼就可以實現,實在是方便,

不過這遙感有個缺點,用網友的話來說:

然而這種搖桿模塊的模擬輸出並不隨搖桿角度線性變化。從中點到端點是躍變的,很是令人抓狂。

摘自:https://www.arduino.cn/thread-98802-1-1.html

並不能很精確的反應搖桿的位置,

這部分完整代碼在這:Joystick.c


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM