找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 900|回复: 2
打印 上一主题 下一主题
收起左侧

51单片机串口使用入门

[复制链接]
跳转到指定楼层
楼主
深入串口@[通信](这里写自定义目录标题)

# 串口通信简介

你好! 如果这是你第一次使用 **串口** ,那么了解一些串口是什么以及怎么用吧。

## 串口是什么

一般来说,单片机与上位机之间以串口通信为主,就串口通信而言。常用的几种通信方式,包括串口自定义协议、Modbus协议、CAN总线。重点介绍一下串口通信。


## IDEA想法
现在小铭有一台电脑和一个外设,现在他想让外设给电脑发送一个字母A。怎么办呢?我们知道计算机的世界只有0和1。那么用0和1怎么发送A甚至是任意数据呢?

假设我们每次发送一个字节那么它要么是0要么是1,接下来我们一次发送8个字节则依然是每一位为0或者1。但是注意到
也就是说第一位可以代表1个数字,第二位代表2个数字......第八位可以代表128个数字,那么这八个字节可以代表1+2+4+8+16+32+64+128=255个数字。如果对照ASCII表基本上可以发送所有字符。
## 通信
好了,现在我们按照ASCII上对应关系开始发送数据吧。还是以A为例。A对应的十进制是65。    65=1+64。那么我们可以让第一位是1,第7位也是1,其他位为0。
现在我们已经成功的发送了A。
  此外还得规定通信速率也就是波特率每秒发送的位数。这里就不详细介绍了。

## 单片机实现串口通信
    51单片机
要想使用串口就要了解一下两个寄存器和定时器。
(1)串口控制寄存器 SCONSCON=0x40  工作方式1;0100  0000  串口不接受数据

SCON=0x50  工作方式1;0101  0000   串口接受数据

(2)电源控制寄存器 PCON

PCON=0X80;波特率加倍

PCON=0X00;波特率不加倍

(3)TMOD   计数器这什么玩意?要不我们换一张图看看吧!

```c
TMOD|=0X20; //设置计数器工作方式 2
TH1=baud; //计数器初始值设置
TL1=baud
```


2)
```c
TMOD|=0X01;//选择为定时器 0 模式,工作方式1  
TH0=初值
TL0=初值
```
## 硬核知识-串口发送字节
了解完以上内容现在我们就可以写一个串口发送的函数了。我们定义函数为uart_init()
在这个函数里我们做这几件事情:
①确定 T1 的工作方式(TMOD 寄存器);
②确定串口工作方式(SCON 寄存器);
③计算 T1 的初值(设定波特率),装载 TH1、TL1;
④启动 T1(TCON 中的 TR1 位);
⑤如果使用中断,需开启串口中断控制位(IE 寄存器)。
如果你要实现不同的功能那么这个初始化的函数也会不一样。这里我们写几个典型的例子:
**实现功能**:串口把接收到的数据发送回单片机
```c
void uart_init(void)
{
TMOD|=0X20; //设置计数器工作方式 2
SCON=0X50; //设置为工作方式 1
PCON=0X80; //波特率加倍 4800倍后为9600
TH1=0XFA; //计数器初始值设置
TL1=OXFA;
ES=1; //打开接收中断
EA=1; //打开总中断
TR1=1; //打开计数器
}

void uart() interrupt 4//串口通信中断函数
{
u8 rec_data;
RI = 0; //清除接收中断标志位
rec_data=SBUF; //存储接收到的数据
SBUF=rec_data; //将接收到的数据放入到发送寄存器
while(!TI); //等待发送数据完成
TI=0; //清除发送完成标志位
}



**串口功能**:发送传感器数据,无需中断


void UART_Init(void)
{
SCON=0x50;                        //串口通信工作方式1
TMOD=0x20;                //定时器1的工作方式2
PCON=0X00;      //不加倍
TH1=0xfd,TL1=0xfd;        //9600baud        
TI=1;            //这里一定要注意
TR1=1;

}
//如果你有一个返回ADC的函数adc_value()
float buff;
buff=adc_value();
printf("data is %f",buff);
//你就可以成功的把数据在串口助手中显示了

这里我重点介绍一下printf函数
printf   打印调试把数据显示在串口助手中或者是oled屏幕上。
配合上面那个串口初始化函数,然后在头文件中包含#Include “stdio.h”就可以使用了。


printf("welcome to the digatal world");

但是使用printf唯一的不好就是如果要与上位机通信它发送的数据位数不一样;比如说你的数据是3.23;45.13;100.64;数据位数不一样在于上位机通信时会给软件编程造成很大的麻烦。最好是003.23;045.13;100.64;怎么做到呢?小白我编程能力有限,如果有大佬会请留言谢谢!
下面介绍用**寄存器**收发数据。

void UART_Init(void)
{
        
        TMOD|=0X20;        //设置计数器工作方式2
        SCON=0X50;        //设置为工作方式1
        PCON=0X00;        //波特率加倍
        TH1=0xfd;        //计数器初始值设置
        TL1=0xfd;
        ES=1;                //打开接收中断
        EA=1;                //打开总中断
        TR1=1;                //打开计数器        
}
void UART_Sendchar(unsigned char d)                  //发送一个字节的数据,形参d即为待发送数据。
{
         SBUF=d; //将数据写入到串口缓冲
        while(!TI);
        TI=0;
}
void UART_SendString(unsigned char *String)
{
    while(*String)
    {
        UART_Sendchar(*String);
        String++;
    }
}
UART_Sendchar(65);        //发送A十进制对应的符号是A
UART_Sendchar('a');//发送a 注意只能单引号
UART_Sendchar(0x65);//发送e十六进制65对应的符号是e
UART_SendString("小七");//发送字符串
/*****在这里你应该理解了吧串口发送就是按照ASCII表的规则来的。无论你发的是十进制还是十六进制最后都会转换成对应的符号

//发送浮点数
float b=3.2;//带转化数据
uchar conver_t[4];//一定要定义成**数组**类型
float b=3.2;
sprintf(conver_t,"%f",b);//通过%.1f /%.2f可以改变小数位数
UART_SendString(conver_t);


## 串口接收中断的设置;一般用于上位机控制


**根据接受的数据做出响应,如灯的翻转,之类的**。
直接上代码!

//串口控制led亮灭
#include "reg52.h"
#include "stdio.h"
sbit  led=P1^5;
unsigned char rec;

void receive_dat( rec)
{
        if(rec==0x00)
        {
                led=0;
        }
        else if(rec==0x01)
        {
                 led=1;
        }
        else if(rec==0x02)
        {
                led=0;
        }
        else
        {
                led=1;
        }
}
void UART_Sendchar(unsigned char d)                  //发送一个字节的数据,形参d即为待发送数据。
{
         SBUF=d; //将数据写入到串口缓冲
        while(!TI);
        TI=0;
}
void uart_init(void)
{
TMOD|=0X20; //设置计数器工作方式 2
SCON=0X50; //设置为工作方式 1
PCON=0X00; //波特率加倍 4800倍后为9600
TH1=0XFd; //计数器初始值设置
TL1=0xfd;
TR1=1; //打开计数器;
ES=1;                //打开串口中断
EA=1;                //打开总中断
TR1=1;                //打开计数器
ET1=0;        
}

void  main()
{
   uart_init();
   UART_Sendchar('E');
        while(1)
        {
        }
}

void Uart() interrupt 4
{
         if(RI==1)
{   RI=0;
        rec=SBUF;
        receive_dat(rec);                          
}
        }

注意,本代码不是万能!如果你的程序很简单那么这些代码基本没问题,但是小白我就曾经遇到了一个问题。我在一个项目中用到了两个中断,定时器、串口中断冲突。结果接收上位机的数据相关代码一直不起作用。我以为是串口接收中断函数有问题。在网上查了一下,可能的原因很多。解决办法嘛主流的是增加中断时间,然后还有一个就是改中断号优先级。我都尝试过对我的程序来说没有用。最后无意中在一个帖子看到降低串口波特率。我把9600改为4800后果然有用。遇到问题,多去查查。几十亿人,他们就做的全都对嘛?也会有类似的问题对吧。
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏2 分享淘帖 顶 踩
回复

使用道具 举报

沙发
ID:562099 发表于 2024-5-26 17:31 | 只看该作者
正好看这方面资料。注解很详细,分析很到位。
回复

使用道具 举报

板凳
ID:961114 发表于 2024-5-30 09:10 | 只看该作者
可以去看免费视频讲解  
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表