串行口通信 上位机发送字符x,单片机返回上位机"I get x"

发布者:RadiantBreeze最新更新时间:2016-03-08 来源: eefocus关键字:串行口通信  上位机  单片机 手机看文章 扫描二维码
随时随地手机看文章
一.程序功能
在上位机上用串口调试助手发送一个字符X, 单片机收到字符后返回给上位机"I get X",串口波特率设为9600bps.
 
二.程序源码
1) 法1 (头文件法, 适用于老手)
#include
 
#define uchar unsigned char
#define uint unsigned int
 
uchar flag, a, i;
uchar code table[] = "I get ";   //定义一个字符类型编码数组
 
void init();
 
void main()
{
    init();   //初始化
    while (1)
    {
        //若检测到flag为1, 说明程序已经执行过串口中断服务程序, 即收到了数据. 
        if (flag == 1)
        {
             //手动将flag清0,方便标志位检测
            flag = 0;   
            //检测到flag为1后,即串口中断发生,先将ES清0, 原因是接下来要发送数据, 若不关闭串口中断, 发送完数据后,
            //单片机同样会申请串口中断,再次进入中断服务程序,flag又为1,又再此发送数据,一直重复
            //因此我们在发送数据前把串口中断关闭,等发送完数据再打开串口中断,这样可以安全地发送数据
            ES = 0;
            //通过for循环将前面数组中字符依次发送出去
            //由于数组table中共有6个字符,所以循环中以6作为结束个数
            for (i = 0; i < 6; i++)
            {
                //发送字符
                SBUF = table[i];
                //当向SBUF中写入一个数据后,使用while(!TI)等待发送完毕, 因为发送完毕后TI会由
                //硬件置1,然后退出while(!TI)
                while (!TI);
                //再将TI手动清0
                TI = 0;
            }
            //向SBUF中写入从中断服务程序读回来的数据
            SBUF = a;
            while (!TI); //同上
            TI = 0;      //同上
            ES = 1;      //重新开启串口中断
 
        }
    }
}
 
void init()
{
    TMOD = 0x20;  //设定定时器1为工作方式2: 8位初值自动重装的8位定时器(设定定时器1目的是其溢出率决定串口波特率)
    TH1 = 0xfd;   //定时器1装初值(波特率为9600)
    TL1 = 0xfd;   //定时器1装初值(波特率为9600),注意:TH1,TL1初值必须相同
    //ET1 = 1;  这里不需要开启定时器1中断,因为定时器1工作在方式2,为8位自动重装方式,进入中断也无事可做
    REN = 1;      //容许串行口接收数据
    SM0 = 0;      //设定串口工作方式1
    SM1 = 1;      //设定串口工作方式1(10位异步收发,波特率可变,且由定时器1的溢出率决定)
    EA = 1;       //开总中断
    ES = 1;       //开串口中断
    TR1 = 1;      //启动定时器1
}
 
//串口中断服务程序
void ser() interrupt 4
{
    //RI为接收中断标志位, 在方式0时, 当串行接收第8位数据结束时, 或在其他方式, 串行接收停止位的
    //中间时, 由内部硬件使RI置1, 向CPU发出中断申请, 也必须在中断服务程序中, 用软件将其清0,取消
    //此中断申请, 以方便下一次中断申请检测, 即这样才能产生下一次中断.
    //这里RI清0, 因为程序既然产生了串口中断, 肯定是收到或发送了数据, 在开始时没有发送任何数据
    //那必然是收到了数据, 此时RI会被硬件置1, 所以进入串口中断服务程序后必须由软件清0, 这样才能
    //产生下一次中断.
    RI = 0;
    //将SBUF中的数据读走给a, 这是此中断服务程序最重要的目的
    a = SBUF;
    //将标志位flag置1, 以方便在主程序中查询判断是否已经收到数据
    flag = 1;
}
 
2) 法2 (完整寄存器地址法)
//中断允许寄存器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;
 
#define uchar unsigned char
#define uint unsigned int
 
uchar flag, a, i;
uchar code table[] = "I get ";   //定义一个字符类型编码数组
 
void init();
 
void main()
{
    init();   //初始化
    while (1)
    {
        //若检测到flag为1, 说明程序已经执行过串口中断服务程序, 即收到了数据. 
        if (flag == 1)
        {
             //手动将flag清0,方便标志位检测
            flag = 0;   
            //检测到flag为1后,即串口中断发生,先将ES清0, 原因是接下来要发送数据, 若不关闭串口中断, 发送完数据后,
            //单片机同样会申请串口中断,再次进入中断服务程序,flag又为1,又再此发送数据,一直重复
            //因此我们在发送数据前把串口中断关闭,等发送完数据再打开串口中断,这样可以安全地发送数据
            ES = 0;
            //通过for循环将前面数组中字符依次发送出去
            //由于数组table中共有6个字符,所以循环中以6作为结束个数
            for (i = 0; i < 6; i++)
            {
                //发送字符
                SBUF = table[i];
                //当向SBUF中写入一个数据后,使用while(!TI)等待发送完毕, 因为发送完毕后TI会由
                //硬件置1,然后退出while(!TI)
                while (!TI);
                //再将TI手动清0
                TI = 0;
            }
            //向SBUF中写入从中断服务程序读回来的数据
            SBUF = a;
            while (!TI); //同上
            TI = 0;      //同上
            ES = 1;      //新开启串口中断
 
        }
    }
}
 
void init()
{
    TMOD = 0x20;  //设定定时器1为工作方式2: 8位初值自动重装的8位定时器(设定定时器1目的是其溢出率决定串口波特率)
    TH1 = 0xfd;   //定时器1装初值(波特率为9600)
    TL1 = 0xfd;   //定时器1装初值(波特率为9600),注意:TH1,TL1初值必须相同
    //ET1 = 1;  //这里不需要开启定时器1中断,因为定时器1工作在方式2,为8位自动重装方式,进入中断也无事可做
    REN = 1;      //容许串行口接收数据
    SM0 = 0;      //设定串口工作方式1
    SM1 = 1;      //设定串口工作方式1(10位异步收发,波特率可变,且由定时器1的溢出率决定)
    EA = 1;       //开总中断
    ES = 1;       //开串口中断
    TR1 = 1;      //启动定时器1
}
 
//串口中断服务程序
void ser() interrupt 4
{
    //RI为接收中断标志位, 在方式0时, 当串行接收第8位数据结束时, 或在其他方式, 串行接收停止位的
    //中间时, 由内部硬件使RI置1, 向CPU发出中断申请, 也必须在中断服务程序中, 用软件将其清0,取消
    //此中断申请, 以方便下一次中断申请检测, 即这样才能产生下一次中断.
    //这里RI清0, 因为程序既然产生了串口中断, 肯定是收到或发送了数据, 在开始时没有发送任何数据
    //那必然是收到了数据, 此时RI会被硬件置1, 所以进入串口中断服务程序后必须由软件清0, 这样才能
    //产生下一次中断.
    RI = 0;
    //将SBUF中的数据读走给a, 这是此中断服务程序最重要的目的
    a = SBUF;
    //将标志位flag置1, 以方便在主程序中查询判断是否已经收到数据
    flag = 1;
}
 
三.程序小结
1) 操作串行口之前,需要对单片机的一些与串口有关的特殊功能寄存器进行初始化设置, 主要是设置产生波特率
的定时器1, 串行口控制和中断控制,其主要步骤包括:
a. 确定T1的工作方式(变成TMOD寄存器)
b. 计算T1的初值, 装载TH1,TL1
c. 启动T1(编程TCON中的TR1位)
d. 确定串行口工作方式(编程SCON寄存器)
e. 串行口工作在中断方式时, 要进行中断设置(变成IE, IP寄存器)
 
2) 51单片机可以通过特殊功能寄存器SBUF对串行接收或串行发送寄存器进行访问, 两个寄存器共用一个地址
99H,但在物理上是两个独立的寄存器, 由指令操作决定访问哪一个寄存器. 执行写指令时, 访问串行发送寄存器, 执行读指令时, 访问串行接收寄存器, 接收器具有双缓冲结构, 即在从接收寄存器中读出前一个已收到
的字节之前, 便能接收第二个字节, 如果第二个字节已经接收完毕, 第一个字节还没有读出, 则丢失其中
一个字节. 对于发送器, 因为数据由CPU控制和发送的, 所以不需要考虑.
a=SBUF;代表单片机自动将串口接收寄存器中的数据取走给a.
SBUF=a;代表单片机将串口发送寄存器中的数据一位位从串口发送出去.
 
3) 此程序可以用在显示串口接收的数据上.
 
4) 程序几个变量的说明
a. 主函数main中while中ES=0可以换成EA=0,ES=1可以换成EA=1(即将串口中断的临时控制改为全局中断的控制)
b. 初始化函数init中ET1=1不能打开,即便主函数外有计数器1的子函数(函数为空)
c. 初始化函数init中TR1=1需要打开(包括TMOD=0x20),因为需要定时器1溢出率决定串口波特率.
d. 初始化函数init中REN=1需要打开,否则串口不会接受数据
e. 初始化函数SM1=1需要打开,否则串口接收乱码
f. 初始化函数ES=1需要打开,否则串口不会接受数据
 
5) 法2中TH1的地址位0x8D, 与TCON中的TF0位地址一样, 这里对TH1赋值时不是会对TF0产生影响吗?
关键字:串行口通信  上位机  单片机 引用地址:串行口通信 上位机发送字符x,单片机返回上位机"I get x"

上一篇:串行口通信 单片机在数码管显示AD采集值
下一篇:串行口通信(STC89C52+MAX232):串行口通信硬件设计详解

推荐阅读最新更新时间:2024-03-16 14:46

【自学51单片机】11 -- UART串口通信
1、串行通信的初步认识 UART串行通信是单片机最常用的一种通信技术,通常用于单片机和电脑之间以及单片机和单片机之间的通信。 通信可分为并行通信和串行通信两种。(1)并行通信:数据的各各位可同时传送。(2)串行通信:数据只能按顺序一个一个位传送。STC89C52中P3^0(RXD)和 P ^1(TXD)是专门用作UART串行通信的引脚。 下面看下图11-1单片机之间的UART串口通信。 说明:GND 表示单片机系统电源的参考地,TXD 是串行发送引脚,RXD 是串行接收引 脚。他们之间通信要保证以下几点。 (1)电源基准相同,所以我们要把两个单片机的 GND 相互 连接起来, (2)TXD与RXD相连,比如单片机
[单片机]
【自学51<font color='red'>单片机</font>】11 -- UART串口<font color='red'>通信</font>
单片机中断实验4
电路图: 程序代码如下: //交通灯控制程序 #include reg51.h unsigned char t0, t1; //定义全局变量,用来保存延时时间循环次数 //delay0_5s1 //功能:用T1的工作方式1编制0.5s延时程序,假定系统采用12Mhz晶振,定时器1,工作方式1定时50ms,再循环10次即可定时到0.5s void delay0_5s1 () { for (t0=0; t0 0x0a; t0++) { TH1 = 0x3c; TL1 = 0xb0; TR1 = 1; while (!TF1); TF1=0; } } //delay_t1 //功能:实现
[单片机]
<font color='red'>单片机</font>中断实验4
PIC单片机的延时函数
  PIC单片机C语言中的延时函数(即延时一定值),有多种等效的编写,这里介绍一种最简延时函数。   函数中的K为给定的整形。
[单片机]
PIC<font color='red'>单片机</font>的延时函数
单片机死机问题
一 振荡器停止振荡 又可以分为电源电压不稳,或者强干扰引起的振荡器停振。 二 PC指针跑飞 电源电压不稳或强干扰引起PC跑飞,如果看门狗不好,也会引起死机。 三 设计上对长引出线的IO没有保护,静电打在IO口上引起单片机死锁,破坏了硬件逻辑功能,导致死机。 四. 复位收到干扰,引起反复复位,在反复复位当中有可能会导致死机。 综上所述: 设计电路时,应该注意: 1.电源稳定 2.IO保护 3.振荡器PCB布线要注意 4.复位电路设计和PCB布线 5.电源,信号线干扰路径的保护,加滤波TVS等 6.高速信号输出远离信号输入端,如SPI总线,I2C总线布线要远离ADC,复位,时钟等布线处,以及其他模拟前端。
[单片机]
兆易GD32 MCU再度包揽2018年中国IC设计成就奖多项大奖
集微网消息,2018年3月30日,由AspenCore旗下《电子工程专辑》、《电子技术设计》和《国际电子商情》联合举办的“2018年度中国IC领袖峰会暨中国IC设计成就奖颁奖典礼”在上海隆重举行。 经过IC产业人士,系统设计工程师以及媒体分析师团队历时6个月的层层选拔,凭借高质量的产品和杰出的市场表现, 兆易创新(GigaDevice)荣获“十大中国 IC 设计公司”奖项,GD32F330/350系列微控制器荣获“年度最佳MCU”奖项,兆易创新产品市场总监金光一先生荣获“市场营销新锐人物”奖项。赢得了业界同行的一致认可,并再度包揽多项殊荣!     2018年度中国IC设计成就奖是针对中国的IC设计公司进行的年度产业现状调
[手机便携]
16.STC15W408AS单片机获取DS18B20温度
之前在树莓派系列的文章中写过DS18B20获取温度数据。当时树莓派有1-wire接口,可以直接以读文件的形式读取到温度。18.树莓派3B+ 1-wire获取DS18B20温度 现在在STC单片机里面没有这个接口,只能使用GPIO模拟单总线的时序来读取DS18B20的温度数据了。 DS18B20的特点 Unique 1-Wire interface requires only oneport pin for communication Can be powered from data line. Power supplyrange is 3.0V to 5.5V Measures temperatures fro
[单片机]
16.STC15W408AS<font color='red'>单片机</font>获取DS18B20温度
基于AT89C51单片机控制LED显示屏的电路设计
LED显示屏广泛应用于工矿企业、学校、商场、店铺、公共场所等进行图文显示,广告宣传,信息发布。本文设计一种由4个16×16点阵LED模块组成的显示屏,由单片机作控制器,平滑移动显示任意多个文字或图形符号,本电路可级联扩展实现由任意多个16×16点阵LED模块组成的显示屏。 1 电路设计 控制电路由AT89C51单片机作控制器,显示屏由4个16×16点阵LED模块组成,每个16×16点阵LED模块由4个8×8点阵LED模块组成,用户可根据需要扩展增加任意多个16×16点阵LED模块。8×8点阵LED模块结构如图1所示,共8行8列,每个发光二极管放置在行线和列线的交叉点上,共64个发光二极管。当某一列为高电平,某一行为低电平时,则
[单片机]
基于AT89C51<font color='red'>单片机</font>控制LED显示屏的电路设计
基于AVR微控制器的电力机车智能辅保系统的实现
    摘要: 给出了以AVR微控制器为核心的电力机车智能辅保系统的设计方案,并介绍了系统硬件及软件的具体实现方法。     关键词: 智能辅保系统 AVR微控制器 硬件 软件 电气机车辅助系统中有劈相机、空气压缩机、通风机及制动风机等各种类型的电机。运行中为了防止出现短路、过流等异常情况而烧毁电机,通常配置辅助保护系统,起到及时监测电机故障并加以处理的作用。目前电力机车上安装的辅保系统都是模拟电路装置,系统硬件复杂,又不方便司机使用和维修。因此,设计一种实时性高、性能可靠的智能辅保系统替代原有的模拟电路装置势在必行。本文将介绍笔者开发的用于韶山型电力机车的智能辅助保护系统的设计及实现。 1 系统的主要功能
[嵌入式]
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved