目录
- 1.串口通信的基础知识
- 串口通信(Serial Communication)
- 同步/异步?
- 全双工?
- 常见的串口软件的参数
- 2.STC32的串口通信实现原理
- 引脚选择:实现分时复用
- 模式选择
- 串口1模式1,模式1波特率计算公式
- 3.串口通信代码实现
- 编写串口1通信程序
- 15.5.4 串口2使用定时器2做波特率发生器
- 新建工程16.串口2通信
- 错误排查及代码分析
- 总结
- 课后练习:
1.串口通信的基础知识
百度百科:串口通信的概念
什么是通信?
例如U盘和电脑,我们电脑需要往U盘存东西,而U盘上只有四个触点,除去一个电源一个地,只剩下两个引脚了。此时我们坑定不能像点亮LED那样单纯的给他两个引脚上输出个高低电平就能写数据了对吧。总不至于输出一个高电平就能在U盘里写入“STC32位8051单片机就是牛!”这么一句话了吧。
通信指设备之间通过一定的协议进行的信息交换。
每次发送一位数据的称为串行通信,多位(严格来说是2位以上)一起传输的称为并行通信。
今天要讲的串口通信是串行通信的其中的一种!也是1位1位的往外发数据,不是像这个图一样8位一起发。
stc共有4个串口!串口1/2和3/4的主要区别在这里,这节课主要讲异步串口。
串口通信(Serial Communication)
是指外设和计算机间,通过数据信号线 、地线、控制线等,按位进行传输数据的一种通讯方式。这种通信方式使用的数据线少,在远距离通信中可以节约通信成本,但其传输速度比并行传输低。
同步/异步?
异步方式(使用较多):
①以字符为单位传送信息。
②相邻两字符间的间隔是任意长。
③因为一个字符中的比特位长度有限,所以需 要的接收时钟和发送时钟只要相近就可以。
④异步方式特点简单的说就是:字符间异步,字符内部各位同步。
同步方式:
①以数据块为单位传送信息。
②在一个数据块(信息帧)内,字符与字符间无间隔。
③因为一次传输的数据块中包含的数据较多,所以接收时钟与发送进钟严格同步,通常要有同步时钟。
全双工?
常见的串口软件的参数
这里的4个数据发送端和接收段必须严格保持一致。否则接收到的就是乱码。
●波特率?
例如9600的波特率意味着每秒钟可以发9600位的数据。
我们这里起始位就是一个位(一个二进制的一个数0或者1),1bit的变量。中间有8个数据为,然后校验位,停止位。速度9600即每秒最快可以发960个这种包,
●数据位? 有几个2进制位的数据
●校验位?
●停止位?
●传输距离
通信距离很短,如何解决?
以下分别为RS232,RS422,RS485的通信原理图。
串口的逻辑电平只有5V或者3.3V,如果单片机是5V供电,逻辑电平只有5V(高电平),因为电平比较低,传输距离受限,
●串口有什么用
2.STC32的串口通信实现原理
引脚选择:实现分时复用
模式选择
每一种模式的配置参考手册,这里不再复述。这里主要讲串口1的模式1(应用最广泛)。
串口1模式1,模式1波特率计算公式
当软件设置SCON的SMO、SM1为“01”时,串行口1则以模式Ⅰ进行工作。此模式为8位UART格式,一帧信息为10位:1位起始位,8位数据位(低位在先)和1位停止位。波特率可变,即可根据需要进行设置波特率。TxD为数据发送口,RxD为数据接收口,串行口全双工接受/发送。
模式Ⅰ的发送过程:串行通信模式发送时,数据由串行发送端TxD输出。当主机执行一条写SBUF的指令就启动串行通信的发送,写“SBUF”信号还把“1”装入发送移位寄存器的第9位,并通知TX控制单元开始发送。移位寄存器将数据不断右移送TxD端口发送,在数据的左边不断移入“O”作补充。当数据的最高位移到移位寄存器的输出位置,紧跟其后的是第9位“1”,在它的左边各位全为“0”,这个状态条件,使TX控制单元作最后一次移位输出,然后使允许发送信号“SEND”失效,完成一帧信息的发送,并置位中断请求位TI,即 TI=1,向主机请求中断处理。
模式1的接收过程:当软件置位接收允许标志位.REN,即REN=1时,接收器便对RxD端口的信号进行检测,当检测到RxD端口发送从“1”→“O”的下降沿跳变时就启动接收器准备接收数据,并立即复位波特率发生器的接收计数器,将IFFH装入移位寄存器。接收的数据从接收移位寄存器的右边移入,已装入的IFFH向左边移出,当起始位"0O"移到移位寄存器的最左边时,使RX控制器作最后一次移位,完成一帧的接收。若同时满足以下两个条件:
·RI=0;
. SM2=0或接收到的停止位为1。
则接收到的数据有效,实现装载入SBUF,停止位进入RB8,RI标志位被置l,向主机请求中断,若上述两条件不能同时满足,则接收到的数据作废并丢失,无论条件满足与否,接收器重又检测RxD端口上的"1"→"0"的跳变,继续下一帧的接收。接收有效,在响应中断后,RI标志位必须由软件清0。通常情况下,串行通信工作于模式1时,SM2设置为"0"。
串口1的波特率是可变的,其波特率可由定时器1或者定时器2产生。当定时器采用IT模式时(12倍速),相应的波特率的速度也会相应提高12倍。
模式2和模式3,大同小异,不深入展开。
3.串口通信代码实现
RXD和TXD接在了P4.6跟P4.7上,使用的是串口2。
编写串口1通信程序
打开手册15.5 范例程序
15.5.4 串口2使用定时器2做波特率发生器
新建工程16.串口2通信
复制"15.NTC温度采集"工程为"16.串口2通信",HARDWARE目录中新建usart子目录,并将usart文件夹加入include,新建usart2.c和usart2.h,:
插入头文件模板,并修改。
先编写初始化函数声明:void Usart2_Init(void);,入口参数肯定是无。函数实现(先把官方例程的初始化抄写过来):
#define BRT (65536 - (MAIN_Fosc / 115200+2)/4) //加2操作是为了让Keil编译器自动实现四舍五入运算
void Usart2_Init(void)
{
P_SW2 = 0X80; //0X80=1000 0000
S2CFG = 0X01;
S2CON = 0X50;
T2L = BRT;
T2H = BRT>>8;
T2x12 = 1;
T2R =1;
wptr = 0x00;
rptr = 0x00;
busy = 0;
}
串口要用到主频,将该定义从demo.c中移动到usart2.h中。单独写在串口里面。
初始化代码中有三个小写的变量,属于用户定义变量,需要增加定义。另外,在头文件中也要增加定义(主函数中也会使用到这些变量),使用extern关键字。
extern bit busy;
extern char wptr;
extern char rptr;
extern char buffer[16];
编写中断服务函数,中断函数的名字可以任意取,只要后面的中断号无错误即可。
增加发送函数Usart2_Send和void Usart2_SendStr(char *p),并在头文件中声明。
在主函数中删除上节课无用内容,再调用串口初始化,demo.c中while循环内的usb串口部分可以注释掉,本节不使用。
时间部分可以留着,方便计时。
修改后的源代码如下。
demo.c中的main函数部分:
void main() //程序开始运行的入口
{
sys_init(); //USB功能+IO口初始化
usb_init(); //usb库初始化
delay_ms(500);
Timer0_Init(); //定时器0初始化
Usart2_Init(); //串口初始化
IE2 = 0X01;
EA = 1; //CPU开放中断,打开总中断。
Usart2_SendStr("Usart2 Test !\r\n");
while(1) //死循环
{
if( TIM_10MS_Flag==1 ) //如果10ms到了
{
TIM_10MS_Flag = 0; //清空标志位
}
if (rptr != wptr)
{
Usart2_Send(buffer[rptr++]); //字符发送
rptr &= 0x0f;
}
}
}
Usart2.c:
#include "usart2.h"
#define BRT (65536 - (MAIN_Fosc / 115200+2)/4) //加2操作是为了让Keil编译器自动实现四舍五入运算
bit busy;
char wptr;
char rptr;
char buffer[16];
void Usart2_Init(void)
{
P_SW2 = 0X80; //0X80=1000 0000
S2CFG = 0X01;
S2CON = 0X50;
T2L = BRT; //BRT为宏定义,
T2H = BRT>>8;
T2x12 = 1;
T2R =1;
wptr = 0x00;
rptr = 0x00;
busy = 0;
}
void Usart_Isr() interrupt 8
{
if( S2TI )
{
S2TI = 0;
busy = 0;
}
if( S2RI )
{
S2RI = 0;
buffer[wptr++] = S2BUF;
wptr &= 0X0F;
}
}
void Usart2_Send(char dat)
{
while(busy);
busy = 1;
S2BUF = dat;
}
void Usart2_SendStr(char *p)
{
while(*p)
{
Usart2_Send(*p++);
}
}
Usart2.h:
#ifndef __USART2_H
#define __USART2_H
#include "COMM/stc.h" //调用头文件
#include "COMM/usb.h"
#define MAIN_Fosc 11059200UL //定义为无符号长整型,避免计算溢出
//------------------------引脚定义------------------------//
//------------------------变量声明------------------------//
extern bit busy;
extern char wptr;
extern char rptr;
extern char buffer[16];
//------------------------函数声明-----------------------//
void Usart2_Init(void);
void Usart2_Send(char dat);
void Usart2_SendStr(char *p);
#endif
编译成功,下载进去看一下。注意,这里的时钟选择11.0592。使用串口线(使用usb转串口线usb-RS232),按要求接线,并选择正确的串口。
错误排查及代码分析
下载完成后,无反应,看一下代码,在执行主函数之前就应该执行串口发送,但是串口工具里无变化,检查串口工具参数设置,
都是对的,回过头看手册,查找问题原因,先找到初始化Usart2_Init,在手册中使用高级搜索功能,查找P_SW2:
找到定义:3.1.2 外设端口切换控制寄存器2(P_sW2):
使能访问XFR,EAXFR在sys_init中已经使能:EAXFR = 1; //扩展寄存器(XFR)访问使能,所以P_SW2 = 0X80; 不使用也是可以的。
查看S2_S:串口2功能脚选择位,开发板上的串口2需切换到P4.6和P4.7引脚上,S2_S位应置为1,这里可以使用或等于,在不影响最高位的情况下,设置S2_S位,即增加:P_SW2 |= 0X01;
再查找S2CFG,目录对应项跳转至:15.4.3 串口⒉配置寄存器(S2CFG)。这里设置W1为1,解释:W1:当需要使用串口2时,此位必须设置为“1”,否则可能会产生不可预期的错误。若不需使用串口
2,则不用特别设置W1。
搜索S2CON,跳转至15.4.1串口2控制寄存器(S2CON)。
S2CON = 0X50; //0X50=01010000,设置为模式1:可变波特率8位数据方式,S2REN=1:允许串口接收数据
T2L,T2H是定时器相关设置,串口需要用到1个定时器来生成波特率,也就是作为一个基准时钟。具体设置为:首先它传入的是一个数组
T2L = BRT; //BRT为宏定义,
T2H = BRT>>8;
T2x12 = 1;
T2R =1;
接下来是3个变量。如果说TI需要发送数据,发送完了以后把这个变量给他清零。发送时先检测一下busy,如果等于1,说明上次的数据还没有发完,阻塞,继续等待。
如果发送完了,先把busy置1,然后把数据写入S2BUF的位里面,之后开始发送,看一下手册15.3.6串口1模式1,模式1波特率计算公式中的时序图,和串口2同理,数据发送完之后,才会变成高电平,
也就是执行if( S2TI ):S2T:串口2发送中断请求标志位。在模式0中,当串口发送数据第8位结束时,由硬件自动将S2TI置1,向主机请求中断,响应中断后S2TI必须用软件清零。在其他模式中,则在停止位开始发送时由硬件自动将S2TI置1,向CPU发请求中断,响应中断后S2TI必须用软件清零。满足条件后S2TI清0,这个BUSY其实是个忙碌标志位。也就是等于0的时候他才能发送数据,把数据直接写进S2BUF寄存器中就可以自动发送。S2BUF进行读操作,实际是读取串口接收缓冲区,对S2BUF进行写操作则是触发串口开始发送数据。
void Usart2_SendStr(char *p) //发送整个字符。while(*p)可以用这个判断他这个字符是否为空,这里用到了一个指针的技巧,首先传入的是一个数组或者说是地址,把里面的数不断发送出去,直到发送至最后一位。发完之后结束。这样写以后,如果想发字符串只要在双引号内写入即可。
接收到8位数值后,RI会变高,进入中断,if( S2RI ),进入后先清0,将读到的数据存入,只会把地址加1,也就是接收到的数值位数。每接收到1个就加1,接收到的最大值是0x0f=16。
接下来,demo.c中判断:if (rptr != wptr),如果rptr不等于tptr,那就就将rptr的数值不断累加,然后直到等于这个数值。
可以看到,分析过程代码段中仅增加了1行:P_SW2 |= 0X01; //开发板上的串口2需切换到P4.6和P4.7引脚上。
重新编译,手动下载,可以看到数值显示了,需要打印的参数显示出来了,也可以再把需要的数据写进去,可以回显出来,可以试试。
这里的演示功能仅为上电显示一段文字,运行过程中发送什么文字,返回显示同样的文字。重点需要注意默认选择的引脚。写的时候一定要按照实际电路引脚去写。
总结
1.学会串口的一专业名词。
2.学会串口的配置和使用(也可以去学习一下试验箱代码,里面的代码非常全面)
总共有4个串口,例程包里4个串口都有示例代码,基本上所有用的的代码均已涵盖,示例里的初始化也写的非常详细,可以选择不一样的定时器,只有定时器2可以同时为4个串口作为波特率发生器,
课后练习:
1.串口发送字符Ax\r\n,(x表示0-7)板子点亮对应LED
2.串口发送Bxxxx\r\n,xxxx表示一个四位数,四位数码管显示这个4位数
2.串口发送Z\r\n,板子给电脑发送“Hello STC”;
3.串口发送字符Cx\r\n,(x表示0-1)板子打开/关闭蜂鸣
4.串口发送字符Dx\r\n,板子通过串口发送当前温度给电脑。
能独立做出这个实验的话,说明你已经快出师了。