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,可做温度检测、电池电压检测、按键扫描、频谱检测等。
一、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口模拟功能控制寄存器p1asfstc15系列单片机的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_contradc_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转换结果调整寄存器位——adrjadc转换结果调整寄存器位——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 中断允许寄存器ieie : 中断允许寄存器 (可位寻址)
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中断}// 初始化adcvoid 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中断}// 初始化adcvoid 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中断}// 初始化adcvoid 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程序空间的mcuvoid 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中断}// 初始化adcvoid 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位 }}

好太太推出家居智慧安防系统,智能锁ID设计
3G与未来移动通信射频功放行业研究报告
新能源汽车市场三大主流趋势分析 未来竞争的焦点是什么
变压器中的谐振是如何发生的?变压器如何防止谐振?
研华科技推出MIO-5350 3.5寸无风扇单板电脑,丰富的I/O接口 & MI/O扩展接口
STC15W408AS单片机A/D转换器的结构和相关寄存器
iPhone8外观已确定,大有改变
东方闪光----隐形切割技术简介
红外传感器有哪些优势?
投影仪什么品牌好?明基这款办公投影仪值得选
诺基亚8运存6G骁龙835配2K屏256G扩存 京东预售3188元 2月26日MWC见
英飞凌率先提出工作栅极电压区域的建议
中国或将在2020年前成为亚洲最大的智能家居市场
OPPO R17/R17 Pro正式发布,vivo的也快来了
零点漂移是什么意思_零点漂移怎么克服
什么是SCDMA无线农话
中国的5G是“扶不起的”吗?
触摸屏控制厂商:深圳纳芯威科技有限公司简介
医疗器械灭菌方法和信息
魅族pro7什么时候上市?魅族pro7最新消息:苹果服气!魅族PRO 7真机曝光:后壳设计太意外!