ADC 驱动
更新时间:2017-08-08 阅读:2909
目录
前言
adc主要实现了将模拟电压转换数字形式,使得CPU可以识别外部的电压大小。QN902x系列拥有2/5路可配置量程的8/10/12位ADC,内置1V标准基准电压,也可外接基准电压,ADC的采样率高达16MHz。
初始化
系统平台初始化
相比之前的例程中,在系统初始化中adc还需要加入一些平台上的初始化,在SystemInit
函数中,之前的子模块初始化需要改为
/* ************************** * Sub module clock setting ************************** */ // DC-DC dc_dc_enable(true); // QN platform initialization plf_init(NORMAL_MODE, __XTAL, QN_32K_RCO, NULL, 0);
GPIO配置
接下来是GPIO的配置
adc_io_config();
P30,P31,P06,P07分别为ADC的输入通道AIN0,AIN1,AIN2,AIN3,P05作为ADC的GPIO触发引脚。
// pin mux syscon_SetPMCR0(QN_SYSCON, P00_UART0_TXD_PIN_CTRL | P05_ADCT_PIN_CTRL #if ADC_EXT_REF_EN==TRUE | P06_AIN2_PIN_CTRL | P07_AIN3_PIN_CTRL #else | P06_SW_DAT_PIN_CTRL | P07_SW_CLK_PIN_CTRL #endif | P17_UART0_RXD_PIN_CTRL ); syscon_SetPMCR1(QN_SYSCON, P27_PWM0_PIN_CTRL | P26_PWM1_PIN_CTRL | P31_AIN1_PIN_CTRL | P30_AIN0_PIN_CTRL );
使能AIN0,AIN1,AIN2,AIN3引脚的模拟输入功能
analog_pin_enable(AIN0, MASK_ENABLE); analog_pin_enable(AIN1, MASK_ENABLE); analog_pin_enable(AIN2, MASK_ENABLE); analog_pin_enable(AIN3, MASK_ENABLE);
串口初始化
初始化串口实乃意外,乃是为了更好的看到ADC的结果。
#if (__AHB_CLK == 32000UL) uart_init(QN_UART0, __USART_CLK, UART_1200); #else uart_init(QN_UART0, __USART_CLK, UART_115200); #endif uart_tx_enable(QN_UART0, MASK_ENABLE);
ADC初始化
ADC的初始化,本例中,不开启32K晶振,不适用外部晶振。
// ADC initialization #if ADC_WORKING_AT_32K==TRUE clk32k_enable(__32K_TYPE); adc_init(ADC_SINGLE_WITHOUT_BUF_DRV, ADC_CLK32K_16000, ADC_INT_REF, ADC_12BIT); #else #if ADC_EXT_REF_EN==TRUE adc_init(ADC_SINGLE_WITHOUT_BUF_DRV, ADC_CLK_1000000, ADC_EXT_REF2, ADC_12BIT); //adc_init(ADC_SINGLE_WITHOUT_BUF_DRV, ADC_CLK_1000000, ADC_EXT_REF1, ADC_12BIT); #else adc_init(ADC_SINGLE_WITHOUT_BUF_DRV, ADC_CLK_1000000, ADC_INT_REF, ADC_12BIT); #endif #endif
现在来分析一下
adc_init(ADC_SINGLE_WITHOUT_BUF_DRV, ADC_CLK_1000000, ADC_INT_REF, ADC_12BIT);
这句代码,第一个参数比较复杂,在工程中搜索(查到定义是跳不过去的,因为这是枚举变量),发现如下语句,在ADC采集有两种方式,一种是差分采集,一种是单信号采集,并且采集的时候如果使用内部buffer的话,不能检测到0.2v一下的电压,但是如果基准电压的1.5倍小于VDD-0.2的话,可以扩大量程到0.2V~1.5*VREF。对于例程来说,配置为单信号采集,不采用系统buffer,采样频率为1MHz,采用内部基准电压,ADC的位数为12位。
/// ADC input mode enum ADC_IN_MOD { ADC_DIFF_WITH_BUF_DRV = 0, /*!< ADC differential input with buffer, input singal 0.2 =< VIN(V) <= VDD-0.2, ADC result [-2048, 2047] map to [-VREF, VREF). */ ADC_DIFF_WITHOUT_BUF_DRV, /*!< ADC differential input without buffer, input singal 0 =< VIN(V) <= VDD, and should have enough driving capability, ADC result [-2048, 2047] map to [-VREF, VREF). */ ADC_SINGLE_WITH_BUF_DRV, /*!< ADC single-ended input with buffer, input singal 0.2 =< VIN(V) <= 1.5*VREF <= VDD-0.2, ADC result [x, 2047] map to [0.2, 1.5*VREF). */ ADC_SINGLE_WITHOUT_BUF_DRV /*!< ADC single-ended input without buffer, input singal 0 =< VIN(V) <= VREF <= VDD, and should have enough driving capability, ADC result [0, 2047] map to [0, VREF). */ }; /// ADC reference voltage enum ADC_REF { ADC_INT_REF = 0, /*!< Internal reference, VREF = 1.0V */ ADC_EXT_REF1, /*!< External reference1(with buffer and gain=2, input PIN is P0.7): VREF = 2*EXT_REF1 (0 < EXT_REF1 < (VDD-1.0)/2). */ ADC_EXT_REF2 /*!< External reference2(without buffer, input PIN is P0.7): VERF = EXT_REF2 (0 < EXT_REF2 < VDD), EXT_REF2 should have driving capability. */ };
ADC采集
adc采集配置声明,adc采集方式比较多,在不同需要的采集之前需要先配置采集方式,例程展示有GPIO触发采集、定时器流控采集、软件触发采集、抽取滤波采集、采样比较。
// Read configuration adc_read_configuration read_cfg;
GPIO触发采集
配置采集触发源为GPIO_P05
read_cfg.trig_src = ADC_TRIG_GPIO; syscon_SetPMCR2WithMask(QN_SYSCON, SYSCON_MASK_ADCT_PIN_SEL, ADC_GPIO15_TRIG);
配置PWM输出,将PWM_CH0与ADC_TRIG_GPIO相连,即可通过IO触发ADC进行电压采集了。
// triger by gpio in burst or burst scan mode, connect PWM output to ADC trigger PIN pwm_init(PWM_CH0); pwm_config(PWM_CH0, PWM_PSCAL_DIV, PWM_COUNT_US(50, PWM_PSCAL_DIV), PWM_COUNT_US(25, PWM_PSCAL_DIV)); pwm_enable(PWM_CH0, MASK_ENABLE);
同样先设置adc采集的标志位,再在回调函数中清零
adc_done = 0;
adc采集模式为突发模式,采集通道为AIN0,采集512个数据存入buf数组中,等待采集完成。
// modify here read_cfg.mode = BURST_MOD; read_cfg.start_ch = AIN0; read_cfg.end_ch = AIN0; adc_read(&read_cfg, buf, 512, adc_test_cb); while (adc_done == 0);
由于采用的是外部缓存,所以buf为adc_example.c中声明的全局变量。
int16_t buf[512];
打印ADC的采样结果
for (int i = 0; i < 512; i++) { printf("%d\t %d\r\n", buf[i], ADC_RESULT_mV(buf[i])); }
计算ADC采样结果的平均值,并打印出来。程序运行到这就算结束了。
int sum = 0; for (int i = 0; i < 10; i++) { sum += buf[511 - 2*i]; } sum = sum / 10; printf("average: %d\t %d\r\n", sum, ADC_RESULT_mV(sum));
定时器触发采集
配置触发源为定时器触发,配置定时器为50us触发一次。
read_cfg.trig_src = ADC_TRIG_TOVF1; // triger by timer1 overflow timer_init(QN_TIMER1, NULL); timer_pwm_config(QN_TIMER1, TIMER_PSCAL_DIV, TIMER_COUNT_US(100, TIMER_PSCAL_DIV), TIMER_COUNT_US(50, TIMER_PSCAL_DIV)); timer_enable(QN_TIMER1, MASK_ENABLE);
同样先设置adc采集的标志位,再在回调函数中清零
adc_done = 0;
adc采集模式为突发模式,采集通道为AIN0,采集512个数据存入buf数组中,等待采集完成。
// modify here read_cfg.mode = BURST_MOD; read_cfg.start_ch = AIN0; read_cfg.end_ch = AIN0; adc_read(&read_cfg, buf, 512, adc_test_cb); while (adc_done == 0);
由于采用的是外部缓存,所以buf为adc_example.c中声明的全局变量。
int16_t buf[512];
打印ADC的采样结果
for (int i = 0; i < 512; i++) { printf("%d\t %d\r\n", buf[i], ADC_RESULT_mV(buf[i])); }
计算ADC采样结果的平均值,并打印出来。程序运行到这就算结束了。
int sum = 0; for (int i = 0; i < 10; i++) { sum += buf[511 - 2*i]; } sum = sum / 10; printf("average: %d\t %d\r\n", sum, ADC_RESULT_mV(sum));
软件触发采集
配置触发源为软件触发。软件触发无法控制adc的采样速度和时间。
read_cfg.trig_src = ADC_TRIG_SOFT;
同样先设置adc采集的标志位,再在回调函数中清零
adc_done = 0;
adc采集模式为突发模式,采集通道为AIN0,采集512个数据存入buf数组中,等待采集完成。
// modify here read_cfg.mode = BURST_MOD; read_cfg.start_ch = AIN0; read_cfg.end_ch = AIN0; adc_read(&read_cfg, buf, 512, adc_test_cb); while (adc_done == 0);
由于采用的是外部缓存,所以buf为adc_example.c中声明的全局变量。
int16_t buf[512];
打印ADC的采样结果
for (int i = 0; i < 512; i++) { printf("%d\t %d\r\n", buf[i], ADC_RESULT_mV(buf[i])); }
计算ADC采样结果的平均值,并打印出来。程序运行到这就算结束了。
int sum = 0; for (int i = 0; i < 10; i++) { sum += buf[511 - 2*i]; } sum = sum / 10; printf("average: %d\t %d\r\n", sum, ADC_RESULT_mV(sum));
抽取滤波采集
抽取滤波采集一般用于对高速信号的采样中。在对高速信号上采样,因为数据量过大,往往很难进行实时的数据处理,需要采用数字下变频(DDC)技术,将采样得到的高速率信号变成低速率基带信号,以便进行下一步的信号处理。下图为对信号进行采样频为64的ADC数据采集。
#if ADC_DECIMATION_EN == TRUE adc_decimation_enable(DECI_RATE_64, MASK_ENABLE); #endif
打印ADC的采样结果
for (int i = 0; i < 512; i++) { printf("%d\t %d\r\n", buf[i], ADC_RESULT_mV(buf[i])); }
计算ADC采样结果的平均值,并打印出来。程序运行到这就算结束了。
int sum = 0; for (int i = 0; i < 10; i++) { sum += buf[511 - 2*i]; } sum = sum / 10; printf("average: %d\t %d\r\n", sum, ADC_RESULT_mV(sum));
ADC比较器
将ADC作为比较器来使用。
#if ADC_COMPARATOR_EN == TRUE //adc_compare_init(DECI_DATA, 2500, -2000, adc_WCMP_cb); adc_compare_init(ADC_DATA, 600, -600, adc_WCMP_cb); #endif
打印adc采集电压值的电压采集比较结果
int m = 0; int n = 0; for (int i = 0; i < 512; i++) { if (buf[i] > 600) { m++; } else if (buf[i] < -600) { n++; } } printf("m = %d\t n = %d\r\n", m, n);
打印ADC的采样结果
for (int i = 0; i < 512; i++) { printf("%d\t %d\r\n", buf[i], ADC_RESULT_mV(buf[i])); }
计算ADC采样结果的平均值,并打印出来。程序运行到这就算结束了。
int sum = 0; for (int i = 0; i < 10; i++) { sum += buf[511 - 2*i]; } sum = sum / 10; printf("average: %d\t %d\r\n", sum, ADC_RESULT_mV(sum));
内置温度传感器
QN902x内置有温度传感器,可以通过ADC读取温度传感器的电压值以计算当前温度值。
打开温度传感器
temp_sensor_enable(MASK_ENABLE);
初始化ADC,采用比较带缓存方式采集,采样频率为1MHz,基准电压为内部基准1V,采用12位ADC采集。
int16_t tempv; adc_init(ADC_DIFF_WITH_BUF_DRV, ADC_CLK_1000000, ADC_INT_REF, ADC_12BIT);
配置为软件触发,触发模式为突发模式,采样通道为内部TEMP通道,清零标志位开始读取
adc_done = 0; read_cfg.trig_src = ADC_TRIG_SOFT; read_cfg.mode = BURST_MOD; read_cfg.start_ch = TEMP; read_cfg.end_ch = TEMP; adc_read(&read_cfg, &tempv, 1, adc_test_cb); while (adc_done == 0);
打印出计算后的温度值
printf("temperature: %0.1f\r\n", (float)(TEMPERATURE_X10(tempv)/10.0));
内置电池电量检测器
QN902x内置有电池电量检测器,可以通过ADC读取电池的电压值以计算当前剩余电量值。
打开电池电量检测器
battery_monitor_enable(MASK_ENABLE);
初始化ADC,采用单信号带缓存方式采集,采样频率为1MHz,基准电压为内部基准1V,采用12位ADC采集。
int16_t battv; adc_init(ADC_SINGLE_WITH_BUF_DRV, ADC_CLK_1000000, ADC_INT_REF, ADC_12BIT);
配置为软件触发,触发模式为突发模式,采样通道为内部BATT通道,清零标志位开始读取
adc_done = 0; read_cfg.trig_src = ADC_TRIG_SOFT; read_cfg.mode = BURST_MOD; read_cfg.start_ch = BATT; read_cfg.end_ch = BATT; adc_read(&read_cfg, &battv, 1, adc_test_cb); while (adc_done == 0);
打印出计算后的电池剩余电量值