串口通信像是蓝桥杯单片机组国赛中一个若隐若现的秘境,总在不经意间为勇者们敞开大门。然而,初次探索这片领域的冒险者,常常会被其神秘莫测的特性所震慑,黯然退场(编不下去了,直接进入正题)。
附件:第十五届蓝桥杯单片机组国赛(串口部分)
一、串口三部曲
1.串口初始化
相关原理:速通串口通信
在STC-ISP中按照以下步骤操作即可
在keil中新建uart.c和uart.h
uart.c
#include "uart.h"
void Uart1_Init(void) //9600bps@12.000MHz
{
SCON = 0x50; //8位数据,可变波特率
AUXR |= 0x01; //串口1选择定时器2为波特率发生器
AUXR &= 0xFB; //定时器时钟12T模式
T2L = 0xE6; //设置定时初始值
T2H = 0xFF; //设置定时初始值
AUXR |= 0x10; //定时器2开始计时
ES = 1; //使能串口1中断
EA = 1;
}
extern char putchar(char ch)
{
SBUF = ch;
while(!TI);
TI = 0;
return ch;
}
uart.h
#include <STC15F2K60S2.H>
#include <stdio.h>
void Uart1_Init();
2.串口中断
main.c
typedef unsigned char u8;
pdata u8 uartBuf[10] = {0,0,0,0,0,0,0,0,0,0};
idata u8 uartBufIndex;
idata u8 uartTick;
idata bit uartFlag;
void Uart1_Isr(void) interrupt 4
{
if(RI)
{
uartFlag = 1;
uartTick = 0;
uartBuf[uartBufIndex++] = SBUF;
RI = 0;
}
if(uartBufIndex > 10)
{
uartTick = uartFlag = 0;
uartBufIndex = 0;
memset(uartBuf,0,10);
}
}
3.串口数据处理函数
#include <string.h>
#include <stdio.h>
#include <math.h>
void uartProc()
{
if(!uartBufIndex) return;
if(uartTick >= 10)
{
uartTick = uartFlag = 0;
//数据解析
memset(uartBuf,0,uartBufIndex);
uartBufIndex = 0;
}
}
void Timer1_Isr(void) interrupt 3
{
if(uartFlag)
uartTick++;
}
二、串口功能-查询设备状态
1.memcmp
与strcmp
对比接收数据存放数组可以使用memcmp
或者strcmp
-
int memcmp(const void *s1, const void *s2, size_t n)
比较两个内存区域的前 n 个字节。 -
int strcmp(const char *s1, const char *s2)
比较两个以\0
结尾的字符串,直到遇到第一个不匹配字符或结束符。
2.运动状态设计
定义idata u8 moveState;
表示运动状态:
0-空闲 1-等待 2-运行
3.串口解析"?"
void uartProc()
{
if(!uartBufIndex) return;
if(uartTick >= 10)
{
uartTick = uartFlag = 0;
//串口解析
if(strcmp(uartBuf, "?") == 0)
{
if(!moveState) //空闲状态
printf("Idle");
else if(moveState == 1) //等待状态
printf("Wait");
else //运行状态
printf("Busy");
}
memset(uartBuf,0,uartBufIndex);
uartBufIndex = 0;
}
}
- 空闲状态
- 运行状态
三、串口功能-查询设备位置
定义idata u16 currentPoint[2]
表示当前位置坐标
void uartProc()
{
if(!uartBufIndex) return;
if(uartTick >= 10)
{
uartTick = uartFlag = 0;
if(strcmp(uartBuf, "?") == 0)
{
if(!moveState) //空闲状态
printf("Idle");
else if(moveState == 1) //等待状态
printf("Wait");
else //运行状态
printf("Busy");
}
else if(strcmp(uartBuf, "#") == 0)
printf("(%u,%u)",currentPoint[0],currentPoint[1]);
memset(uartBuf,0,uartBufIndex);
uartBufIndex = 0;
}
}
四、串口功能-设置目的地坐标
接收坐标,例如(30,40)
数据 | 数组位置 | uartBufIndex |
---|---|---|
( | uartBuf[0] | 1 |
3 | uartBuf[1] | 2 |
0 | uartBuf[2] | 3 |
, | uartBuf[3] | 4 |
4 | uartBuf[4] | 5 |
0 | uartBuf[5] | 6 |
) | uartBuf[6] | 7 |
- 所以判断两个括号只需判断
uartBuf[0]
和uartBuf[uartBufIndex-1]
是否为(
和)
即可。 - 然后再将
uartBuf[1]
到uartBuf[uartBufIndex-2]
依次取出(用循环,每次循环取出一位),判断是否为数字(0~9),未碰到,
时,取出的数据是终点坐标的横坐标,用临时变量x
保存,碰到,
后,取出的数据是终点坐标的纵坐标,用临时变量y
保存。
pdata u16 pointOver[2] = {0,0}; //终点坐标
idata bit dateFlag; //串口接收坐标标志位
void uartProc()
{
if(!uartBufIndex) return;
if(uartTick >= 10)
{
uartTick = uartFlag = 0;
/*
if(strcmp(uartBuf, "#") == 0)
printf("(%u,%u)",currentPoint[0],currentPoint[1]);
else if(strcmp(uartBuf, "?") == 0)
{
if(!moveState) //空闲状态
printf("Idle");
else if(moveState == 1) //等待状态
printf("Wait");
else //运行状态
printf("Busy");
}
*/
else if(uartBuf[0] == '(' && uartBuf[uartBufIndex-1] == ')')
{
idata u16 x = 0, y = 0;
idata bit parse = 0; //解析标志位 0-解析x 1-解析y
idata bit useful = 0;//收到的坐标是否有效 0-有效 1-无效
idata u8 i;
for(i = 1; i < uartBufIndex - 1; i++)
{
u8 ch = uartBuf[i];
if(ch >= '0' && ch <= '9')
!parse ? (x = x * 10 + ch - '0') : (y = y * 10 + ch - '0');
else if(ch == ',')
parse = 1;
else//接收到无效数据
{
useful = 1;//收到的坐标无效
break;//直接退出循环
}
}
if(!useful)//数据有效时才保存坐标
{
pointOver[0] = x;
pointOver[1] = y;
dateFlag = 1; //收到目的地坐标
if(!moveState)
printf("Got it");
else
printf("Busy");
}
}
else
printf("Error");
memset(uartBuf,0,uartBufIndex);
uartBufIndex = 0;
}
}