第一题
1.STC15F2K60S2 上的串口接收任意字符串内容,直到 e 为止,接收到 e 就把接收到的内容发送出去(除了 e),在串口调试助手上打印出来。例如:STC15 串口接收到了 “1234qwsa”,不会在串口上打印出来信息。但如果 STC15 串口接收到了 “pauitydsertt”,那么就会把 “pauityds” 在串口调试助手上打印出来。
代码
//uart.c
#include "sys.h"
//字符串输出
void uart1_sendstring(u8* str){
while(*str != '\0'){ //当输出到'\0'时,停止输出
SBUF = *str ++;
while(!TI); //当TI为1时跳出循环,即输出完毕时跳出循环
TI = 0;
}
}
//串口初始化
void UART_Init(void){
SCON = 0x50;
T2H = (65536-3E6/9600)/256; //9600波特率
T2L = (u16)(65536-3E6/9600)%256;
AUXR &= 0xF7; // 定时器2定时器模式
AUXR |= 0x15; //允许定时器2运行,定时器2不分频,定时器2作为波特率发生器
ES = 1;
EA = 1;
}
void UART(void) interrupt 4{
static u16 i = 0;
if(RI){
RI = 0; //接收完毕,软件清零
BUF[i] = SBUF; //接收数据,并存储到u8类型BUF数组里。
if(BUF[i ++] == 'e') //当接收到'e'时
{
BUF[i] = '\0'; //使得当前位置为'0'
flag = 1; //falg置一
i = 0;
}
}
if(TI)TI = 0; //软件清零
}
//main.c
#include <STC15F2K60S2.H>
#include "sys.h"
#include <string.h>
u8 BUF[100];
u8 flag;
void main(){
All_Init();
UART_Init();
while(1){
if(flag == 1){ //当flag为1
uart1_sendstring(BUF); //输出BUF数据
memset( BUF , 0 , ( 100 * sizeof(u8) ) );//清零BUF数组
flag = 0; //清零flag
}
}
}
//sys.h
#ifndef ___SYS_H_
#define ___SYS_H_
#include <STC15F2K60S2.H>
#include <intrins.h>
#ifndef u8
#define u8 unsigned char
#endif
#ifndef u16
#define u16 unsigned int
#endif
//sys.c
void All_Init(void); //单片机初始化函数,大家都知道是什么,就不加上去了。
//uart.c
extern u8 BUF[100]; //定义外部变量
extern u8 flag;
void uart1_sendstring(u8* str);
void UART_Init(void);
#endif
要点:
1、把发送函数写在中断外面,用一个flag来判断是否发送。因为发送数据需要一定时间,而中断是要快进快出的。
2、烧入程序时,记得把频率调成12MHz(因为写的定时器初始值是12MHz的)。
3、记得把数组清零,不然下一次输出会把上次没输出的字符串一起输出。
4、注意memset函数的格式。
参考博客 :
1、@Reage–——C 语言数组清空的几种方法比较。
2、
@面包呢 ——memset。
第二题
2. 自定义一个协议:数据格式为 “0xAA 0xBB dat1 dat2 dat3 dat1+dat2+dat3”,把这个协议的发送端和接收端代码都实现出来。
代码
uart.c
//第二题:
#include "sys.h"
void UART_Init(void){
SCON = 0x50;
T2H = (65536-3E6/9600)/256;
T2L = (u16)(65536-3E6/9600)%256;
AUXR &= 0xF7;
AUXR |= 0x15;
ES = 1;
EA = 1;
}
void uart1_sendstring(u8* str){
while(*str != '\0'){
SBUF = *str ++;
while(!TI);TI = 0;
}
}
void UART() interrupt 4
{
static u8 num = 0; //存储位数
static u8 step = 0; //逻辑步
if(RI)
{
RI = 0;
switch(step)
{
case 0:{
BUF[num] = SBUF;
if(BUF[num ++] == 0xAA)step = 1;//接收第一个数据,若为0xAA,则进入下一逻辑步
else num = 0;
}break;
case 1:{
BUF[num] = SBUF;
if(BUF[num ++] == 0xBB)step = 2; //接收第二个数据,若为0xBB,则进入下一逻辑步
else {step = 0;num = 0;}//负责返回逻辑步0
}break;
case 2:{
BUF[num ++] = SBUF;//接收剩余数据
if(num == 6){
BUF[6] = BUF[2] + BUF[3] + BUF[4];
if(BUF[5] == BUF[6])//当第六个数据为前三个数据之和
{
BUF[6] = '\0';//则让第七个数据为停止位
flag = 1;//置一flag
num = 0;//清零num
}else {step = 0;num = 0;}//否则返回逻辑步0
}
}break;
}
}
if(TI)TI = 0;
}
main.c
#include <STC15F2K60S2.H>
#include "sys.h"
#include <string.h>
u8 BUF[10];
u8 flag;
void main(){
All_Init();
UART_Init();
while(1){
if(flag == 1){
uart1_sendstring(BUF);
memset( BUF , 0 , ( 10 * sizeof(u8) ) );
flag = 0;
}
}
}
sys.h
#ifndef ___SYS_H_
#define ___SYS_H_
#include <STC15F2K60S2.H>
#include <intrins.h>
#ifndef u8
#define u8 unsigned char
#endif
#ifndef u16
#define u16 unsigned int
#endif
//sys.c
void All_Init(void);
//uart.c
extern u8 BUF[10];
extern u8 flag;
void uart1_sendstring(u8* str);
void UART_Init(void);
#endif
要点:
1、存储数组要为 unsigned char
类型,因为当数据大于256时 char
类型会变成负数。
2、该写 else
的地方记得要写上!!!
参考博客 :
1、@关注我,优秀你 ——51 单片机自定义串口协议实现握手。
第三题
3. 使用普通的 IO 口实现 uart 的发送和接收功能。
(提示用定时器,做出来会对 uart 理解得更加深刻)
代码
uart.c
#include "sys.h"
void Timer0_Init(void) //定时器0初始化
{
AUXR |= 0x80;
TMOD &= 0xF0;
TH0=(65536 - 1250)/256; //12,000,000 / 9600 = 1250
TL0=(65536 - 1250)%256;
TR0=0;
TF0=0;
ET0 = 1;
EA = 1;
}
//检验波特率计数
void uart_scan(void)
{
while(!flag); //当flag为1时,跳出循环
flag=0; //清零flag
}
//发送字符
void uart_show(u8 str)
{
u8 i=8;
TR0=1; //开始计数
P31=(bit)0; //发送起始位
uart_scan(); //等待一个位的时间
while(i--) //发送8位数据位
{
P31=(bit)(str & 0x01); //先传低位
uart_scan(); //等待一个位的时间
str = str >> 1; //字符右移一位
}
P31=(bit)1; //发送结束位
uart_scan(); //等待一个位的时间
TR0=0; //停止计数
}
u8 uart_get(void){ //接收一个字符
u8 str_get = 0;
u8 i = 8;
if(P30 == 0){ //起始位
TR0=1; //启动Timer0
uart_scan(); //等过起始位
while(i--){ //接收8位数据位
str_get >>= 1;
if(P30)str_get |=0x80; //先收低位
uart_scan(); //等待一个位的时间
}
TR0=0; //停止Timer0
flag_show = 1; //置一flag_show
}
return str_get;
}
void Timer0(void) interrupt 1{
flag = 1; //每计数一个位的时间,使得flag为1
}
void All_Init(void){ //单片机初始化
P0 = 0xFF;
P2 = P2 & 0x1F | 0x80;
P2 = P2 & 0x1F | 0xE0;
P2 &= 0x1F;
P0 = 0x00;
P2 |= 0xA0;
P2 &= 0x1F;
}
main.c
#include "sys.h"
u8 flag = 0;
u8 flag_show = 0;
u8 str;
void main(){
Timer0_Init();
All_Init();
while(1){
str = uart_get();
if(flag_show == 1){
uart_show(str);
flag_show = 0;
}
}
}
sys.h
#ifndef ___SYS_H_
#define ___SYS_H_
#include <STC15F2K60S2.H>
#include <intrins.h>
#ifndef u8
#define u8 unsigned char
#endif
#ifndef u16
#define u16 unsigned int
#endif
//sys.c
extern u8 flag;
extern u8 flag_show;
void Timer0_Init(void);
void uart_show(u8 str);
void uart_scan(void);
u8 uart_get(void);
void All_Init(void);
#endif
要点:
1、注意9600波特率所需要的计数时间。
2、注意用位变量bit
,如果直接用字符型,会一次性输出八位。
3、接收时,要注意先等起始位。
参考博客:
1、@Crazzy_M—— 什么是波特率,波特率怎么计算。
2、
@丁林夕 —— 波特率与晶振。
3、
@HopesunIce—— 使用单片机普通 IO 口模拟串口的三种方法。