第9章.STC12C5A60S2系列单片机的A/D转换器9.1 A/D转换器的结构
STC12C5A60AD/S2系列带A/D转换的单片机的A/D转换口在P1口( (P1.7-P1.0),有8路10位高速A/D转换器,速度可达到250KHz(25万次/秒)。8路电压输入型A/D,可做温度检测、电池电压检测、按键扫描、频谱检测等。上电复位后P1口为弱上拉型I/O口,用户可以通过软件设置将8路中的任何一路设置为A/D转换,不需作为A/D使用的口可继续作为I/O口使用。
STC12C5A60S2系列单片机ADC(A/D转换器)的结构如下图所示:
当AUXR.1/ADRJ = 0时,A/D转换结果寄存器格式如下:
当AUXR.1/ADRJ = 1时,A/D转换结果寄存器格式如下:
STC12C5A60S2系列单片机ADC由多路选择开关、比较器、逐次比较寄存器、10位DAC、转换结果寄存器(ADC_RES和ADC_RESL)以及ADC_CONTR构成。
STC12C5A60S2系列单片机的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位。
当ADRJ=0时,如果取10位结果,则按下面公式计算:
当ADRJ=0时,如果取8位结果,按下面公式计算:
当ADRJ=1时,如果取10位结果,则按下面公式计算:
式中,Vin为模拟输入通道输入电压,Vcc为单片机实际工作电压,用单片机工作电压作为模拟参考电压。
9.2 与A/D转换相关的寄存器
与STC12C5A60S2系列单片机A/D转换相关的寄存器列于下表所示。
1.P1口模拟功能控制寄存器P1ASF
STC12C5A60S2系列单片机的A/D转换通道与P1口(P1.7-P1.0)复用,上电复位后P1口为弱上拉型I/O口,用户可以通过软件设置将8路中的任何一路设置为A/D转换,不需作为A/D使用的P1口可继续作为I/O口使用(建议只作为输入)。需作为A/D使用的口需先
将P1ASF特殊功能寄存器中的相应位置为‘1’,将相应的口设置为模拟功能。P1ASF寄存器的格式如下:
P1ASF : P1口模拟功能控制寄存器(该寄存器是只写寄存器,读无效)
当P1口中的相应位作为A/D使用时,要将P1ASF中的相应位置1。
2.ADC控制寄存器ADC_CONTR
ADC_CONTR寄存器的格式如下:
ADC_CONTR : ADC控制寄存器
对ADC_CONTR寄存器进行操作,建议直接用MOV赋值语句,不要用‘与’和‘或’语句。
ADC_POWER:ADC电源控制位。
0:关闭A/D转换器电源;
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:模数转换器转换速度控制位
STC12C5A60S2系列单片机的A/D转换模块说使用的时钟是内部R/C振荡器所产生的
系统时钟,不使用时钟分频寄存器CLK_DIV对系统时钟分频后所产生的供给CPU工作所使用的时钟。
好处:
这样可以让ADC用较高的频率工作,提高A/D的转换速度。
这样可以让CPU用较低的频率工作,降低系统的功耗。
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套时钟,所以,设置ADC_CONTR控制寄存器后,要个4个空操作延时才可以正确读到ADC_CONTR寄存器的值,原因是设置ADC_CONTR控制寄存器的语句执行后,要经过4个CPU时钟的延时,其值才能够保证被设置进ADC_CONTR控制寄存器。
MOV ADC_CONTR, #DA TA
NOP
NOP
NOP
NOP
MOV A, ADC_CONTR
;经过4个时钟延时后,才能够正确读到ADC_CONTR控制寄存器的值3.A/D转换结果寄存器ADC_RES、ADC_RESL
特殊功能寄存器ADC_RES和ADC_RESL寄存器用于保存A/D转换结果,其格式如下:
AUXR1寄存器的ADRJ位是A/D转换结果寄存器(ADC_RES,ADC_RESL)的数据格式调整控制位。
当ADRJ=0时,10位A/D转换结果的高8位存放在ADC_RES中,低2位存放在ADC_RESL的低2位中。
此时,如果用户需要完整10位结果,按下面公式计算:
如果用户只需要8位结果,按下面公式计算:
式中,Vin为模拟输入通道输入电压,Vcc为单片机实际工作电压,用单片机工作电压作为模拟参考电压。
当ADRJ=1时,10位A/D转换结果的高2位存放在ADC_RES的低2位中,低8位存放在ADC_RESL中。
此时,如果用户需取完整10位结果,按下面公式计算:
式中,Vin为模拟输入通道输入电压,Vcc为单片机实际工作电压,用单片机工作电压作为模拟参考电压。
4.与A/D中断有关的寄存器
IE : 中断允许寄存器(可位寻址)
EA : CPU的中断开放标志,EA=1,CPU开放中断,EA=0,CPU屏蔽所有的中断申
请。EA的作用是使中断允许形成多级控制。即各中断源首先受EA控制;其次还受各中断源自己的中断允许控制位控制。
EADC : A/D转换中断允许位。
EADC=1,允许A/D转换中断;
EADC=0,禁止A/D转换中断。
如果要允许A/D转换中断则需要将相应的控制位置1:
1、将EADC置1,允许ADC中断,这是ADC中断的中断控制位。
2、将EA置1,打开单片机总中断控制位,此位不打开,也是无法产生ADC中断的A/D中断服务程序中要用软件清A/D中断请求标志位ADC_FLAG(也是A/D转换结束标志位)。
IPH : 中断优先级控制寄存器高 (不可位寻址)
IP :中断优先级控制寄存器低(可位寻址)
PADCH, PADC: A/D转换中断优先级控制位。
当PADCH=0且PADC=0时,A/D转换中断为最低优先级中断(优先级0)
当PADCH=0且PADC=1时,A/D转换中断为较低优先级中断(优先级1)
当PADCH=1且PADC=0时,A/D转换中断为较高优先级中断(优先级2)
当PADCH=1且PADC=1时,A/D转换中断为最高优先级中断(优先级3)
9.5.A/D转换模块的参考电压源
STC12C5A60S2系列单片机的参考电压源是输入工作电压Vcc,所以一般不用外接参考电压源。如7805的输出电压是5V,但实际电压可能是4.88V到4.96V,用户需要精度比较高的话,可在出厂时将实际测出的工作电压值记录在单片机内部的EEPROM里面,以供计算。
如果有些用户的Vcc不固定,如电池供电,电池电压在5.3V-4.2V之间漂移,则Vcc 不固定,就需要在8路A/D转换的一个通道外接一个稳定的参考电压源,来计算出此时的工作电压Vcc,再计算出其他几路A/D转换通道的电压。如可在ADC转换通道的第七通道外接一个1.25V(或1V,或...)的基准参考电压源,由此求出此时的工作电压Vcc,再计算出其它几路A/D转换通道的电压(理论值据是短时间之内,Vcc不变)。
9.6.A/D转换测试程序
1. C程序(中断方式):
/*------------------------------------------------------------------------------------*/
/* --- STC MCU International Limited ---------------------------------------*/
/* --- 演示STC 1T 系列单片机A/D转换功能-----------------------------*/
/* --- Mobile: (86)139******** -----------------------------------------------*/
/* --- Fax: 86-755-82944243 ---------------------------------------------------*/
/* --- Tel: 86-755-82948412 ----------------------------------------------------*/
/* --- Web: https://www.wendangku.net/doc/ca18122491.html, -----------------------------------------------*/
/* 如果要在程序中使用或在文章中引用该程序,-------------------- ---*/
/* 请在程序中或文章中注明使用了宏晶科技的资料及程序-----------*/
/*-------------------------------------------------------------------------------------*/
#include "reg51.h"
#include "intrins.h"
#define FOSC 18432000L
#define BAUD 9600
typedef unsigned char BYTE;
typedef unsigned int WORD;
/*Declare SFR associated with the ADC */
sfr ADC_CONTR = 0xBC; //ADC control register
sfr ADC_RES = 0xBD; //ADC hight 8-bit result register
sfr ADC_LOW2 = 0xBE; //ADC low 2-bit result register
sfr P1ASF = 0x9D; //P1 secondary function control regis /*Define ADC operation const for ADC_CONTR*/
#define ADC_POWER 0x80 //ADC power control bit
#define ADC_FLAG 0x10 //ADC complete flag
#define ADC_START 0x08 //ADC start control bit
#define ADC_SPEEDLL 0x00 //540 clocks
#define ADC_SPEEDL 0x20 //360 clocks
#define ADC_SPEEDH 0x40 //180 clocks
#define ADC_SPEEDHH 0x60 //90 clocks
void InitUart();
void SendData(BYTE dat);
void Delay(WORD n);
void InitADC();
BYTE ch = 0; //ADC channel NO.
void main()
{
InitUart(); //Init UART, use to show ADC result
InitADC(); //Init ADC sfr
IE = 0xa0; //Enable ADC interrupt and Open master interrupt switch
//Start A/D conversion
while (1);
}
/*----------------------------
ADC interrupt service routine
----------------------------*/
void adc_isr() interrupt 5 using 1
{
ADC_CONTR &= !ADC_FLAG; //Clear ADC interrupt flag
SendData(ch); //Show Channel NO.
SendData(ADC_RES); //Get ADC high 8-bit result and Send to
UART
//if you want show 10-bit result, uncomment next line
// SendData(ADC_LOW2); //Show ADC low 2-bit result
if (++ch > 7) ch = 0; //switch to next channel
ADC_CONTR = ADC_POWER | ADC_SPEEDLL | ADC_START | ch;
}
/*----------------------------
Initial ADC sfr
----------------------------*/
void InitADC( )
{
P1ASF = 0xff; //Set all P1 as analog input port
ADC_RES = 0; //Clear previous result
ADC_CONTR = ADC_POWER | ADC_SPEEDLL | ADC_START | ch;
Delay(2); //ADC power-on delay and Start A/D conversion
}
/*----------------------------
Initial UART
----------------------------*/
void InitUart()
{
SCON = 0x5a; //8 bit data ,no parity bit
TMOD = 0x20; //T1 as 8-bit auto reload
TH1 = TL1 = -(FOSC/12/32/BAUD); //Set Uart baudrate
TR1 = 1; //T1 start running
/*----------------------------
Send one byte data to PC
Input: dat (UART data)
Output:-
----------------------------*/
void SendData(BYTE dat)
{
while (!TI); //Wait for the previous data is sent
TI = 0; //Clear TI flag
SBUF = dat; //Send current data
}
/*----------------------------
Software delay function
----------------------------*/
void Delay(WORD n)
{
WORD x;
while (n--)
{
x = 5000;
while (x--);
}
}
2、C程序(查询方式):
/*------------------------------------------------------------------------------------*/
/* --- STC MCU International Limited ---------------------------------------*/
/* --- 演示STC 1T 系列单片机A/D转换功能------------------------------*/
/* --- Mobile: (86)139******** -----------------------------------------------*/
/* --- Fax: 86-755-82944243 ---------------------------------------------------*/
/* --- Tel: 86-755-82948412 ----------------------------------------------------*/
/* --- Web: https://www.wendangku.net/doc/ca18122491.html, -----------------------------------------------*/
/* 如果要在程序中使用或在文章中引用该程序,-------------------- ---*/
/* 请在程序中或文章中注明使用了宏晶科技的资料及程序-----------*/
/*-------------------------------------------------------------------------------------*/
#include "reg51.h"
#include "intrins.h"
#define FOSC 18432000L
#define BAUD 9600
typedef unsigned char BYTE;
typedef unsigned int WORD;
/*Declare SFR associated with the ADC */
sfr ADC_CONTR = 0xBC; //ADC control register
sfr ADC_RES = 0xBD; //ADC high 8-bit result register
sfr ADC_LOW2 = 0xBE; //ADC low 2-bit result register
sfr P1ASF = 0x9D; //P1 secondary function control register /*Define ADC operation const for ADC_CONTR*/
#define ADC_POWER 0x80 //ADC power control bit
#define ADC_FLAG 0x10 //ADC complete flag
#define ADC_START 0x08 //ADC start control bit
#define ADC_SPEEDLL 0x00 //540 clocks
#define ADC_SPEEDL 0x20 //360 clocks
#define ADC_SPEEDH 0x40 //180 clocks
#define ADC_SPEEDHH 0x60 //90 clocks
void InitUart();
void InitADC();
void SendData(BYTE dat);
BYTE GetADCResult(BYTE ch);
void Delay(WORD n);
void ShowResult(BYTE ch);
void main()
{
InitUart(); //Init UART, use to show ADC result InitADC(); //Init ADC sfr
while (1)
{
ShowResult(0); //Show Channel0
ShowResult(1); //Show Channel1
ShowResult(2); //Show Channel2
ShowResult(3); //Show Channel3
ShowResult(4); //Show Channel4
ShowResult(5); //Show Channel5
ShowResult(6); //Show Channel6
ShowResult(7); //Show Channel7
}
}
/*----------------------------
Send ADC result to UART
----------------------------*/
void ShowResult(BYTE ch)
{
SendData(ch); //Show Channel NO.
SendData(GetADCResult(ch)); //Show ADC high 8-bit result
//if you want show 10-bit result, uncomment next line
// SendData(ADC_LOW2); //Show ADC low 2-bit result }
/*----------------------------
Get ADC result
----------------------------*/
BYTE GetADCResult(BYTE ch)
{
ADC_CONTR = ADC_POWER | ADC_SPEEDLL | ch | ADC_START;
_nop_(); //Must wait before inquiry
_nop_();
_nop_();
_nop_();
while (!(ADC_CONTR & ADC_FLAG)); //Wait complete flag
ADC_CONTR &= ~ADC_FLAG; //Close ADC
return ADC_RES; //Return ADC result
/*----------------------------
Initial UART
----------------------------*/
void InitUart()
{
SCON = 0x5a; //8 bit data ,no parity bit
TMOD = 0x20; //T1 as 8-bit auto reload
TH1 = TL1 = -(FOSC/12/32/BAUD); //Set Uart baudrate
TR1 = 1; //T1 start running
}
/*----------------------------
Initial ADC sfr
----------------------------*/
void InitADC()
{
P1ASF = 0xff; //Open 8 channels ADC function ADC_RES = 0; //Clear previous result
ADC_CONTR = ADC_POWER | ADC_SPEEDLL;
Delay(2); //ADC power-on and delay
}
/*----------------------------
Send one byte data to PC
Input: dat (UART data)
Output:-
----------------------------*/
void SendData(BYTE dat)
{
while (!TI); //Wait for the previous data is sent TI = 0; //Clear TI flag
SBUF = dat; //Send current data
}
/*----------------------------
Software delay function
----------------------------*/
void Delay(WORD n)
{
WORD x;
while (n--)
{
x = 5000;
while (x--);
}
}