赞
踩
目录
前言
关于蓝桥杯比赛时会提供的资料前几篇都有提到,这里就不在赘述了,只放一个下载链接:
除了基础部分的按键、LED灯,数码管扫描,还有温度传感器,AD/DA转化,EEPROM存储器,RTC之外,还有三个模块考试的时候可能会考,分别是超声波,NE555和串口。近几年的题也是越来越难,这三个模块也逐渐出现在了省赛的舞台上(当然如果进国赛了,这几个模块就都可能考了)。提升篇主要针对这三个模块进行介绍。
由于这三个模块比赛时不会提供底层代码,所以许多都需要咱们自己来完成,所以不同人写的代码,差异性可能会更大。此外这些代码会涉及到单片机运行的底层知识,关于单片机基础部分的内容,提升篇也会尽可能介绍一部分(当然如果你不会也没关系,文章会教你如何用stc生成或者查数据手册,就算不知道原理,小背一背也是能自己实现的)
串口收发数据简单点说就是单片机给电脑发数据,电脑给单片机发数据,平时下载程序就是电脑在给单片机发数据。我们在收发数据之前,首先要调整好COM以及波特率。COM口我们可以在设备管理器——端口处查看,一般情况下stc—isp都能正确扫描到COM口。
本篇文章关于串口的部分会着重告诉大家如何去找代码,而非自己去写这串代码,因为这串代码虽然考试时不会给,但是isp里面提供的有现成的代码,至少底层代码不用自己写了,可以直接去移植。这里先介绍一些串口有关的基础知识(当然比赛时用不到),当然也不用记,第二章会告诉大家如何获取与修改现成的代码。
波特率是表示每秒传输的位数,通常用波特(bps)来表示。在串口通信中,发送方和接收方必须配置相同的波特率才能正确地进行数据传输。STC15F2K单片机的串口模块可以通过设定计数器的值来实现不同的波特率,也就是说在使用串口时,需要一个定时器/计时器作为波特率发生器。
帧格式指的是如何将数据转换为连续的位流进行传输。通常包括起始位(Start Bit)、数据位(Data Bits)、校验位(Parity Bit)和停止位(Stop Bit)。起始位指示数据的传输开始,停止位指示数据的传输结束,数据位是实际要传输的数据,而校验位用于检验数据传输的正确性。常见的帧格式包括8N1(8个数据位,无校验位,1个停止位)和8E1(8个数据位,偶校验,1个停止位)。
SBUF寄存器是STC15F2K单片机中用于串口通信的特殊寄存器。
对于发送数据,在发送之前,将要发送的数据写入SBUF寄存器。当发送完成后,SBUF寄存器会自动清空等待下一次写入。对于接收数据,接收到的数据会存放在SBUF寄存器中,然后可以通过读取SBUF寄存器来获取接收到的数据。
数据传输:对于发送数据,可以通过将要发送的数据写入SBUF寄存器,然后通过串口模块发送出去。发送数据时,需要检查串口发送完成标志位(TI)是否被置位,以避免数据的丢失。对于接收数据,可以通过读取SBUF寄存器来获取接收到的数据。接收数据时,需要检查串口接收完成标志位(RI)是否被置位,以判断是否有新的数据到达。
写成代码的话,unsigned char da;da=SBUF就是读数据,SBUF=da就是写数据了,当然,这样说太理想了,实际上还需要一些其他处理。SBUF其实是两个寄存器,只是名字一样而已,在发送和接受时虽然都是SBUF,但是对应的寄存器其实是不相同的。
在接收数据时,可以使用串口接收中断来实现异步接收数据的功能,以提高系统的实时性和效率。串口中断跟定时器中断一样,不过定时器中断是每隔一定时间进一次中断,而串口中断是每次接收到数据之后,就会进一次中断。
stc-isp有一个“范例程序”的功能,在菜单中选择stc15系列单片机,就能找到许多范例程序,这里我们只看串口收发数据的,这里以定时器1模式0(16为自动重载)作为波特率发生器,其他定时器作为波特率发生器的代码也都一样。另外,比赛时一定要合理安排单片机资源。
下面是stc-isp复制过来的代码,这个代码是带有main函数的,我们后续还要对其进行修改:
- /*---------------------------------------------------------------------*/
- /* --- STC MCU Limited ------------------------------------------------*/
- /* --- STC15F4K60S4 系列 定时器1用作串口1的波特率发生器举例------------*/
- /* --- Mobile: (86)13922805190 ----------------------------------------*/
- /* --- Fax: 86-0513-55012956,55012947,55012969 ------------------------*/
- /* --- Tel: 86-0513-55012928,55012929,55012966-------------------------*/
- /* --- Web: www.STCMCU.com --------------------------------------------*/
- /* --- Web: www.GXWMCU.com --------------------------------------------*/
- /* 如果要在程序中使用此代码,请在程序中注明使用了STC的资料及程序 */
- /* 如果要在文章中应用此代码,请在文章中注明使用了STC的资料及程序 */
- /*---------------------------------------------------------------------*/
-
- //本示例在Keil开发环境下请选择Intel的8058芯片型号进行编译
- //若无特别说明,工作频率一般为11.0592MHz
-
-
- #include "reg51.h"
- #include "intrins.h"
-
- typedef unsigned char BYTE;
- typedef unsigned int WORD;
-
- #define FOSC 11059200L //系统频率
- #define BAUD 115200 //串口波特率
-
- #define NONE_PARITY 0 //无校验
- #define ODD_PARITY 1 //奇校验
- #define EVEN_PARITY 2 //偶校验
- #define MARK_PARITY 3 //标记校验
- #define SPACE_PARITY 4 //空白校验
-
- #define PARITYBIT NONE_PARITY //定义校验位
-
- 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; //辅助寄存器
-
- sfr P_SW1 = 0xA2; //外设功能切换寄存器1
-
- #define S1_S0 0x40 //P_SW1.6
- #define S1_S1 0x80 //P_SW1.7
-
- sbit P22 = P2^2;
-
- bit busy;
-
- void SendData(BYTE dat);
- void SendString(char *s);
-
- void main()
- {
- 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;
-
- ACC = P_SW1;
- ACC &= ~(S1_S0 | S1_S1); //S1_S0=0 S1_S1=0
- P_SW1 = ACC; //(P3.0/RxD, P3.1/TxD)
-
- // ACC = P_SW1;
- // ACC &= ~(S1_S0 | S1_S1); //S1_S0=1 S1_S1=0
- // ACC |= S1_S0; //(P3.6/RxD_2, P3.7/TxD_2)
- // P_SW1 = ACC;
- //
- // ACC = P_SW1;
- // ACC &= ~(S1_S0 | S1_S1); //S1_S0=0 S1_S1=1
- // ACC |= S1_S1; //(P1.6/RxD_3, P1.7/TxD_3)
- // P_SW1 = ACC;
-
- #if (PARITYBIT == NONE_PARITY)
- SCON = 0x50; //8位可变波特率
- #elif (PARITYBIT == ODD_PARITY) || (PARITYBIT == EVEN_PARITY) || (PARITYBIT == MARK_PARITY)
- SCON = 0xda; //9位可变波特率,校验位初始为1
- #elif (PARITYBIT == SPACE_PARITY)
- SCON = 0xd2; //9位可变波特率,校验位初始为0
- #endif
-
- AUXR = 0x40; //定时器1为1T模式
- TMOD = 0x00; //定时器1为模式0(16位自动重载)
- TL1 = (65536 - (FOSC/4/BAUD)); //设置波特率重装值
- TH1 = (65536 - (FOSC/4/BAUD))>>8;
- TR1 = 1; //定时器1开始启动
- ES = 1; //使能串口中断
- EA = 1;
-
- SendString("STC15F2K60S2\r\nUart Test !\r\n");
- while(1);
- }
-
- /*----------------------------
- UART 中断服务程序
- -----------------------------*/
- void Uart() interrupt 4
- {
- if (RI)
- {
- RI = 0; //清除RI位
- P0 = SBUF; //P0显示串口数据
- P22 = RB8; //P2.2显示校验位
- }
- if (TI)
- {
- TI = 0; //清除TI位
- busy = 0; //清忙标志
- }
- }
-
- /*----------------------------
- 发送串口数据
- ----------------------------*/
- void SendData(BYTE dat)
- {
- while (busy); //等待前面的数据发送完成
- ACC = dat; //获取校验位P (PSW.0)
- if (P) //根据P来设置校验位
- {
- #if (PARITYBIT == ODD_PARITY)
- TB8 = 0; //设置校验位为0
- #elif (PARITYBIT == EVEN_PARITY)
- TB8 = 1; //设置校验位为1
- #endif
- }
- else
- {
- #if (PARITYBIT == ODD_PARITY)
- TB8 = 1; //设置校验位为1
- #elif (PARITYBIT == EVEN_PARITY)
- TB8 = 0; //设置校验位为0
- #endif
- }
- busy = 1;
- SBUF = ACC; //写数据到UART数据寄存器
- }
-
- /*----------------------------
- 发送字符串
- ----------------------------*/
- void SendString(char *s)
- {
- while (*s) //检测字符串结束标志
- {
- SendData(*s++); //发送当前字符
- }
- }
-
可以看到这串代码引用的头文件是#include "reg51.h",而我们之前引用的都是stc15.h,其实,我们可以理解为15单片机是51单片机的哥哥,是向下兼容51的,而stc15.h其实是包含有reg51.h的所有内容,并且stc15.h里还有一些51单片机没有,但是15单片机有的东西(不知道上述内容对不对,反正大概是这样的),所以我们需要对代码进行修改,大致分四步走:
删除头文件reg51.h并添加头文件stc15.h
删除所有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;
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内找到这些引脚的配置代码,可以根据自己的需要生成:
代码上已经定义了波特率,我们直接修改其数值即可,改为9600
在stc-isp的串口助手选择正确的串口,并调整波特率为9600,然后就可以打开串口了。默认的代码只有上电之后发送一串数据,所以可以重启一下开发板,就可以看到发送的数据了!
经过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
- /*---------------------------------------------------------------------*/
- /* --- STC MCU Limited ------------------------------------------------*/
- /* --- STC15F4K60S4 系列 定时器1用作串口1的波特率发生器举例------------*/
- /* --- Mobile: (86)13922805190 ----------------------------------------*/
- /* --- Fax: 86-0513-55012956,55012947,55012969 ------------------------*/
- /* --- Tel: 86-0513-55012928,55012929,55012966-------------------------*/
- /* --- Web: www.STCMCU.com --------------------------------------------*/
- /* --- Web: www.GXWMCU.com --------------------------------------------*/
- /* 如果要在程序中使用此代码,请在程序中注明使用了STC的资料及程序 */
- /* 如果要在文章中应用此代码,请在文章中注明使用了STC的资料及程序 */
- /*---------------------------------------------------------------------*/
-
- //本示例在Keil开发环境下请选择Intel的8058芯片型号进行编译
- //若无特别说明,工作频率一般为11.0592MHz
-
-
- #include <stc15.h>
- #include "intrins.h"
-
- typedef unsigned char BYTE;
- typedef unsigned int WORD;
-
- #define FOSC 11059200L //系统频率
- #define BAUD 9600 //串口波特率
-
- #define NONE_PARITY 0 //无校验
- #define ODD_PARITY 1 //奇校验
- #define EVEN_PARITY 2 //偶校验
- #define MARK_PARITY 3 //标记校验
- #define SPACE_PARITY 4 //空白校验
-
- #define PARITYBIT NONE_PARITY //定义校验位
-
-
- #define S1_S0 0x40 //P_SW1.6
- #define S1_S1 0x80 //P_SW1.7
-
-
- bit busy;
-
- void SendData(BYTE dat);
- void SendString(char *s);
-
- void main()
- {
- ACC = P_SW1;
- ACC &= ~(S1_S0 | S1_S1); //S1_S0=0 S1_S1=0
- P_SW1 = ACC; //(P3.0/RxD, P3.1/TxD)
-
- #if (PARITYBIT == NONE_PARITY)
- SCON = 0x50; //8位可变波特率
- #elif (PARITYBIT == ODD_PARITY) || (PARITYBIT == EVEN_PARITY) || (PARITYBIT == MARK_PARITY)
- SCON = 0xda; //9位可变波特率,校验位初始为1
- #elif (PARITYBIT == SPACE_PARITY)
- SCON = 0xd2; //9位可变波特率,校验位初始为0
- #endif
-
- AUXR = 0x40; //定时器1为1T模式
- TMOD = 0x00; //定时器1为模式0(16位自动重载)
- TL1 = (65536 - (FOSC/4/BAUD)); //设置波特率重装值
- TH1 = (65536 - (FOSC/4/BAUD))>>8;
- TR1 = 1; //定时器1开始启动
- ES = 1; //使能串口中断
- EA = 1;
-
- //不能连续发送!(直接放在while(1)里)可以在发送之间加一个100ms延时,否则容易接收不到,发送不出去
- SendString("STC15F2K60S2\r\nUart Test !\r\n");
- while(1);
- }
-
- /*----------------------------
- UART 中断服务程序
- -----------------------------*/
- void Uart() interrupt 4
- {
- if (RI)
- {
- RI = 0; //清除RI位
- P0 = SBUF; //P0显示串口数据
- P2|=0x80;P2&=0x9F;P2&=0x1F;//打开LED灯
- }
- if (TI)
- {
- TI = 0; //清除TI位
- busy = 0; //清忙标志
- }
- }
-
- /*----------------------------
- 发送串口数据
- ----------------------------*/
- void SendData(BYTE dat)
- {
- while (busy); //等待前面的数据发送完成
- ACC = dat; //获取校验位P (PSW.0)
- if (P) //根据P来设置校验位
- {
- #if (PARITYBIT == ODD_PARITY)
- TB8 = 0; //设置校验位为0
- #elif (PARITYBIT == EVEN_PARITY)
- TB8 = 1; //设置校验位为1
- #endif
- }
- else
- {
- #if (PARITYBIT == ODD_PARITY)
- TB8 = 1; //设置校验位为1
- #elif (PARITYBIT == EVEN_PARITY)
- TB8 = 0; //设置校验位为0
- #endif
- }
- busy = 1;
- SBUF = ACC; //写数据到UART数据寄存器
- }
-
- /*----------------------------
- 发送字符串
- ----------------------------*/
- void SendString(char *s)
- {
- while (*s) //检测字符串结束标志
- {
- SendData(*s++); //发送当前字符
- }
- }
-
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。