Linux裸机开发|ADC实验

ADC 实验

一、ADC 介绍
1.1 ADC 简介

ADC(Analog to Digital Converter)即模数转换器。它可以将外部的模拟信号转化成数字信号。对于 GPIO 口来说高于某个电压值,它读出来的只有高电平,低于就是低电平。如果想知道具体的电压数值就要借助于 ADC 的帮助,它可以将一个范围内的电压精确的读取出来。

假设 GPIO 口只要高于 1.7V 的都认为是高电平,比如某个 IO 口上外接了一个设备它能提供 0-2V 的电压变化,如果在这个 IO 口上使用 GPIO 模式去读取的话只能获得 0 和 1 两个数据,但是使用 ADC 模式去读取就可以获得 0-2V 之间连续变化的数值

ADC 有几个比较重要的参数:

– 测量范围:决定了外接的设备其信号输出电压范围
– 分辨率:能分辨出来的最小电压值
– 精度:影响结果准确度的因素之一
– 采样时间:保持某一时刻的外部信号,保持时间就是采样时间
– 采样率:在一秒的时间内采集多少次

1.2 I.MX6ULL ADC 简介

I.MX6ULL 提供了两个 12 位 ADC 通道和 10 个输入接口给我们使用。 I.MX6ULL 的 ADC 外设特性如下:

– 线性连续逼近算法,分辨率高达 12 位。
– 多达 10 个通道可以选择。
– 最高采样率 1MS/s。
– 多达 8 个单端外部模拟输入。
– 单次或连续转换(单次转换后自动返回空闲状态)。
– 可以配置为 12/10/8 位。
– 可配置的采样时间和转换速度/功率
– 支持转换完成、硬件平均完成标志和中断。
– 自我校准模式

ADC 有三种工作状态:

  • 禁止状态( Disabled):ADC 模块被禁止工作
  • 闲置状态( Idle):当前转换已经完成,下次转换尚未准备时的状态
  • 工作状态( Performing conversions):当 ADC 初始化完成后,并设置好输入通道后,将进入的状态

接下来看一下 ADC 的几个重要的寄存器

  • ADC 配置寄存器ADCx_CFG(x=1~2):

在这里插入图片描述

– OVWREN (bit16):数据复写使能位,为1时使能复写功能,0时关闭复写功能
– AVGS(bit15:14):硬件平均次数,只有当ADC1_GC寄存器的AVGE位为1时才有效
– ADTRG(bit13):转换触发选择,为0时选择软件触发,1时不选择软件触发
– REFSEL(bit12:11):参考电压选择,为00时选择 VREFH/VREFL 这两个引脚上的电压为参考电压
– ADHSC(bit10):高速转换使能位,为0时为正常模式,1时为高速模式
– ADSTS(bit9:8):设置ADC的采样周期,与ADLSMP位一起决定采样周期
– ADIV(bit6:5):时钟分频选择,为00时不分频,01时2分频,10时4分频,11时8分频
– ADLSMP(bit4):长采样周期使能位,为0时短采样周期,1时为长采样周期模式
– MODE(bit3:2):选择转换精度
– ADICLK(bit1:0):输入时钟源选择,为00时选择IPG Clock,01时选择IPG Clock/2,10时无效,11时选择ADACK

  • 通用控制寄存器 ADCx_GC

在这里插入图片描述

– CAL(bit7):该位写入1时,硬件校准功能将会启动,校准过程中该位会一直保持 1,完成后会清 0,校准完成后需要检查一下 ADC_GS[CALF]位,确认校准结果
– ADCO(bit6):连续转换使能位,只有在开启了硬件平均功能时有效,为0时只能转换一次或一组,为1时可以连续转换或多组
– AVGE(bit5):硬件平均使能位,为0时关闭,为1时使能
– ACFE(bit4):比较功能使能位,为0时关闭,为1时使能
– ACFGT(bit3):配置比较方法,为0的话就比较转换结果是否小于 ADC_CV 寄存器值,为1的话就比较装换结果是否大于或等于 ADC_CV 寄存器值
– ACREN(bit2):范围比较功能使能位,为0的话仅和ADC_CV里的CV1比较,为1的话和ADC_CV里的CV1、CV2比较
– ACREN(bit2):范围比较功能使能位,为0的话仅和ADC_CV里的CV1比较,为1的话和ADC_CV里的CV1、CV2比较
– DMAEN(bit1):DMA 功能使能位,为0是关闭,为1是开启
– ADACKEN(bit0):异步时钟输出使能位,为0是关闭,为1时开启

  • 通用状态寄存器 ADCx_GS

在这里插入图片描述

– AWKST(bit2):异步唤醒中断状态,为1时表示发生了异步唤醒中断,为0时没有发生异
步中断
– CALF(bit1):校准失败标志位,为0时表示校准正常完成,为1时表示校准失败
– ADACT(bit0):转换活动标志,为0时表示转换没有进行,为1时表示正在进行转换

  • 状态寄存器 ADCx_HS

在这里插入图片描述

– COCO0:转换完成标志位,为只读位,当关闭比较功能和硬件平均以后每次转换完整此位就会被置 1;使能硬件平均以后,只有在设置的转换次数达到以后此位才置 1

  • 控制寄存器 ADCx_HC0

在这里插入图片描述

– AIEN(bit7):转换完成中断控制位,为1时打开转换完成中断,为0时关闭
– ADCH(bit4:0):转换通道选择,可以设置为 00000 ~ 01111 分别对应通道 0~15;11001 为内部通道,用于 ADC 自测

  • 数据结果寄存器 ADCx_R0

在这里插入图片描述

– CDATA:bit11:0 这12位有效,用来保存 ADC 转换结果

1.3 I.MX6ULL ADC 配置步骤

本文使用 I.MX6ULL 的 ADC1 通道 1, ADC1 通道 1 的引脚为 GPIO1_IO01,配置步骤如下:

  • 初始化 ADC1_CH1:配置 ADC 位数,时钟源,采样时间等
  • 校准 ADC:在使用之前需要校准一次
  • 使能 ADC:配置好 ADC 以后就可以开启了
  • 读取 ADC 值:ADC 正常工作以后就可以读取 ADC 值
二、硬件介绍

本例程需要用到的硬件资源:

  • LED0
  • RGB LCD接口
  • GPIO1_IO01 引脚

将 GPIO1_IO01 引脚作为 ADC1_CH1,并使用杜邦线在此引脚上引入一个 0~3.3V 的电压,然后使用内部 ADC 进行测量

三、程序编写
  • 新建adc文件夹,在文件夹中新建 bsp_adc.c 和 bsp_adc.h 这两个文件
/******************bsp_adc.h*******************/
#ifndef __ADC_H
#define __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);

#endif
/******************bsp_adc.c*******************/
#include "bsp_adc.h"
#include "bsp_delay.h"
#include "stdio.h"
/* 初始化ADC1_CH1,使用GPIO1_IO01引脚 */
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:11 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;
}
/* 初始化 ADC1 校准 */
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;
}
/* 获取 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 值 */
}
/* 获取 ADC 平均值 */
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;
}
/* 获取 ADC 对应的电压值 */
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中编写测试程序
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 */
	uart_init(); /* 初始化串口,波特率 115200 */
	lcd_init(); /* 初始化 LCD */
	adc1ch1_init(); /* ADC1_CH1 */

	tftlcd_dev.forecolor = LCD_RED;
	lcd_show_string(50, 10, 400, 24, 24, (char*)"ALPHA-IMX6U ADC TEST");
	lcd_show_string(50, 40, 200, 16, 16, (char*)"ATOM@ALIENTEK");
	lcd_show_string(50, 60, 200, 16, 16, (char*)"2019/12/16");
	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); /* 显示电压值的整数部分 */
		lcd_showxnum(178, 110, fract, 3, 16, 0X80);/* 显示电压值小数部分*/
		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;
}
四、下载验证
  • 修改Makefile文件:修改TARGET为adc,追加“bsp/adc”文件夹
  • 使用imxdownload软件将bin文件下载到SD卡中
  • 烧写成功后,插入SD卡,复位开发板后,用杜邦线将 GPIO1 引脚接到 外接的电压上,即可在屏幕上显示出对应的电压值
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

安迪西嵌入式

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值