1.引言
ADC是模拟信号往数字信号转换的器件。因此,ADC的用处非常广法,各类家用电器中基本都含有ADC.
根据中学或大学物理知识,我知道模拟信号是随时间变化的连续信号。数字信号是离散的,随时间不连续的。模拟信号比较能过反应现实,如温度、湿度、压力、长度、电流、电压都是模拟信号。有了模拟信号为什么又要往数字信号转换呢?这是因为数字处理芯片的发展。当我们使用了数字芯片,又使用了温度、湿度、压力等传感器。我们通过传感器采集的温度、湿度等信号就要通过ADC模块转化为数字信号,这样数字芯片才能处理。
而数字往模拟转换的原理无非就是“采样-量化-编码”或者是“采样-保持-量化-编码”。
2.ADC的功能框图
下图分为了7个部分,我们按照序号顺序,从左往右把这7个部分看一下。
首先,ADC工作要有电源和时钟。1就是电源部分,5就是时钟部分。
其次,信号是如何输入到ADC的。一般是传感器搭配ADC来使用,而ADC的输入一般是电压变化值。而这些随时间连续的电压变化是如何输入到ADC的呢,那就通过IO口和通道。
以STM32的ADC为例,拥有19个通道,其中包括16个外部通道,三个内部通道(只有ADC1,ADC2和ADC3才有)。顾名思义,外部通道是外部输入到ADC的通道,一般是复用的GPIO端口,通过GPIO端口对应到 相应的通道上。
我们可以看一下ADC的通道和IO口的对应关系,以STM32F407为例。
外部通道有16个,说明最多可以连接16个外部输入。
2.1 规则通道和注入通道
这16个通道都可以采集信号,但采集的信号在放入数据寄存器之前,必须经过转化转换。在转换时,ADC的这16个通道又分为两种,一个是规则通道,一个是注入通道。对一个ADC来说,规则通道最多16个,注入通道最多4个。也就是说这16个外部通道,哪一个是规则通道,哪一个是注入通道,是可以配置的。
规则通道是我们常规使用的通道,安分守己。注入通道是一个插入通道,当规则通道转换过程中,来了一个注入通道,则先转换注入通道,注入通道转换完成后,再转换规则通道。有点像主程序和中断的关系,下图形象地表明了规则通道和注入通道在转换过程中的关系。
这16个外部通道是有转换顺序的。是可以配置的。
规则通道:
由3个规则序列寄存器SQR3、SQR2、SQR1来决定先转换哪个通道的。这三个寄存器都是32位寄存器。如下图所示。当把第一转换的通道配置为通道1时,应该把SQR3的位4:0,即寄存器SQ1[4:0]配置为1(按二进制,5位就是0
0001)。 注:SQR位23:20,即SQL[3:0]的值决定了一个ADC的16个外部通道中有多少通道用于规则通道。
注入序列:
注入序列寄存器只有一个,叫JSQR。配置如下图所示:
2.2 触发
转换的顺序设置好了,就要开始转换了。但什么时候转换,这需要触发。
触发方式有:软件写寄存器出发和外部事件触发。
2.3 数据的存放:数据寄存器
ADC规则组数据寄存器ADC_DR只有一个,是一个32位的寄存器。
ADC注入组数据寄存器有4个, 而注入通道最多有4个通道。因此,这四个寄存器正好分别对应这四个注入通道。
2.3 准换时间
读者可以研究下:为什么是12个ADC周期?
提示:逐次逼近型ADC的工作原理,逐位比较
3.代码实现
代码实现,基本逻辑就是上述ADC原理所讲述的。
一般是先配置时钟
之后复位ADC(使得在一个确定状态)
配置ADC的转换模拟(单词,连续,还是其它)
配置规则通道和注入通道,配置数据对其方式(左对齐
or 右对齐)
配置触发方式
获取转换接收标志、读取ADC数据。
代码实现部分,配置设置 ADC0的通道 10来进行 AD转换。使用ADC0的通道10来采集外部电位器的模拟电压值,并将其
原始值和转化成的数字量发送到串口助手,调节电位器,数字量会发生变化。
1.先配置IO口时钟,并把ADC0的通道10 复用的IO口设置为模拟量输入模式。
rcu_periph_clock_enable(RCU_GPIOC); // GPIOC时钟使能
gpio_mode_set(GPIOC, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, GPIO_PIN_0); // GPIO 模式设置
查询GD32F407xx_Datasheet,ADC0的通道 10复用的IO为PC0,因此配置GPIOC
注:ADC012_IN10 指的是ADC0,ADC1和ADC2的通道10.
- ADCO是在APB2总线上。需要先使能ADC0的时钟,并且将时钟配置为APB2时钟的八分频。
rcu_periph_clock_enable(RCU_ADC0); // 使能ADC时钟
adc_clock_config(ADC_ADCCK_PCLK2_DIV8); //将时钟配置为APB2时钟的八分频
3.接下来进行 ADC相关的采样配置,封装成函数ADC_Init
首先,复位ADC(复位的目的是使之进入一个确定的状态,从这个确定的状态开始进行配置) 其次,使能ADC连续采样模式
再次,配置数据右对齐(因为存储ADC结果的数据寄存器是16位,而ADC的分辨率可配置12位、10位、8位、或者6位分辨率,常用12位分辨率)
再再次,配置规则组通道,配置ADC0的通道10,配置采样频率为56个时钟周期 再再再次,配置ADC的触发源为timer0 capture
compare 3 之后,使能ADC接口,这里使能通道0 最后,是校准ADC和复位ADC校准
void ADC_Init(void)
{
adc_deinit(); // 复位ADC
adc_special_function_config(ADC0, ADC_CONTINUOUS_MODE, ENABLE); // 使能连续转换模式
adc_data_alignment_config(ADC0, ADC_DATAALIGN_RIGHT); // 数据右对齐
adc_channel_length_config(ADC0, ADC_REGULAR_CHANNEL, 1); // 通道配置,规则组
adc_routine_channel_config(ADC0, 0, ADC_CHANNEL_10, ADC_SAMPLETIME_56); // 对规则组进行配置
adc_external_trigger_source_config(ADC0, ADC_REGULAR_CHANNEL, ADC_EXTTRIG_INSERTED_T0_CH3); // ADC 触发配置
adc_external_trigger_config(ADC0, ADC_REGULAR_CHANNEL, ENABLE);
adc_enable(ADC0); // 使能ADC接口
delay_1ms(1); // 等待1ms
adc_calibration_enable(ADC0); // ADC校准和复位ADC校准
}
4.设置触发模式为 软件触发
adc_software_trigger_enable(ADC0, ADC_REGULAR_CHANNEL); // 规则采样软件触发
5.在主程序中进行循环进行循环采样,每次采样前,首先判断上一次是否采样转换结束,每次读取模拟量转换成数字
量的原始值,并且按照12位ADC对应的分辨率,转换成对应的电压值,最后将数据发送到串口助手。
while(1) {
adc_flag_clear(ADC0,ADC_FLAG_EOC); // 清除结束标志
while(SET != adc_flag_get(ADC0,ADC_FLAG_EOC)){ // 获取转换结束标志
}
adc_value = ADC_RDATA(ADC0); // 读取ADC数据
Vol_Value = adc_value*3.3/4095; // 转换成电压值
printf("adc_value=%d\n",adc_value); // 结果输出原始值
printf("Vol_Value=%d\n",Vol_Value); // 结果输出电压值
delay_1ms(10); // 等待10ms
}
3.1 文件
main.c:
nt main(void)
{
systick_config();
// GPIO_OutputConfig(GPIOA, GPIO_PIN_2, 0);
//
// FlagStatus value = GPIO_InputConfig(GPIOA, GPIO_PIN_3);
//
// /* 通用TIMER初始化 */
// Timer_Config();
//
// /* 基础TIMER初始化 */
// TIMER_Basic_Config();
//
//
//
// /* 初始化232 */
// RS232_Config();
//
// /* 初始化485 */
// RS485_Config();
//
//
// /* 初始化调试串口 */
USART_Config();
/* ADC配置 */
rcu_periph_clock_enable(RCU_GPIOC); // GPIOC时钟使能
gpio_mode_set(GPIOC, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, GPIO_PIN_0); // GPIO 模式设置
rcu_periph_clock_enable(RCU_ADC0); // 使能ADC时钟
adc_clock_config(ADC_ADCCK_PCLK2_DIV8); // 配置时钟
ADC_Init(); // ADC配置
adc_software_trigger_enable(ADC0, ADC_REGULAR_CHANNEL); // 规则采样软件触发
delay_1ms(100);
/*
while(1)
{
//if (redata_flag)
{
redata_flag = 0;
adc_value++;
//RS232_SendStr_length(tx_buffer,6); // 发送数据
printf("adc_value=%d\n",adc_value);
//RS485_SendStr_length(tx_buffer,6);
//delay_1ms(10); // 等待10ms
}
}
*/
while(1) {
adc_flag_clear(ADC0,ADC_FLAG_EOC); // 清除结束标志
while(SET != adc_flag_get(ADC0,ADC_FLAG_EOC)){ // 获取转换结束标志
}
adc_value = ADC_RDATA(ADC0); // 读取ADC数据
Vol_Value = adc_value*3.3/4095; // 转换成电压值
printf("adc_value=%d\n",adc_value); // 结果输出原始值
printf("Vol_Value=%d\n",Vol_Value); // 结果输出电压值
delay_1ms(10); // 等待10ms
if(Vol_Value > 2) // 当电压值超过2V
{
//LED1_ON(); // 开灯
}
else{
//LED1_OFF(); // 关灯
}
}
}
ADC.c:
void ADC_Init(void)
{
adc_deinit(); // 复位ADC
adc_special_function_config(ADC0, ADC_CONTINUOUS_MODE, ENABLE); // 使能连续转换模式
adc_data_alignment_config(ADC0, ADC_DATAALIGN_RIGHT); // 数据右对齐
adc_channel_length_config(ADC0, ADC_REGULAR_CHANNEL, 1); // 通道配置,规则组
adc_routine_channel_config(ADC0, 0, ADC_CHANNEL_10, ADC_SAMPLETIME_56); // 对规则组进行配置
adc_external_trigger_source_config(ADC0, ADC_REGULAR_CHANNEL, ADC_EXTTRIG_INSERTED_T0_CH3); // ADC 触发配置
adc_external_trigger_config(ADC0, ADC_REGULAR_CHANNEL, ENABLE);
adc_enable(ADC0); // 使能ADC接口
delay_1ms(1); // 等待1ms
adc_calibration_enable(ADC0); // ADC校准和复位ADC校准
}
附录
ADC通道所复用IO口对照表
文字版:
ADC0,ADC1,ADC2的第0个通道 复用的IO口是PA0
ADC0,ADC1,ADC2的第1个通道 复用的IO口是PA1
ADC0,ADC1,ADC2的第2个通道 复用的IO口是PA2
ADC0,ADC1,ADC2的第3个通道 复用的IO口是PA2
ADC0,ADC1的第4个通道 复用的IO口是PA4
ADC0,ADC1的第5个通道 复用的IO口是PA5
ADC0,ADC1的第6个通道 复用的IO口是PA6
ADC0,ADC1的第7个通道 复用的IO口是PA7
ADC0,ADC1的第8个通道 复用的IO口是PB0
ADC0,ADC1的第9个通道 复用的IO口是PB1
ADC0,ADC1,ADC2的第10个通道 复用的IO口是PC0
ADC0,ADC1,ADC2的第11个通道 复用的IO口是PC1
ADC0,ADC1,ADC2的第12个通道 复用的IO口是PC2
ADC0,ADC1,ADC2的第13个通道 复用的IO口是PC3
ADC0,ADC1的第14个通道 复用的IO口是PC4
ADC0,ADC1的第15个通道 复用的IO口是PC5
ADC2的第9个通道 复用的IO口是PF3
ADC2的第14个通道 复用的IO口是PF4
ADC2的第15个通道 复用的IO口是PF5
ADC2的第4个通道 复用的IO口是PF6
ADC2的第5个通道 复用的IO口是PF7
ADC2的第6个通道 复用的IO口是PF8
ADC2的第7个通道 复用的IO口是PF9
ADC2的第8个通道 复用的IO口是PF10
写作不易,麻烦大家给个关注吧!
如有疑问,欢迎留言评论!