程序编写
ADC大家非常熟悉了,具体内容不再赘述,接下来只讨论和I.MX6ULL板子相关的ADC内容。首先要初始化ADC1_CH1相关的寄存器CFG寄存器、GC寄存器,初始化的过程中需要对初始化结果进行校准,我们单独编写一个校准函数,即需要关注GS寄存器的CALF位和HS寄存器的COCO0位,最后就是读取ADC寄存器R的值得到结果。
ADC.h
#include "imx6ul.h"
int adc1ch1_init(void);
status_t adc1_autocalibration(void);
uint32_t getadc_value(void);
unsigned short getadc_average(unsigned char times);
unsigned short getadc_volt(void);
ADC.c
#include "bsp_adc.h"
#include "bsp_delay.h"
#include "stdio.h"
/*
* @description : 初始化ADC1_CH1,使用GPIO1_IO01这个引脚。
* @param : 无
* @return : 0 成功,其他值 错误代码
*/
int adc1ch1_init(void)
{
int ret = 0;
/* 1、初始化ADC1 CH1 */
/* CFG寄存器
* bit16 0 关闭复写功能
* bit15:14 00 硬件平均设置为默认值,00的时候4次平均,
* 但是得ADC_GC寄存器的AVGE位置1来使能硬件平均
* bit13 0 软件触发
* bit12:1 00 参考电压为VREFH/VREFL,也就是3.3V/0V
* bit10 0 正常转换速度
* bit9:8 00 采样时间2/12,ADLSMP=0(短采样)的时候为2个周期
* ADLSMP=1(长采样)的时候为12个周期
* bit7 0 非低功耗模式
* bit6:5 00 ADC时钟源1分频
* bit4 0 短采样
* bit3:2 10 12位ADC
* bit1:0 11 ADC时钟源选择ADACK
*/
ADC1->CFG = 0;
ADC1->CFG |= (2 << 2) | (3 << 0);
/* GC寄存器
* bit7 0 先关闭校准功能,后面会校准
* bit6 0 关闭持续转换
* bit5 0 关闭硬件平均功能
* bit4 0 关闭比较功能
* bit3 0 关闭比较的Greater Than功能
* bit2 0 关闭比较的Range功能
* bit1 0 关闭DMA
* bit0 1 使能ADACK
*/
ADC1->GC = 0;
ADC1->GC |= 1 << 0;
/* 2、校准ADC */
if(adc1_autocalibration() != kStatus_Success)
ret = -1;
return ret;
}
/*
* @description : 初始化ADC1校准
* @param : 无
* @return : kStatus_Success 成功,kStatus_Fail 失败
*/
status_t adc1_autocalibration(void)
{
status_t ret = kStatus_Success;
ADC1->GS |= (1 << 2); /* 清除CALF位,写1清零 */
ADC1->GC |= (1 << 7); /* 使能校准功能 */
/* 校准完成之前GC寄存器的CAL位会一直为1,直到校准完成此位自动清零 */
while((ADC1->GC & (1 << 7)) != 0) {
/* 如果GS寄存器的CALF位为1的话表示校准失败 */
if((ADC1->GS & (1 << 2)) != 0) {
ret = kStatus_Fail;
break;
}
}
/* 校准成功以后HS寄存器的COCO0位会置1 */
if((ADC1->HS & (1 << 0)) == 0)
ret = kStatus_Fail;
/* 如果GS寄存器的CALF位为1的话表示校准失败 */
if((ADC1->GS & (1 << 2)) != 0)
ret = kStatus_Fail;
return ret;
}
/*
* @description : 获取ADC原始值
* @param : 无
* @return : 获取到的ADC原始值
*/
unsigned int getadc_value(void)
{
/* 配置ADC通道1 */
ADC1->HC[0] = 0; /* 关闭转换结束中断 */
ADC1->HC[0] |= (1 << 0); /* 通道1 */
while((ADC1->HS & (1 << 0)) == 0); /* 等待转换完成 */
return ADC1->R[0]; /* 返回ADC值 */
}
/*
* @description : 获取ADC平均值
* @param times : 获取次数
* @return : times次转换结果平均值
*/
unsigned short getadc_average(unsigned char times)
{
unsigned int temp_val = 0;
unsigned char t;
for(t = 0; t < times; t++){
temp_val += getadc_value();
delayms(5);
}
return temp_val / times;
}
/*
* @description : 获取ADC对应的电压值
* @param : 无
* @return : 获取到的电压值,单位为mV
*/
unsigned short getadc_volt(void)
{
unsigned int adcvalue=0;
unsigned int ret = 0;
adcvalue = getadc_average(5);
ret = (float)adcvalue * (3300.0f / 4096.0f); /* 获取计算后的带小数的实际电压值 */
return ret;
}
main.c
#include "bsp_clk.h"
#include "bsp_delay.h"
#include "bsp_led.h"
#include "bsp_beep.h"
#include "bsp_key.h"
#include "bsp_int.h"
#include "bsp_uart.h"
#include "bsp_lcd.h"
#include "bsp_lcdapi.h"
#include "bsp_rtc.h"
#include "bsp_backlight.h"
#include "bsp_adc.h"
#include "stdio.h"
/*
* @description : 使能I.MX6U的硬件NEON和FPU
* @param : 无
* @return : 无
*/
void imx6ul_hardfpu_enable(void)
{
uint32_t cpacr;
uint32_t fpexc;
/* 使能NEON和FPU */
cpacr = __get_CPACR();
cpacr = (cpacr & ~(CPACR_ASEDIS_Msk | CPACR_D32DIS_Msk))
| (3UL << CPACR_cp10_Pos) | (3UL << CPACR_cp11_Pos);
__set_CPACR(cpacr);
fpexc = __get_FPEXC();
fpexc |= 0x40000000UL;
__set_FPEXC(fpexc);
}
volatile
/*
* @description : main函数
* @param : 无
* @return : 无
*/
int main(void)
{
unsigned char i = 0;
unsigned int adcvalue;
unsigned char state = OFF;
signed int integ; /* 整数部分 */
signed int fract; /* 小数部分 */
imx6ul_hardfpu_enable(); /* 使能I.MX6U的硬件浮点 */
int_init(); /* 初始化中断(一定要最先调用!) */
imx6u_clkinit(); /* 初始化系统时钟 */
delay_init(); /* 初始化延时 */
clk_enable(); /* 使能所有的时钟 */
led_init(); /* 初始化led */
beep_init(); /* 初始化beep */
uart_init(); /* 初始化串口,波特率115200 */
lcd_init(); /* 初始化LCD */
adc1ch1_init(); /* ADC1_CH1 */
tftlcd_dev.forecolor = LCD_RED;
lcd_show_string(50, 10, 400, 24, 24, (char*)"ADC TEST");
lcd_show_string(50, 40, 200, 16, 16, (char*)"CSDN jiajia 2020");
lcd_show_string(50, 60, 200, 16, 16, (char*)"2021/8/3");
lcd_show_string(50, 90, 400, 16, 16, (char*)"ADC Ori Value:0000");
lcd_show_string(50, 110, 400, 16, 16,(char*)"ADC Val Value:0.00 V");
tftlcd_dev.forecolor = LCD_BLUE;
while(1)
{
adcvalue = getadc_average(5);
lcd_showxnum(162, 90, adcvalue, 4, 16, 0); /* ADC原始数据值 */
printf("ADC orig value = %d\r\n", adcvalue);
adcvalue = getadc_volt();
integ = adcvalue / 1000;
fract = adcvalue % 1000;
lcd_showxnum(162, 110, integ, 1, 16, 0); /* 显示电压值的整数部分,3.1111的话,这里就是显示3 */
lcd_showxnum(178, 110, fract, 3, 16, 0X80); /* 显示电压值小数部分(前面转换为了整形显示),这里显示的就是111. */
printf("ADC vola = %d.%dV\r\n", integ, fract);
delayms(50);
i++;
if(i == 10)
{
i = 0;
state = !state;
led_switch(LED0,state);
}
}
return 0;
}
结果验证
编译下载后,程序正常运行,LCD显示的Ori Value为从ADC寄存器读取的值,Val Value为经过运算后得到的实际采集电压值。