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

posted @ 2021-09-06 09:38  哈拎  阅读(1756)  评论(0编辑  收藏  举报