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