一橘蜜、 IO模擬UART發(fā)送
串口通信屬于 串行 異步 半雙工的通信模式
1、 最近在調(diào)試一個(gè)IO模擬UART的程序,把調(diào)試過程中遇到的問題總結(jié)一下。對(duì)于UART的發(fā)送部分(主機(jī)模式)還是比較容易實(shí)現(xiàn)的舞竿。比較麻煩的做從機(jī)時(shí),UART接收還在調(diào)試窿冯,可以接收數(shù)據(jù)骗奖,但還存在很多問題。
(1) 起始位:總線沒通信是高電平狀態(tài)醒串,要進(jìn)行通信時(shí)执桌,總線拉低發(fā)出“邏輯0”信號(hào),表示開始傳輸數(shù)據(jù)
(2) 數(shù)據(jù)位:通常以1byte數(shù)據(jù)為標(biāo)準(zhǔn)厦凤,從低位開始傳輸鼻吮,通過時(shí)鐘頻率來定位數(shù)據(jù)的傳輸
(3) 奇偶校驗(yàn)位:通過在數(shù)據(jù)末尾加上1位,使得數(shù)據(jù)包中的1的個(gè)數(shù)為偶數(shù)(偶校驗(yàn))或者是奇數(shù)(奇校驗(yàn))较鼓,來確定數(shù)據(jù)的準(zhǔn)確性椎木。
(4) 停止位:在通信結(jié)束時(shí)违柏,將總線拉高發(fā)出“邏輯1”信號(hào),表示傳輸結(jié)束香椎,也給下次進(jìn)行通信提供了校準(zhǔn)時(shí)間的機(jī)會(huì)
2漱竖、波特率
波特率表示每秒鐘傳送的碼元符號(hào)的個(gè)數(shù),是衡量數(shù)據(jù)傳輸速率的指標(biāo)畜伐,它用單位時(shí)間內(nèi)載波調(diào)制狀態(tài)改變的次數(shù)來表示馍惹,1波特即指每秒傳輸1個(gè)符號(hào)。
若 波特率為9600bps,那么傳輸一位數(shù)據(jù)的時(shí)間是在1000ms/9600bps=0.104ms
3玛界、串口發(fā)送數(shù)據(jù)組成
數(shù)據(jù)由:1bit起始位+8bit數(shù)據(jù)+1bit停止位構(gòu)成万矾;
所以,搞懂這些原理慎框,串口發(fā)送并不難良狈,主要把單片機(jī)的定時(shí)器配置在104us左右溢出中斷,保證每一位時(shí)間間隔在104us 左右即可笨枯。
我用的是51單片機(jī)薪丁,51本身自帶一個(gè)串口,但自己想用IO模擬一個(gè)串口出來馅精,便于自己更好的理解串口严嗜,也作為自己的一個(gè)技術(shù)儲(chǔ)備。
/**********************************************51單片機(jī)IO模擬UART實(shí)驗(yàn)************************************************************************************/
uart源文件
/****************************************************
*文件名:uart.c
****************************************************/
#include "reg52.h"
#include "type.h"
#include "uart.h"
#include "timer.h"
void UART_INIT(void)
{
? ? TX_D=1;
? ? Timer1Init();? ? //T1 初始化
}
void WAIT_TF1(void)
{
? ? while(!TF1);? //查詢計(jì)數(shù)器溢出標(biāo)志位
? ? TF1=0;
}
//寫數(shù)據(jù)很快的話洲敢,定時(shí)器不穩(wěn)定而導(dǎo)致發(fā)送的數(shù)據(jù)會(huì)有錯(cuò)碼漫玄,發(fā)送時(shí)應(yīng)適當(dāng)延時(shí)降低錯(cuò)碼概率
void Write_DATA(uint8_t input)
{
? ? uint8_t i=8;? //寫入1byte數(shù)據(jù)
? ? TR1=1;? ? ? ? //開始計(jì)時(shí)
? ? TX_D=0;? ? ? ? //拉低信號(hào)線準(zhǔn)備發(fā)送數(shù)據(jù)? 起始信號(hào)
? ? WAIT_TF1();? ? //106us溢出一次,溢出進(jìn)行下一位的傳輸
? ? while(i--)? ? //開始寫數(shù)據(jù)
? ? {
? ? ? ? TX_D=input&0X01;
? ? ? ? WAIT_TF1();
? ? ? ? input>>=1;
? ? }
? ? TX_D=1;? ? ? //8位數(shù)據(jù)發(fā)完拉高信號(hào)線? 停止信號(hào)
? ? WAIT_TF1();
? ? TR1=0;? ? ? ? //停止計(jì)時(shí)
}
//發(fā)送字符串
void Send_Char(uint8_t *buf)
{
? ? while(*buf != '\0')
? ? {
? ? Write_DATA(*buf);
? ? buf++;
? ? }
}
//發(fā)送數(shù)字
void Send_Num(uint8_t *buf,uint16_t s)
{
? ? while(s--)
? ? {
? ? ? ? Write_DATA(*buf+48);? ? //ASCII 將字符轉(zhuǎn)換成數(shù)字
? ? ? ? buf++;
? ? }
}
//發(fā)送多個(gè)數(shù)據(jù)
void Send_ND(uint8_t *buf,uint16_t len)
{
? ? while(len--)
? ? {
? ? ? ? Write_DATA(*buf);?
? ? ? ? buf++;
? ? }
}
uart頭文件
#include "type.h"
#ifndef? __UART_H__
#define? __UART_H__
sbit TX_D=P0^6;
extern uint8_t flag2;
extern void Send_Char(uint8_t *buf);? ? ? ? ? ? ? ? //發(fā)送字符
extern void Send_ND(uint8_t *buf,uint16_t len);? ? //發(fā)送N個(gè)數(shù)據(jù)
extern void Send_Num(uint8_t *buf,uint16_t s);? ? ? //發(fā)送數(shù)字
extern void UART_INIT(void);? ? ? ? ? ? ? ? ? ? ? ? //串口初始化
extern void WAIT_TF1(void);? ? ? ? ? ? ? ? ? ? ? ? //等待TF1溢出
extern void Write_DATA(uint8_t input);? ? ? ? ? ? ? //寫數(shù)據(jù)
#endif
timer源文件
/*****************************************************************
文件名:timer.c
*******************************************************************/
#include "reg52.h"
#include "type.h"
#include "timer.h"
uint8_t flag1=0;
void Timer1Init()
{
? TMOD = 0x10;? //T1? 方式1
? ? TH1 = 0xFF;? ? //波特率9600bps 1000ms/9600bps=0.104ms
? ? TL1 = 0xA0;? ? //定時(shí)器誤差2us左右? 106us
? ? EA = 1;
? ? ET1 = 1;
? ? TR1 = 1;
? TF1=0;
}
void Time1() interrupt 3
{
if(TR1==1)
{
TH1=0XFF;
TL1=0xA0;
flag1=1;
}
}
timer頭文件
#include "type.h"
#ifndef? __TIMER_H__
#define? __TIMER_H__
extern uint8_t flag1;
extern void Timer1Init(void);
#endif
type頭文件
#ifndef? __TYPE_H__
#define? __TYPE_H__
#define uint8_t? unsigned char
#define uint16_t? unsigned int
#endif
/*******************************************************主程序--測試部分****************************************************************/
#include "reg52.h"
#include "type.h"
#include "timer.h"
#include "uart.h"
#include "delay.h"
uint8_t buffer[5];
uint8_t Buff[5]={1,2,3,4,5};
void main(void)
{
UART_INIT();
Write_DATA('A');
Send_Char("testuart");
Send_Num(Buff,5);
while(1);
}
如果發(fā)送發(fā)在while(1)中跑的話压彭,需要加延時(shí)來降低由于查詢過快導(dǎo)致的發(fā)送亂碼的概率称近,事實(shí)證明加延時(shí)效果更好,結(jié)果更準(zhǔn)確哮塞。
二刨秆、 IO模擬UART接收
1、模擬串口接收部分是可以接收忆畅,但存在很多問題
2衡未、我對(duì)問題進(jìn)行了總結(jié)以便后期改進(jìn)
3、讀數(shù)據(jù)? 接收數(shù)據(jù)的效果不是很好家凯,需要連續(xù)的點(diǎn)擊發(fā)送字符才會(huì)成功
? ? 問題解析:估計(jì)是等到單片機(jī)掃描時(shí)缓醋,串口helper已經(jīng)把數(shù)據(jù)發(fā)送完了,單片機(jī)這邊對(duì)不上起始信號(hào)從而導(dǎo)致錯(cuò)峰绊诲,所以串口helper需要不停的點(diǎn)擊發(fā)送在這個(gè)期間對(duì)上了就進(jìn)去了送粱。
最近調(diào)試了一下,現(xiàn)在接收部分已經(jīng)可以正常接收了掂之。
接收部分
uint8_t Receive_Data(void)
{
uint8_t receive=0,t=0;
uint8_t i=8;
while(RX_D);? ? //等待起始信號(hào)抗俄,超時(shí)自動(dòng)退出
TR1=1;? ? //開始計(jì)時(shí)
WAIT_TF1();
while(i--)
{
receive>>=1;
if(RX_D)receive|=0x80;
WAIT_TF1();
}
TR1=0;
return receive;
}
void Recevice(uint8_t *temp,uint8_t data_size)? ?
{
while(data_size--)
{
*temp++=Receive_Data();
}
}
TEST
uint8_t recv[20]={0};
void main(void)
{
UART_INIT();
while(1)
{
? Recevice(recv,5);
? Send_ND(recv,5);
}
}