您的位置 首页 IOT

串行口通讯 单片机在数码管显现AD收集值

一程序功能单片机上电后等待从上位机串口发送来的命令(波特率为9600bps),同时在数码管的前三位以十进制显示AD采集来的数值。1)当收

一. 程序功用

单片机上电后等候从上位机串口发送来的指令(波特率为9600bps),一起在数码管的前三位以十进制显现 AD收集来的数值。
1)当收到以十六进制发送来的01后,向上位机发送字符串“Turn on ad!”,一起距离 一秒读取AD的值,然后把AD转化过来的数表明成实践电压值(浮点数),而且从串口发 送给上位机,方法如“The voltage is 3.398438V”发送周期也是一秒一次。
2)当收到02时,向上位机发送“Turn off ad!”并中止发送电压值
3)收到其他数时显现“Error!”
二. 程序源码
1)法1
#include
#include
#include
#define uchar unsigned char
#define uint unsigned int
sbit dula = P2 ^ 6; //恳求U1锁存器的锁存端
sbit wela = P2 ^ 7; //恳求U1锁存器的锁存端
sbit adwr = P3 ^ 6; //界说AD的WR端口
sbit adrd = P3 ^ 7; //界说AD的RD端口
uchar flag, flag_uart, flag_time, flag_on;
uchar a, i, t0_num, ad_val;
float ad_vo;
//数码管的编码数组
uchar code table[] = {
0x3f,0x06,0x5b,0x4f,
0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,
0x39,0x5e,0x79,0x71
};
void delayms(uint xms);
void init();
void display(uchar value);
uchar get_ad();
void main()
{
//初始化
init();
//为了节约IO端口,ADC0804的片选端CS衔接U2锁存器的Q7输出端
//即位选时,P0的最高位
//所以通过位选时对P0赋值置CSAD为0,选通后,今后不用在管
wela = 1;
P0 = 0x7f;
wela = 0;
while (1)
{
//若检测到flag_uart为1,阐明串口现已履行过串口中止服务程序, 即收到了数据
if (flag_uart == 1)
{
//手动将flag_uart清0,便利标志位检测
flag_uart = 0;
//检测到收到数据后,先将ES清0,原因是接下来要发送数据,若不封闭串口,
//发送完数据后,单片时机从头恳求串口中止flag_uart又为1,又再次发送,一向重复
ES = 0;
//由于下面switch中需求调用puts,printf,在单片机的puts,printf中需求对TI是否为1
//进行检测,便是需求等候TI为1才会将字符发送出去,不然一向等候下去
//所以这儿需求先将TI置为1
TI = 1;
//依据履行串口中止后获取的flag_on的值履行相应的动作
switch(flag_on)
{
case 0:
puts(“Turn on ad!\n”);
TR0 = 1; //敞开计数器0
break;
case 1:
printf(“Turn off ad!\n”);
TR0 = 0; //封闭计数器0
break;
case 2:
puts(“Error!\n”);
break;
}
//等候发送结束,由于发送结束后TI会由硬件清0
while (!TI);
//手动TI清0
TI = 0;
//从头敞开串口中止
ES = 1;
}
//通过flag_time判别时刻是否到1s, 然后去获取AD转化的值
if (flag_time == 1)
{
//手动将flag_time清0,便利下次检测
flag_time = 0;
//获取AD转化的值
ad_val = get_ad();
//获取浮点数表明的AD实践收集到的电压值
ad_vo = (float)ad_val*5.0/256.0;
//封闭串口中止
ES = 0;
//下面要调用printf,所以将TI置1, 原因见上面
TI = 1;
printf(“The voltage is %fV\n”, ad_vo);
//检测发送是否完结
while (!TI);
//手动TI清0
TI = 0;
//敞开串口中止
ES = 1;
}
//数码管上显现AD转化的值
display(ad_val);
}
}
void delayms(uint xms)
{
uint i, j;
for (i = xms; i > 0; i–)
for (j = 110; j > 0; j–)
;
}
void init()
{
//设定T1定时器作业方法2, T0定时器作业方法1
TMOD = 0x21;
//为T0定时器装入初值
TH0 = (65536 – 50000) / 256;
TL0 = (65536 – 50000) % 256;
//为T1定时器装入初值
TH1 = 0xfd;
TL1 = 0xfd;
//ET1 = 1; 这儿不需求敞开定时器1中止,由于定时器1作业在方法2,为8位主动重装方法,进入中止也无事可做
//发动T1定时器
TR1 = 1;
//敞开定时器0中止
ET0 = 1;
//发动定时器0
//TR0 = 1; TR0的初始化放在主函数的while中,便利检测到串口发送数据后的1s延时,即延时1s从串口发送完数据开端
//设定串口作业方法
//11位异步收发,含9位数据,波特率可变,且由定时器1溢出率操控
SM0 = 1;
SM1 = 1;
//容许串口中止
REN = 1;
//敞开总中止
EA = 1;
//敞开串口中止
ES = 1;
}
void display(uchar num)
{
uchar bai, shi, ge;
bai = num / 100;
shi = num % 100 / 10;
ge = num % 10;
dula = 1;
P0 = table[bai];
dula = 0;
P0 = 0xff;
wela = 1;
//为了节约IO端口,ADC0804的片选端CS衔接U2锁存器的Q7输出端
//即位选时,P0的最高位需求为0
P0 = 0x7e;
wela = 0;
delayms(5);
dula = 1;
P0 = table[shi];
dula = 0;
P0 = 0xff;
wela = 1;
P0 = 0x7d;
wela = 0;
delayms(5);
dula = 1;
P0 = table[ge];
dula = 0;
P0 = 0xff;
wela = 1;
P0 = 0x7b;
wela = 0;
delayms(5);
}
uchar get_ad()
{
uchar adval;
adwr = 1;
_nop_();
adwr = 0; //发动AD转化
_nop_();
adwr = 1;
P1 = 0xff;
adrd = 1;
_nop_();
adrd = 0; //AD读使能
_nop_();
adval = P1; //AD数据读赋给P1
adrd = 1;
return adval;
}
void timer0() interrupt 1
{
TH0 = (65536 – 50000) / 256;
TL0 = (65536 – 50000) % 256;
t0_num++;
if (t0_num == 20)
{
t0_num = 0;
flag_time = 1; //flag_time置1,便于主程序检测是否到1s
}
}
void ser() interrupt 4
{
//RI为接纳中止标志位, 在方法0时, 当串行接纳第8位数据结束时, 或在其他方法, 串行接纳中止位的
//中心时, 由内部硬件使RI置1, 向CPU宣布中止恳求, 也有必要在中止服务程序中, 用软件将其清0,撤销
//此中止恳求, 以便利下一次中止恳求检测, 即这样才干发生下一次中止.
//这儿RI清0, 由于程序已然发生了串口中止, 肯定是收到或发送了数据, 在开端时没有发送任何数据
//那必定是收到了数据, 此刻RI会被硬件置1, 所以进入串口中止服务程序后有必要由软件清0, 这样才干
//发生下一次中止.
RI = 0;
//将SBUF中的数据读走给a, 这是此中止服务程序最重要的意图
a = SBUF;
//将串口中止标志位设置为1,便于主程序检测
flag_uart = 1;
if (a == 1)
flag_on = 0;
else if (a == 2)
flag_on = 1;
else
flag_on = 2;
}
2)法2
#include
#include
//中止答应寄存器IE,字节地址位0xA8
//单片机复位时, IE中一切位被清0
sfr IE = 0xA8;
//EA为大局中止答应位
//EA = 1时翻开大局中止操控,在这样条件下,由各个中止操控位翻开或封闭相应的中止
//EA = 0时封闭一切中止
sbit EA = IE^7;
//ET2为定时器/计数器2中止答应位
//ET2 = 1时翻开T2中止, ET2 = 0时封闭T2中止
sbit ET2 = IE^5; //8052 only
//ES为串行口中止答应位
//ES = 1时翻开串行口中止, ES = 0时封闭串行口中止
sbit ES = IE^4;
//ET1为定时器/计数器1中止答应位
//ET1 = 1时翻开T1中止, ET1 = 0时封闭T1中止
sbit ET1 = IE^3;
//EX1为外部中止1中止答应位
//EX1 = 1时翻开外部中止1中止, EX1 = 0时封闭外部中止1中止
sbit EX1 = IE^2;
//ET0为定时器/计数器0中止答应位
//ET0 = 1时翻开T0中止, ET0 = 1时封闭T0中止
sbit ET0 = IE^1;
//EX0为外部中止0中止答应位
//EX0 = 1时翻开外部中止0中止, EX0 = 0时封闭外部中止0中止
sbit EX0 = IE^0;
//SBUF为串行数据缓冲寄存器
//51单片机中含有两个SBUF,其间一个为发送缓冲寄存器,另一个为接纳缓冲寄存器
//这两个寄存器共有一个地址0x99, 但物理上是两个独立的寄存器,有指令操作决议拜访哪个寄存器
//履行写指令时, 拜访串行发送寄存器, 履行读指令时, 拜访串行接纳寄存器
//接纳器具有双缓冲结构, 即在从接纳寄存器中读出前一个已收到的字节之前, 便能承受第二个字节
//假如第二个字节现已接纳结束,第一个字节还没有读出,则丢掉其间一个字节
//关于发送器,数据由CPU操控和发送,所以不需求考虑
sfr SBUF = 0x99;
//SCON为串行口操控寄存器
//SCON可位寻址, 即能够拜访它的详细某一位
//SCON用以设定串行口的作业方法, 接纳/发送操控以及设置状况标志
//单片机复位时SCON悉数被清0
sfr SCON = 0x98;
//SM0,SM1为作业方法挑选位, 串行口有4中作业方法, 由SM0,SM1设定
//SM0=0,SM1=0为方法0,即同步移位寄存器方法,用于扩展I/O口
//SM0=0,SM1=1为方法1,即10位异步收发,含8位数据,波特率可变,且由定时器1的溢出率操控
//SM0=1,SM1=0为方法2,即11位异步收发,含9位数据,波特率固定
//SM0=1,SM1=1为方法3,即11位异步收发,含9位数据,波特率可变,且由定时器1的溢出率操控
sbit SM0 = SCON^7;
sbit SM1 = SCON^6;
//SM2为多机通讯操控位,首要用于方法2和方法3
//当接纳机的SM2=1时,能够运用收到的RB8来操控是否激活RI,即RB8=0时不激活RI,收到的信息丢掉
//RB8=1时收到的数据进入SBUF,并激活RI,进而在中止服务中将数据从SBUF读走
//当SM0=0时,不论收到的RB8是0仍是1,均能够使收到的数据进入SBUF,并激活RI,即此刻RB8不具有操控RI激活功用
//通过操控SM2,能够完结多机通讯.
//在方法0时,SM2有必要是0
//在方法1时,若SM2=1,则只要接纳到有用中止位时,RI才置1
sbit SM2 = SCON^5;
//REN为答应串行接纳位
//REN=1时答应串行口接纳数据
//REN=0时制止串行口接纳数据
sbit REN = SCON^4;
//TB8为方法2,3中发送数据的第9位
//方法2或方法3中,时发送数据的第9位,能够用软件规则其效果,能够用作数据的奇偶校验位
//或在多机通讯中,作为地址帧/数据帧的标志位
//方法0和方法1时,该位未用
sbit TB8 = SCON^3;
//RB8为方法2,3中接纳数据的第9位
//方法2或方法3中,是接纳数据的第9位,可作为奇偶校验位或地址帧/数据帧的标志位
//方法1时,若SM2=0,则RB8是接纳到的中止位
sbit RB8 = SCON^2;
//TI为发送中止标志位
//方法0时,当串行发送第8位数据结束时,或在其他方法,串行发送中止位的开端时,
//由内部硬件使TI置1,向CPU宣布中止恳求,在中止服务程序中,有必要用软件将其清0,撤销此中止恳求
sbit TI = SCON^1;
//RI为接纳中止标志位
//方法0时,当串行接纳第8位数据结束时,或在其他方法,串行接纳中止位的中心时,
//由内部邮件使RI置1,向CPU宣布中止恳求,也有必要在中止服务程序中,用软件将其清0,撤销此中止恳求
sbit RI = SCON^0;
//TMOD为定时器/计数器作业方法寄存器
//字节地址位0x89,不能位寻址
//单片机复位时TMOD悉数被清0
//TMOD的高4位用于设置定时器1,低4位用于设置定时器0,
//其间凹凸4位均由GATE,C/T,M1,M0构成
//GATE为门操控位
//GATE=0,定时器/计数器发动与中止仅受TCON寄存器中TRX(X=0,1)来操控
//GATE=1,定时器/计数器发动与中止由TCON寄存器中TRX(X=0,1)和外部中止引脚(INT0或INT1)的电平一起操控
//C/T为定时器形式和计数器形式挑选位
//C/T=1为计数器形式,C/T=0为定时器形式
//M1M0为作业方法挑选位
//M1=0,M0=0为方法0,为13位定时器/计数器
//M1=0,M0=1为方法1,为16位定时器/计数器
//M1=1,M0=0为方法2,8位出值主动重装的8位定时器/计数器
//M1=1,M1=0为方法3,仅适用于T0,分红两个8位计数器,T1中止计数
sfr TMOD = 0x89;
//TCON为定时器/计数器操控寄存器
//TCON字节地址为88H,可位寻址
//TCON寄存器用来操控定时器的启,停,标志定时器溢出和中止
//单片机复位时TCON悉数被清0
//TCON包括的TF1,TR1,TF0,TR0用于定时器/计数器
//TCON包括的IE1,IT1,IE0,IT0用于外部中止
sfr TCON = 0x88;
//TF1为定时器1溢出标志位
//当计数器1计满溢出时,由硬件使TF1置1,而且恳求中止,进入中止服务程序后,由硬件主动清0
//假如运用定时器的中止,那么该位彻底不用人为去操作
//假如运用软件查询的方法,查询该位为1后,就需求用软件清0
sbit TF1 = TCON^7;
//TR1为定时器1运转操控位
//由软件清0封闭定时器1,当GATE=1,且INT1为高电平时,TR1置1发动定时器1
//当GATE=0时,TR1置1发动定时器1
sbit TR1 = TCON^6;
//TF0为定时器0溢出标志,功用及操作方法同TF1
sbit TF0 = TCON^5;
//TR0为定时器0运转操控位,其功用及操作方法同TR1
sbit TR0 = TCON^4;
//IE1为外部中止1恳求标志
//IT1=0时,为电平触发方法,每个机器周期的S5P2采样INT1引脚,
//若INT1脚为低电平,则置1,不然IE1清0
//IT1=1时,INT1位跳变沿触发方法,当第一个机器周期采样到INT1为低电平时,则IE1置1
//IE1=1时,表明外部中止1正在向CPU恳求中止,当CPU呼应中止,转向中止服务程序,该位由硬件清0
sbit IE1 = TCON^3;
//IT1为外部中止1触发方法挑选位
//IT1=0,电平触发方法,引脚INT1上低电平有用
//IT1=1,跳变沿触发方法,引脚INT1上的电平从高到低的负跳变有用
sbit IT1 = TCON^2;
//IE0为外部中止0恳求标志,功用及操作同IE1
sbit IE0 = TCON^1;
//IT0为外部中止0触发方法挑选位,功用及操作同IT1
sbit IT0 = TCON^0;
//定时器1初值高8位
sfr TH1 = 0x8D;
//定时器1初值低8位
sfr TL1 = 0x8B;
//定时器0初值高8位
sfr TH0 = 0x8C;
//定时器0初值低8位
sfr TL0 = 0x8A;
//声明单片机P0口的地址
sfr P0 = 0x80;
//声明单片机P1口的地址
sfr P1 = 0x90;
//声明单片机P2口的地址
sfr P2 = 0xA0;
//声明单片机P3口的地址
sfr P3 = 0xB0;
#define uchar unsigned char
#define uint unsigned int
sbit dula = P2 ^ 6; //恳求U1锁存器的锁存端
sbit wela = P2 ^ 7; //恳求U1锁存器的锁存端
sbit adwr = P3 ^ 6; //界说AD的WR端口
sbit adrd = P3 ^ 7; //界说AD的RD端口
uchar flag, flag_uart, flag_time, flag_on;
uchar a, i, t0_num, ad_val;
float ad_vo;
//数码管的编码数组
uchar code table[] = {
0x3f,0x06,0x5b,0x4f,
0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,
0x39,0x5e,0x79,0x71
};
void delayms(uint xms);
void init();
void display(uchar value);
uchar get_ad();
void main()
{
//初始化
init();
//为了节约IO端口,ADC0804的片选端CS衔接U2锁存器的Q7输出端
//即位选时,P0的最高位
//所以通过位选时对P0赋值置CSAD为0,选通后,今后不用在管
wela = 1;
P0 = 0x7f;
wela = 0;
while (1)
{
//若检测到flag_uart为1,阐明串口现已履行过串口中止服务程序, 即收到了数据
if (flag_uart == 1)
{
//手动将flag_uart清0,便利标志位检测
flag_uart = 0;
//检测到收到数据后,先将ES清0,原因是接下来要发送数据,若不封闭串口,
//发送完数据后,单片时机从头恳求串口中止flag_uart又为1,又再次发送,一向重复
ES = 0;
//由于下面switch中需求调用puts,printf,在单片机的puts,printf中需求对TI是否为1
//进行检测,便是需求等候TI为1才会将字符发送出去,不然一向等候下去
//所以这儿需求先将TI置为1
TI = 1;
//依据履行串口中止后获取的flag_on的值履行相应的动作
switch(flag_on)
{
case 0:
puts(“Turn on ad!\n”);
TR0 = 1; //敞开计数器0
break;
case 1:
printf(“Turn off ad!\n”);
TR0 = 0; //封闭计数器0
break;
case 2:
puts(“Error!\n”);
break;
}
//等候发送结束,由于发送结束后TI会由硬件清0
while (!TI);
//手动TI清0
TI = 0;
//从头敞开串口中止
ES = 1;
}
//通过flag_time判别时刻是否到1s, 然后去获取AD转化的值
if (flag_time == 1)
{
//手动将flag_time清0,便利下次检测
flag_time = 0;
//获取AD转化的值
ad_val = get_ad();
//获取浮点数表明的AD实践收集到的电压值
ad_vo = (float)ad_val*5.0/256.0;
//封闭串口中止
ES = 0;
//下面要调用printf,所以将TI置1, 原因见上面
TI = 1;
printf(“The voltage is %fV\n”, ad_vo);
//检测发送是否完结
while (!TI);
//手动TI清0
TI = 0;
//敞开串口中止
ES = 1;
}
//数码管上显现AD转化的值
display(ad_val);
}
}
void delayms(uint xms)
{
uint i, j;
for (i = xms; i > 0; i–)
for (j = 110; j > 0; j–)
;
}
void init()
{
//设定T1定时器作业方法2, T0定时器作业方法1
TMOD = 0x21;
//为T0定时器装入初值
TH0 = (65536 – 50000) / 256;
TL0 = (65536 – 50000) % 256;
//为T1定时器装入初值
TH1 = 0xfd;
TL1 = 0xfd;
//ET1 = 1; 这儿不需求敞开定时器1中止,由于定时器1作业在方法2,为8位主动重装方法,进入中止也无事可做
//发动T1定时器
TR1 = 1;
//敞开定时器0中止
ET0 = 1;
//发动定时器0
//TR0 = 1; TR0的初始化放在主函数的while中,便利检测到串口发送数据后的1s延时,即延时1s从串口发送完数据开端
//设定串口作业方法
//11位异步收发,含9位数据,波特率可变,且由定时器1溢出率操控
SM0 = 1;
SM1 = 1;
//容许串口中止
REN = 1;
//敞开总中止
EA = 1;
//敞开串口中止
ES = 1;
}
void display(uchar num)
{
uchar bai, shi, ge;
bai = num / 100;
shi = num % 100 / 10;
ge = num % 10;
dula = 1;
P0 = table[bai];
dula = 0;
P0 = 0xff;
wela = 1;
//为了节约IO端口,ADC0804的片选端CS衔接U2锁存器的Q7输出端
//即位选时,P0的最高位需求为0
P0 = 0x7e;
wela = 0;
delayms(5);
dula = 1;
P0 = table[shi];
dula = 0;
P0 = 0xff;
wela = 1;
P0 = 0x7d;
wela = 0;
delayms(5);
dula = 1;
P0 = table[ge];
dula = 0;
P0 = 0xff;
wela = 1;
P0 = 0x7b;
wela = 0;
delayms(5);
}
uchar get_ad()
{
uchar adval;
//CS在主函数现已为低电平,/WR初始化为高电平,通过至少tw时刻后, /WR拉高
//随后A/D转化器被发动,而且通过1~8个A/D时钟周期和内部Tc时刻后,模数转化完结转化
//转化成果存入数据锁存器,一起INTR(其为AD转化结束信号)主动变成低电平,告诉单片机本次转化结束
adwr = 1;
_nop_();
adwr = 0; //发动AD转化
_nop_();
adwr = 1;
//读取P1口之前先给其写全1
P1 = 0xff;
//INTR为低电平后,将/CS置低,初始化/RD为1,再将/RD置低(主函数现已置低),
//在/RD至少通过tACC时刻后,数字输出口上的数据到达安稳状况,此刻直接读取数字输出端口
//数据便可得到转化后的数字信号,读走数据后,马大将/RD拉高,
//RD置低tR1时刻后,/INTR主动拉高,
adrd = 1;
_nop_();
adrd = 0; //AD读使能
_nop_();
adval = P1; //AD数据读赋给P1
adrd = 1;
return adval;
}
void timer0() interrupt 1
{
TH0 = (65536 – 50000) / 256;
TL0 = (65536 – 50000) % 256;
t0_num++;
if (t0_num == 20)
{
t0_num = 0;
flag_time = 1; //flag_time置1,便于主程序检测是否到1s
}
}
void ser() interrupt 4
{
//RI为接纳中止标志位, 在方法0时, 当串行接纳第8位数据结束时, 或在其他方法, 串行接纳中止位的
//中心时, 由内部硬件使RI置1, 向CPU宣布中止恳求, 也有必要在中止服务程序中, 用软件将其清0,撤销
//此中止恳求, 以便利下一次中止恳求检测, 即这样才干发生下一次中止.
//这儿RI清0, 由于程序已然发生了串口中止, 肯定是收到或发送了数据, 在开端时没有发送任何数据
//那必定是收到了数据, 此刻RI会被硬件置1, 所以进入串口中止服务程序后有必要由软件清0, 这样才干
//发生下一次中止.
RI = 0;
//将SBUF中的数据读走给a, 这是此中止服务程序最重要的意图
a = SBUF;
//将串口中止标志位设置为1,便于主程序检测
flag_uart = 1;
if (a == 1)
flag_on = 0;
else if (a == 2)
flag_on = 1;
else
flag_on = 2;
}
三. 程序小结
1) 串口初始化中先设定串口形式, 再容许串口接纳,即次序是SM0 = 0;SM1 = 1;REN=1;(这三位都在串口操控寄存器SCON里)
这样做的原因是单片机刚上电时SCON被清0,由于串口方法为方法0,串行口为同步移位寄存器的输入输出方法,当履行完REN为1的
句子后,直接从RXD接纳数据,不论衔接的体系有无发送数据.把REN放在形式设置的后边能够防止串口形式还未设置下就读取数据.
2) 调用printf()或许puts()前需求手动将TI置1, 由于这两个函数需求对TI进行检测, 以代表是否发送结束.
3) 调用printf()或许puts()前还需求手动封闭串口中止(ES = 0).如不封闭串口中止,每发送一个字节,程序就会恳求加入串口中止,然后导致程序犯错.
4) ad_val * 5.0/256.0能够得到浮点数表明的AD实践收集到的电压规范值.
5) 假如要程序承受十六进制的01或许字符格局的1, 那么需求将串口中止子程序中的if(a == 1)改成if(a == 1 || a == 1),
02, 03相似.

声明:本文内容来自网络转载或用户投稿,文章版权归原作者和原出处所有。文中观点,不代表本站立场。若有侵权请联系本站删除(kf@86ic.com)https://www.86ic.net/yingyong/iot/262276.html

为您推荐

联系我们

联系我们

在线咨询: QQ交谈

邮箱: kf@86ic.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部