51单片机笔记[8]-串口通信2
Proteus仿真时Virtual Terminal不显示弹出窗口
解决方法☞
调试->选中Virtual Terminal
STC-ISP生成代码
STC-ISP可以生成串口初始化代码
STC-ISP还可以下载到肯定能用的示例程序:
下载实验箱4程序包
虚拟串口的使用
[https://blog.csdn.net/farsight_2098/article/details/90210349]
[https://www.cnblogs.com/zhenghaoyu/p/10059848.html]
软件:VSPD(虚拟串口)和串口调试助手
虚拟串口软件激活COM1和COM2,
COM1负责发送数据,COM2负责接收数据
因而,串口调试助手连接COM2,Proteus相应元件绑定COM1.COM2模拟另一台设备向COM1发送/接收数据
,波特率两边一致即可
串口实现命令
已知的bug:仿真时一会儿就会卡死,好像接收数据有问题
/*---------------------------------------------------------------------*/
/* --- STC MCU Limited ------------------------------------------------*/
/*------------------------------------------------------*/
#include <stc15f2k60s2.h>
#include "intrins.h"
#include <stdio.h>
typedef unsigned char BYTE;
typedef unsigned int WORD;
#define FOSC 12000000L //系统频率
#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 //定义校验位
#ifndef __STC15_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; //辅助寄存器
sfr P_SW1 = 0xA2; //外设功能切换寄存器1
sbit P22 = P2^2;
#endif
#define S1_S0 0x40 //P_SW1.6
#define S1_S1 0x80 //P_SW1.7
bit busy;
/*
********************全局变量***************************
*/
#define uchar unsigned char
#define uint unsigned int
#define time 100 //在接收函数中通过判断是否接收超时来判断字符串是否接收完毕
#ifndef S4RI
#define S4RI 0x01 //S4CON.0
#define S4TI 0x02 //S4CON.1
#endif
#define send_string SendString
unsigned char code distab[16] = {
0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,
0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71
};//0-F
//字符串开始$,字符串结束#
int _dollar=0;
int _diese=0;
//接收/发送数据变量
uchar rec_data;
uchar send_data;
unsigned char *rec_data_array;
char send_data_array[25];
char code xuehao[]="$xuaohao:20211009128#\n";
/*
指令含义(ASCII):
$+数字+# 数码管显示数字
$+字母+# 流水灯字母状态
*/
#define SMG P7
sbit KEY = P3^3;
sbit LED1=P4^6;
sbit LED2=P4^7;
//按键状态
int count=-1;
/*
*********************结束全局变量************************
*/
//函数声明
void SendData(BYTE dat);
void SendString(char *s);
void delay(int);
void smg_display(int);
void led(uchar);
void ana_command(unsigned char*);
void key_scan();
unsigned char* Receive(int rec_i);
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;
/**
用户程序
*/
smg_display(4);
led('B');
SendString(xuehao);
delay(1000);
while(1){
key_scan();
rec_data_array=Receive(3);
if(*rec_data_array){
send_string(rec_data_array);
//LED2=~LED2;
}
ana_command(rec_data_array);
}
}
/*----------------------------
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++); //发送当前字符
}
}
void delay(int ms){//@12MHz
int i,j;
for(i=0;i<ms;i++){
for(j=0;j<1000;j++);
}
}
//数码管
void smg_display(int num){
SMG = ~distab[num];
}
//LED灯
void led(uchar status){
switch(status){
case 'A':LED1=0;LED2=0;break;
case 'B':LED1=1;LED2=0;break;
case 'C':LED1=0;LED2=1;break;
case 'D':LED1=1;LED2=1;break;
default:LED1=1;LED2=0;
}
}
//解析指令
void ana_command(uchar *dat){
if(*dat=="$0#"){
//数码管0
smg_display(0);
send_string("received:0");
}
if(*dat=="$1#"){
//数码管1
smg_display(1);
send_string("received:1");
}
if(*dat=="$2#"){
//数码管2
smg_display(2);
}
if(*dat=="$3#"){
//数码管3
smg_display(3);
}
if(*dat=="$4#"){
//数码管4
smg_display(4);
}
if(*dat=="$5#"){
//数码管5
smg_display(5);
}
if(*dat=="$6#"){
//数码管6
smg_display(6);
}
if(*dat=="$7#"){
//数码管7
smg_display(7);
}
if(*dat=="$8#"){
//数码管8
smg_display(8);
}
if(*dat=="$9#"){
//数码管9
smg_display(9);
}
if(*dat=="$A#"){
led('A');
send_string("received:A");
}
if(*dat=="$B#"){
led('B');
send_string("received:B");
}
if(*dat=="$C#"){
led('C');
send_string("received:C");
}
if(*dat=="$D#"){
led('D');
send_string("received:D");
}
}
//按键监听
void key_scan(){
uchar temp[5];
if(KEY==0){
delay(200);
if(KEY==0){
count++;
if(count>9) count=0;
smg_display(count);
LED1=~LED1;LED2=~LED2;
sprintf(temp,"$%d#",count);
send_string(temp);
//sprintf(temp,"$%c#",'A');
//send_string(temp);
}
}
}
//字符串接受
unsigned char* Receive(int rec_i)
{
int rec_fin_flag=0;
//unsigned count=0; //超时机制 用于短暂计数用作延时
unsigned char rec_str[6];
//unsigned char* rec_str;
unsigned count1;
while(rec_fin_flag!=1)
{
rec_str[rec_i]=SBUF;
RI=0;
rec_i++;
count1=0;
while(rec_fin_flag==0)
{
if(RI==1) break;
count1++;
if(count1==time) rec_fin_flag=1;
}
}
RI=0;
return rec_str;
}
效果
#include "reg52.h"
#include <stdio.h>
//uint 和 uchar近似等同
#define uchar unsigned char
#define uint unsigned int
/**全局变量*/
int count=0;
int num=0;
char *xuehao="xuehao:20211009128\n";
char *received_string;
unsigned char code table[16]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,
0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71
};//smg-共阴
sbit KEY1=P3^1;
sbit KEY2=P3^0;
sbit LED1=P2^0;
sbit LED2=P2^1;
bit busy=0;
#define SMG P0;
#define LED P2;
uchar com_temp;//串口数据传递
/**结束全局变量*/
/**函数头部声明*/
void delay(int ms);
void init_uart();
void key_scan();
void show_num(int num);
void led(int status);
void send_string(char*);
void rec_string(char *);//接收字符串
void ana_string(char *);//分析字符串
/**结束函数头部声明*/
//主函数
void main(){
init_uart();
send_string("welcome to 51 mcu sys!\n");
send_string("author:20211009128\n");
send_string("please send command!\n");
//P0=table[5];
show_num(2);
led(1);
while(1){
key_scan();
rec_string(received_string);
if(*received_string) ana_string(received_string);
}
}
/**函数本体*/
void delay(int ms){
int i,j;
for(i=0;i<ms;i++){
for(j=0;j<1000;j++);
}
}
//中断方式
//如果是查询方式,ES=0;EA=0;
void init_uart(){//9600bps@12MHz
TMOD|=0X20; //设置计数器工作方式2
SCON=0X50; //设置为工作方式1
PCON=0X80; //波特率加倍
TH1=0xFA; //计数器初始值设置
TL1=0xFA;
ES=1; //打开接收中断
EA=1; //打开总中断
TR1=1; //打开计数器
delay(100);//100ms
}
//中断函数
void uart() interrupt 4 {//串口通信中断函数
uchar rec_data;
if(RI){
RI = 0; //清除接收中断标志位
rec_data=SBUF; //存储接收到的数据
}
if(com_temp){//如果非空
SBUF=com_temp; //数据放入到发送寄存器
if(TI){
TI=0; //清除发送完成标志位
busy=0;//空闲
}
rec_data=com_temp;//传递到全局变量
delay(5000);
}
}
//按键扫描
void key_scan(){
if(KEY1==0){
delay(1);
if(KEY1==0){
int k;
send_string("xuehao:20211009128\n");
}
}
if(KEY2==0){
delay(1);
if(KEY2==0){
show_num(count);
num++;
if(num>9) num=0;
}
}
}
//数码管显示
void show_num(int num){
if(num<=9 || num>=0){
P0=table[num];
}
}
//发送字符串
void send_string(char *input){
while(*input){//检查是否到结尾
while(busy);//等待上一帧数据发送完毕
busy=1;
SBUF=*input++;
delay(1);
busy=0;
}
}
//接收字符串
void rec_string(char *input){
while(busy);//等待串口
busy=1;
// static int _count=0;
while(count<20 || com_temp!='#'){
count++;
if(count>20) count=0;
*input=com_temp;
*input++;
}
send_string(input);
busy=0;
*input='#';
}
//流水灯
void led(int status){
int k;
switch (status) {
case 1:
for(k=0;k<8;k++){
P2=(1<<k);
delay(10);
}
break;
default:
P2=0x02;
delay(1);
P2=0x08;
delay(1);
break;
}
}
//分析字符串
void ana_string(char *input){
if(*input=='#') show_num(0);
if(*input=='1') led(1);
else{
led(2);//default
}
}
/**bugfixed
1.PC端只能接收到一个数据、而非连续的数据
while(!TI); //当发送到停止位时 TI自动变为1; while(!TI)和while(TI == 1)是不一样的
2.pc端接受数据时,第一个数据总为00.
初始化串口不能立马发送数据,需要一个短暂的延迟!
delay(100);//100ms
*/
效果:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」