- 打卡等级:偶尔看看II
- 打卡总天数:28
- 最近打卡:2025-04-25 19:41:38
已绑定手机
注册会员
- 积分
- 187
|
发表于 2025-4-11 14:39:15
|
显示全部楼层
第十九课 串口通信多机通信
1.串口1波特率的设定
(1)模式0的波特率
当UART_MOx6-0时,波特率为SYSclk/12;当UART_MOx6-1时,波特率为SYSc1k/2。
(2)模式2的波特率
串行口1工作于模式2时,波特率有两种波特率可选,取决于电源控制寄存器PCON中SMOD位的值,当SMOD-0时,为SYSclk/64:当SM0D-1时,为SYSclk/32.
(3)模式1和3的波特率
串行口1工作于模式1和3时,波特率是可变的,可以通过编程改变定时器1的溢出率或者定时器2的溢出率来确定波特率。
编程时应注意,当定时器作为波特率发生器使用时,应禁止定时器产生中断(ET1=0或ET2-0)。典型用法是将定时器设置工作在自动重装入时间常数的定时方式。设置完成后,启动定时器(TR1=1或TR2-1)。
假设SYSclk为系统时钟频率。串口1用T1作为波特率发生器,且T1工作于模式0(16位自动重
装模式)时的公式如下:
波特率=(T1的溢出率)/4=SYSclk/12-2 65536-[RL THL,RL TL1])/4
其中,12T模式时,TIx12=0:1T模式时,TIx12=1。
RL, TH1是TH1的自动重装载寄存器,RL,TL1是TL1的自动重装载寄存器。注意:此时波特率与
SMOD无关。
T2只有一种工作方式,即16位自动重装方式,因此使用T2作为波特率发生器时的公式如下:串口1的波特率=SYSclk/121-12x12/(65536-[RL, TH2,RL TL2])/4其中,RL TH2是TH2的自动重装寄存器;RLTL2是TL2的自动重装寄存器。在实际应用中,一般选用模式1或模式3。此时,波特率的设置关键在于T1和T2的溢出率的计算。
2.串口2~4的波特率设定
对于串口2、串口3和串口4,具体使用T2、T3或T4中的哪一个定时器作为波特率发生器前面已有详细叙述。在此,以T2作为波特率发生器为例,波特率计算方法如下公式所示:
波特率=SYSclk/12n/(65536-[RL TH2,RL TL2])/4串口3和串口4波特率的设定方式与串口2类似。
串口接口应用举例
(1)串口1的编程要点
① 设置串行口的工作模式
设置SCON寄存器的SMO和SM1的内容。若需要串行口具有接收功能,则置REN=1。
②设置正确的波特率
(a)使用T1作为波特率发生器时,需要设置T1的工作模式和时间常数(设定TMOD和TH1、TL1寄存器的内容);启动T1(置TR1=1)。
(b)使用T2作为波特率发生器时,需要设置T2寄存器和相应的位,包括:T2自动重装寄存器TH2和TL2,T2_C/T位,T2x12位,SMOD位。启动T2(置TR2=1)。
③ 设置串行口的中断优先级(设置PS寄存器的内容,也可以不设置,取默认值),设置相应的中断控制位(ES和EA)
4)如要串口1发送,将数据送入SBUF。
编制串行中断服务程序,在中断服务程序中要有清除中断标志指令(将TI和RI清0)。
(2)串口2的编程要点
首先 串口相关的IO设置
① 设置串口2的工作模式
设置S2CON寄存器中的S2SMO位。如需要串行口2具有接收功能,则置S2REN=1。2设置串口2的波特率
串口2只能使用T2作为波特率发生器,设置内容包括:TH2和TL2,T2C/位,T2x12位。
启动T2(置TR2=1)。
③ 设置串口2的中断优先级(设置PS2,也可以不设置,取默认值),设置打开相应的中断控制位(ES2和EA)
④如要串口2发送,将数据送入S2BUF。
⑤ 编制串行中断服务程序,在中断服务程序中要设置清除中断标志指令(分别是接收完成标志S2RI和发送完成标志S2TI)。
例: 设有甲、乙两台单片机,编写程序使两台单片机问实现如下串行通信功能。(假设系统时钟为11.0592MHz)
甲机发送:将首址为ADDRT的128字节的外部RAM数据块顺序向乙机发送:
乙机接收:将接收的128字节的数据,顺序存放在以首址为ADDRR的外部RAM中
//包含单片机的寄存器定义头文件//在外部RAM区定义128个单元
#include "stc8h.h"
unsigned char xdata ADDRT[128];
unsigned char num=0;//声明计数变量
unsigned char *myp;//指向发送数据区的指针
void main(void)//主程序,在C语言的主程序中可以不设置堆栈指针
{
SCON=0x40; //8位数据,可变波特率
AUXR &= 0xFE; //串口1选择定时器1为波特
TMOD &= 0x0F: //设定定时器1为16位自动
TL1 = 0xE8; //设定定时初值
TH1 = 0xFF; //设定定时初值
TR1 = 1; //启动定时器1
ES=1; //串行口开中断
EA=1; //开中断
psend=ADDRT; //设置发送数据缓冲区指针
SBUF=*psend; //发送第一个数据
while(1); //等待中断
}
void UART1_ISR(void)interrupt UART1_VECTOR //串口1中断服务函数
{
TI=0; //清发送中断标志
num++; //修改计数变量值
if(num==0x7F)ES=0; //判断是否发送完,若已完,则关中断
else //否则,修改指针,发送下一个数据
{
myp++:
SBUF=*myp;
}
}
发送代码
#include "stc8h.h"
unsigned char xdata ADDRT[128];
unsigned char num = 0; // 计数变量
unsigned char *psend; // 发送数据指针
void main() {
SCON = 0x40; // 串口模式1(8位UART,无校验)
AUXR &= 0xFE; // 串口1使用定时器1为波特率发生器
TMOD |= 0x20; // 定时器1设为8位自动重装模式
TH1 = 0xFD; // 波特率9600(11.0592MHz)
TL1 = 0xFD; // 初始值
TR1 = 1; // 启动定时器1
ES = 1; // 允许串口中断
EA = 1; // 开总中断
psend = ADDRT; // 指针指向数据首地址
SBUF = *psend; // 发送第一个字节
while(1); // 等待中断
}
void UART1_ISR() interrupt 4 {
if (TI) { // 发送中断
TI = 0; // 清除标志
num++; // 计数+1
if (num == 127) {
ES = 0; // 发送完成,关闭中断
} else {
psend++; // 指针递增
SBUF = *psend; // 发送下一字节
}
}
}
接收代码
#include "stc8h.h"
unsigned char xdata ADDRR[128];
unsigned char num = 0; // 计数变量
unsigned char *precv; // 接收数据指针
void main() {
SCON = 0x50; // 串口模式1,允许接收
AUXR &= 0xFE; // 定时器1作波特率发生器
TMOD |= 0x20; // 定时器1设为8位自动重装
TH1 = 0xFD; // 波特率9600(11.0592MHz)
TL1 = 0xFD;
TR1 = 1; // 启动定时器1
ES = 1; // 允许串口中断
EA = 1; // 开总中断
precv = ADDRR; // 指针指向接收缓冲区
while(1); // 等待中断
}
void UART1_ISR() interrupt 4 {
if (RI) { // 接收中断
RI = 0; // 清除标志
*precv = SBUF; // 存储数据
precv++; // 指针递增
if (++num == 128) ES = 0; // 接收完成,关中断
}
}
例:
多机通信编程举例
现用简单实例说明多机串行通信中从机的基本工作过程。而实际应用中还需要考虑通信的规范协议。有些协议很复杂,在此不加以考虑。假设系统晶振频率为11.0592MHz。
主机:先向从机发送一帧地址信息,然后再向从机发送10个数据信息,
从机:接收主机发来的地址帧信息,并与本机的地址号相比较,若不符合,仍保持SM2=1不变;若相等,则使SM2清零,准备接收后续的数据信息,直至接收完10个数据信息。
从机代码
#include "stc8h.h"
unsigned char xdata ADDRR[10];
unsigned char SLAVE = 5; // 从机地址设为5
unsigned char num = 10; // 待接收数据计数
unsigned char *mypdata = ADDRR;
void UART_Init() {
SCON = 0xF0; // 模式3(9位UART),SM2=1,允许接收
AUXR |= 0x01; // 使用定时器2作为波特率发生器
T2L = 0xE8; // 定时器2初值(11.0592MHz, 9600bps)
T2H = 0xFF;
AUXR |= 0x10; // 启动定时器2
ES = 1; // 允许串口中断
EA = 1; // 开总中断
}
void main() {
UART_Init();
mypdata = ADDRR; // 指向接收缓冲区
while(1);
}
void UART1_ISR() interrupt 4 {
if (RI) {
RI = 0; // 清除接收中断标志
unsigned char rdata = SBUF;
if (RB8 == 1) { // 地址帧
if (rdata == SLAVE) {
SM2 = 0; // 地址匹配,准备接收数据
}
} else { // 数据帧
if (SM2 == 0) { // 仅当SM2=0时接收数据
*mypdata++ = rdata;
num--;
if (num == 0) {
SM2 = 1; // 接收完成,恢复SM2=1
num = 10; // 重置计数
mypdata = ADDRR; // 重置指针
}
}
}
}
if (TI) TI = 0; // 清除发送中断标志(若启用发送)
}
主机代码
#include "stc8h.h"
void UART_Init() {
SCON = 0x80; // 模式2(9位UART),SM2=0
AUXR |= 0x01; // 使用定时器2
T2L = 0xE8; // 波特率9600
T2H = 0xFF;
AUXR |= 0x10; // 启动定时器2
}
void SendAddress(unsigned char addr) {
TB8 = 1; // 地址帧标志
SBUF = addr;
while(!TI); // 等待发送完成
TI = 0;
}
void SendData(unsigned char *data, unsigned char len) {
TB8 = 0; // 数据帧标志
for (unsigned char i=0; i<len; i++) {
SBUF = data;
while(!TI);
TI = 0;
}
}
void main() {
UART_Init();
unsigned char xdata data[10] = {0x01, 0x02, ..., 0x0A};
SendAddress(5); // 发送从机地址5
SendData(data, 10); // 发送10字节数据
while(1);
}
|
-
流程图
|