蓝桥杯单片机-国赛4——基于sprintf()函数的串口通讯收与发

2024-05-25 01:28

本文主要是介绍蓝桥杯单片机-国赛4——基于sprintf()函数的串口通讯收与发,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

本文基于小蜜蜂课程代码,其他知识点可参考本人其他博客


        在国赛中,如果考到串口通讯的话,主要是考从上位机接收数据,然后单片机做出反应,并返回一部分数据。因此本文目的在于,正确接收上位机数据,并利用正确的格式将内容发送回上位机。

目前常见的方法有三种:

  • 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();}
}

这篇关于蓝桥杯单片机-国赛4——基于sprintf()函数的串口通讯收与发的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1000090

相关文章

hdu1171(母函数或多重背包)

题意:把物品分成两份,使得价值最接近 可以用背包,或者是母函数来解,母函数(1 + x^v+x^2v+.....+x^num*v)(1 + x^v+x^2v+.....+x^num*v)(1 + x^v+x^2v+.....+x^num*v) 其中指数为价值,每一项的数目为(该物品数+1)个 代码如下: #include<iostream>#include<algorithm>

基于51单片机的自动转向修复系统的设计与实现

文章目录 前言资料获取设计介绍功能介绍设计清单具体实现截图参考文献设计获取 前言 💗博主介绍:✌全网粉丝10W+,CSDN特邀作者、博客专家、CSDN新星计划导师,一名热衷于单片机技术探索与分享的博主、专注于 精通51/STM32/MSP430/AVR等单片机设计 主要对象是咱们电子相关专业的大学生,希望您们都共创辉煌!✌💗 👇🏻 精彩专栏 推荐订阅👇🏻 单片机

C++操作符重载实例(独立函数)

C++操作符重载实例,我们把坐标值CVector的加法进行重载,计算c3=c1+c2时,也就是计算x3=x1+x2,y3=y1+y2,今天我们以独立函数的方式重载操作符+(加号),以下是C++代码: c1802.cpp源代码: D:\YcjWork\CppTour>vim c1802.cpp #include <iostream>using namespace std;/*** 以独立函数

函数式编程思想

我们经常会用到各种各样的编程思想,例如面向过程、面向对象。不过笔者在该博客简单介绍一下函数式编程思想. 如果对函数式编程思想进行概括,就是f(x) = na(x) , y=uf(x)…至于其他的编程思想,可能是y=a(x)+b(x)+c(x)…,也有可能是y=f(x)=f(x)/a + f(x)/b+f(x)/c… 面向过程的指令式编程 面向过程,简单理解就是y=a(x)+b(x)+c(x)

Java Websocket实例【服务端与客户端实现全双工通讯】

Java Websocket实例【服务端与客户端实现全双工通讯】 现很多网站为了实现即时通讯,所用的技术都是轮询(polling)。轮询是在特定的的时间间隔(如每1秒),由浏览器对服务器发 出HTTP request,然后由服务器返回最新的数据给客服端的浏览器。这种传统的HTTP request 的模式带来很明显的缺点 – 浏 览器需要不断的向服务器发出请求,然而HTTP

利用matlab bar函数绘制较为复杂的柱状图,并在图中进行适当标注

示例代码和结果如下:小疑问:如何自动选择合适的坐标位置对柱状图的数值大小进行标注?😂 clear; close all;x = 1:3;aa=[28.6321521955954 26.2453660695847 21.69102348512086.93747104431360 6.25442246899816 3.342835958564245.51365061796319 4.87

OpenCV结构分析与形状描述符(11)椭圆拟合函数fitEllipse()的使用

操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C++11 算法描述 围绕一组2D点拟合一个椭圆。 该函数计算出一个椭圆,该椭圆在最小二乘意义上最好地拟合一组2D点。它返回一个内切椭圆的旋转矩形。使用了由[90]描述的第一个算法。开发者应该注意,由于数据点靠近包含的 Mat 元素的边界,返回的椭圆/旋转矩形数据

单片机毕业设计基于单片机的智能门禁系统的设计与实现

文章目录 前言资料获取设计介绍功能介绍程序代码部分参考 设计清单具体实现截图参考文献设计获取 前言 💗博主介绍:✌全网粉丝10W+,CSDN特邀作者、博客专家、CSDN新星计划导师,一名热衷于单片机技术探索与分享的博主、专注于 精通51/STM32/MSP430/AVR等单片机设计 主要对象是咱们电子相关专业的大学生,希望您们都共创辉煌!✌💗 👇🏻 精彩专栏 推荐订

Unity3D 运动之Move函数和translate

CharacterController.Move 移动 function Move (motion : Vector3) : CollisionFlags Description描述 A more complex move function taking absolute movement deltas. 一个更加复杂的运动函数,每次都绝对运动。 Attempts to

✨机器学习笔记(二)—— 线性回归、代价函数、梯度下降

1️⃣线性回归(linear regression) f w , b ( x ) = w x + b f_{w,b}(x) = wx + b fw,b​(x)=wx+b 🎈A linear regression model predicting house prices: 如图是机器学习通过监督学习运用线性回归模型来预测房价的例子,当房屋大小为1250 f e e t 2 feet^