1.概要
1.实验名称:串口通讯实验
2.实验环境:IAP15F2K61S2国信长天实验板
3.实验配置:J13跳线配置为I/O模式,J5配置为BTN模式,J2配置为11-3,2-4模式,
J6配置为蜂鸣器电源
4.实验时间:2025-1-22
5.实验内容:PC与MCU进行串口通信,分别进行MCU向PC 循环发送内容和MUC与PC之间的收发实验
2.技术实现
1.硬件环境
IAP15F2K61S2单片机部分
由于硬件设计,仍需手动关闭LED和蜂鸣器
74HC138译码器
74HC573锁存器
74HC02高速硅栅CMOS器件
2.原理图
3.代码实现
1.串口通讯
#include <STC15F2K60S2.h>
#include <intrins.h>
#define LED(X) {P2=((P2&0X1F)|0X80);P0=X;P2&=0X1F;}
#define BAUD 9600
#define SYSclk 11059200L
/*延时函数*/
void Delay1000ms(void) //@11.0592MHz
{
unsigned char data i, j, k;
_nop_();
_nop_();
i = 43;
j = 6;
k = 203;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
/*串口发送函数*/
void uart_sendstring(unsigned char *str)
{
unsigned char *p;
p = str;
while(*p != '\0')
{
SBUF = *p;
while(TI == 0); //等待发送标志位置1
TI = 0;
p++;
}
}
void close_buz()
{
P2 = ((P2&0x1f) | 0xA0);
P0 = 0X00;
P2 &= 0X1F;
}
/*主函数*/
void main(void)
{
LED(0XFF); //关闭LED
close_buz(); //关闭蜂鸣器
SCON = 0x50; //串行控制寄存器,8位数据。波特率可变
AUXR = 0X40; //辅助寄存器,定时器时钟1T模式
TMOD &= 0x00; //定时器/计时器工作模式寄存器,将定时器1设置为16位自动重载定时器
TL1 = (65536 - (SYSclk / 4 / BAUD));
TH1 = (65536 - (SYSclk / 4 / BAUD)) >> 8;
TR1 = 1; //定时器1运行控制位
while(1)
{
uart_sendstring("\r\n");
uart_sendstring("hello");
uart_sendstring("\r\n");
uart_sendstring("你好");
Delay1000ms();
}
}
MCU向PC发送数据,本实验中MCU向PC发送制表符换行符,然后发送hello,然后再发送制表符和换行符,然后发送“你好”。
2.串口收发实验
#include <STC15F2K60S2.h>
#define BAUD 9600 //波特率
#define SYSclk 11059200L //系统时钟频率 /*11059200L中L表示宏SYSclk的数据类型为long型*/
#define BUZ(X) {P0 = X;P2 = ((P2 & 0X1F) | 0XA0);P2 = P2 & 0x1f;}
bit Rx_flag = 0; //数据接收标志位
char Rx_data = '1'; //接收到的数据
void uart_sendstring(unsigned char *str);
void main()
{
BUZ(0X00); //关闭蜂鸣器
SCON = 0X50; //串行控制寄存器,8位UART,波特率可变
/*
设置定时器1为波特率发生器
*/
AUXR = 0X40; //辅助寄存器,定时器1时钟1T模式
TMOD = 0X00; //计时器/定时器工作模式寄存器,将定时器设置为模式0(16位自动重载)
TL1 = (65536 - (SYSclk/4/BAUD));
TH1 = (65536 - (SYSclk/4/BAUD))>>8;
TR1 = 1; //定时器1控制允许位
ES = 1; //串口1中断允许位
EA = 1; //总中断允许位
while(1)
{
if(Rx_flag == 1) //判断数据标志位
{
Rx_flag = 0;
switch(Rx_data)
{
case '1':
{
P2 = ((P2 & 0x1f) | 0x80);
P0 = 0xFE;
P2 &= 0x1f;
uart_sendstring("1");
}
break;
case '2':
{
P2 = ((P2 & 0x1f) | 0x80);
P0 = 0xFD;
P2 &= 0x1f;
uart_sendstring("2");
}
break;
case '3':
{
P2 = ((P2 & 0x1f) | 0x80);
P0 = 0xFB;
P2 &= 0x1f;
uart_sendstring("3");
}
break;
case '4':
{
P2 = ((P2 & 0x1f) | 0x80);
P0 = 0xF7;
P2 &= 0x1f;
uart_sendstring("4");
}
break;
case '5':
{
P2 = ((P2 & 0x1f) | 0x80);
P0 = 0xEF;
P2 &= 0x1f;
uart_sendstring("5");
}
break;
case '6':
{
P2 = ((P2 & 0x1f) | 0x80);
P0 = 0xDF;
P2 &= 0x1f;
uart_sendstring("6");
}
break;
case '7':
{
P2 = ((P2 & 0x1f) | 0x80);
P0 = 0xBF;
P2 &= 0x1f;
uart_sendstring("7");
}
break;
case '8':
{
P2 = ((P2 & 0x1f) | 0x80);
P0 = 0x7F;
P2 &= 0x1f;
uart_sendstring("8");
}
break;
default:
uart_sendstring("error\r\n"); //输入其他数据返回error
break;
}
ES = 1;
}
}
}
void isr_uart(void) interrupt 4 //UART1中断的中断号为4
{
/*
发送数据之前,并未发送数据,
恰恰说明单片机已经接收到了数据,
硬件会将RI置1,
此时SBUF中接收到的数据为空。
*/
if(RI) //RI接收中断请求标志位,接受到8位数据后由硬件置1
{
RI = 0; //清除接受标志位
Rx_data = SBUF;
ES = 0; //串口中断允许标志位,置0,禁止中断
Rx_flag = 1;
}
}
void uart_sendstring(unsigned char *str)
{
unsigned char *p;
p = str;
while(*p != '\0')
{
SBUF = *p;
while(TI == 0); //串口1发送中断标志,方式0中,发送完8位数据后,由硬件置1
/*
此处等待中断标志位由硬件置1
*/
TI = 0;
p++;
}
}
PC向MCU发送1~8任意一个数字,8个LED按从右向左递减的位置显示接收到的数字对应的位置,同时MCU将接收到的数据再发送给PC
3.内容要点
SCON = 0X50; //串行控制寄存器,8位UART,波特率可变
AUXR = 0X40; //辅助寄存器,定时器1时钟1T模式
将T1x12置1,设置定时器为1T模式,不分频。将S1ST2置0 ,选择定时器1作为串口1的波特率发生器。
波特率发生器在串行通信中用于确保数据能够以正确的速率被发送或接收。当使用定时器作为波特率发生器时,每次中断的工作流程主要用于控制比特的发送或接收时间。以下是一个简化的过程描述,解释了每次中断是如何工作的,特别是在发送数据的情况下:
### 中断工作原理
1. **初始化阶段**:
- 配置定时器:根据所需的波特率和系统时钟频率,设置定时器的预分频系数和比较值(或者溢出值),使得定时器每隔一个比特周期触发一次中断。例如,在9600波特率下,每个比特时间为104微秒。
- 准备数据帧:组织好要发送的数据帧,通常包括起始位、数据位(通常是8位)、奇偶校验位(可选)和停止位。2. **开始发送数据**:
- 启动定时器并发送起始位:通过设置UART TX引脚为低电平来表示起始位,并启动定时器。
3. **中断服务程序(ISR)执行**:
- **每次中断触发**:当定时器计数到预设值时,会产生一个中断请求,CPU暂停当前任务跳转至中断服务程序(ISR)。
- 在ISR中:
- **切换TX引脚状态**:根据当前需要发送的比特位置更新TX引脚的状态(高电平或低电平)。首先发送最低有效位(LSB),然后依次发送直至最高有效位(MSB)。
- **管理比特计数器**:维护一个内部计数器来跟踪已经发送了多少个比特。每发送完一位,该计数器递增。
- **检查是否完成数据帧发送**:如果所有比特(包括起始位、数据位、奇偶校验位(如果有)、停止位)都已发送完毕,则可以在ISR中关闭定时器,或者标记发送完成标志以便主程序处理下一个待发送的数据字节。
在定时器中断服务程序(ISR)中执行的操作主要是为了确保数据能够按照设定的波特率准确地发送或接收。每次中断发生时,意味着一个比特周期已经过去,这时需要处理当前比特的数据并为下一个比特做准备。以下是在中断服务程序中可能执行的主要步骤:
### 发送数据时的中断处理
1. **更新计数器**:
- 每次进入中断服务程序时,首先增加比特计数器,用于跟踪当前正在发送或接收的是哪一个比特。2. **发送起始位**:
- 如果这是发送过程中的第一个中断(即比特计数器为0),则设置UART TX引脚为低电平以发送起始位。3. **发送数据位**:
- 根据比特计数器的值,从最低有效位(LSB)到最高有效位(MSB)依次发送数据位。这通常涉及到将待发送字节与一个掩码进行操作来提取当前应发送的比特值,并相应地设置TX引脚的状态。
4. **处理奇偶校验位(如果使用的话)**:
- 在所有数据位发送完毕之后,根据所选择的奇偶校验方式计算并发送奇偶校验位。5. **发送停止位**:
- 最后,在所有数据位和奇偶校验位(如果有)发送完毕后,设置TX引脚为高电平来发送停止位。6. **检查是否完成**:
- 判断是否所有的比特(包括起始位、数据位、奇偶校验位和停止位)都已发送完毕。如果是,则可以关闭定时器或者重置状态机以便开始下一次传输。7. **清除中断标志**:
- 最后不要忘记清除定时器的中断标志,允许下一次中断的发生。
TMOD &= 0x00; //定时器/计时器工作模式寄存器,将定时器1设置为16位自动重载定时器
TL1 = (65536 - (SYSclk / 4 / BAUD));
TH1 = (65536 - (SYSclk / 4 / BAUD)) >> 8;
TR1 = 1; //定时器1控制允许位
ES = 1; //串口1中断允许位
EA = 1; //总中断允许位
void isr_uart(void) interrupt 4 //UART1中断的中断号为4
{
/*
发送数据之前,并未发送数据,
恰恰说明单片机已经接收到了数据,
硬件会将RI置1,
此时SBUF中接收到的数据为空。
*/
if(RI) //RI接收中断请求标志位,接受到8位数据后由硬件置1
{
RI = 0; //清除接受标志位
Rx_data = SBUF;
ES = 0; //串口中断允许标志位,置0,禁止中断
Rx_flag = 1;
}
}
void uart_sendstring(unsigned char *str)
{
unsigned char *p;
p = str;
while(*p != '\0')
{
SBUF = *p;
while(TI == 0); //串口1发送中断标志,方式0中,发送完8位数据后,由硬件置1
/*
此处等待中断标志位由硬件置1
*/
TI = 0;
p++;
}
}
4.实验结果
实验1
实验2