【51单片机】单片机与串口通信

一、任务目标

1.甲、乙两单片机进行 方式3(或方式2)串行通信。甲机把控制8个流水灯点亮的数据发送给乙机并点亮其P1口的8个LED。方式3比方式1多了1个可编程位TB8,该位一般作奇偶校验位。乙机接收到的8位二进制数据有可能出错,需进行奇偶校验,其方法是将乙机的RB8和PSW的奇偶校验位P进行比较,如果相同,接收数据;否则拒绝接收。

2.将单片机串口与笔记本电脑串口模块相连,单片机每隔2秒发送“Hello C51”,笔记本电脑用串口助手软件接收。 如果串口助手发送字符“0" 给单片机,则单片机停止发送; 如果单片机收到“1”,则继续每隔2秒发送“Hello C51”。

二、理论基础

串行口的内部结构如下:
串行口的内部结构
想要利用串行口通信,发送端TXD单向导向RXD。
半双工
单工

全双工
串行口控制寄存器SCON,字节地址98H,可位寻址,位地址为98H~9FH,即SCON的所有位都可用软件来进行位操作清“0”或置“1”。
在这里插入图片描述
在这里插入图片描述

三、实现过程

3.1 串口传送流水灯数据

3.1.1 仿真图

在这里插入图片描述

3.1.2 甲机代码

#include <reg51.h>  
void Send(unsigned char dat);
int i,j;
sbit P_x=PSW^0; 
unsigned char Tab[8] = {0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x7f}; // 控制流水灯显示数据数组  

void delay(void) // 延时函数,这里假设晶振为12MHz,大约延时200ms  
{  
    unsigned char m, n;  
    for (m = 200; m > 0; m--) // 调整循环次数以达到约200ms的延时效果  
    {  
        for (n = 248; n > 0; n--); // 内层循环同样需要调整  
    }  
}

void main(void)  
{  
    unsigned char i;  
    TMOD = 0x20; // 设置定时器T1为方式2
    SCON = 0x50; // 设置串口为方式1,8位可变波特率,允许接收。方式3通常用于多机通信。  
    TH1 = 0xfd; // 波特率设置需要根据晶振频率来确定,这里假设晶振为11.0592MHz。  
    TL1 = 0xfd;  
    TR1 = 1; // 启动定时器T1  
    while (1)  
    {  
        for(i = 0; i < 8; i++)  
        {  
            Send(Tab[i]);  
            delay(); // 延时函数应根据实际晶振频率调整以达到约200ms的效果。  
        }  
    }  
}  
  
void Send(unsigned char dat) // 发送1字节数据的函数  
{  
    unsigned char temp;  
    temp = dat; // 保存原始数据以便后面计算偶校验位  
    TB8 = 0; // 初始设置为0,之后根据数据计算偶校验位  
    for(j = 0; j < 8; j++) // 计算偶校验位  
    {  
        if ((temp & 0x01) ^ TB8) // 如果当前位和TB8不同,则TB8取反  
        {  
            TB8 = ~TB8;  
        }  
        temp >>= 1; // 右移一位处理下一位  
    }  
    SBUF = dat; // 发送数据  
    while (!TI); // 等待发送完成  
    TI = 0; // 清除发送完成标志  
}  
  

3.1.3 乙机代码

#include <reg51.h>
sbit P_x=PSW^0;		// P位为PSW 寄存器的第0位,即奇偶校验位
void main(void) 		//主函数
{	
 TMOD=0x20;		//设置定时器T1为方式2
 SCON=0xd0;		//设置串口为方式3,允许接收REN=1
 PCON=0x00;  		// SMOD=0  
 TH1=0xfd;		//给定时器T1赋初值,波特率为9600
 TL1=0xfd;
 TR1=1;			//接通定时器T1
 REN=1; 			//允许接收
 while(1)
 {	
  P1=Receive();			//将接收到的数据送P1口显示
 }
}
  
unsigned char Receive(void)		//接收1字节数据的函数
{	
 unsigned char dat;
 while(RI==0); 		//检测RI,RI=0,未接收完,则循环等待
 ;
 RI=0;			//已接收一帧数据,将RI清0
 ACC=SBUF;			//将接收缓冲器的数据存于ACC
 if(RB8==P_x) 			//只有偶校验成功才能往下执行,接收数据
 {	
  dat=ACC;		//将接收缓冲器的数据存于dat
  return dat;		//将接收的数据返回
 }
}    

3.2 串口通信

3.2.1 仿真图

在这里插入图片描述

3.2.2 实现代码

#include <reg51.h>
#include <stdio.h>
#include <string.h>
char send_flag = 0;
char received_char;
// 假设波特率设置为9600,根据实际情况调整
void serial_init() {
    SCON = 0x50; // 设置为模式1,8位数据,可变波特率
    TMOD &= 0x0F; // 清除定时器1模式位
    TMOD |= 0x20; // 设置定时器1为8位自动重装模式
    TH1 = TL1 = 256 - 12; // 定时器初值,根据波特率调整
    TR1 = 1; // 启动定时器1
    ES = 1; // 开启串口中断
    EA = 1; // 开启总中断
}

// 发送字符串函数
void serial_send_string(char *str) {
    while (*str) {
        while (!TI); // 等待上一次发送完成
        SBUF = *str++; // 发送当前字符
        TI = 0; // 清除发送完成标志
    }
}

// 串口中断服务程序
void serial_ISR() interrupt 4 using 1 {
    if (RI) {
        RI = 0; // 清除接收标志
        received_char = SBUF; // 读取接收到的字符
        if (received_char == '0') {
            // 如果接收到字符'0',停止发送
        } else if (received_char == '1') {
            // 如果接收到字符'1',继续发送
        }
    }
}

// 主函数
void main() {
    serial_init(); // 初始化串口
    send_flag = 1; // 发送标志

    while (1) {
        if (send_flag) {
            serial_send_string("Hello C51"); // 发送字符串
            // 延时2秒,可以使用定时器中断实现
        }
    }
}

四、实验总结

通过本次实验,我深刻体会到了理论与实践相结合的重要性。在实验过程中,我不仅加深了对相关理论知识的理解,还提高了自己的实践操作能力。同时,我也学会了如何分析和解决实验中遇到的问题,这对我的学习和工作都具有重要的意义。

以上则是我此次的实验内容,如有错漏请各位大佬多多指教!

  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值