今日得以继续蓝桥杯国赛备赛之旅:

有道是 “不知何事萦怀抱,醒也无聊,醉也无聊,梦也何曾到谢桥。”

那我们该如何 让这位诗人纳兰 “再听乐府曲 ,畅解相思苦”呢?

那就建立起串口通信吧!

我将从SCT15F2K61S2单片机串口通信基础出发,逐步缓解此等繁琐愁杂。

附上工程文件下载传送门:

https://download.csdn.net/download/qq_64257614/87800670?spm=1001.2014.3001.5503

本篇文章除了学习基础,还主要研究学习以下问题:

1.在蓝桥杯单片机实训平台上串口通信实现printf()函数

2.蓝桥杯单片机串口通信发送变量乱码问题

3.接收中断数据处理的实现

4.字符串与变量拼接到一起,变成新的字符串。

目录

一、串口通信知识基础:

二、串口通信基础程序设计:

总设计方针:

首先生成我们要用的串口初始化程序:

其次就是编写发送与接受函数:

中断服务函数:(这个要先定义俩变量):

三、串口通信的一些处理操作:

输出的处理:

1.重定义改造printf函数:

2.连续发送单个字节:

3. sprintf 函数可以格式化输出:

输入的处理:

4.ASCLL码与发送变量值的处理:

5.字符串与变量的拼接:

6.中断接收字符串:

三、实践拓展延伸:

实验要求:

程序设计:

#include "usart_main.h"

#include "Public.h"

#include "uart.h"



一、串口通信知识基础:

串口按位(bit)发送和接收字节的通信方式

IAP15F2k61s2单片机内部有俩个采用UART工作方式的全双工串行通信接口,

全双工就是俩个单工的结合,A与B俩个机能同时收发数据。

此外半双工,单工就不做介绍,大家可以百度,这个了解就可以了。

每个串行口的组成:

2个数据缓冲器:(SBUF)     串口1的地址是99H,串口2的地址是9BH。

1个移位寄存器:

1个串行控制器:   (SCON)

1个波特率发生器:

串口1和串口2:

串口1对应硬件部分是TxD和RxD

串口1可以在:

【P30/RXD,P31/TXD】、【P36/RXD_2,P37/TXD_2】、【P1^6/RXD_3,P17/TXD_3】

之间切换。

(1)串口1可以使用定时器1、定时器2为波特率发生器

(2)定时器2默认为16位自动重装载模式

(3)定时器2的时间常数保存在T2H、T2L

串口2对应硬件部分是TxD2和RxD2

串口2可以在:

【P10/TXD2,P11/RXD2】、【P46/RXD2_2,P47/TXD2_2】

之间切换。

二、串口通信基础程序设计:

总设计方针:

步骤1~4不需要我们自己背记,可用软件生成,下面会介绍。

步骤5~7需要加在软件生成的函数里面。

1.设定定时器1工作方式(TMOD寄存器)

2.计算波特率,如果用的是定时器1作为波特率发生器,那就赋值给TH1,TL1

3.设置AUXR寄存器

4.设置SCON寄存器

5.打开定时器1

6.使能串口中断 ES

7.开总中断EA

串口通信中需要用到的寄存器SCON——串行通信控制寄存器(一般设置为0x50)

首先生成我们要用的串口初始化程序:

这里不需要我们背寄存器了,直接用STC_isp软件生成:

如图设定好要用的串口1、频率、波特率等

这个初始化代码没有需要改动的地方,但后续有添加语句,

这个void UartInit(void)函数就是串口1的初始化函数了,

一般在主函数while(1)之前调用一下就行了。

此处要注意,不论其他红框怎么配置,

在竞赛板子上,这个系统频率不要调整!就是11.0592Mhz:

否则收发数据会出大问题!:

然后添加必要的开启中断语句,添加语句后就是这样:

void UartInit(void)      //9600bps@11.0592MHz
{SCON = 0x50;       //8位数据,可变波特率AUXR |= 0x40;      //定时器时钟1T模式AUXR &= 0xFE;       //串口1选择定时器1为波特率发生器TMOD &= 0x0F;        //设置定时器模式TL1 = 0xE0;         //设置定时初始值TH1 = 0xFE;           //设置定时初始值ET1 = 0;            //禁止定时器%d中断TR1 = 1;            //定时器1开始计时REN = 1;         //允许串口接受ES = 1;          //开启串口中断EA = 1;          //开启总中断
}

其次就是编写发送与接受函数:

发送一个字节函数:

//发送一个字节
void SendData(unsigned char dat)
{SBUF=dat; //把要发送的数据存入写SBUFwhile(TI == 0);  //等待发送标志位置1TI = 0;
}

发送字符串函数:


void Uart_Sendstring(unsigned char *pucStr)
{while(*pucStr!='\0'){SBUF=*pucStr;while(TI==0);   //等待串口发送完成TI=0;                   //因为TI在串口发送后,硬件自动置1,//我们需要重新发送数据的话,就需要在置1后,软件清零,pucStr++;}
}

中断服务函数:(这个要先定义俩变量):

这样写可以接收上位机数据进行分析

u8 R_sign;           //接收缓冲区内容完成标志
u8 R_dat;               //用于接收缓冲区内容//串口1中断服务函数
void Uart_1_serv() interrupt 4
{if(RI)    //查询串口接收程序,如果接收标志位为1,说明有数据接收完毕{RI = 0;        //清除接收标志位R_dat = SBUF;  //将接收缓冲区内容转至R_dat变量中R_sign = 1;    }
}

三、串口通信的一些处理操作:

输出的处理:

1.重定义改造printf函数:

printf()函数的使用需要注意引用头文件 stdio.h

然后需要重定向putchar()函数

这样以后就能用printf()函数向串口打印数据了

这类思想常见于嵌入式的程序设计,在我的另一篇文章有所研究:

使用printf()函数时该注意,会占用大量资源

嵌入式硬件中Printf函数的原理_NULL指向我的博客-CSDN博客

void send_date(unsigned char date){SBUF = date;while(!TI);TI = 0;
}char putchar (char ch){send_date(ch);return ch;
}

printf()用法举例:

unsigned char test=128;printf("Welcome to stc15f2k60s2\n");
printf("%d",test);

2.连续发送单个字节:

void send_string(unsigned char *date, unsigned char len){while(len--){send_date(*date);date ++;}
}

我们有时要输出变量,用这种方式就要把变量转换成字符型:

cent + '0' ;

3. sprintf 函数可以格式化输出:

 sprintf(string,"num:%d",(int)cent);send_string(string,strlen(string));

输入的处理:

通过将接收到的数据保持到一个数组当中,

就可以之后在循环中对接收到的数据进行判断。

此段代码是接收中断函数的处理更改

 if(RI==1){str[rev] = SBUF;RI = 0;       rev++;}else{TI = 0;}
if(str[rev-1] == '\n'){if(str[0] == 'S' && str[1] == 'T'){printf("STOK\r\n");}rev = 0;for(i = 0; i<8;i++){str[i] = '\0'; }

4.ASCLL码与发送变量值的处理:

单片机与上位机之间的通信出现问题。例如我如下方式发送一个变量test:

会发现接收不是我们要的效果

 u8 test=1;printf("%c",test+'0');SendData(test+'0');

总所周知,ASCII码是由八位二进制小数进行一映射的,从0-127的数字分别对应了不同字符:

造成这样的结果在于上位机给我们发送的是ASCII码,

并且上位机接收显示的也是ASCII码,而不是数值大小。

也就是说,我们给上位机发 “1”,上位机实际接收到的是0000_0001(B),

对应ASCII为SOH(SOH(start of headline) 标题开始),

因此上位机并不显示1,而是显示SOH,

要想上位机显示1,应该给它发送49(u8)才对。

同理,上位机发送“1“,我们实际接收到的为49(u8)

因此应该减去48才为我们需要得到的数字1。

在单片机中,我们的数字一律是实际的数值,我们定义变量时,

定义为u8,u6,就是代表无符号的整数,单片机串口发送也是发送这些数值。

而在上位机中,则是发ASCII码,显示ASCII码。

以下为ASCLL码对照表:

我们如果把发送的数据改成:

uint8_t Data[10]={'1','2','3','4'};

那么这样上位机就可以完美接收到1234。

单引号代表取这个符号的ASCII值,因此比较字符串,

可以使用Data[x]=='A',这样的操作进行比较。

5.字符串与变量的拼接:

可以使用C语言中的字符串拼接函数strcat()来将字符串和整数变量拼接在一起。

下面是一个示例代码:

#include <stdio.h>
#include <string.h>int main() {char str1[20] = "Hello";int num = 123;char str2[10];// 将整数转换为字符串格式sprintf(str2, "%d", num);// 使用strcat()函数将两个字符串拼接在一起strcat(str1, str2);printf("%s", str1);return 0;
}

输出结果为:Hello123

在代码中,我们首先定义了一个字符串str1和一个整数num

然后,我们使用sprintf()函数将整数转换为字符串格式并将其保存在另一个字符串str2中。

最后,我们使用strcat()函数将str1str2拼接在一起,并将结果打印到控制台上。

6.中断接收字符串:

像这样编写即可:

//中断接收字符串,根据选择接收字符还是字符串
unsigned char i;
unsigned char string[20];
void Usart_interrupt() interrupt 4  //用与接收字符串
{if (RI) {RI = 0;string[i] = SBUF;if (string[i] == '\n')  //回车为接收结束标志{i = 0;} else {i++;}}
}

三、实践拓展延伸:

实验要求:

/*本实验练习串口通信:主要练习以下内容:1.串口接受命令,串口中断并执行接收到上位机传来0xff后,单片机发送'Hello'接收到上位机传来0xfe后,单片机发送'World'接收到上位机传来0xfd后,单片机发送'Hello World'关于接收到上位机其他数据,单片机都返回发送'Wrong Order'  2.串口发送字符,数字。用Uart_Sendstring()函数做一些1中指定的发送字符串的任务3.配置Printf函数,开机用Printf函数向上位机发送'Welcome to stc15f2k60s2\n'开机用Printf函数向上位机发送   变量test的值,test=1
*/
/*本实验有以下注意点:printf函数需要重定向
*/

程序设计:

具体设计没有特别难的地方,注意发送变量ASCLL码规则即可。

#include "usart_main.h"

#include "usart_main.h"void main()
{u8 test=1;
//  u16 test2=1234;cls_buzz_led();             //关闭外设UartInit();                       //初始化串口printf("Welcome to stc15f2k60s2\n");printf("%c",test+'0');
//  printf("\n");         //发送换行测试SendData(test+'0');
//  SendData('\n');       //发送换行测试
//  SendData(test+'0');    //发送换行测试while(1){ if(R_sign==1)                                 //如果有数据接收完毕{R_sign=0;switch(R_dat){case 0xff:Uart_Sendstring("Hello");       break;case 0xfe:Uart_Sendstring("World");       break;case 0xfd:Uart_Sendstring("Hello World"); break;default :Uart_Sendstring("Wrong Order");break;  }}}
}
#ifndef _usart_main_h_
#define _usart_main_h_#include "stc15f2k60s2.h"
#include "Public.h"
#include "stdio.h"
#include "uart.h"#endif

#include "Public.h"

#include "Public.h"void inint_74HC573(u8 select)             //74HC573片选函数(简记:0x53f0)
{switch(select){case 4: P2 = (P2 & 0X1F) | 0X80; break;//开启 Y4 LED端口 装填case 5: P2 = (P2 & 0X1F) | 0XA0; break;//开启 Y5 ULN2003驱动端口 装填case 6:   P2 = (P2 & 0X1F) | 0XC0; break;//开启 Y6 数码管位选端口 装填case 7:   P2 = (P2 & 0X1F) | 0XE0; break;//开启 Y7 数码管段选端口 装填}
}void cls_buzz_led()
{inint_74HC573(5);buzz = 0;inint_74HC573(4);P0 = 0xff;
}
#ifndef _Public_h_
#define _Public_h_#include "stc15f2k60s2.h"typedef unsigned int u16;
typedef unsigned char u8;sbit buzz=P0^6;                               //蜂鸣器void inint_74HC573(u8 select);             //74HC573片选函数(简记:0x53f0)
void delay_xms(u8 xms);                    //传入参数 参数为几就 延时几毫秒void cls_buzz_led();#endif

#include "uart.h"

#include "uart.h"u8 R_sign;            //接收缓冲区内容完成标志
u8 R_dat;               //用于接收缓冲区内容void UartInit(void)      //9600bps@11.0592MHz
{SCON = 0x50;       //8位数据,可变波特率AUXR |= 0x40;      //定时器时钟1T模式AUXR &= 0xFE;       //串口1选择定时器1为波特率发生器TMOD &= 0x0F;        //设置定时器模式TL1 = 0xE0;         //设置定时初始值TH1 = 0xFE;           //设置定时初始值ET1 = 0;            //禁止定时器%d中断TR1 = 1;            //定时器1开始计时REN = 1;         //允许串口接受ES = 1;          //开启串口中断EA = 1;          //开启总中断
}//发送字符串函数
void Uart_Sendstring(unsigned char *pucStr)
{while(*pucStr!='\0'){SBUF=*pucStr;while(TI==0);   //等待串口发送完成TI=0;                   //因为TI在串口发送后,硬件自动置1,//我们需要重新发送数据的话,就需要在置1后,软件清零,pucStr++;}
}//发送一个字节
void SendData(unsigned char dat)
{SBUF=dat; //把要发送的数据存入写SBUFwhile(TI == 0);  //等待发送标志位置1TI = 0;
}
/*
//发送字符串,以'\0'形式结尾
void SendString(unsigned char *s)
{while (*s!='\0')              //检查是否到结尾{SendData(*s++);     //地址递增进行逐个发送}
}
*/
void send_date(unsigned char date){SBUF = date;while(!TI);TI = 0;
}char putchar (char ch){send_date(ch);return ch;
}//串口1中断服务函数
void Uart_1_serv() interrupt 4
{if(RI)    //查询串口接收程序,如果接收标志位为1,说明有数据接收完毕{RI = 0;        //清除接收标志位R_dat = SBUF;  //将接收缓冲区内容转至R_dat变量中R_sign = 1;    }
}
#ifndef _uart_h_
#define _uart_h_#include "stc15f2k60s2.h"
#include "Public.h"
#include "stdio.h"extern u8 R_sign;
extern u8 R_dat;void Uart_Sendstring(unsigned char *pucStr);
void UartInit(void);        //9600bps@11.0592MHz
void Uart_1_serv();
void SendData(unsigned char dat);#endif

蓝桥杯单片机串口通信学习提升笔记相关推荐

  1. 蓝桥杯单片机——串口通信1(11)

    一.原理 串口通信(Serial Communications)的概念非常简单,串口按位(bit)发送和接收字节的通信方式.51单片机内部有一个可编程全双工串行通信接口.该部件不仅能同时进行数据的发送 ...

  2. 蓝桥杯单片机串口通信收发

    文章目录 前言 一.串口是什么? 二.使用步骤 1.单字符接受命令 2.多字符接受命令 3.重定向的使用 总结 前言 在小蜜蜂老师的学习视频中,我们学习到了串口收发基础,实现了简单的上位机与单片机之间 ...

  3. 蓝桥杯单片机组_学习路线

    文章目录 前言 一.参赛建议 二.备赛过程 1.资料 2. 学习路线 三.比赛过程 总结 附-模板 main.c 前言 前段时间整理硬盘发现了当时大二参加蓝桥杯时的一些记录,现在回想当时踩过的坑和备赛 ...

  4. 蓝桥杯单片机第十届国赛练习

    单片机型号:IAP15F2K60S2. 蓝桥杯第十届国赛有串口,本文着手进行一番练习, 文末会附上整个工程下载地址. 这一届赛题的练习对我来说还是有些难度 我看题后做了以下几个设计方面的点总结列举: ...

  5. 电子信息工程专业打工人的蓝桥杯单片机竞赛时记

    文章目录 前言 一.基础入门 1.基础知识 2.51系列单片机 单片机周期知识: 单片机IO口知识 二.开发学习 1.基本外设功能 LED跑马灯 蜂鸣器与继电器 数码管 按键 外部中断 定时器中断 串 ...

  6. 【全套完结】蓝桥杯单片机--- 从省赛到国赛

    前言 本人曾在第十三届蓝桥杯大赛单片机组中获得国一,以下是我在准备比赛过程中的一些记录,希望能给大家带来帮助 本人持续分享更多关于嵌入式和单片机的知识,如果大家喜欢,别忘点个赞加个关注哦,让我们一起共 ...

  7. 蓝桥杯单片机比赛学习:7、中断系统之串口中断的基本原理

    这一节我们讲一下蓝桥杯单片机比赛中断中的最后一个模块--串口.中断的其他模块在我的个人主页(4条消息) Do My Best的博客_CSDN博客-蓝桥杯单片机比赛学习领域博主有兴趣的可以去学习. 通信 ...

  8. 【蓝桥杯—单片机学习笔记(四)】共阳数码管的动态显示

    一.要求 在CT107D单片机训练综合平台上,实现数码管的动态显示,在8位数码管中,前面4位显示年份"2022",接着是2个分隔符"--",最后两位是月份,从1 ...

  9. 【蓝桥杯-单片机学习笔记(六)】矩阵键盘控制数码管显示0~F

    一.要求 在CT107D单片机训练平台上,首先将J5处的跳线帽接到1~2引脚,使按键S4~S19组成4*4的矩阵键盘.在扫描按键的过程中,发现有按键触发信号后,待按键松开后,数码管显示相应的数字.按键 ...

最新文章

  1. AngularJS 学习
  2. 各种环境下的渗透测试
  3. python学习实例(3)
  4. [Tarjan] 洛谷 P1726 上白泽慧音
  5. onvif device manager 找不到ipc_Qt音视频开发32-Onvif网络设置
  6. Flask—10-项目部署(02)
  7. ipqc的工作流程图_IPQC流程图
  8. android 8.1 跳过,直接跳过8.0!Essential Phone正式迎来Android 8.1
  9. 56)函数模板的基本语法
  10. wsdl2java 工具_apache CXF wsdl2java工具的使用
  11. linux内核Device Drivers设备驱动程序
  12. 目前航信版开票软件自身导入文本数据的问题
  13. STM32入门开发:编写XPT2046电阻触摸屏驱动(模拟SPI)
  14. pano2vr 制作交互热点模板时常显示文字
  15. 如何比较两种方法的灵敏度和特异度
  16. 俄亥俄大学计算机科学系,俄亥俄大学课程及专业设置
  17. 有道单词导入 大量有道单词 生词本 批量导入 添加 有道单词XML 背单词
  18. Arduino连接LCD1602显示屏
  19. 计算机里面看不到桌面是什么原因,电脑桌面图标都没了是什么情况?电脑桌面图标都没了解决方法...
  20. 笔记本电池修复常见方法大全

热门文章

  1. Python爬虫爬取笔趣阁小说
  2. mysql 列转行union all_sql:mysql:列转行(转为多个字段)
  3. python水位传感器输出水位_水位传感器原理 怎么判断水位传感器是否坏了
  4. 生命不息,折腾不止——新的起点
  5. C++:建立Cylinder类,有三个double型私有数据成员:半径、高和体积,构造函数传递两个值:半径和高,计算体积。成员函数showVolume()用来显示每个对象的体积。
  6. 架构设计-支付宝、京东、美团、去哪儿的支付系统架构整体设计详解!!!
  7. Web端-缓存数据及user_agent修改
  8. 橘络有活血的功效,泡水喝可有效预防血管硬化、血管破裂、脑溢血
  9. 周记:Fifty-eight
  10. 整理任正非思想:要建立一个均衡的平台-1995