10.STC15W408AS单片机A/D转换器

Posted 小猿猪哥

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了10.STC15W408AS单片机A/D转换器相关的知识,希望对你有一定的参考价值。

        10.STC15W408AS单片机A/D转换器

        STC15系列单片机内部集成了8路10位高速A/D转换器。STC15系列单片机的A/D转换口在P1口(P1.7-P1.0),有8路10位高速A/D转换器,速度到300KHz(30万次/秒)。8路电压输入型A/D,可做温度检测、电池电压检测、按键扫描、频谱检测等。

 

1.A/D转换器的结构

STC15系列单片机ADC由多路选择开关、比较器、逐次比较寄存器、10位DAC、转换结果寄存器(ADC_RES和ADC_RESL)以及ADC_CONTR构成。

STC15系列单片机的ADC是逐次比较型ADC。逐次比较型ADC由一个比较器和D/A转换器构成,通过逐次比较逻辑,从最高位(MSB)开始,顺序地对每一输入电压与内置D/A转换器输出进行比较,经过多次比较,使转换所得的数字量逐次逼近输入模拟量对应值。逐次比较型A/D转换器具有速度高,功耗低等优点。

从上图可以看出,通过模拟多路开关,将通过ADC0~7的模拟量输入送给比较器。用数/模转换器(DAC)转换的模拟量与输入的模拟量通过比较器进行比较,将比较结果保存到逐次比较寄存器,并通过逐次比较寄存器输出转换结果。A/D转换结束后,最终的转换结果保存到ADC转换结果寄存器ADC_RES和ADC_RESL,同时,置位ADC控制寄存器ADC_CONTR中的A/D转换结束标志位ADC_FLAG,以供程序查询或发出中断申请。模拟通道的选择控制由ADC控制寄存器ADC_CONTR中的CHS2~CHS0确定。ADC的转换速度由ADC控制寄存器中的SPEED1和SPEED0确定。在使用ADC之前,应先给ADC上电,也就是置位ADC控制寄存器中的ADC_POWER位。

当CLK_DIV.5(PCON2.5)/ADRJ = 0时,A/D转换结果寄存器格式如下:

当ADRJ=0时,如果取10位结果,则按下面公式计算:

当ADRJ=0时,如果取8位结果,按下面公式计算:

当CLK_DIV.5(PCON2.5)/ADRJ = 1时,A/D转换结果寄存器格式如下:

当ADRJ=1时,如果取10位结果,则按下面公式计算:

式中,Vin为模拟输入通道输入电压,Vcc为单片机实际工作电压,用单片机工作电压作为

模拟参考电压。

2.与A/D转换相关的寄存器

与STC15系列单片机A/D转换相关的寄存器列于下表所示。

2.1 P1口模拟功能控制寄存器P1ASF

STC15系列单片机的A/D转换口在P1口(P1.7-P1.0),有8路10位高速A/D转换器速度可达到到300KHz(30万次/秒)。8路电压输入型A/D,可做温度检测、电池电压检测、按键扫描、频谱检测等。上电复位后P1口为弱上拉型I/O口,用户可以通过软件设置将8路中的任何一路设置为A/D转换,不需作为A/D使用的P1口可继续作为I/O口使用(建议只作为输入)。需作为A/D使用的口需先将P1ASF特殊功能寄存器中的相应位置为‘1’,将相应的口设置为模拟功能。 P1ASF存器的格式如下:

P1ASF : P1口模拟功能控制寄存器(该寄存器是只写寄存器,读无效)

2.2 ADC控制寄存器ADC_CONTR

ADC_CONTR寄存器的格式如下:

ADC_CONTR : ADC控制寄存器

对ADC_CONTR寄存器进行操作,建议直接用MOV赋值语句,不要用‘与’和‘或’语句。

ADC_POWER: ADC 电源控制位。

0:关闭ADC 电源;

1:打开A/D转换器电源.

建议进入空闲模式和掉电模式前,将ADC电源关闭,即ADC_POWER =0,可降低功耗。

启动A/D转换前一定要确认A/D电源已打开,A/D转换结束后关闭A/D电源可降低功耗,也可

不关闭。初次打开内部A/D转换模拟电源,需适当延时,等内部模拟电源稳定后,再启

动A/D转换。

建议启动A/D转换后,在A/D转换结束之前,不改变任何I/O口的状态,有利于高精度A/D转换,如能将定时器/串行口/中断系统关闭更好。

SPEED1,SPEED0:模数转换器转换速度控制位

ADC_FLAG: 模数转换器转换结束标志位,当A/D转换完成后,ADC_FLAG = 1,要由软件清0。

不管是A/D 转换完成后由该位申请产生中断,还是由软件查询该标志位A/D转换是

否结束,当A/D转换完成后,ADC_FLAG = 1,一定要软件清0。

ADC_START:模数转换器(ADC)转换启动控制位,设置为“1”时,开始转换,转换结束后为0。

CHS2/CHS1/CHS0:模拟输入通道选择,CHS2/CHS1/CHS0

2.3 ADC转换结果调整寄存器位——ADRJ

ADC转换结果调整寄存器位——ADRJ位于寄存器CLK_DIV/PCON中,用于控制ADC转换

结果存放的位置。

ADRJ:ADC转换结果调整

0:ADC_RES[7:0]存放高8位ADC结果,ADC_RESL[1:0]存放低2位ADC结果

1:ADC_RES[1:0]存放高2位ADC结果,ADC_RESL[7:0]存放低8位ADC结果

2.4 A/D转换结果寄存器ADC_RES、ADC_RESL

特殊功能寄存器ADC_RES和ADC_RESL寄存器用于保存A/D转换结果,其格式如下:

CKKO_DIV寄存器的ADRJ位是A/D转换结果寄存器(ADC_RES,ADC_RESL)的数据格式调整控制位。

当ADRJ=0时,10位是A/D转换结果的高8位存放在在ADC_RES中,低2位存放在ADC_RESL的低2位中。

2.5 中断允许寄存器IE

IE : 中断允许寄存器 (可位寻址)

EA : CPU的中断开放标志

EA=1,CPU开放中断,

EA=0,CPU屏蔽所有的中断申请。

EA的作用是使中断允许形成多级控制。即各中断源首先受EA控制;其次还受各中断源自己的

中断允许控制位控制。

EADC : A/D转换中断允许位

EADC=1,允许A/D转换中断,

EADC=0,禁止A/D转换中断。

2.6 A/D转换典型应用线路

3.测试程序

3.1 中断方式

#include "stc15.h"
#include "intrins.h"
#include "delay.h"

#define uchar unsigned char
#define uint unsigned int

#define FOSC 11059200L          //系统频率
#define BAUD 9600               //串口波特率

void UatrInit();
void SendData(uchar dat);
void SendString(char *s);
void AdInit();


uchar num[10] = {'0','1','2','3','4','5','6','7','8','9'};
uint adc_result = 0;

void main()
{
	P1M0 = 0x02;
	P1M1 = 0x00;
	UatrInit();
	AdInit();
	EA = 1;    // CPU开放中断
	while (1);
}
// 初始化串口
void UatrInit()
{
	SCON = 0x50;                //8位可变波特率 串口工作模式1
	T2L = (65536 - (FOSC/4/BAUD));   //设置波特率重装值
	T2H = (65536 - (FOSC/4/BAUD))>>8;
	AUXR = 0x14;                //T2为1T模式, 并启动定时器2
	AUXR |= 0x01;               //选择定时器2为串口1的波特率发生器
	ES = 1;                     //使能串口1中断
}
// 初始化ADC
void AdInit()
{
	P1ASF = 0x01;  // P1.0作为模拟功能A/D使用
	ADC_RES = 0;
	ADC_RESL = 0;   // 结果寄存器清零
	ADC_CONTR = 0x88;  // 打开ADC的电源  540个周期转换一次 选择P1.0作为A/D输入来用  
	delayus(20);
	EADC = 1;  // 允许A/D转换中断
}
// ADC中断服务函数
void adc_isr() interrupt 5
{
	ADC_CONTR &= !0x10; // 清除ADC中断标志
	adc_result = ADC_RES*4 + ADC_RESL;    // 获取ADC结果,高2位在前
	SendData(num[adc_result/1000]);       // 千
	SendData(num[adc_result%1000/100]);   // 百
	SendData(num[adc_result%100/10]);     // 十
	SendData(num[adc_result%10]);         // 个

//	SendData(ADC_RES);
//	SendData(ADC_RESL);

	SendString("\\r\\n");  // 换行
	ADC_CONTR = 0x88;    // 开始ADC 转换
	delayms(2000);
}
// 发送串口数据
void SendData(uchar dat)
{
  SBUF = dat;
	while(TI == 0);
	TI = 0;
}
// 发送字符串
void SendString(char *s)
{
    while (*s)                  //检测字符串结束标志
    {
        SendData(*s++);         //发送当前字符
    }
}
// 串口中断
void Uart() interrupt 4
{	
	// 接收中断标志位
	if (RI)
	{
		RI = 0;                 //清除RI位
		//  P0 = SBUF;              //P0显示串口数据
		SendString("HELLO\\r\\n");
	}
	// 发送中断标志位
	if (TI)
	{
		TI = 0;                 //清除TI位
		SendString("发送完成!\\r\\n");
	 }
}

3.2 查询方式

#include "stc15.h"
#include "intrins.h"
#include "delay.h"

#define uchar unsigned char
#define uint unsigned int

#define FOSC 11059200L          //系统频率
#define BAUD 9600               //串口波特率

void UatrInit();
void SendData(uchar dat);
void SendString(char *s);
void AdInit();
uint GetADCResult();

uchar num[10] = {'0','1','2','3','4','5','6','7','8','9'};
uint adc_result = 0;

void main()
{
	P1M0 = 0x02;
	P1M1 = 0x00;
	UatrInit();
	AdInit();
	EA = 1;    // CPU开放中断
	while (1)
	{
		adc_result = GetADCResult();
		SendData(num[adc_result/1000]);
		SendData(num[adc_result%1000/100]);
		SendData(num[adc_result%100/10]);
		SendData(num[adc_result%10]);
		SendString("\\r\\n");
		delayms(2000);
	}
}
// 初始化串口
void UatrInit()
{
	SCON = 0x50;                //8位可变波特率 串口工作模式1
	T2L = (65536 - (FOSC/4/BAUD));   //设置波特率重装值
	T2H = (65536 - (FOSC/4/BAUD))>>8;
	AUXR = 0x14;                //T2为1T模式, 并启动定时器2
	AUXR |= 0x01;               //选择定时器2为串口1的波特率发生器
	ES = 1;                     //使能串口1中断
}
// 初始化ADC
void AdInit()
{
	P1ASF = 0x01;  // P1.0作为模拟功能A/D使用
	ADC_RES = 0;
	ADC_RESL = 0;   // 结果寄存器清零
	ADC_CONTR = 0x88;  // 打开ADC的电源  540个周期转换一次 选择P1.0作为A/D输入来用  
	delayus(20);
// 	EADC = 1;  // 允许A/D转换中断
}
// 读取ADC结果
uint GetADCResult()
{
	ADC_CONTR = 0x88;
	_nop_();                        //等待4个NOP
	_nop_();
	_nop_();
	_nop_();
	while (!(ADC_CONTR & 0x10));//等待ADC转换完成
	ADC_CONTR &= ~0x10;         //Close ADC
	return ADC_RES*4 + ADC_RESL;                //返回ADC结果

}
// 发送串口数据
void SendData(uchar dat)
{
  SBUF = dat;
	while(TI == 0);
	TI = 0;
}
// 发送字符串
void SendString(char *s)
{
	while (*s)                  //检测字符串结束标志
	{
		SendData(*s++);         //发送当前字符
	}
}
// 串门中断
void Uart() interrupt 4
{	
	// 接收中断标志位
	if (RI)
	{
		RI = 0;                 //清除RI位
	}
	// 发送中断标志位
	if (TI)
	{
		TI = 0;                 //清除TI位
	 }
}

四、利用新增的ADC第9通道测量内部参考电压的测试程序

ADC的第9通道是用来测试内部BandGap参考电压的,由于内部BandGap参考电压很稳定,不会随芯片的工作电压的改变而变化,所以可以通过测量内部BandGap参考电压,然后通过ADC的值便可反推出VCC的电压,从而用户可以实现自己的低压检测功能。ADC的第9通道的测量方法:首先将P1ASF初始化为0,即关闭所有P1口的模拟功能然后通过正常的ADC转换的方法读取第0通道的值,即可通过ADC的第9通道读取当前内部BandGap参考电压值。用户实现自己的低压检测功能的实现方法:首先用户需要在VCC很精准的情况下(比如5.0V),测量出内部BandGap参考电压的ADC转换值(比如为BGV5),并将这个值保存到EEPROM中,然后在低压检测的代码中,在实际VCC变化后,测量出的内部BandGap参考电压的ADC转换值(比如为BGVx),最后通过计算公式: 实际VCC = 5.0V * BGV5 / BGVx,即可计算出实际的VCC电压值 ,需要注意的是,第一步的BGV5的基准测量一定要精确。

测试程序:

#include "stc15.h"
#include "intrins.h"
#include "delay.h"

#define uchar unsigned char
#define uint unsigned int

#define FOSC 11059200L          //系统频率
#define BAUD 9600               //串口波特率

void UatrInit();
void SendData(uchar dat);
void SendString(char *s);
void AdInit();
uint GetADCResult();


uchar num[] = {'0','1','2','3','4','5','6','7','8','9'};
uint adc_result = 0;

void main()
{
	P1M0 = 0x02;
	P1M1 = 0x00;
	UatrInit();
	AdInit();
	EA = 1;    // CPU开放中断
	while (1)
	{
		adc_result = GetADCResult();
		SendData(num[adc_result/1000]);       
		SendData(num[adc_result%1000/100]);
		SendData(num[adc_result%100/10]);
		SendData(num[adc_result%10]);
        // 串口输出的是0259
        // 实际内部电压  259÷1024*5 = 1.264V
        // 这个假设电源电压是5V,和下载工具显示的内部电压1245mV很接近了
		SendString("\\r\\n");
		delayms(2000);
	}
}
// 初始化串口
void UatrInit()
{
	SCON = 0x50;                //8位可变波特率 串口工作模式1
	T2L = (65536 - (FOSC/4/BAUD));   //设置波特率重装值
	T2H = (65536 - (FOSC/4/BAUD))>>8;
	AUXR = 0x14;                //T2为1T模式, 并启动定时器2
	AUXR |= 0x01;               //选择定时器2为串口1的波特率发生器
	ES = 1;                     //使能串口1中断
}
// 初始化ADC
void AdInit()
{
	P1ASF = 0x00;  // P1不作为模拟功能A/D使用
	ADC_RES = 0;
	ADC_RESL = 0;   // 结果寄存器清零
	ADC_CONTR = 0x88;  // 打开ADC的电源  540个周期转换一次 选择P1.0作为A/D输入来用  
	delayus(20);
// 	EADC = 1;  // 允许A/D转换中断
}
// 读取ADC结果
uint GetADCResult()
{
	ADC_CONTR = 0x88;
	_nop_();                        //等待4个NOP
	_nop_();
	_nop_();
	_nop_();
	while (!(ADC_CONTR & 0x10));  //等待ADC转换完成
	ADC_CONTR &= ~0x10;           //Close ADC
	return ADC_RES*4 + ADC_RESL;  //返回ADC结果
}
// 发送串口数据
void SendData(uchar dat)
{
  SBUF = dat;
	while(TI == 0);
	TI = 0;
}
// 发送字符串
void SendString(char *s)
{
	while (*s)                  //检测字符串结束标志
	{
		SendData(*s++);         //发送当前字符
	}
}
// 串口中断服务函数
void Uart() interrupt 4
{	
	// 接收中断标志位
	if (RI)
	{
		RI = 0;                 //清除RI位
		//  P0 = SBUF;              //P0显示串口数据
		SendString("HELLO\\r\\n");
	}
	// 发送中断标志位
	if (TI)
	{
		TI = 0;                 //清除TI位
//		SendString("发送完成!\\r\\n");
	 }
}

五、利用BandGap推算出电源电压

在上面的例子中,我们通过A/D转换的第九通道得到了电源电压的AD值。由于内部BandGap参考电压很稳定,不会随芯片的工作电压的改变而变化,所以可以通过两次测量和一次计算便可得到外部的精确电压。

计算公式:

电源电压 = BandGap(电压mV)÷BandGap(AD转换值)×1024

获取内部BandGap电压的程序

#include "stc15.h"
#include "intrins.h"
#include "delay.h"

#define uchar unsigned char
#define uint unsigned int

#define FOSC 11059200L          //系统频率
#define BAUD 9600               //串口波特率

#define ID_ADDR_RAM 0xef        //对于只有256字节RAM的MCU存放地址为0EFH

//注意:需要在下载代码时选择"在ID号前添加重要测试参数"选项,才可在程序中获取此参数
#define ID_ADDR_ROM 0x1ff7      //8K程序空间的MCU

void UatrInit();
void SendData(uchar dat);
void SendString(char *s);
void AdInit();
uint GetADCResult();

uchar num[] = {'0','1','2','3','4','5','6','7','8','9'};
uint adc_result = 0;
uint BandGap = 0;
uint Vcc = 0;

void main()
{
	uchar idata *iptr;
	uchar code *cptr;
	UatrInit();
	AdInit();
	EA = 1;    // CPU开放中断

	while (1)
	{	
		iptr = ID_ADDR_RAM;         //从RAM区读取BandGap电压值(单位:毫伏mV)  实际结果和STC-ISP软件读取的一样  1245mV
		BandGap =  *iptr++ * 256 + *iptr++;  // 1245mV
		adc_result = GetADCResult();         // 结果为  259
		Vcc = (double)BandGap/adc_result * 1024; // 得到电源电压 4.92V  实际使用万用表测试的也是这个数值
		SendData(num[Vcc/1000]);
		SendData(num[Vcc%1000/100]);
		SendData(num[Vcc%100/10]);
		SendData(num[Vcc%10]);
		SendString("\\r\\n");		delayms(2000);
	}
}
// 初始化串口
void UatrInit()
{
	SCON = 0x50;                //8位可变波特率 串口工作模式1
	T2L = (65536 - (FOSC/4/BAUD));   //设置波特率重装值
	T2H = (65536 - (FOSC/4/BAUD))>>8;
	AUXR = 0x14;                //T2为1T模式, 并启动定时器2
	AUXR |= 0x01;               //选择定时器2为串口1的波特率发生器
	ES = 1;                     //使能串口1中断
}
// 初始化ADC
void AdInit()
{
	P1ASF = 0x00;  // P1不作为模拟功能A/D使用
	ADC_RES = 0;
	ADC_RESL = 0;   // 结果寄存器清零
	ADC_CONTR = 0x88;  // 打开ADC的电源  540个周期转换一次 选择P1.0作为A/D输入来用  
	delayus(20);
// 	EADC = 1;  // 允许A/D转换中断
}
// 读取ADC结果 获取内部BandGap电压的AD转换值
uint GetADCResult()
{
	ADC_CONTR = 0x88;
	_nop_();                        //等待4个NOP
	_nop_();
	_nop_();
	_nop_();
	while (!(ADC_CONTR & 0x10));  //等待ADC转换完成
	ADC_CONTR &= ~0x10;           //Close ADC
	return ADC_RES*4 + ADC_RESL;  //返回ADC结果
}
// 发送串口数据
void SendData(uchar dat)
{
    SBUF = dat;
	while(TI == 0);
	TI = 0;
}
// 发送字符串
void SendString(char *s)
{
	while (*s)                  //检测字符串结束标志
	{
		SendData(*s++);         //发送当前字符
	}
}
// 串口服务函数
void Uart() interrupt 4
{	
	// 接收中断标志位
	if (RI)
	{
		RI = 0;                 //清除RI位
	}
	// 发送中断标志位
	if (TI)
	{
		TI = 0;                 //清除TI位
	 }
}

以上是关于10.STC15W408AS单片机A/D转换器的主要内容,如果未能解决你的问题,请参考以下文章

10.STC15W408AS单片机A/D转换器

10.STC15W408AS单片机A/D转换器

STC15W408AS单片机通过ADC端口采集模拟量并从串口打印数值

1.STC15W408AS单片机硬件资源

1.STC15W408AS单片机硬件资源

1.STC15W408AS单片机硬件资源