找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 1988|回復(fù): 2
打印 上一主題 下一主題
收起左側(cè)

51單片機(jī)串口使用入門

[復(fù)制鏈接]
跳轉(zhuǎn)到指定樓層
樓主
深入串口@[通信](這里寫自定義目錄標(biāo)題)

# 串口通信簡介

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

## 串口是什么

一般來說,單片機(jī)與上位機(jī)之間以串口通信為主,就串口通信而言。常用的幾種通信方式,包括串口自定義協(xié)議、Modbus協(xié)議、CAN總線。重點(diǎn)介紹一下串口通信。


## IDEA想法
現(xiàn)在小銘有一臺(tái)電腦和一個(gè)外設(shè),現(xiàn)在他想讓外設(shè)給電腦發(fā)送一個(gè)字母A。怎么辦呢?我們知道計(jì)算機(jī)的世界只有0和1。那么用0和1怎么發(fā)送A甚至是任意數(shù)據(jù)呢?

假設(shè)我們每次發(fā)送一個(gè)字節(jié)那么它要么是0要么是1,接下來我們一次發(fā)送8個(gè)字節(jié)則依然是每一位為0或者1。但是注意到
也就是說第一位可以代表1個(gè)數(shù)字,第二位代表2個(gè)數(shù)字......第八位可以代表128個(gè)數(shù)字,那么這八個(gè)字節(jié)可以代表1+2+4+8+16+32+64+128=255個(gè)數(shù)字。如果對照ASCII表基本上可以發(fā)送所有字符。
## 通信
好了,現(xiàn)在我們按照ASCII上對應(yīng)關(guān)系開始發(fā)送數(shù)據(jù)吧。還是以A為例。A對應(yīng)的十進(jìn)制是65。    65=1+64。那么我們可以讓第一位是1,第7位也是1,其他位為0。
現(xiàn)在我們已經(jīng)成功的發(fā)送了A。
  此外還得規(guī)定通信速率也就是波特率每秒發(fā)送的位數(shù)。這里就不詳細(xì)介紹了。

## 單片機(jī)實(shí)現(xiàn)串口通信
    51單片機(jī)
要想使用串口就要了解一下兩個(gè)寄存器和定時(shí)器。
(1)串口控制寄存器 SCONSCON=0x40  工作方式1;0100  0000  串口不接受數(shù)據(jù)

SCON=0x50  工作方式1;0101  0000   串口接受數(shù)據(jù)

(2)電源控制寄存器 PCON

PCON=0X80;波特率加倍

PCON=0X00;波特率不加倍

(3)TMOD   計(jì)數(shù)器這什么玩意?要不我們換一張圖看看吧!

```c
TMOD|=0X20; //設(shè)置計(jì)數(shù)器工作方式 2
TH1=baud; //計(jì)數(shù)器初始值設(shè)置
TL1=baud
```


2)
```c
TMOD|=0X01;//選擇為定時(shí)器 0 模式,工作方式1  
TH0=初值
TL0=初值
```
## 硬核知識(shí)-串口發(fā)送字節(jié)
了解完以上內(nèi)容現(xiàn)在我們就可以寫一個(gè)串口發(fā)送的函數(shù)了。我們定義函數(shù)為uart_init()
在這個(gè)函數(shù)里我們做這幾件事情:
①確定 T1 的工作方式(TMOD 寄存器);
②確定串口工作方式(SCON 寄存器);
③計(jì)算 T1 的初值(設(shè)定波特率),裝載 TH1、TL1;
④啟動(dòng) T1(TCON 中的 TR1 位);
⑤如果使用中斷,需開啟串口中斷控制位(IE 寄存器)。
如果你要實(shí)現(xiàn)不同的功能那么這個(gè)初始化的函數(shù)也會(huì)不一樣。這里我們寫幾個(gè)典型的例子:
**實(shí)現(xiàn)功能**:串口把接收到的數(shù)據(jù)發(fā)送回單片機(jī)
```c
void uart_init(void)
{
TMOD|=0X20; //設(shè)置計(jì)數(shù)器工作方式 2
SCON=0X50; //設(shè)置為工作方式 1
PCON=0X80; //波特率加倍 4800倍后為9600
TH1=0XFA; //計(jì)數(shù)器初始值設(shè)置
TL1=OXFA;
ES=1; //打開接收中斷
EA=1; //打開總中斷
TR1=1; //打開計(jì)數(shù)器
}

void uart() interrupt 4//串口通信中斷函數(shù)
{
u8 rec_data;
RI = 0; //清除接收中斷標(biāo)志位
rec_data=SBUF; //存儲(chǔ)接收到的數(shù)據(jù)
SBUF=rec_data; //將接收到的數(shù)據(jù)放入到發(fā)送寄存器
while(!TI); //等待發(fā)送數(shù)據(jù)完成
TI=0; //清除發(fā)送完成標(biāo)志位
}



**串口功能**:發(fā)送傳感器數(shù)據(jù),無需中斷


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

}
//如果你有一個(gè)返回ADC的函數(shù)adc_value()
float buff;
buff=adc_value();
printf("data is %f",buff);
//你就可以成功的把數(shù)據(jù)在串口助手中顯示了

這里我重點(diǎn)介紹一下printf函數(shù)
printf   打印調(diào)試把數(shù)據(jù)顯示在串口助手中或者是oled屏幕上。
配合上面那個(gè)串口初始化函數(shù),然后在頭文件中包含#Include “stdio.h”就可以使用了。


printf("welcome to the digatal world");

但是使用printf唯一的不好就是如果要與上位機(jī)通信它發(fā)送的數(shù)據(jù)位數(shù)不一樣;比如說你的數(shù)據(jù)是3.23;45.13;100.64;數(shù)據(jù)位數(shù)不一樣在于上位機(jī)通信時(shí)會(huì)給軟件編程造成很大的麻煩。最好是003.23;045.13;100.64;怎么做到呢?小白我編程能力有限,如果有大佬會(huì)請留言謝謝!
下面介紹用**寄存器**收發(fā)數(shù)據(jù)。

void UART_Init(void)
{
        
        TMOD|=0X20;        //設(shè)置計(jì)數(shù)器工作方式2
        SCON=0X50;        //設(shè)置為工作方式1
        PCON=0X00;        //波特率加倍
        TH1=0xfd;        //計(jì)數(shù)器初始值設(shè)置
        TL1=0xfd;
        ES=1;                //打開接收中斷
        EA=1;                //打開總中斷
        TR1=1;                //打開計(jì)數(shù)器        
}
void UART_Sendchar(unsigned char d)                  //發(fā)送一個(gè)字節(jié)的數(shù)據(jù),形參d即為待發(fā)送數(shù)據(jù)。
{
         SBUF=d; //將數(shù)據(jù)寫入到串口緩沖
        while(!TI);
        TI=0;
}
void UART_SendString(unsigned char *String)
{
    while(*String)
    {
        UART_Sendchar(*String);
        String++;
    }
}
UART_Sendchar(65);        //發(fā)送A十進(jìn)制對應(yīng)的符號是A
UART_Sendchar('a');//發(fā)送a 注意只能單引號
UART_Sendchar(0x65);//發(fā)送e十六進(jìn)制65對應(yīng)的符號是e
UART_SendString("小七");//發(fā)送字符串
/*****在這里你應(yīng)該理解了吧串口發(fā)送就是按照ASCII表的規(guī)則來的。無論你發(fā)的是十進(jìn)制還是十六進(jìn)制最后都會(huì)轉(zhuǎn)換成對應(yīng)的符號

//發(fā)送浮點(diǎn)數(shù)
float b=3.2;//帶轉(zhuǎn)化數(shù)據(jù)
uchar conver_t[4];//一定要定義成**數(shù)組**類型
float b=3.2;
sprintf(conver_t,"%f",b);//通過%.1f /%.2f可以改變小數(shù)位數(shù)
UART_SendString(conver_t);


## 串口接收中斷的設(shè)置;一般用于上位機(jī)控制


**根據(jù)接受的數(shù)據(jù)做出響應(yīng),如燈的翻轉(zhuǎn),之類的**。
直接上代碼!

//串口控制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)                  //發(fā)送一個(gè)字節(jié)的數(shù)據(jù),形參d即為待發(fā)送數(shù)據(jù)。
{
         SBUF=d; //將數(shù)據(jù)寫入到串口緩沖
        while(!TI);
        TI=0;
}
void uart_init(void)
{
TMOD|=0X20; //設(shè)置計(jì)數(shù)器工作方式 2
SCON=0X50; //設(shè)置為工作方式 1
PCON=0X00; //波特率加倍 4800倍后為9600
TH1=0XFd; //計(jì)數(shù)器初始值設(shè)置
TL1=0xfd;
TR1=1; //打開計(jì)數(shù)器;
ES=1;                //打開串口中斷
EA=1;                //打開總中斷
TR1=1;                //打開計(jì)數(shù)器
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);                          
}
        }

注意,本代碼不是萬能!如果你的程序很簡單那么這些代碼基本沒問題,但是小白我就曾經(jīng)遇到了一個(gè)問題。我在一個(gè)項(xiàng)目中用到了兩個(gè)中斷,定時(shí)器、串口中斷沖突。結(jié)果接收上位機(jī)的數(shù)據(jù)相關(guān)代碼一直不起作用。我以為是串口接收中斷函數(shù)有問題。在網(wǎng)上查了一下,可能的原因很多。解決辦法嘛主流的是增加中斷時(shí)間,然后還有一個(gè)就是改中斷號優(yōu)先級。我都嘗試過對我的程序來說沒有用。最后無意中在一個(gè)帖子看到降低串口波特率。我把9600改為4800后果然有用。遇到問題,多去查查。幾十億人,他們就做的全都對嘛?也會(huì)有類似的問題對吧。
分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏4 分享淘帖 頂 踩
回復(fù)

使用道具 舉報(bào)

沙發(fā)
ID:562099 發(fā)表于 2024-5-26 17:31 | 只看該作者
正好看這方面資料。注解很詳細(xì),分析很到位。
回復(fù)

使用道具 舉報(bào)

板凳
ID:961114 發(fā)表于 2024-5-30 09:10 | 只看該作者
可以去看免費(fèi)視頻講解  
回復(fù)

使用道具 舉報(bào)

您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規(guī)則

手機(jī)版|小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術(shù)交流QQ群281945664

Powered by 單片機(jī)教程網(wǎng)

快速回復(fù) 返回頂部 返回列表