常用的一些文件
常用的一些文件
数码管真值表
unsigned char code LedChar[] = {
0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8,
0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E};
这里介绍一下51单片机的关键字
code
。当数据使用code
进行修饰时,这个数据就会存储到Flash中,大大节省单片机的RAM用量。当然,这个数据就不能够更改。
使用示例
功能:令普中51开发板的最右侧数码管进行16进制定时。
#include <reg52.h>
unsigned char code LedChar[] = {
0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8,
0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E};
unsigned char cnt = 0; // 记录T0中断次数
unsigned char sec = 0; // 记录经过的秒数
sbit addr_1 = P2 ^ 2;
sbit addr_2 = P2 ^ 3;
sbit addr_3 = P2 ^ 4;
void main()
{
addr_1 = 0;
addr_2 = 0;
addr_3 = 0;
TMOD = 0x01;
TH0 = 0xB8;
TL0 = 0x00;
TR0 = 1;
while (1)
{
if (TF0 == 1)
{
TF0 = 0;
TH0 = 0xB8;
TL0 = 0x00;
P0 = ~LedChar[sec];
cnt++;
if (cnt >= 50)
{
cnt = 0;
sec++;
if (sec >= 16)
{
sec = 0;
}
}
}
}
return;
}
四相五线步进电机
步进电机内部结构示意图
四相五线步进电机八拍模式绕组控制顺序表
步进电机控制电路
28BYJ-48 步进电机参数表
真实减速比:1:63.684,转过一圈所需节拍数:64*63.684≈4076
步进电机节拍对应的 IO 控制代码
unsigned char code BeatCode[8] = { // 步进电机节拍对应的 IO 控制代码
0xE, 0xC, 0xD, 0x9, 0xB, 0x3, 0x7, 0x6};
示例代码
#include <reg52.h>
unsigned char code BeatCode[8] = { // 步进电机节拍对应的 IO 控制代码
0xE, 0xC, 0xD, 0x9, 0xB, 0x3, 0x7, 0x6};
void delay();
void main()
{
unsigned char tmp; // 定义一个临时变量
unsigned char index = 0; // 定义节拍输出索引
while (1)
{
tmp = P1; // 暂存P1口
tmp &= 0xF0; // 清除低四位
tmp |= BeatCode[index]; // 将节拍写入低四位
P1 = tmp; // 把低 4 位的节拍代码和高 4 位的原值送回 P1
index++; // 节拍输出索引递增
index &= 0x07; // 到 8 归零
delay(); // 延时 2ms,即 2ms 执行一拍
}
}
void delay()
{
unsigned char sec = 200;
while (sec--)
;
}
配置T0中断
/*配置并启动T0即T0中断,ms为T0定时时间*/
void configTimer0(unsigned int ms)
{
unsigned long temp = 0;
temp = 11059200 / 12;
temp = (temp * ms) / 1000;
temp = 65536 - temp;
temp = temp + 13; // 补偿中断响应延时造成的误差(按需调整)
T0RH = (unsigned char)(temp >> 8); // 定时器重载值拆分为高低字节
T0RL = (unsigned char)temp;
TMOD &= 0xF0;
TMOD |= 0x01;
TH0 = T0RH;
TL0 = T0RL;
ET0 = 1;
TR0 = 1;
}
串口配置函数
/*串口配置函数,baud为通信波特率*/
void configUART(unsigned int baud)
{
SCON = 0x50;
TMOD &= 0x0F;
TMOD |= 0x20;
TH1 = 256 - 11059200 / 12 / 2 / 16 / baud;
TL1 = TH1;
ES = 1; // 使能串口中断
TR1 = 1;
}
LCD1602驱动代码
LCD1602.c:
// 基于普中51开发板——LCD1601驱动
#include <REG52.H>
#define LCD1602_DB P0
sbit LCD1602_RS = P2 ^ 6;
sbit LCD1602_RW = P2 ^ 5;
sbit LCD1602_EN = P2 ^ 7;
/*等待液晶准备好*/
void lcdWaitReady()
{
unsigned char sta;
LCD1602_DB = 0xFF;
LCD1602_RS = 0;
LCD1602_RW = 1;
do
{
LCD1602_EN = 1;
sta = LCD1602_DB; // 读取状态字
LCD1602_EN = 0;
} while (sta & 0x80); // bit_7为1时表示液晶正忙,重复检测直到其等于1为止
}
/*向LCD1602液晶写入一字节命令,cmd为待写入命令值*/
void lcdWriteCmd(unsigned char cmd)
{
lcdWaitReady();
LCD1602_RS = 0;
LCD1602_RW = 0;
LCD1602_DB = cmd;
LCD1602_EN = 1;
LCD1602_EN = 0;
}
/*向LCD1602液晶写入一字节命令,dat为待写入命令*/
void lcdWriteDta(unsigned char dat)
{
lcdWaitReady();
LCD1602_RS = 1;
LCD1602_RW = 0;
LCD1602_DB = dat;
LCD1602_EN = 1;
LCD1602_EN = 0;
}
/*设置显示RAM起始地址,亦即光标位置,(x,y)为对应屏幕上的字符坐标*/
void lcdSetCursor(unsigned char x, unsigned char y)
{
unsigned char addr;
if (y == 0)
{ // 由输入的屏幕坐标计算显示RAM的地址
addr = 0x00 + x; // 第一行字符地址从0x00起始
}
else
{
addr = 0x40 + x; // 第二行字符地址从0x40起始
}
lcdWriteCmd(addr | 0x80); // 设置RAM地址
}
/*在液晶上显示字符串,(x,y)为对应屏幕上的起始坐标,str为字符串指针,len为需显示的字符长度*/
void lcdShowStr(unsigned char x, unsigned char y, unsigned char *str, unsigned char len)
{
lcdSetCursor(x, y); // 设置起始地址
while (len--)
{ // 连续写入字符串数据
lcdWriteDta(*str++); // 先取str指向的数据,然后str自加1
}
}
/*区域清除,清除从(x,y)坐标起始的len个字符*/
void lcdAreaClear(unsigned char x, unsigned char y, unsigned char len)
{
lcdSetCursor(x, y);
while (len--)
{
lcdWriteDta(' ');
}
}
/*清屏指令*/
void lcdFullClear()
{
lcdWriteCmd(0x01);
}
/*初始化1602液晶*/
void initLCD1602()
{
lcdWriteCmd(0x38); // 16*2显示,5*7点阵,8位数据接口
lcdWriteCmd(0x0C); // 显示器开,光标关闭
lcdWriteCmd(0x06); // 文字不动,地址自动+1
lcdWriteCmd(0x01); // 清屏
}
使用示例
功能:字幕滚动
main.c:
#include <REG52.H>
bit flag500ms = 0;
unsigned char T0RH = 0;
unsigned char T0RL = 0;
unsigned char code str1[] = "Hello world! ";
unsigned char code str2[] = "I'm Liukanxi.";
void configTimer0(unsigned int ms);
extern void initLCD1602();
extern void lcdShowStr(unsigned char x, unsigned char y, unsigned char *str, unsigned char len);
void main()
{
unsigned char i;
unsigned char index = 0;
unsigned char pdata bufMove1[16 + sizeof(str1) + 16];
unsigned char pdata bufMove2[16 + sizeof(str2) + 16];
for (i = 0; i < 16; i++)
{
bufMove1[i] = ' ';
bufMove2[i] = ' ';
}
for (i = 16; i < 16 + sizeof(str1) - 1; i++)
{
bufMove1[i] = str1[i - 16];
bufMove2[i] = str2[i - 16];
}
for (i = 16 + sizeof(str1) - 1; i < sizeof(bufMove1); i++)
{
bufMove1[i] = ' ';
bufMove2[i] = ' ';
}
EA = 1;
configTimer0(10);
initLCD1602();
while (1)
{
if (flag500ms)
{
flag500ms = 0;
lcdShowStr(0, 0, bufMove1 + index, 16);
lcdShowStr(0, 1, bufMove2 + index, 16);
index++;
if (index >= 16 + sizeof(str1) - 1)
index = 0;
}
}
}
/*配置并启动T0即T0中断,ms为T0定时时间*/
void configTimer0(unsigned int ms)
{
unsigned long temp = 0;
temp = 11059200 / 12;
temp = (temp * ms) / 1000;
temp = 65536 - temp;
temp = temp + 12; // 补偿中断响应延时造成的误差
T0RH = (unsigned char)(temp >> 8); // 定时器重载值拆分为高低字节
T0RL = (unsigned char)temp;
TMOD &= 0xF0;
TMOD |= 0x01;
TH0 = T0RH;
TL0 = T0RL;
ET0 = 1;
TR0 = 1;
}
void interruptTimer0() interrupt 1
{
static unsigned char cnt500ms = 0;
TH0 = T0RH;
TL0 = T0RL;
cnt500ms++;
if (cnt500ms >= 50)
{
flag500ms = 1;
cnt500ms = 0;
}
}
keyboard.c
#include <REG52.H>
sbit keyIn_1 = P1 ^ 7;
sbit keyIn_2 = P1 ^ 6;
sbit keyIn_3 = P1 ^ 5;
sbit keyIn_4 = P1 ^ 4;
sbit keyOut_1 = P1 ^ 3;
sbit keyOut_2 = P1 ^ 2;
sbit keyOut_3 = P1 ^ 1;
sbit keyOut_4 = P1 ^ 0;
unsigned char code keyCodeMap[4][4] = {
{0x01, 0x02, 0x03, 0x0A},
{0x04, 0x05, 0x06, 0x0B},
{0x07, 0x08, 0x09, 0x0C},
{0x0E, 0x00, 0x0F, 0x0D}};
unsigned char pdata keySta[4][4] = {
{1, 1, 1, 1},
{1, 1, 1, 1},
{1, 1, 1, 1},
{1, 1, 1, 1}};
extern void keyAction(unsigned char keyCode); // 按钮响应动作函数,需要在main.c中定义
/*按键驱动函数,检测按键动作,调度相应动作函数,需在主循环中调用*/
void keyDriver()
{
unsigned char i, j;
static unsigned char pdata backup[4][4] = {// 按键值备份,保存前一次的值
{1, 1, 1, 1},
{1, 1, 1, 1},
{1, 1, 1, 1},
{1, 1, 1, 1}};
for (i = 0; i < 4; i++)
{
for (j = 0; j < 4; j++)
{
if (backup[i][j] != keySta[i][j])
{ // 检测按键动作
if (backup[i][j] == 1)
{ // 按键按下时执行动作
keyAction(keyCodeMap[i][j]);
}
backup[i][j] = keySta[i][j];
}
}
}
}
/*按键扫描函数,需在定时器中断中调用,推荐调用间隔1ms*/
void keyScan()
{
unsigned char i;
static unsigned char keyOut = 0; // 矩阵按键扫描输出索引
static unsigned char pdata keyBuf[4][4] = {// 矩阵按键扫描缓存
{0xFF, 0xFF, 0xFF, 0xFF},
{0xFF, 0xFF, 0xFF, 0xFF},
{0xFF, 0xFF, 0xFF, 0xFF},
{0xFF, 0xFF, 0xFF, 0xFF}};
keyBuf[keyOut][0] = (keyBuf[keyOut][0] << 1) | keyOut_1;
keyBuf[keyOut][1] = (keyBuf[keyOut][1] << 1) | keyOut_2;
keyBuf[keyOut][2] = (keyBuf[keyOut][2] << 1) | keyOut_3;
keyBuf[keyOut][3] = (keyBuf[keyOut][3] << 1) | keyOut_4;
for (i = 0; i < 4; i++)
{
if ((keyBuf[keyOut][i] & 0x0F) == 0x0F)
{
keySta[keyOut][i] = 1;
}
else if ((keyBuf[keyOut][i] & 0x0F) == 0x00)
{
keySta[keyOut][i] = 0;
}
}
switch (keyOut)
{
case 0:
keyIn_1 = 1;
keyIn_2 = 0;
keyOut++;
break;
case 1:
keyIn_2 = 1;
keyIn_3 = 0;
keyOut++;
break;
case 2:
keyIn_3 = 1;
keyIn_4 = 0;
keyOut++;
break;
case 3:
keyIn_4 = 1;
keyIn_1 = 0;
keyOut = 0;
break;
default:
break;
}
}
使用示例(计算器)
main.c
#include <REG52.H>
unsigned char step = 0; // 操作步数,0——第一步(num1),1——第二步(num2),2——第三步(result)
unsigned char oprt = 0; // 操作符
signed long num1 = 0; // 第一个数
signed long num2 = 0; // 第二个数
signed long result = 0; // 结果
unsigned char T0RH = 0;
unsigned char T0RL = 0;
void configTimer0(unsigned int ms);
extern void keyScan();
extern void keyDriver();
extern void initLCD1602();
extern void lcdShowStr(unsigned char x, unsigned char y,
unsigned char *str);
extern void lcdAreaClear(unsigned char x, unsigned char y, unsigned char len);
extern void lcdFullClear();
void main()
{
EA = 1;
configTimer0(1);
initLCD1602();
lcdShowStr(15, 1, "0"); // 在第二行最左边输出一个0
while (1)
{
keyDriver();
}
}
/*将dat转换为字符串*/
unsigned char longToString(unsigned char *str, signed long dat)
{
signed char i = 0;
unsigned char len = 0; // 转换后的字符串长度
unsigned char buf[12];
if (dat < 0) // 如果dat是负数,要将dat转换为正数
{
dat = -dat;
*str++ = '-'; // 在str最左侧加上一个负号
len++;
}
/*两次转换是为了不让字符串颠倒*/
do
{
buf[i++] = dat % 10 + '0';
dat /= 10;
} while (dat > 0);
len += i;
while (i--)
{
*str++ = buf[i];
}
*str = '\0';
return len;
}
/*显示运算符*/
void showOprt(unsigned char y, unsigned char type)
{
switch (type)
{
case 0:
lcdShowStr(0, y, "+");
break;
case 1:
lcdShowStr(0, y, "-");
break;
case 2:
lcdShowStr(0, y, "*");
break;
case 3:
lcdShowStr(0, y, "/");
break;
default:
break;
}
}
/*计算器复位,清零变量,清除屏幕显示*/
void reset()
{
num1 = 0;
num2 = 0;
step = 0;
lcdFullClear();
}
/*数字键动作函数,n为按键输入的数值*/
void numKeyAction(unsigned char n)
{
unsigned char len;
unsigned char str[12];
if (step > 1) // 如果计算完成,要重新开始进行新的运算
{
reset();
}
if (step == 0) // 输入第一个数字
{
num1 = num1 * 10 + n;
len = longToString(str, num1);
lcdShowStr(16 - len, 1, str);
}
else // 输入第二个数字
{
num2 = num2 * 10 + n;
len = longToString(str, num2);
lcdShowStr(16 - len, 1, str);
}
}
/*运算符按键动作函数*/
void oprtKeyAction(unsigned char type)
{
unsigned char len;
unsigned char str[12];
if (step == 0) // 第二个数字没有输入的时候响应,不支持连续操作
{
len = longToString(str, num1); // 第一操作数转换为字符串
lcdAreaClear(0, 0, 16 - len); // 清除第一行左边的字符位
lcdShowStr(16 - len, 0, str); // 字符串靠右显示在第一行
showOprt(1, type);
lcdAreaClear(1, 1, 14);
lcdShowStr(15, 1, "0");
oprt = type;
step = 1;
}
}
void getResult()
{
unsigned char len;
unsigned char str[12];
if (step == 1)
{
step = 2;
switch (oprt)
{
case 0:
result = num1 + num2;
break;
case 1:
result = num1 - num2;
break;
case 2:
result = num1 * num2;
break;
case 3:
result = num1 / num2;
break;
default:
break;
}
len = longToString(str, num2);
showOprt(0, oprt);
lcdAreaClear(1, 0, 16 - 1 - len);
lcdShowStr(16 - len, 0, str);
len = longToString(str, result);
lcdShowStr(0, 1, "=");
lcdAreaClear(1, 1, 16 - 1 - len);
lcdShowStr(16 - len, 1, str);
}
}
void keyAction(unsigned char keyCode)
{
if (keyCode >= 0x00 && keyCode <= 0x09)
{
numKeyAction(keyCode);
}
else if (keyCode == 0x0A)
{
oprtKeyAction(0);
}
else if (keyCode == 0x0B)
{
oprtKeyAction(1);
}
else if (keyCode == 0x0C)
{
oprtKeyAction(2);
}
else if (keyCode == 0x0D)
{
oprtKeyAction(3);
}
else if (keyCode == 0x0E)
{
getResult();
}
else if (keyCode == 0x0F)
{
reset();
lcdShowStr(15, 1, "0");
}
}
void configTimer0(unsigned int ms)
{
unsigned long temp;
temp = 11059200 / 12;
temp = (temp * ms) / 1000;
temp = 65536 - temp;
temp = temp + 29; // 等待校准
T0RH = (unsigned char)(temp >> 8);
T0RL = (unsigned char)temp;
TMOD &= 0xF0;
TMOD |= 0x01;
TH0 = T0RH;
TL0 = T0RL;
ET0 = 1;
TR0 = 1;
}
void interruptTimer0() interrupt 1
{
TH0 = T0RH;
TL0 = T0RL;
keyScan();
}
keyboard.c
#include <REG52.H>
sbit keyIn_1 = P1 ^ 7;
sbit keyIn_2 = P1 ^ 6;
sbit keyIn_3 = P1 ^ 5;
sbit keyIn_4 = P1 ^ 4;
sbit keyOut_1 = P1 ^ 3;
sbit keyOut_2 = P1 ^ 2;
sbit keyOut_3 = P1 ^ 1;
sbit keyOut_4 = P1 ^ 0;
unsigned char code keyCodeMap[4][4] = {
{0x01, 0x02, 0x03, 0x0A},
{0x04, 0x05, 0x06, 0x0B},
{0x07, 0x08, 0x09, 0x0C},
{0x0E, 0x00, 0x0F, 0x0D}};
unsigned char pdata keySta[4][4] = {
{1, 1, 1, 1},
{1, 1, 1, 1},
{1, 1, 1, 1},
{1, 1, 1, 1}};
extern void keyAction(unsigned char keyCode); // 按钮响应动作函数,需要在main.c中定义
/*按键驱动函数,检测按键动作,调度相应动作函数,需在主循环中调用*/
void keyDriver()
{
unsigned char i, j;
static unsigned char pdata backup[4][4] = {// 按键值备份,保存前一次的值
{1, 1, 1, 1},
{1, 1, 1, 1},
{1, 1, 1, 1},
{1, 1, 1, 1}};
for (i = 0; i < 4; i++)
{
for (j = 0; j < 4; j++)
{
if (backup[i][j] != keySta[i][j])
{ // 检测按键动作
if (backup[i][j] == 1)
{ // 按键按下时执行动作
keyAction(keyCodeMap[i][j]);
}
backup[i][j] = keySta[i][j];
}
}
}
}
/*按键扫描函数,需在定时器中断中调用,推荐调用间隔1ms*/
void keyScan()
{
unsigned char i;
static unsigned char keyOut = 0; // 矩阵按键扫描输出索引
static unsigned char pdata keyBuf[4][4] = {// 矩阵按键扫描缓存
{0xFF, 0xFF, 0xFF, 0xFF},
{0xFF, 0xFF, 0xFF, 0xFF},
{0xFF, 0xFF, 0xFF, 0xFF},
{0xFF, 0xFF, 0xFF, 0xFF}};
keyBuf[keyOut][0] = (keyBuf[keyOut][0] << 1) | keyOut_1;
keyBuf[keyOut][1] = (keyBuf[keyOut][1] << 1) | keyOut_2;
keyBuf[keyOut][2] = (keyBuf[keyOut][2] << 1) | keyOut_3;
keyBuf[keyOut][3] = (keyBuf[keyOut][3] << 1) | keyOut_4;
for (i = 0; i < 4; i++)
{
if ((keyBuf[keyOut][i] & 0x0F) == 0x0F)
{
keySta[keyOut][i] = 1;
}
else if ((keyBuf[keyOut][i] & 0x0F) == 0x00)
{
keySta[keyOut][i] = 0;
}
}
switch (keyOut)
{
case 0:
keyIn_1 = 1;
keyIn_2 = 0;
keyOut++;
break;
case 1:
keyIn_2 = 1;
keyIn_3 = 0;
keyOut++;
break;
case 2:
keyIn_3 = 1;
keyIn_4 = 0;
keyOut++;
break;
case 3:
keyIn_4 = 1;
keyIn_1 = 0;
keyOut = 0;
break;
default:
break;
}
}
LCD1602.c
// 基于普中51开发板——LCD1601驱动
#include <REG52.H>
#define LCD1602_DB P0
sbit LCD1602_RS = P2 ^ 6;
sbit LCD1602_RW = P2 ^ 5;
sbit LCD1602_EN = P2 ^ 7;
/*等待液晶准备好*/
void lcdWaitReady()
{
unsigned char sta;
LCD1602_DB = 0xFF;
LCD1602_RS = 0;
LCD1602_RW = 1;
do
{
LCD1602_EN = 1;
sta = LCD1602_DB; // 读取状态字
LCD1602_EN = 0;
} while (sta & 0x80); // bit_7为1时表示液晶正忙,重复检测直到其等于1为止
}
/*向LCD1602液晶写入一字节命令,cmd为待写入命令值*/
void lcdWriteCmd(unsigned char cmd)
{
lcdWaitReady();
LCD1602_RS = 0;
LCD1602_RW = 0;
LCD1602_DB = cmd;
LCD1602_EN = 1;
LCD1602_EN = 0;
}
/*向LCD1602液晶写入一字节命令,dat为待写入命令*/
void lcdWriteDta(unsigned char dat)
{
lcdWaitReady();
LCD1602_RS = 1;
LCD1602_RW = 0;
LCD1602_DB = dat;
LCD1602_EN = 1;
LCD1602_EN = 0;
}
/*设置显示RAM起始地址,亦即光标位置,(x,y)为对应屏幕上的字符坐标*/
void lcdSetCursor(unsigned char x, unsigned char y)
{
unsigned char addr;
if (y == 0)
{ // 由输入的屏幕坐标计算显示RAM的地址
addr = 0x00 + x; // 第一行字符地址从0x00起始
}
else
{
addr = 0x40 + x; // 第二行字符地址从0x40起始
}
lcdWriteCmd(addr | 0x80); // 设置RAM地址
}
/*在液晶上显示字符串,(x,y)为对应屏幕上的起始坐标,str为字符串指针*/
void lcdShowStr(unsigned char x, unsigned char y, unsigned char *str)
{
lcdSetCursor(x, y); // 设置起始地址
while (*str != '\0')
{ // 连续写入字符串数据,直到检测到结束符
lcdWriteDta(*str++); // 先取str指向的数据,然后str自加1
}
}
/*区域清除,清除从(x,y)坐标起始的len个字符*/
void lcdAreaClear(unsigned char x, unsigned char y, unsigned char len)
{
lcdSetCursor(x, y);
while (len--)
{
lcdWriteDta(' ');
}
}
/*清屏指令*/
void lcdFullClear()
{
lcdWriteCmd(0x01);
}
/*初始化1602液晶*/
void initLCD1602()
{
lcdWriteCmd(0x38); // 16*2显示,5*7点阵,8位数据接口
lcdWriteCmd(0x0C); // 显示器开,光标关闭
lcdWriteCmd(0x06); // 文字不动,地址自动+1
lcdWriteCmd(0x01); // 清屏
}
UART.c
#include <REG52.H>
bit flagFrame = 0; // 帧接收完成标志,即接收到一帧新数据
bit flagTxd = 0; // 单字节发送完成标志,用来替代TXD中断标志位
unsigned char cntRxd = 0; // 接收字节计数器
unsigned char pdata bufRxd[64]; // 接收字节缓冲区
extern void uartAction(unsigned char *buf, unsigned char len);
/*串口配置函数*/
void configUART(unsigned int baud)
{
SCON = 0x50;
TMOD &= 0x0F;
TMOD |= 0x20;
TH1 = 256 - 11059200 / 12 / 2 / 16 / baud;
TL1 = TH1;
ES = 1;
TR1 = 1;
}
/*串口数据写入,即串口发送函数,buf为待发送数据的指针,len为指定的发送长度*/
void uartWrite(unsigned char *buf, unsigned char len)
{
while (len--)
{ // 循环发送所有字节
flagTxd = 0; // 清零发送标志
SBUF = *buf++; // 发送一个字节数据
while (!flagTxd)
; // 等待该字节发送完毕
}
}
/*串口数据读取函数,buf为接收指针,len为指定的读取长度,返回值为实际读取到的长度*/
unsigned char uartRead(unsigned char *buf, unsigned char len)
{
unsigned char i;
if (len > cntRxd)
{ // 指定读取长度大于实际接收到的数据长度时
len = cntRxd; // 读取长度设置为实际接受到的数据长度
}
for (i = 0; i < len; i++)
{ // 复制接收到的数据到接收指针上
*buf++ = bufRxd[i];
}
cntRxd = 0; // 接收计数器请零
return len;
}
/*串口接收监控,由空闲时间判定帧结束,需在定时中断中调用,ms为定时间隔*/
void uartMonitor(unsigned char ms)
{
static unsigned char cntBkp = 0;
static unsigned char tm = 0;
if (cntRxd > 0)
{ // 接收计数器大于0,监控总线空闲时间
if (cntBkp != cntRxd)
{ // 接收计数器改变,即刚接收到数据时,清0空闲计时器
cntBkp = cntRxd;
tm = 0;
}
else
{ // 接收计数器未改变,即总线空闲时,累积空闲时间
if (tm < 30)
{ // 空闲时间小于30ms(经验值)
tm += ms;
if (tm >= 30)
{ // 空闲时间达到30ms时,即判定为一帧接收完毕
flagFrame = 1; // 设置帧接收完成标志
}
}
}
}
else
{
cntBkp = 0;
tm = 0;
}
}
/*串口驱动函数,检测数据帧的接收,调度功能函数,需在主循环中调用*/
void uartDriver()
{
unsigned char len;
unsigned char pdata buf[40];//按需调整
if (flagFrame) // 有命令到达时,读取处理该命令
{
flagFrame = 0;
len = uartRead(buf, sizeof(buf)); // 将接收到的命令读取到缓冲区中
uartAction(buf, len); // 传递数据帧,调用动作执行函数
}
}
/*串口中断服务函数*/
void interruptUART() interrupt 4
{
if (RI) // 接收到新字节
{
RI = 0; // 清0接收中断标志位
if (cntRxd < sizeof(bufRxd)) // 接收缓冲区尚未用完
{
bufRxd[cntRxd++] = SBUF; // 保存接收字节,并递增计数器
}
}
if (TI) // 字节发送完毕
{
TI = 0; // 清零发送中断标志位
flagTxd = 1; // 设置字节发送完成标志
}
}