STC15W408AS单片机A/D转换器的结构和相关寄存器

控制/MCU

1814人已加入

描述

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

一、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为单片机实际工作电压,用单片机工作电压作为模拟参考电压。

二、与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.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位
   }
}
打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分