好记性不如烂笔头,以前总以为自己记性比较好,但事实总是一次一次的打我脸,刚开始学习单片机的时候在串口通信这一块发了一段时间,才将这一块弄懂了个七七八八,这几天回头想一下那一方面的知识,感觉忘得差不多了,现在重新理一遍,里面的程序大部分都是以前抄袭大佬的,但具体是哪一位大佬的博客现在也找不到了,希望大佬见谅。
首先了解串口通信先要熟悉SCON,PCON,TMOD三个寄存器
串口工作方式寄存器SCON,
位 | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
功能 | SM0 | SM1 | SM2 | REN | TB8 | RB8 | TI | RI |
RI:接收中断标志位,数据接收结束时,标志位会自动置1,需要通过程序将其置0
TI:发送中断标志位,数据发送结束时,标志位会自动置1,需要通过程序将其置0
RB8:存放发送数据的第9位
TB8:存放接收数据的第9位
REN:串行接收允许位,0允许串行接收,1禁止串行接收
SM2:多机控制位
SM1,SM0:串行工作方式
SM0 | SM1 | 方式 | 说明 | 波特率 |
0 | 0 | 0 | 移位寄存器 | fosc/12 |
0 | 1 | 1 | 10位异步收发器(8位数据) | 可变 |
1 | 0 | 2 | 11位异步收发器(9位数据) | fosc/64或fosc/32 |
1 | 1 | 3 | 11位异步收发器(9位数据) | 可变 |
PCON寄存器
位 | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
功能 | SMOD | - | - | - | - | - | - | - |
SOMD:波特率是否加倍选择位,0波特率不加倍,1波特率加倍
定时器工作方式寄存器TMOD
位 | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
功能 | GATE | C/T | M1 | M0 | GATE | C/T | M1 | M0 |
高四位为定时计数器1的设置,低四位是定时计数器0设置,串口通信波特率设置占用定时计数器1,这里主要说串口通信,不过多说定时计数器,只需要设置定时计数器1的工作方式即可
振荡周期:也称时钟周期(频率的倒数),单片机提供时钟信号的振荡源周期,频率一般有11.0592MHz,12MHz等
状态周期:是时钟周期的2倍,
机器周期:是包含6个状态周期,机器周期=1/单片机时钟频率
单片机时钟频率:是外部时钟的12分频,如果是12MHz的晶振,机器周期=1/单片机时钟频率=1/(12MHz/12)=12/12M=1us
这里一个机器周期为1us,若定时时间为1ms,则需要1000个机器周期,计算出初值;如果机器周期为2us,则只需要500个机器周期。
定时器初值计算:初值=(65536-机器周期数量)
波特率计算:
当串口工作在工作方式0和2是,波特率固定,方式0时fosc/12;方式2时fosc/32或fosc/64(根据SMOD判断)。
当串口工作在方式1时,波特率=(2^SMOD/32)*(单片机时钟频率/(256-X)),X是初值
C/T:定时器和计数器选择位,0为定时器,1为计数器
M1 | M0 | 工作方式 |
0 | 0 | 工作方式0:为13位定时/计数器 |
0 | 1 | 工作方式1:为16位定时/计数器 |
1 | 0 | 工作方式2:8位初值自动重装定时/计数器 |
1 | 1 | 工作方式3:仅适用于T0,分成两个8位计数器,T1停止计数 |
下面是程序结构和代码:
mian.c
#include "main.h"
uchar UartRxBuffer[ 64 ] = { 0 }; //uart串口接收数据
uchar TX_Cnt = 0; //发送计数
uchar RX_Cnt = 0; //接收计数
bit TX_Busy = 0; //发送忙标志
void main()
{
uart_init();
while(1)
{
if((TX_Cnt!=RX_Cnt)&&(!TX_Busy))
{
//将数组中的数据放入到发送缓冲区
SBUF=UartRxBuffer[TX_Cnt];
if(++TX_Cnt>=64)
{
TX_Cnt=0;
}
TX_Busy==1;
}
}
}
/*************************************************
函数:串口程序
功能:将接收到的数据存入到UartRxBuffer
*************************************************/
void UART_INT (void) interrupt 4
{
if(RI)
{
RI = 0;
UartRxBuffer[RX_Cnt] = SBUF;//将数据写入数组
//防止溢出,当计数=16时,将计数清零
if(++RX_Cnt >= 64){
RX_Cnt = 0;
}
}
if(TI)
{
TI = 0;
TX_Busy = 0;
}
}
mcu_uart.c
#include "mcu_uart.h"
/*************************************************
函数:uart_init
功能:初始化串口
出口:void
入口:void
*************************************************/
void uart_init()
{
SCON = 0x50;//设置串口工作方式1
TMOD = 0x20;//设置计数器工作方式2
PCON = 0x00;//即SMOD=1,波特率不加倍
TH1 = 0xFD;//计数器初值,波特率是9600,晶振为11.0592MHz
TL1 = 0xFD;
ES = 1;//打开接收中断
EA = 1;//打开总中断
TR1 = 1;//打开计数器
}
/*************************************************
函数:uart_tx_byte
功能:串口发送一个字节
出口:void
入口:一个字节
*************************************************/
void uart_tx_byte(uchar str)
{
SBUF=str;
while(!TI);
TI==0;
}
/*************************************************
函数:uart_tx_string
功能:串口发送一个字符串
出口:void
入口:字符串数组
*************************************************/
void uart_tx_string(uchar *str)
{
while(*str!='\0')
{
uart_tx_byte(*str++);
}
}
/*************************************************
函数:uart_rx_string
功能:串口接收一个字符串
出口:字符串的长度
入口:字符串数组
*************************************************/
uchar uart_rx_string( uchar* RxBuffer )
{
uchar rxLength = 0;
uint uartRxTimOut = 0x7FFF;
while( uartRxTimOut-- )
{
if( 0 != RI )
{
RI = 0;
*RxBuffer = SBUF;
RxBuffer++;
rxLength++;
uartRxTimOut = 0x7FFF;
}
}
return rxLength;
}
mian.h
#ifndef __MAIN_H__
#define __MAIN_H__
#include "mcu_uart.h"
#endif
mcu_uart.h
#ifndef __MCU_UART_H__
#define __MCU_UART_H__
#include "mcu_type.h"
void uart_init();
#endif
mcu_type.h
#ifndef __MCU_TYPE_H__
#define __MCU_TYPE_H__
#include
/*无符号*/
typedef unsigned char uchar;
typedef unsigned int uint;
#endif