ADC,模数转换器,会将模拟信号(连续变化的电压值)转换为数字值,以便在处理和控制系统中使用。特定的外围元器件,可以将温度、湿度、光线亮暗、气压大小等等转化成一个会随之变化的电压信号。再通过它,我们的处理器可以得到一个会随之变化的数值。利用其特性,我们可以间接的得到各种信息并加以处理。ADC的位数是其精度的描述,又或者说是最小分辨率,即数字值变化1对应的模拟信号变化是多大。如一个ADC的位数是12位,参考电压为3.3V,那么其数字值变化1对应的模拟信号电压变化为3.3V/(2^12)≈0.8mV。想要获得更高的精度,可以选择位数跟高的ADC或者降低参考电压。ADC又有许多种类,这里不多做介绍。
————————————————
原文链接:https://blog.csdn.net/qq_37554315/article/details/119698889
ad的数据采集的方式有两种的——中断与查询
本文使用查询方式(目的是调试ADC资源),主要由编写的程序进行查询状态来获取数据(因为写在主函数里,实际项目应该写在中断里),但是要注意查询占用CPU资源,而中断速度快
查看GD32E103的用户手册,第12章写有,MCU片上集成了12位逐次逼近式模数转换器模块(ADC),可以采样来自于16个外部通道和2个内部通道上的模拟信号。这18个ADC采样通道都支持多种运行模式,采样转换后,转换结果可以按照最低有效位对齐或最高有效位对齐的方式保存在相应的数据寄存器中。片上的硬件过采样机制可以通过减少来自MCU的相关计算负担来提高性能。
所以我们知道GD32E103的ADC具有12位分辨率。它一共有18个通道,16个外部通道,2个内部通道。各种通道的AD转换可以配置成单次、连续、扫描或间断转换模式
驱动板所使用的主控芯片为GD32E103C8,该芯片总共有2个ADC单元,即ADC0、ADC1,因为驱动板上使用的是LQFP48封装,所以该芯片的每个ADC单元只有10个外部模拟输入通道,并且共用相同的GPIO口,这10个外部模拟输入通道依次的对应关系为:
1.设置 ADC 工作时钟
2.初始 ADC 用到的 GPIO
3.设置 ADC 的工作参数并初始化
4.配置ADC外部触发源
5.配置ADC外部触发
6.使能 ADC
7.ADC校准复位
8.使能ADC软件触发 ADC
注意每个资源都有对应的时钟,分别查看用户手册和Datasheet
在这里ADC最大能跑40MHz,所以主时钟经分频后,不能大于40MHz;
#include
#include
#include
#include
#include "gd32e10x.h"
#include
//485 PB10--Tx PB11--Rx
//ADC PA0
ts_uart logo;
volatile uint16_t adcValue = 0;//adc采集值
volatile uint16_t volValue = 0;//转换的电压值
void usart2_init() // 初始化串口0
{
rcu_periph_clock_enable(RCU_GPIOB); // 使能GPIOA时钟
rcu_periph_clock_enable(RCU_USART2); // 使能串口0时钟
/* connect port to USARTx_Tx *///配置TX 推挽复用模式
gpio_init(GPIOB, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_10);
/* connect port to USARTx_Rx *///配置RX 浮空输入模式
gpio_init(GPIOB, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_11); //
/***** 485 TX enable ****/ //pin19--PB1
gpio_init(GPIOB, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_1); //GPIO_OSPEED_2MHZ
gpio_bit_set(GPIOB,GPIO_PIN_1); //0-Rx
// 步骤1-7:
usart_deinit(USART2);
usart_word_length_set(USART2,USART_WL_8BIT); ///2、配置USART字长
usart_stop_bit_set(USART2,USART_STB_1BIT); ///3、配置USART停止位
usart_baudrate_set(USART2,115200); ///5、配置USART波特率
usart_transmit_config(USART2,USART_TRANSMIT_ENABLE); // 6、USART发送配置
usart_receive_config(USART2,USART_RECEIVE_ENABLE);
usart_enable(USART2);//使能串口
// 在nvic中配置中断向量和中断优先级
nvic_irq_enable(USART2_IRQn,0,0); //使能NVIC的中断
// 使能USART子中断
usart_interrupt_enable(USART2,USART_INT_RBNE);
// usart_interrupt_enable(USART2,USART_INT_RT);
}
int main(void)
{
systick_config(); //初始化延时函数
delay_ms(100);
usart2_init();
adc_init();
while(1)
{
adcValue=adc_sync_mode_convert_value_read();
printf("ADC0:%d\r\n",adcValue); //不连接温度传感器时,adcValue为最大值4095
volValue = adcValue * 3300 / 4096;//转成电压值
printf("vol :%d\r\n",volValue);
delay_ms(1000);
}
}
/**
@brief ADC驱动初始化
@param 无
@return 无
*/
void adc_init(void)
{
/* enable GPIOA clock */
rcu_periph_clock_enable(RCU_GPIOA);
/* config the GPIO as analog mode */
gpio_init(GPIOA, GPIO_MODE_AIN, GPIO_OSPEED_MAX, GPIO_PIN_0);//GPIO_MODE_AIN 模拟输入模式
/* enable ADC0 clock */
rcu_periph_clock_enable(RCU_ADC0);
/* config ADC clock */
rcu_adc_clock_config(RCU_CKADC_CKAPB2_DIV16);//和主芯片时钟有关 //ADC prescaler select CK_APB2/4 DIV4:120/4=30MHz小于42MHz可以 ADC最大42MHz
/* reset ADC *///复位ADC0外设
adc_deinit(ADC0);
/* ADC mode config *///配置ADC同步模式——所有ADC运行于独立模式
adc_mode_config(ADC_MODE_FREE);
/* ADC scan mode function enable *///使能或禁能ADC特殊功能
adc_special_function_config(ADC0,ADC_CONTINUOUS_MODE,ENABLE);
/* ADC data alignment config *///配置ADC数据对齐方式
adc_data_alignment_config(ADC0,ADC_DATAALIGN_RIGHT);//LSB 对齐
/* ADC channel length config *///配置规则通道组或注入通道组的长度
adc_channel_length_config(ADC0,ADC_REGULAR_CHANNEL,1);
/* ADC regular channel config *///配置ADC规则通道组
adc_regular_channel_config(ADC0,0,ADC_CHANNEL_0,ADC_SAMPLETIME_55POINT5); //0通道
/* ADC external trigger enable *///这两句重要,不能注释
adc_external_trigger_source_config(ADC0,ADC_REGULAR_CHANNEL,ADC0_1_EXTTRIG_REGULAR_NONE);//配置ADC外部触发源 software trigger
adc_external_trigger_config(ADC0,ADC_REGULAR_CHANNEL,ENABLE);//配置ADC外部触发
/* enable ADC interface *///使能ADC0外设
adc_enable(ADC0);
delay_ms(1);
/* ADC calibration and reset calibration *///ADC0校准复位
adc_calibration_enable(ADC0);
/* ADC software trigger enable *///ADC软件触发使能
adc_software_trigger_enable(ADC0,ADC_REGULAR_CHANNEL);
}
// 中断处理函数
void USART2_IRQHandler(void)
{
if( RESET != usart_interrupt_flag_get(USART2,USART_INT_FLAG_RBNE) )// 发生中断,则返回RESET
{
usart_data_receive(USART2); // 读取串口2接收到的数据
}
usart_interrupt_flag_clear(USART2, USART_INT_FLAG_EB);
}
/* retarget the C library printf function to the USART */
int fputc(int ch, FILE *f)
{
usart_data_transmit(USART2, (uint8_t)ch);
while(RESET == usart_flag_get(USART2, USART_FLAG_TBE));
return ch;
}
连接温度传感器(我手里的就是一个在25摄氏度时10K的热敏电阻,温度越大,电阻越大)
我插上温控座子时,ADC0和vol的值都变小了,就是因为有电阻分压了。
Vref、ADC、R2已知,用串联分压公式可以算出Rt的阻值
不插温控座子时,走红色这条线,所以ADC0的值应该是满值4095或者接近4095(不同芯片有所不同,我的ADC为12的精度,2的12次方=4096,范围0-4095),但是我感觉ADC0的值受主芯片时钟和ADC时钟分频的影响,ADC0波动范围有变化;
MCU的电压值等于绿色点处的值;
我的GD32E103最大可以跑120MHz的时钟,ADC最大分频16;
主芯片120MHz的外部时钟,ADC分频16:
主芯片120MHz的外部时钟,ADC分频12:
主芯片120MHz的外部时钟,ADC分频8:
主芯片120MHz的外部时钟,ADC分频6:
插了温度传感器,热敏电阻分压
本文是菜鸟程序员学习和小结使用,如有错误,请大佬予以指正!