当前位置:   article > 正文

蓝桥杯电子类单片机提升二——串口发送与接收

蓝桥杯电子类单片机提升二——串口发送与接收

目录

单片机资源数据包_2023

一、串口收发数据的介绍

1.波特率(Baud Rate)

2.帧格式

3.SBUF寄存器(Serial Buffer)

4.中断处理

二、如何从stc-isp获取串口收发数据的代码

1.代码的获取

2.代码的修改

1)第一步,修改头文件

2)第二步,删除重复定义

3)第三步,删除I/0口配置

4)第四步,修改波特率并测试

3.代码的使用

三、代码演示


前言
关于蓝桥杯比赛时会提供的资料前几篇都有提到,这里就不在赘述了,只放一个下载链接:

单片机资源数据包_2023

除了基础部分的按键、LED灯,数码管扫描,还有温度传感器,AD/DA转化,EEPROM存储器,RTC之外,还有三个模块考试的时候可能会考,分别是超声波,NE555和串口。近几年的题也是越来越难,这三个模块也逐渐出现在了省赛的舞台上(当然如果进国赛了,这几个模块就都可能考了)。提升篇主要针对这三个模块进行介绍。

由于这三个模块比赛时不会提供底层代码,所以许多都需要咱们自己来完成,所以不同人写的代码,差异性可能会更大。此外这些代码会涉及到单片机运行的底层知识,关于单片机基础部分的内容,提升篇也会尽可能介绍一部分(当然如果你不会也没关系,文章会教你如何用stc生成或者查数据手册,就算不知道原理,小背一背也是能自己实现的)

一、串口收发数据的介绍

串口收发数据简单点说就是单片机给电脑发数据,电脑给单片机发数据,平时下载程序就是电脑在给单片机发数据。我们在收发数据之前,首先要调整好COM以及波特率。COM口我们可以在设备管理器——端口处查看,一般情况下stc—isp都能正确扫描到COM口。

本篇文章关于串口的部分会着重告诉大家如何去找代码,而非自己去写这串代码,因为这串代码虽然考试时不会给,但是isp里面提供的有现成的代码,至少底层代码不用自己写了,可以直接去移植。这里先介绍一些串口有关的基础知识(当然比赛时用不到),当然也不用记,第二章会告诉大家如何获取与修改现成的代码。

1.波特率(Baud Rate)

波特率是表示每秒传输的位数,通常用波特(bps)来表示。在串口通信中,发送方和接收方必须配置相同的波特率才能正确地进行数据传输。STC15F2K单片机的串口模块可以通过设定计数器的值来实现不同的波特率,也就是说在使用串口时,需要一个定时器/计时器作为波特率发生器。

2.帧格式

帧格式指的是如何将数据转换为连续的位流进行传输。通常包括起始位(Start Bit)、数据位(Data Bits)、校验位(Parity Bit)和停止位(Stop Bit)。起始位指示数据的传输开始,停止位指示数据的传输结束,数据位是实际要传输的数据,而校验位用于检验数据传输的正确性。常见的帧格式包括8N1(8个数据位,无校验位,1个停止位)和8E1(8个数据位,偶校验,1个停止位)。

3.SBUF寄存器(Serial Buffer)

SBUF寄存器是STC15F2K单片机中用于串口通信的特殊寄存器。

对于发送数据,在发送之前,将要发送的数据写入SBUF寄存器。当发送完成后,SBUF寄存器会自动清空等待下一次写入。对于接收数据,接收到的数据会存放在SBUF寄存器中,然后可以通过读取SBUF寄存器来获取接收到的数据。

数据传输:对于发送数据,可以通过将要发送的数据写入SBUF寄存器,然后通过串口模块发送出去。发送数据时,需要检查串口发送完成标志位(TI)是否被置位,以避免数据的丢失。对于接收数据,可以通过读取SBUF寄存器来获取接收到的数据。接收数据时,需要检查串口接收完成标志位(RI)是否被置位,以判断是否有新的数据到达。

写成代码的话,unsigned char da;da=SBUF就是读数据,SBUF=da就是写数据了,当然,这样说太理想了,实际上还需要一些其他处理。SBUF其实是两个寄存器,只是名字一样而已,在发送和接受时虽然都是SBUF,但是对应的寄存器其实是不相同的。

4.中断处理

在接收数据时,可以使用串口接收中断来实现异步接收数据的功能,以提高系统的实时性和效率。串口中断跟定时器中断一样,不过定时器中断是每隔一定时间进一次中断,而串口中断是每次接收到数据之后,就会进一次中断。

二、如何从stc-isp获取串口收发数据的代码

1.代码的获取

stc-isp有一个“范例程序”的功能,在菜单中选择stc15系列单片机,就能找到许多范例程序,这里我们只看串口收发数据的,这里以定时器1模式0(16为自动重载)作为波特率发生器,其他定时器作为波特率发生器的代码也都一样。另外,比赛时一定要合理安排单片机资源。

下面是stc-isp复制过来的代码,这个代码是带有main函数的,我们后续还要对其进行修改:

  1. /*---------------------------------------------------------------------*/
  2. /* --- STC MCU Limited ------------------------------------------------*/
  3. /* --- STC15F4K60S4 系列 定时器1用作串口1的波特率发生器举例------------*/
  4. /* --- Mobile: (86)13922805190 ----------------------------------------*/
  5. /* --- Fax: 86-0513-55012956,55012947,55012969 ------------------------*/
  6. /* --- Tel: 86-0513-55012928,55012929,55012966-------------------------*/
  7. /* --- Web: www.STCMCU.com --------------------------------------------*/
  8. /* --- Web: www.GXWMCU.com --------------------------------------------*/
  9. /* 如果要在程序中使用此代码,请在程序中注明使用了STC的资料及程序 */
  10. /* 如果要在文章中应用此代码,请在文章中注明使用了STC的资料及程序 */
  11. /*---------------------------------------------------------------------*/
  12. //本示例在Keil开发环境下请选择Intel的8058芯片型号进行编译
  13. //若无特别说明,工作频率一般为11.0592MHz
  14. #include "reg51.h"
  15. #include "intrins.h"
  16. typedef unsigned char BYTE;
  17. typedef unsigned int WORD;
  18. #define FOSC 11059200L //系统频率
  19. #define BAUD 115200 //串口波特率
  20. #define NONE_PARITY 0 //无校验
  21. #define ODD_PARITY 1 //奇校验
  22. #define EVEN_PARITY 2 //偶校验
  23. #define MARK_PARITY 3 //标记校验
  24. #define SPACE_PARITY 4 //空白校验
  25. #define PARITYBIT NONE_PARITY //定义校验位
  26. sfr P0M1 = 0x93;
  27. sfr P0M0 = 0x94;
  28. sfr P1M1 = 0x91;
  29. sfr P1M0 = 0x92;
  30. sfr P2M1 = 0x95;
  31. sfr P2M0 = 0x96;
  32. sfr P3M1 = 0xb1;
  33. sfr P3M0 = 0xb2;
  34. sfr P4M1 = 0xb3;
  35. sfr P4M0 = 0xb4;
  36. sfr P5M1 = 0xC9;
  37. sfr P5M0 = 0xCA;
  38. sfr P6M1 = 0xCB;
  39. sfr P6M0 = 0xCC;
  40. sfr P7M1 = 0xE1;
  41. sfr P7M0 = 0xE2;
  42. sfr AUXR = 0x8e; //辅助寄存器
  43. sfr P_SW1 = 0xA2; //外设功能切换寄存器1
  44. #define S1_S0 0x40 //P_SW1.6
  45. #define S1_S1 0x80 //P_SW1.7
  46. sbit P22 = P2^2;
  47. bit busy;
  48. void SendData(BYTE dat);
  49. void SendString(char *s);
  50. void main()
  51. {
  52. P0M0 = 0x00;
  53. P0M1 = 0x00;
  54. P1M0 = 0x00;
  55. P1M1 = 0x00;
  56. P2M0 = 0x00;
  57. P2M1 = 0x00;
  58. P3M0 = 0x00;
  59. P3M1 = 0x00;
  60. P4M0 = 0x00;
  61. P4M1 = 0x00;
  62. P5M0 = 0x00;
  63. P5M1 = 0x00;
  64. P6M0 = 0x00;
  65. P6M1 = 0x00;
  66. P7M0 = 0x00;
  67. P7M1 = 0x00;
  68. ACC = P_SW1;
  69. ACC &= ~(S1_S0 | S1_S1); //S1_S0=0 S1_S1=0
  70. P_SW1 = ACC; //(P3.0/RxD, P3.1/TxD)
  71. // ACC = P_SW1;
  72. // ACC &= ~(S1_S0 | S1_S1); //S1_S0=1 S1_S1=0
  73. // ACC |= S1_S0; //(P3.6/RxD_2, P3.7/TxD_2)
  74. // P_SW1 = ACC;
  75. //
  76. // ACC = P_SW1;
  77. // ACC &= ~(S1_S0 | S1_S1); //S1_S0=0 S1_S1=1
  78. // ACC |= S1_S1; //(P1.6/RxD_3, P1.7/TxD_3)
  79. // P_SW1 = ACC;
  80. #if (PARITYBIT == NONE_PARITY)
  81. SCON = 0x50; //8位可变波特率
  82. #elif (PARITYBIT == ODD_PARITY) || (PARITYBIT == EVEN_PARITY) || (PARITYBIT == MARK_PARITY)
  83. SCON = 0xda; //9位可变波特率,校验位初始为1
  84. #elif (PARITYBIT == SPACE_PARITY)
  85. SCON = 0xd2; //9位可变波特率,校验位初始为0
  86. #endif
  87. AUXR = 0x40; //定时器1为1T模式
  88. TMOD = 0x00; //定时器1为模式0(16位自动重载)
  89. TL1 = (65536 - (FOSC/4/BAUD)); //设置波特率重装值
  90. TH1 = (65536 - (FOSC/4/BAUD))>>8;
  91. TR1 = 1; //定时器1开始启动
  92. ES = 1; //使能串口中断
  93. EA = 1;
  94. SendString("STC15F2K60S2\r\nUart Test !\r\n");
  95. while(1);
  96. }
  97. /*----------------------------
  98. UART 中断服务程序
  99. -----------------------------*/
  100. void Uart() interrupt 4
  101. {
  102. if (RI)
  103. {
  104. RI = 0; //清除RI位
  105. P0 = SBUF; //P0显示串口数据
  106. P22 = RB8; //P2.2显示校验位
  107. }
  108. if (TI)
  109. {
  110. TI = 0; //清除TI位
  111. busy = 0; //清忙标志
  112. }
  113. }
  114. /*----------------------------
  115. 发送串口数据
  116. ----------------------------*/
  117. void SendData(BYTE dat)
  118. {
  119. while (busy); //等待前面的数据发送完成
  120. ACC = dat; //获取校验位P (PSW.0)
  121. if (P) //根据P来设置校验位
  122. {
  123. #if (PARITYBIT == ODD_PARITY)
  124. TB8 = 0; //设置校验位为0
  125. #elif (PARITYBIT == EVEN_PARITY)
  126. TB8 = 1; //设置校验位为1
  127. #endif
  128. }
  129. else
  130. {
  131. #if (PARITYBIT == ODD_PARITY)
  132. TB8 = 1; //设置校验位为1
  133. #elif (PARITYBIT == EVEN_PARITY)
  134. TB8 = 0; //设置校验位为0
  135. #endif
  136. }
  137. busy = 1;
  138. SBUF = ACC; //写数据到UART数据寄存器
  139. }
  140. /*----------------------------
  141. 发送字符串
  142. ----------------------------*/
  143. void SendString(char *s)
  144. {
  145. while (*s) //检测字符串结束标志
  146. {
  147. SendData(*s++); //发送当前字符
  148. }
  149. }

2.代码的修改

可以看到这串代码引用的头文件是#include "reg51.h",而我们之前引用的都是stc15.h,其实,我们可以理解为15单片机是51单片机的哥哥,是向下兼容51的,而stc15.h其实是包含有reg51.h的所有内容,并且stc15.h里还有一些51单片机没有,但是15单片机有的东西(不知道上述内容对不对,反正大概是这样的),所以我们需要对代码进行修改,大致分四步走:

1)第一步,修改头文件

删除头文件reg51.h并添加头文件stc15.h

2)第二步,删除重复定义

删除所有sfr以及sbit开头的定义,因为这些定义都是51没有但是15有的,所以引用51的头文件之后,15的东西需要单独定义出来,而引用15的头文件之后,这些都在头文件定义过了,就不用再定义了,具体删除的代码如下:

#include "reg51.h"

sfr P0M1 = 0x93;
sfr P0M0 = 0x94;
sfr P1M1 = 0x91;
sfr P1M0 = 0x92;
sfr P2M1 = 0x95;
sfr P2M0 = 0x96;
sfr P3M1 = 0xb1;
sfr P3M0 = 0xb2;
sfr P4M1 = 0xb3;
sfr P4M0 = 0xb4;
sfr P5M1 = 0xC9;
sfr P5M0 = 0xCA;
sfr P6M1 = 0xCB;
sfr P6M0 = 0xCC;
sfr P7M1 = 0xE1;
sfr P7M0 = 0xE2;


sfr AUXR  = 0x8e;               //辅助寄存器

sbit P22 = P2^2;

3)第三步,删除I/0口配置

I/0口配置在main函数内,while(1)之前,这些代码都是配置引脚为准双向的(或者叫初始化为准双向),对于stc15f2k单片机这些是不必要的,因为默认就是准双向,但是对于某些单片机,引脚必须先初始化之后才可以使用。需要删除的代码如下:

    P0M0 = 0x00;
    P0M1 = 0x00;
    P1M0 = 0x00;
    P1M1 = 0x00;
    P2M0 = 0x00;
    P2M1 = 0x00;
    P3M0 = 0x00;
    P3M1 = 0x00;
    P4M0 = 0x00;
    P4M1 = 0x00;
    P5M0 = 0x00;
    P5M1 = 0x00;
    P6M0 = 0x00;
    P6M1 = 0x00;
    P7M0 = 0x00;
    P7M1 = 0x00;

其实,我们也可以在stc-isp内找到这些引脚的配置代码,可以根据自己的需要生成:

4)第四步,修改波特率并测试

代码上已经定义了波特率,我们直接修改其数值即可,改为9600

在stc-isp的串口助手选择正确的串口,并调整波特率为9600,然后就可以打开串口了。默认的代码只有上电之后发送一串数据,所以可以重启一下开发板,就可以看到发送的数据了!

3.代码的使用

经过2代码的修改之后,剩下的代码已经十分的干净了,剩下的代码中在main函数的while(1)之前完成了定时器的初始化。此外代码还写好了两个发送数据的代码,我们可以直接拿来用。对于接收数据,UART中断服务函数内可以自行对数据处理:

/*----------------------------
UART 中断服务程序
-----------------------------*/
void Uart() interrupt 4
{
    if (RI)
    {
        RI = 0;                 //清除RI位
        P0 = SBUF;              //P0显示串口数据
        P22 = RB8;              //P2.2显示校验位
    }
    if (TI)
    {
        TI = 0;                 //清除TI位
        busy = 0;               //清忙标志
    }
}

从P0=SBUF那一行那里开始,就是我们可以修改的地方。比如我们可以用一个自己定义的全局变量来记录SBUF的值,再放到其他地方处理。

但是实际上,在真正的比赛中,我们还需要考虑的内容还有许多,这里只是一种最简单的情况。

此外呢,其实也不难发现,刚才的几个过程也并非必要(因为我一个字都不改时,这串代码就可以正常运行),有的人真的把15单片机当51使用也不是不可以。

三、代码演示

刚才已经把代码介绍与修改好了,这里演示一个修改好之后完整的代码,实现以下功能:

1.上电发送数据"STC15F2K60S2\r\nUart Test !\r\n"

2.LED灯显示接收到的数据:

main.c

  1. /*---------------------------------------------------------------------*/
  2. /* --- STC MCU Limited ------------------------------------------------*/
  3. /* --- STC15F4K60S4 系列 定时器1用作串口1的波特率发生器举例------------*/
  4. /* --- Mobile: (86)13922805190 ----------------------------------------*/
  5. /* --- Fax: 86-0513-55012956,55012947,55012969 ------------------------*/
  6. /* --- Tel: 86-0513-55012928,55012929,55012966-------------------------*/
  7. /* --- Web: www.STCMCU.com --------------------------------------------*/
  8. /* --- Web: www.GXWMCU.com --------------------------------------------*/
  9. /* 如果要在程序中使用此代码,请在程序中注明使用了STC的资料及程序 */
  10. /* 如果要在文章中应用此代码,请在文章中注明使用了STC的资料及程序 */
  11. /*---------------------------------------------------------------------*/
  12. //本示例在Keil开发环境下请选择Intel的8058芯片型号进行编译
  13. //若无特别说明,工作频率一般为11.0592MHz
  14. #include <stc15.h>
  15. #include "intrins.h"
  16. typedef unsigned char BYTE;
  17. typedef unsigned int WORD;
  18. #define FOSC 11059200L //系统频率
  19. #define BAUD 9600 //串口波特率
  20. #define NONE_PARITY 0 //无校验
  21. #define ODD_PARITY 1 //奇校验
  22. #define EVEN_PARITY 2 //偶校验
  23. #define MARK_PARITY 3 //标记校验
  24. #define SPACE_PARITY 4 //空白校验
  25. #define PARITYBIT NONE_PARITY //定义校验位
  26. #define S1_S0 0x40 //P_SW1.6
  27. #define S1_S1 0x80 //P_SW1.7
  28. bit busy;
  29. void SendData(BYTE dat);
  30. void SendString(char *s);
  31. void main()
  32. {
  33. ACC = P_SW1;
  34. ACC &= ~(S1_S0 | S1_S1); //S1_S0=0 S1_S1=0
  35. P_SW1 = ACC; //(P3.0/RxD, P3.1/TxD)
  36. #if (PARITYBIT == NONE_PARITY)
  37. SCON = 0x50; //8位可变波特率
  38. #elif (PARITYBIT == ODD_PARITY) || (PARITYBIT == EVEN_PARITY) || (PARITYBIT == MARK_PARITY)
  39. SCON = 0xda; //9位可变波特率,校验位初始为1
  40. #elif (PARITYBIT == SPACE_PARITY)
  41. SCON = 0xd2; //9位可变波特率,校验位初始为0
  42. #endif
  43. AUXR = 0x40; //定时器1为1T模式
  44. TMOD = 0x00; //定时器1为模式0(16位自动重载)
  45. TL1 = (65536 - (FOSC/4/BAUD)); //设置波特率重装值
  46. TH1 = (65536 - (FOSC/4/BAUD))>>8;
  47. TR1 = 1; //定时器1开始启动
  48. ES = 1; //使能串口中断
  49. EA = 1;
  50. //不能连续发送!(直接放在while(1)里)可以在发送之间加一个100ms延时,否则容易接收不到,发送不出去
  51. SendString("STC15F2K60S2\r\nUart Test !\r\n");
  52. while(1);
  53. }
  54. /*----------------------------
  55. UART 中断服务程序
  56. -----------------------------*/
  57. void Uart() interrupt 4
  58. {
  59. if (RI)
  60. {
  61. RI = 0; //清除RI位
  62. P0 = SBUF; //P0显示串口数据
  63. P2|=0x80;P2&=0x9F;P2&=0x1F;//打开LED灯
  64. }
  65. if (TI)
  66. {
  67. TI = 0; //清除TI位
  68. busy = 0; //清忙标志
  69. }
  70. }
  71. /*----------------------------
  72. 发送串口数据
  73. ----------------------------*/
  74. void SendData(BYTE dat)
  75. {
  76. while (busy); //等待前面的数据发送完成
  77. ACC = dat; //获取校验位P (PSW.0)
  78. if (P) //根据P来设置校验位
  79. {
  80. #if (PARITYBIT == ODD_PARITY)
  81. TB8 = 0; //设置校验位为0
  82. #elif (PARITYBIT == EVEN_PARITY)
  83. TB8 = 1; //设置校验位为1
  84. #endif
  85. }
  86. else
  87. {
  88. #if (PARITYBIT == ODD_PARITY)
  89. TB8 = 1; //设置校验位为1
  90. #elif (PARITYBIT == EVEN_PARITY)
  91. TB8 = 0; //设置校验位为0
  92. #endif
  93. }
  94. busy = 1;
  95. SBUF = ACC; //写数据到UART数据寄存器
  96. }
  97. /*----------------------------
  98. 发送字符串
  99. ----------------------------*/
  100. void SendString(char *s)
  101. {
  102. while (*s) //检测字符串结束标志
  103. {
  104. SendData(*s++); //发送当前字符
  105. }
  106. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/84209
推荐阅读
相关标签
  

闽ICP备14008679号