ADC
在电子和通信技术中,ADC(模拟数字转换器)是一种将模拟信号转换为数字信号的电子设备。这种转换是电子系统中非常关键的一个环节,因为数字信号更易于处理、存储和传输。ADC的工作原理通常包括采样、保持、量化和编码等步骤。采样是从模拟信号中提取离散的值,保持是确保在采样期间信号值不变,量化是将采样值转换为最接近的数字值,而编码则是将这些数字值表示为二进制代码。ADC的性能指标包括分辨率、转换速率和精度等。
ADC在多个领域都有广泛的应用。在通信领域,ADC用于将模拟信号(如语音信号)转换为数字信号,以便在无线通信、有线通信和光纤通信系统中进行传输。在医疗领域,ADC用于将生物信号(如心电图和脑电图)转换为数字信号,以便于进行医学分析和诊断。此外,在汽车领域,ADC用于检测传感器信号,如发动机转速、车速和油位等,以实现对汽车系统的精确控制。
以上介绍来自文心一言。
GD32E230C8T6中的ADC
GD32E230C8T6只有一个ADC,但是可以用的外部通道有10个,还是够用的。
那么我们需要看看ADC的不同通道对应着哪些GPIO。
可用的GPIO是GPIOA的0号引脚到7号引脚,以及GPIOB的0和1号引脚。
接下来我们来看看怎么使用ADC。
固件库函数
在介绍ADC相关固件库之前我们需要先打开外设时钟。
rcu_periph_clock_enable(RCU_ADC);
还需要指定一下ADC的时钟。
rcu_adc_clock_config
可以选择的选项在上面表格中。关于如何选择,我们还需要看看GD32E230的时钟树。
由于ADC最大只能28MHz,因此配置的时钟频率不能大于28MHz。
ADC相关的固件库函数很多,我这边就挑着讲。
adc_enable
无需参数,调用即可使能ADC外设。
当然了,这个函数我们需要等ADC配置完再调用,可以参考STM32的ADC。这边先讲是因为我按照固件库使用指南的顺序说的,函数具体调用的时机可以参考我最后的示例代码。
adc_disable
有使能就有失能。调用之后就相当于关闭了ADC外设。
adc_calibration_enable
开启ADC校准复位。也就是让ADC开始校准,在STM32中也有类似的做法,不过在GD32的固件库中我们只需要调用这个函数即可。
adc_channel_length_config
配置通道组的长度,一般来说都是用的规则组(ADC_REGULAR_CHANNEL),我们一共用到几个ADC通道,我们第二个参数就填几。
adc_regular_channel_config
设置规则组,设置rank也就是通道序列,还有ADC的通道,一般来说通道几我们的rank也就是几。还有就是采样时间,可供选择的参数可以参考上表,采样周期越长,采样结果越精确,按照实际需求配置就行。
adc_external_trigger_config
配置ADC外部触发。我们选择规则组和使能。
这个函数描述会有点歧义,我们ADC一般是由软件触发的,实际上软件触发也属于外部触发的一种,这边外部触发的外部和外部中断的外部不太一样。
adc_external_trigger_source_config
选择外部触发源,我们选择软件触发。
adc_software_trigger_enable
开启软件转换。
adc_regular_data_read
读取规则组的ADC的转换值,但是我们需要在ADC转换之后再读取。
adc_flag_get
获取ADC的标志位,我们在读取数据之前需要等待ADC转换结束,用的就是这个函数。
adc_resolution_config
配置ADC分辨率,可以选6,8,10,12位,一般来说我们就多多益善,选择12位的分辨率。
adc_special_function_config
开启ADC特殊功能,我们这边打开扫描模式。如果有用到DMA的话可以选择连续模式。
adc_data_alignment_config
选择数据对齐方式,因为存放ADC转换结果的寄存器是16位,而我们的分辨率是小于16位的,因此我们需要选择把这12位的数据靠左放还是靠右放,一般就是右对齐。
示例代码
我们再梳理一下,首先先调用配置函数,配置函数的顺序无所谓,都可以。
然后是使能ADC,接着是校准。
最后是读取数据,开启软件触发之后我们就等待转换完毕,转换结束之后调用函数读取即可。
#include "gd32e23x.h"
#include "systick.h"
#include <stdio.h>
#include "OLED.h"
uint16_t getValue(uint8_t ADC_CHANNEL_x){
adc_regular_channel_config(0, ADC_CHANNEL_x, ADC_SAMPLETIME_239POINT5); //设置采集通道
adc_software_trigger_enable(ADC_REGULAR_CHANNEL); //开始软件转换
while ( adc_flag_get(ADC_FLAG_EOC) == RESET ) ; //等待 ADC 采样完成
uint16_t res = adc_regular_data_read(); //读取采样值
return res;
}
int main(void){
systick_config(); //延时函数初始化
OLED_Init(); //OLED初始化
rcu_periph_clock_enable(RCU_GPIOA); //开启GPIOA的外设时钟
rcu_periph_clock_enable(RCU_ADC); //开启ADC的外设时钟
rcu_adc_clock_config(RCU_ADCCK_APB2_DIV4); //指定ADC的时钟为5分频的APB2(72MHz/4)
gpio_mode_set(GPIOA, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, GPIO_PIN_0); //将ADC的对应通道的对应引脚设为模拟输入
adc_special_function_config(ADC_SCAN_MODE, ENABLE); //开启扫描模式
adc_external_trigger_config(ADC_REGULAR_CHANNEL,ENABLE); //开启规则组外部触发
adc_external_trigger_source_config(ADC_REGULAR_CHANNEL, ADC_EXTTRIG_REGULAR_NONE); //配置规则组为软件触发
adc_regular_channel_config(0, ADC_CHANNEL_0, ADC_SAMPLETIME_239POINT5); //设置规则组通道
adc_data_alignment_config(ADC_DATAALIGN_RIGHT); //数据右对齐
adc_resolution_config(ADC_RESOLUTION_12B); //配置分辨率为12位
adc_channel_length_config(ADC_REGULAR_CHANNEL, 1); //配置规则组长度为1
adc_enable(); //使能ADC
adc_calibration_enable(); //开启校准
uint16_t val;
OLED_ShowString(1,1,"Hello World");
while(1){
val=getValue(ADC_CHANNEL_0);
OLED_ShowNum(2,1,val,6);
delay_ms(1000);
}
}