本文基于小蜜蜂课程代码,其他知识点可参考本人其他博客
在国赛中,如果考到串口通讯的话,主要是考从上位机接收数据,然后单片机做出反应,并返回一部分数据。因此本文目的在于,正确接收上位机数据,并利用正确的格式将内容发送回上位机。
目前常见的方法有三种:
- 1.使用传统的串口收发函数,直接发送信息和接收信息。缺点:发送变量不好弄
- 2.使用print()打印函数,直接将信息打印在屏幕上。缺点:需要重定向print函数
- 3.使用sprintf()输出函数,将信息存到缓冲数组内,再用send_string()发送。缺点:没有缺点
本文介绍第3种方法,也是使用人数最多的方法
1.sprintf ()函数介绍
sprintf函数与C语言中常用函数print函数师出同源,做法基本一致,区别在于:
print函数是将输出直接打印在屏幕上,直接被你从屏幕上看到。在串口通讯中,我们需要将输出重定向到串口中,就可以直接发送到上位机了。但是本人调试了一下午,忽明忽暗,效果不好,于是放弃。
sprintf函数是将输出直接存储在一个缓冲区中(缓冲区:说人话就是一个自己定义的数组),不能直接被肉眼看到,但是可以通过读取数组看到。我们可以将要输出的 unsigned char、unsigned int、float先用格式化字符规定格式后,存储进数组中,再用send_string函数发送。实际调试中效果很不错。
我们查看keil5的帮助文档,可以看到sprintf的介绍:
从文档中可以提取到以下信息:
- 需要包含头文件stdio.h
- sprintf有三个参数,依次为:存储缓冲区指针,格式化字符,被格式化输出的变量
- 缓冲数组可自定义大小,且支持连续存储
在实际操作中,有以下笔记:
存储缓冲区数组:
- 我一般定义为unsigned char ri_value[8],基本可以满足测试需要。可以根据实际赛题调整大小和类型
- 由于是数组,因此ri_value就是一个指向索引值0的数组指针,可以直接写进sprintf函数
格式化字符:
- unsigned char : %bd(字符格式输出)
- unsigned int、 int :%d
- float: %f (带8个0的浮点数输出)、%.2f(只带两位小数的四舍五入数值输出)
- 数组:%s(将存储在数组中的数字,转变为字符形式输出)
- 此外:每一个格式化字符后要带一个\r\n,且顺序固定,用于在显示窗口换行
被格式化的变量:
- 直接格式化:可以是温度传感器数值,时间数值……
- 间接格式化:通常是我们从上位机接收的数据,要先放入一个数组中,在进行格式化转换
操作示例:
2.实际编程中的sprintf()函数
先包含头文件:#include <stdio.h>
发送float变量时:直接将变量丢进sprintf函数,再将函数输出的数组内容发送出去。运行结果为:25.56
发送unsigned char变量:
发送int、unsigned int时(负数也不会出错):
发送一整个数组时:先将数组格式化为%s类型,再进行发送
接收到数据进行处理时:开发板如果需要接收数据,并处理后显示到数码管上,那么需要直接将接收的数据-48。因为字符‘0’的ASCII为48,即可转换为数字0
发送接收到的数据时:先将接收的数据逐个存入数组中,再转换为%s发送。要注意,由于接收采用中断,发送采用查询,可能出现还没把完整的数据接收完就开始发送了。因此要定义一个10ms的标志变量,当开始检测到有数据进来时,就等待10ms再读取接收的数据。
计算:波特率为9600,则发送一个8位字符的时间为0.8ms,由此可见10ms足够
(图片只是示例,重要的是思想,不是代码)
3.串口通讯函数
首先,配置串口初始化代码:用定时计数器2!!!节约资源
注意:需要自己手动添加上ES和EA标志位。并手动添加相关寄存器:
寄存器地址可以在stc-isp的头文件中查询到
然后,三个函数如下:
4.代码目的
配置串口通讯程序,使之具备以下功能:
开机自动发送一行字,并换行:I‘m xx! xx为年龄
上位机发送指令:ST123,单片机返回数值 1 2 3 中间有空格
上位机发送指令:SM123,最后两个数字+1并显示在数码管,不返回内容
要求:每一行各自独立,且字符显示正常。串口助手配置在文本模式下
5.参考代码
#include <REGX51.H>
#include < intrins.h >
#include <stdio.h>
//\0在字符形式下就是0x0A
sfr AUXR = 0x8e;
sfr T2H = 0xD6; //0000,0000 T2高字节
sfr T2L = 0xD7; //0000,0000 T2低字节
unsigned char code duanma[20] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,
0x88,0x83,0xc6,0xc0,0x86,0x8e,0xbf,0xc7,0x89,0x8c};
//锁存器通道选择函数
void select_HC573 ( unsigned char channal )
{
switch ( channal )
{
case 4:
P2 = ( P2 & 0x1f ) | 0x80;
break;
case 5:
P2 = ( P2 & 0x1f ) | 0xa0;
break;
case 6:
P2 = ( P2 & 0x1f ) | 0xc0;
break;
case 7:
P2 = ( P2 & 0x1f ) | 0xe0;
break;
case 0:
P2 = ( P2 & 0x1f ) | 0x00;
break;
}
}
//单位数码管显示函数
void state_SMG ( unsigned char pos_SMG , unsigned char value_SMG )
{
select_HC573 ( 0 );
P0 = 0x01 << pos_SMG;
select_HC573( 6 );
select_HC573 ( 0 );
P0 = value_SMG;
select_HC573( 7 );
select_HC573 ( 0 );
}
void init_uart ()
{
SCON = 0x50; //8位数据,可变波特率
AUXR |= 0x01; //串口1选择定时器2为波特率发生器
AUXR &= 0xFB; //定时器时钟12T模式
T2L = 0xE6; //设置定时初始值
T2H = 0xFF; //设置定时初始值
AUXR |= 0x10; //定时器2开始计时
ES = 1;
EA = 1;
}
//接收信息采用中断
unsigned char ri_value[6];//接收的指令不超过5个字符,再留一位给\0
unsigned char ri_index = 0;
void recive_uart () interrupt 4
{
if ( RI == 1 )
{
RI = 0;
ri_value[ri_index++] = SBUF;
}
}
void send_uart ( unsigned char value )
{
SBUF = value;
while ( TI == 0 );
TI = 0;
}
void send_string ( unsigned char *value )
{
while ( *value != '\0' )
{
send_uart( *value++ );
}
}
void init_timer0 (void) //50微秒@12.000MHz
{
AUXR &= 0x7F; //定时器时钟12T模式
TMOD &= 0xF0; //设置定时器模式
TL0 = 0xCE; //设置定时初始值
TH0 = 0xFF; //设置定时初始值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0 = 1;
EA = 1;
}
unsigned char count_50us = 0;
bit flag_uart = 0;
bit flag_10ms = 0;
void timer0_service () interrupt 1
{
if ( flag_uart == 1 )
{
if ( ++count_50us == 200 )
{
flag_10ms = 1;
}
}
}
unsigned char smg_value1 = 0;
unsigned char smg_value2 = 0;
unsigned char age = 18;
unsigned char buffer_value[8];//发送缓冲,
void uart_work()
{
if ( ri_index != 0 )
{
flag_uart = 1;
if ( flag_10ms == 1 )
{
flag_uart = 0;
if ( ri_value[0] == 'S' && ri_value[1] == 'T' )
{
sprintf ( buffer_value , "%c %c %c\r\n" , ri_value[2],ri_value[3],ri_value[4] );
send_string ( buffer_value );
// sprintf ( buffer_value , "%s\r\n" , ri_value );//这样写没法添加空格
// send_string ( buffer_value+2 );
ri_index = 0;
flag_10ms = 0;
}
else if ( ri_value[0] == 'S' && ri_value[1] == 'M' )
{
smg_value1 = ri_value[3]-48;
smg_value2 = ri_value[4]-48;
ri_index = 0;
flag_10ms = 0;
}
}
}
}
void Delay1ms() //@12.000MHz
{
unsigned char i, j;
i = 12;
j = 169;
do
{
while (--j);
} while (--i);
}
void flash_SMG ()
{
state_SMG ( 6 , duanma[smg_value1+1] );
Delay1ms();
state_SMG ( 7 , duanma[smg_value2+1] );
Delay1ms();
}
void main ()
{
init_uart ();
init_timer0 ();
sprintf ( buffer_value , "I'm %bd!\r\n" , age );
send_string ( buffer_value );
while ( 1 )
{
flash_SMG ();
uart_work();
}
}