51 microcontroller analog-to-digital conversion ADC principle and code 1

51 microcontroller analog-to-digital conversion ADC principle and code 1

1 Overview

This article is an introductory article for analog-to-digital conversion. This article mainly introduces the concept, principles, core indicators, and professional terms of analog-to-digital conversion, as well as an example code of analog-to-digital conversion to detect the numerical change of the potentiometer.

2.ADC introduction

2.1.ADC concept

ADC (Analog-to-Digital Converter) is a type of device used to convert continuous signals in analog form into discrete signals in digital form.

Analog: Directly translated, it means simulation, which means it is an analog signal. Here we need to understand a concept: What is an analog signal? In fact, it is not difficult to understand. Analog signal is to simulate a circuit into a signal. Electrical signals have voltage, current and other factors;
Digital: directly translated is digital, that is, digital signal. Then we need to understand another concept, what Is it a digital signal? In fact, it is easier to understand than analog signals. The circuit signal is simulated into a digital signal. Normally, a high level represents 1 and a low level represents 0;
Converter: After we understand the previous two concepts, you may There are some meanings that I don’t understand, so the translation is that the analog signal is converted into a digital signal! That's right, we use the corresponding acquisition device to collect the value of the voltage. At this time we need to use our analog-to-digital conversion to convert it into a digital signal.

2.2.ADC indicator

ADC has some core performance indicators, which provide a reference for us to select the ADC module. The main indicators are as follows.

  • Number of output digits

    • AD acquisition has a total of 2n scales, which means that if it is an 8-bit AD, its final output value is a value between 0 and 255.
  • resolution

    • The sensitivity to changes in the input signal is expressed by the number of output digits of the ADC converter. For example, the output digits are 8 digits, and the variation range of the output is 0~255. When the reference voltage is 5V, the resolution of the converter to the input analog voltage The calculation formula is 5V / 255 = 18.6mV
  • conversion speed

    • The reciprocal of the clock cycle it takes to complete an AC conversion, that is, how many AD conversions can be completed in one cycle
  • Reference voltage

    • Calculate resolution from reference voltage

2.3.ADC register

ADC register introduction

The internal working principle of the ADC is not introduced here because our focus is on practical operation, operating the ADC to achieve analog-to-digital conversion, and applying it in real scenarios to complete some work, rather than studying its principles, so we will not introduce too much theory here. Knowledge.
After understanding the ADC concepts and indicators, you will have a preliminary understanding, and then you can operate the ADC by understanding the ADC registers. The settings of the ADC register vary depending on the model. Please check the instructions corresponding to the actual model. Any model of AD converter is slightly different in register design, but the operation method is the same. Therefore, if you master one model, you can operate other models.
Here we mainly use the instruction manual of the STC12C2052AD model to explain.

Insert image description here
The above figure is the internal structure diagram of the ADC converter. The following describes in detail how to configure each bit of the ADC_CONTR Register register.

Insert image description here
ADC_POWER:
It is the microcontroller to turn on the ADC power control bit; 0: turn off the ADC power supply; 1: turn on the A/D converter power supply

1.建议进入空闲模式和掉电模式前,将ADC电源关闭,可降低功耗。
2.启动A/D转换前一定要确认A/D电源已打开,A/D转换结束后关闭A/D电源可降低功耗,也可不关闭。
3.初次打开内部A/D转换模拟电源,需适当延时,等内部模拟电源稳定后,再启动A/D转换。

SPEED1, SPEED0:
used to control the conversion speed of the microcontroller ADC

SPEED1 SPEED0 ADC clock cycle N clock conversion 1 time
0 0 1080
0 1 810
1 0 540
1 1 270

ADC_FLAG:
Conversion flag bit: This bit will be automatically set to "1" every time the AD conversion is completed. You need to manually reset this bit to "0".

ADC_START:
Conversion start bit: Whenever it is manually set to "1", the AD conversion starts. When the AD conversion is completed, this bit will automatically be set to "0".

CHS2/CHS1/CHS0:
Select the acquisition signal input port

CHS2 CHS1 CHS0 ADC input channel
0 0 0 ADC0(P1^0)
0 0 1 ADC0(P1^1)
0 1 0 ADC0(P1^2)
0 1 1 ADC0(P1^3)
1 0 0 ADC0(P1^4)
1 0 1 ADC0(P1^5)
1 1 0 ADC0(P1^6)
1 1 1 ADC0(P1^7)

P1M0 and P1M1
set the IO port status of microcontroller P1. If P1 port is to be used as AD, it needs to be set to high impedance or open drain mode.

P1M0 P1M1 I/O port mode
0 0 Quasi-bidirectional port (traditional 8051 IO port mode, weak pull-up), sink current up to 20mA, source current 270uA, due to manufacturing error, the actual value is 270uA~150uA
0 1 Push-pull output (strong pull-up output, up to 20mA, need to add a current limiting resistor)
1 0 High impedance input (current can neither flow in nor flow out)
1 1 Open Drain, internal pull-up resistor is disconnected. Open-drain mode can both read external status and output (high level or low level). If you want to read the external status correctly or need to output a high level to the outside, you need to add a pull-up resistor. Otherwise, the external status cannot be read and the high level cannot be output to the outside.

Interrupt enable register IE
This register is used to control various interrupts of the microcontroller. Only the register bits related to ADC are introduced here.
Insert image description here
EA: The function of the CPU interrupt open flag
EA is to allow interrupts to form multi-level control. That is, each interrupt source is first controlled by EA; secondly, it is also controlled by each interrupt source's own interrupt enable control bit.
EA=1, the CPU enables interrupts,
EA=0, the CPU blocks all interrupt requests.

EADC_SPI: A/D conversion interrupt enable bit and SPI interrupt enable bit
EADC_SPI is used to control the start and stop of AD conversion.
EADC_SPI=1, enables A/D conversion interrupt and SPI interrupt,
EADC_SPI=0, disables A/D conversion interrupt and SPI interrupt.

ADC register configuration

All the contents of the ADC registers have been introduced above. After understanding their functions, they may not be configured in the code, so here is a special introduction on how to use the registers.
Insert image description here
The ADC_CONTR Register register has a total of 8 bits that are bitwise addressable. They can be set bitwise by converting them into corresponding hexadecimal numbers.
Here are a few examples to introduce how to use it. Other parameter configuration methods are the same.

ADC_STARTsettings

Meaning: Conversion start bit: Whenever it is manually set to "1", AD conversion starts. When AD conversion is completed, this bit will automatically be set to "0".
Turn on conversion: ADC_START is the fourth digit from right to left in the register, corresponding to hexadecimal is 0x08, converted to binary is 0000 1000, the fourth digit is 1, which means turning on conversion

ADC_POWER:

Meaning: It is the microcontroller to turn on the ADC power control bit; 0: turn off the ADC power; 1: turn on the A/D converter power and
turn on the ADC power: ADC_POWER is the eighth digit from right to left in the register, and the corresponding hexadecimal value is 0x80 , converted to binary, it is 1000 0000. The eighth bit is 1, which means turning on the ADC power supply.

3.ADC code

The ADC code is relatively simple and can be divided into functions. When used, the function can be called directly. The ADC conversion code is divided into two functions

  • ADC initialization function
    • Set the acquisition signal input port, such as P1.0 to collect data.
    • Set acquisition speed
    • Start ADC power supply
  • ADC read output data function
    • ADC conversion starts
    • Check if the conversion is complete
    • Set the conversion flag to 0
    • Output conversion data
Initialize ADC function

mainCall the ADC initialization function in the function . adcInit(0,ADC_SPEEDH,0x01,0x00);The workflow is as follows

  • ADC_CONTR |= (inIOnum & 0x07); Pass in 0 and set the P1.0 pin for data acquisition
  • ADC_CONTR = speed; Pass in ADC_SPEEDH to set medium to high conversion speed
  • P1M0 = p1m0; Pass in 0X01 and set the P1.0 pin to high impedance state
  • ADC_CONTR |= ADC_POWER; Turn on ADC conversion power
/*定义ADC寄存器*/
#define ADC_POWER   0x80            //ADC 电源打开
#define ADC_FLAG    0x10            //ADC 打开转换标志位
#define ADC_START   0x08            //ADC 开始转换
#define ADC_SPEEDLL 0x00            //420 设置低速转换
#define ADC_SPEEDL  0x20            //280 设置低中速转换
#define ADC_SPEEDH  0x40            //140 设置中高速转换
#define ADC_SPEEDHH 0x60            //70  设置低高速转换

/*
函数名:8位A/D转换初始化函数
参  数:
inDate:采集数据端口(0000 0XXX 其中XXX是设置输入端口号,可用十进制0~7表示,0表示P1.0,7表示P1.7)
speed: 设置转换速度
ioMode:设置IO口模式,P1口如果要作为AD使用需要将它设置为高阻或者开漏模式。
返回值:无
功  能:开启ADC功能并设置ADC的输入端口
备  注:适用于STC12C2052AD系列单片机(必须使用STC12C2052AD.h头文件)
*/
void adcInit (unsigned char inIOnum, unsigned char speed, unsigned char p1m0, unsigned char p1m1){
    
    
	ADC_CONTR |= (inIOnum & 0x07); //选择A/D当前输入信号的通道,当前选择是P1.0(P1.0~P1.7),选择ADC的8个接口中的一个(0000 0111 清0高5位)
	ADC_CONTR = speed; //ADC转换的速度(0XX0 0000 其中XX控制速度,请根据数据手册设置)
	P1M0 = p1m0;
	P1M1 = p1m1; // 设置IO口模式,P1口如果要作为AD使用需要将它设置为高阻或者开漏模式。 
	_nop_();
	ADC_CONTR |= ADC_POWER;      //启动A/D电源
	DELAY_MS(1);            //使输入电压达到稳定(1ms即可)
}
Get ADC conversion results
/*
函数名:8位A/D转换函数
参  数:无
返回值:8位的ADC数据
结  果:读出指定ADC接口的A/D转换值,并返回数值
备  注:适用于STC12C2052AD系列单片机(必须使用STC12C2052AD.h头文件)
*/
unsigned char getADCResult (){
    
    
	ADC_CONTR |= ADC_START;      //启动A/D转换(0000 1000 令ADCS = 1)
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	while (!(ADC_CONTR & ADC_FLAG));//等待转换完成,ADC_FLAG为0x10转换完成
  ADC_CONTR &= ~ADC_FLAG;    //1111 0111 清ADC_FLAG位, 关闭A/D转换, 
	return ADC_DATA;          //返回A/D转换结果(8位)
}
main calls ADC
void main(){
    
    
	// 调用ADC初始化函数
	adcInit(0,ADC_SPEEDH,0x01,0x00);
	while(1){
    
    
		// 获取ADC转换结果
		m = getADCResult ();
		printChar(5,1,m/100+0x30);//1011 0101
		printChar(6,1,m/10%10+0x30);//1011 0101
		printChar(7,1,m%10+0x30);//1011 0101
	}
}
1602 display shows ADC conversion effect

Add the above ADC code to the 51 microcontroller 4-wire concurrent IO port control 1602LCD experiment, and display the converted value on the 1602 display by adjusting the potentiometer.

Hardware circuit: Wire
according to the figure below. There are two points that need to be changed.

  • P1.0~P1.3 lines do not need to be connected
  • The 1st pin of the potentiometer is connected to GND, the 2nd pin is connected to P1.0, and the 3rd pin is connected to VCC.
    Insert image description here

code

#include <STC12C2052AD.H>
#include <string.h>
#include <intrins.h>	//51基本运算(包括_nop_空函数)
typedef unsigned char uint8;
// 定义引脚
#define	LCD1602_DB4_DB7	P1		// 定义高4位LCD1602的数据总线
sbit LCD1602_RS = P3 ^ 2;					// 定义LCD1602的RS控制线
sbit LCD1602_RW = P3 ^ 3;					// 定义LCD1602的RW控制线
sbit LCD1602_E  = P3 ^ 4;					// 定义LCD1602的E控制线
sbit LCD1602_Busy = P1 ^ 7;					// 定义LCD1602的测忙线(与LCD1602_DB4_DB7关联)


// 定义指令集
/*设置显示模式*/
#define LCD_MODE_PIN8 0x38	// 8位数据线,两行显示
#define LCD_MODE_PIN4 0x28	// 4位数据线,两个显示
#define LCD_SCREEN_CLR 0x01	// 清屏
#define LCD_CURSOR_RET 0x02	// 光标复位
#define LCD_CURSOR_RIGHT 0x06	// 光标右移,显示不移动
#define LCD_CURSOR_LEFT 0x04	// 光标左移,显示不移动
#define LCD_DIS_MODE_LEFT 0x07 	// AC自增,画面左移
#define LCD_DIS_MODE_RIGHT 0X05	// AC自增,画面右移


/*光标开关控制*/
#define LCD_DIS_CUR_BLK_ON 0x0f	// 显示开,光标开,光标闪烁
#define LCD_DIS_CUR_ON 0x0e	// 显示开,光标开,光标不闪烁
#define LCD_DIS_ON 0x0c	// 显示开,光标关,光标不闪烁
#define LCD_DIS_OFF 0x08	// 显示关,光标关,光标不闪烁

/*光标、显示移动*/
#define LCD_CUR_MOVE_LEFT 0x10	// 光标左移
#define LCD_CUR_MOVE_RIGHT 0x14	// 光标右移
#define LCD_DIS_MOVE_LEFT 0x18	// 显示左移
#define LCD_DIS_MOVE_RIGHT 0x1c	// 显示右移

/*定义ADC寄存器*/
#define ADC_POWER   0x80            //ADC 电源打开
#define ADC_FLAG    0x10            //ADC 打开转换标志位
#define ADC_START   0x08            //ADC 开始转换
#define ADC_SPEEDLL 0x00            //420 设置低速转换
#define ADC_SPEEDL  0x20            //280 设置低中速转换
#define ADC_SPEEDH  0x40            //140 设置中高速转换
#define ADC_SPEEDHH 0x60            //70  设置低高速转换

void DELAY_MS (unsigned int a){
    
    
	unsigned int i;
	while( --a != 0){
    
    
		for(i = 0; i < 600; i++);
	}
}

/**
LCD1602忙碌状态不会接收新指令,因此在发送新指令前先检测是否忙碌。
判断LCD1602_Busy变量的值为低电平则为不忙。
*/
void LCD1602_TestBusy(void){
    
    
	LCD1602_DB4_DB7 = 0xf0;	//高4位IO口设置为1,低4位IO口保持原态
	LCD1602_RS = 0; // 指令状态
	LCD1602_RW = 1;	// 读状态
	LCD1602_E = 1;
	while(LCD1602_Busy);	//读取LCD1602_Busy(P1.7)为低电平则结束循环
	LCD1602_E = 0;	// 关闭LCD显示器读指令
}


/********************************************************************************************
// 写指令程序 //
// 向LCD1602写命令 本函数需要1个指令集的入口参数 //
/********************************************************************************************/
void LCD1602_WriteCMD(uint8 LCD1602_command) {
    
     
	LCD1602_TestBusy();
	LCD1602_RS = 0;
	LCD1602_RW = 0;
	//输入的命令高4位赋值给LCD1602_DB4_DB7
	LCD1602_DB4_DB7 = LCD1602_command;
	DELAY_MS(1);
	LCD1602_E = 1;
	LCD1602_E = 0;
	//将命令低4位移到高四位供IO口读取
	LCD1602_DB4_DB7 = LCD1602_command << 4;
	DELAY_MS(1);
	LCD1602_E = 1;
	LCD1602_E = 0;
}
/********************************************************************************************
// 写数据程序 //
// 向LCD1602写数据 //
/********************************************************************************************/
void LCD1602_WriteData(uint8 LCD1602_data){
    
     
	LCD1602_TestBusy();
	
	LCD1602_RS = 1;
	LCD1602_RW = 0;
	//写入高4位数据
	LCD1602_DB4_DB7 = LCD1602_data;
	DELAY_MS(1);
	LCD1602_E = 1;
	LCD1602_E = 0;
	//将低4位数据移到高4位IO口写入
	LCD1602_DB4_DB7 = LCD1602_data << 4;
	DELAY_MS(1);
	LCD1602_E = 1;
	LCD1602_E = 0;
}


// LCD1602初始化
void LCD1602_Init(void){
    
    
	// 设置4线并行口
	LCD1602_WriteCMD(LCD_MODE_PIN4);	// 显示模式设置:显示2行,每个字符为5*7个像素
	LCD1602_WriteCMD(LCD_DIS_ON); 	// 显示开及光标设置:显示开,光标关
	LCD1602_WriteCMD(LCD_CURSOR_RIGHT);		//显示光标移动设置:文字不动,光标右移
	LCD1602_WriteCMD(LCD_SCREEN_CLR);	// 显示清屏
}


/*
输出字符串
x:数据地址
y:输出的行位置,第一行和第二行
str:输入字符串
*/
void printStr(uint8 x, uint8 y, uint8 *str){
    
    
	if(0 == y){
    
    
		LCD1602_WriteCMD(0x80 | x);
	}
	else{
    
    
		// 第二行起始位置是0x40
		LCD1602_WriteCMD(0x80 | (0x40+x));
	}
	while(*str != '\0'){
    
    
		LCD1602_WriteData(*str++);
	}

}

/*
打印单字符程序 // 
第一行位置 0x00~0x17  第二行位置 0x40~0x57 
向LCM发送一个字符,以十六进制(0x00)表示 
应用举例:print(0xc0,0x30); //在第二行第一位处打印字符“0”
*/
void printChar(uint8 x, uint8 y, uint8 c){
    
    
	if(0 == y){
    
    
		LCD1602_WriteCMD(0x80 | x);
	}
	else{
    
    
		// 第二行起始位置是0x40
		LCD1602_WriteCMD(0x80 | (0x40+x));
	}
	LCD1602_WriteData(c);
}

/*
函数名:8位A/D转换初始化函数
参  数:
inDate:采集数据端口(0000 0XXX 其中XXX是设置输入端口号,可用十进制0~7表示,0表示P1.0,7表示P1.7)
speed: 设置转换速度
ioMode:设置IO口模式,P1口如果要作为AD使用需要将它设置为高阻或者开漏模式。
返回值:无
功  能:开启ADC功能并设置ADC的输入端口
备  注:适用于STC12C2052AD系列单片机(必须使用STC12C2052AD.h头文件)
*/
void adcInit (unsigned char inIOnum, unsigned char speed, unsigned char p1m0, unsigned char p1m1){
    
    
	ADC_CONTR |= (inIOnum & 0x07); //选择A/D当前输入信号的通道,当前选择是P1.0(P1.0~P1.7),选择ADC的8个接口中的一个(0000 0111 清0高5位)
	ADC_CONTR = speed; //ADC转换的速度(0XX0 0000 其中XX控制速度,请根据数据手册设置)
	P1M0 = p1m0;
	P1M1 = p1m1; // 设置IO口模式,P1口如果要作为AD使用需要将它设置为高阻或者开漏模式。 
	_nop_();
	ADC_CONTR |= ADC_POWER;      //启动A/D电源
	DELAY_MS(1);            //使输入电压达到稳定(1ms即可)
}

/*
函数名:8位A/D转换函数
调  用:? = Read ();
参  数:无
返回值:8位的ADC数据
结  果:读出指定ADC接口的A/D转换值,并返回数值
备  注:适用于STC12C2052AD系列单片机(必须使用STC12C2052AD.h头文件)
*/
unsigned char getADCResult (){
    
    
	ADC_CONTR |= ADC_START;      //启动A/D转换(0000 1000 令ADCS = 1)
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	while (!(ADC_CONTR & ADC_FLAG));//等待转换完成,ADC_FLAG为0x10转换完成
  ADC_CONTR &= ~ADC_FLAG;    //1111 0111 清ADC_FLAG位, 关闭A/D转换, 
	return ADC_DATA;          //返回A/D转换结果(8位)
}


void main(){
    
    
	unsigned char m;
	unsigned char code str[] = "Hello LCD 1602";
	unsigned char code str1[] = "ABC";
	LCD1602_Init();
	printStr(0,0,str);
	printStr(0,1,str1);
	adcInit(0,ADC_SPEEDH,0x01,0x00);
	while(1){
    
    
		m = getADCResult ();
		printChar(5,1,m/100+0x30);//1011 0101
		printChar(6,1,m/10%10+0x30);//1011 0101
		printChar(7,1,m%10+0x30);//1011 0101
	}
}

おすすめ

転載: blog.csdn.net/m0_38039437/article/details/135156746