(1)单片机->电脑(一个字节)
要模拟串口,首先肯定是需要了解串口的协议,根据协议来编写程序。
UART的通信方式是由1个起始位,8个数据位,包含一个奇偶校验位,和结束位构成 。在本次的设计中默认为波特率为9600,停止位为1位,8位数据位,无奇偶校验位。
先介绍起始位,从高电平跳变为低电平,表示通信开始。再来简单介绍下波特率,单位时间内传送码元符号的个数。波特率9600,也就是1s内传送9600个bit,一个bit所需要的时间为 1000000us / 9600 = 104.166 us,也就是104us。
这是用示波器调出来的,就不一一展示示波器的使用教程了。
空指令使用时会占用执行一个指令的CPU时间片。常用于程序延时或精确计时,不过在较快的CPU上不明显。如果使用这个空指令nop()就一定要引入
当单片机 想给电脑 发送数据时,比如发送一个 0xE4 这个数据,用二进制形式表 示就是 0b11100100,在 UART 通信过程中,是低位先发,高位后发的原则,那么就让 TXD 首先拉低电平,持续一个波特率的时间,发送一位 0,然后继续拉低,再持续一段时间,又发送了一 位 0,然后拉高电平,持续一段时间,发了一位 1……一直到把 8 位二进制数字 0b11100100 全部发送完毕
\
你可能会问为什么上面代码为什么用的是P31?
答:单片机有两个引脚是专门用来做 UART 串行通信的,它们分别叫做 RXD 和 TXD,由它们组成的通信接口就叫做串行接口,简称串口,STC8H8K64U用的是P31为TXD发送,P30为RXD接收。
代码示例:单片机通过P31发送十六进制0X21给电脑:
/****************************数据类型******************************
unsigned char uchar 8 0~255
signed char char 8 -128~127
unsignec int uint 16 0~65535
signed int int 16 -32768~32767
******************************************************************/
#include"STC8H.h" //单片机头文件(包含单片机的特殊功能寄存器的定义)
#include "intrins.h"
#include "Delay_ms.h"
/************************函数声明*************************************/
void STC8H_IOinit(void); //STC15W单片机引脚初始化函数
/*===========================================================*/
/*函数:void STC8H_IOinit(void)
/*功能:STC18H单片机引脚初始化函数
/*说明:统一将单片机所有引脚初始化为准双向口工作模式
/*修改日期:2023.10.16
/*===========================================================*/
void STC8H_IOinit(void)
{
P0M1 = 0; P0M0 = 0; //设置为准双向口 参考用户手册387页
P1M1 = 0; P1M0 = 0; //设置为准双向口 参考用户手册387页
P2M1 = 0; P2M0 = 0; //设置为准双向口 参考用户手册387页
P3M1 = 0; P3M0 = 0; //设置为准双向口 参考用户手册387页
P4M1 = 0; P4M0 = 0; //设置为准双向口 参考用户手册387页
P5M1 = 0; P5M0 = 0; //设置为准双向口 参考用户手册387页
P6M1 = 0; P6M0 = 0; //设置为准双向口 参考用户手册387页
P7M1 = 0; P7M0 = 0; //设置为准双向口 参考用户手册387页
P0 = 0XFF; P1 = 0XFF; P2 = 0XFF;
P3 = 0XFF; P4 = 0XFF; P5 = 0XFF;
P6 = 0XFF; P7 = 0XFF; //设置P0~P7所有I/O口为高电平
}
/****************************************************************************************************
/*函数void Pote(unsigned char x)
/*功能:
/*说明:
/*修改日期:2023.10.23
/****************************************************************************************************/
void Pote()
{
unsigned int i;
for(i=0;i<466;i++)//通过改变i的参数获得准确的时间
{
_nop_();//空指令,用于消耗时间
}
}
void Pote_Half()
{
unsigned int i;
for(i=0;i<189;i++)
{
_nop_();
}
}
void Uart_TX(unsigned char temp)
{
unsigned char x;
unsigned char i=8;
//起始位
P31=0;//置零
Pote();//过1个波特率的时间
//数据位
for(x=1;x<=8;x++)
{
//从低位到高位
P31=temp&0x01;
temp=temp>>1;
Pote();//过1个波特率的时间
}
//结束位
P31=1;//置1
Pote();//过1个波特率的时间
}
/****************************************************************************************************
/*函数:void main()
/*功能:
/*说明:主函数
/*修改日期:2023.10.16
/****************************************************************************************************/
void main()
{
STC8H_IOinit(); //STC15W单片机引脚初始化函数
while(1)
{
Uart_TX(0x21);//单片机发送十六进制0x21给电脑
Delay_ms(300);//延时300ms
}
}
烧录+结果:(箭头从左到右,从上到下)
示波器展示:
(2)单片机->电脑(字符串)
首先,一个字符串是由一个个字符组成的,每个字符在计算机中都有相应的编码。在ASCII编码中,一个字符通常用一个字节(8位二进制)表示。
我们可以借助指针用while循环(条件为*S!=NULL)将字符串中的字符一个个传进之前写好的Uart_TX
函数,并且使用后缀递增运算符 ++
将指针移动到下一个字符。
单片机每隔300ms发送“Hello,world!”给电脑
/****************************数据类型******************************
unsigned char uchar 8 0~255
signed char char 8 -128~127
unsignec int uint 16 0~65535
signed int int 16 -32768~32767
******************************************************************/
#include"STC8H.h" //单片机头文件(包含单片机的特殊功能寄存器的定义)
#include "intrins.h"
#include "Delay_ms.h"
/************************函数声明*************************************/
void STC8H_IOinit(void); //STC15W单片机引脚初始化函数
/*===========================================================*/
/*函数:void STC8H_IOinit(void)
/*功能:STC18H单片机引脚初始化函数
/*说明:统一将单片机所有引脚初始化为准双向口工作模式
/*修改日期:2023.10.16
/*===========================================================*/
void STC8H_IOinit(void)
{
P0M1 = 0; P0M0 = 0; //设置为准双向口 参考用户手册387页
P1M1 = 0; P1M0 = 0; //设置为准双向口 参考用户手册387页
P2M1 = 0; P2M0 = 0; //设置为准双向口 参考用户手册387页
P3M1 = 0; P3M0 = 0; //设置为准双向口 参考用户手册387页
P4M1 = 0; P4M0 = 0; //设置为准双向口 参考用户手册387页
P5M1 = 0; P5M0 = 0; //设置为准双向口 参考用户手册387页
P6M1 = 0; P6M0 = 0; //设置为准双向口 参考用户手册387页
P7M1 = 0; P7M0 = 0; //设置为准双向口 参考用户手册387页
P0 = 0XFF; P1 = 0XFF; P2 = 0XFF;
P3 = 0XFF; P4 = 0XFF; P5 = 0XFF;
P6 = 0XFF; P7 = 0XFF; //设置P0~P7所有I/O口为高电平
}
void Pote()
{
unsigned int i;
for(i=0;i<466;i++)//通过改变i的参数获得准确的时间
{
_nop_();//空指令,用于消耗时间
}
}
void Pote_Half()
{
unsigned int i;
for(i=0;i<189;i++)
{
_nop_();
}
}
void Uart_TX(unsigned char temp)
{
unsigned char x;
unsigned char i=8;
//起始位
P31=0;//置零
Pote();//过1个波特率的时间
//数据位
for(x=1;x<=8;x++)
{
//从低位到高位
P31=temp&0x01;
temp=temp>>1;
Pote();
}
//结束位
P31=1;
Pote();
}
void UART_SendString(unsigned char *S)
{
while(*S)
{
Uart_TX(*S++);
}
}
void main()
{
STC8H_IOinit(); //STC15W单片机引脚初始化函数
while(1)
{
UART_SendString("Hello,World!");
Delay_ms(300);//延时300ms
}
}
实验结果如下:
(3)电脑->单片机->电脑
上面我们将了单片机发送数据给电脑,那单片机如何接收电脑所给的数据呢?
示例:
/****************************数据类型******************************
unsigned char uchar 8 0~255
signed char char 8 -128~127
unsignec int uint 16 0~65535
signed int int 16 -32768~32767
******************************************************************/
#include"STC8H.h" //单片机头文件(包含单片机的特殊功能寄存器的定义)
#include "intrins.h"
#include "Delay_ms.h"
/************************函数声明*************************************/
void STC8H_IOinit(void); //STC15W单片机引脚初始化函数
/*===========================================================*/
/*函数:void STC8H_IOinit(void)
/*功能:STC18H单片机引脚初始化函数
/*说明:统一将单片机所有引脚初始化为准双向口工作模式
/*修改日期:2023.10.16
/*===========================================================*/
void STC8H_IOinit(void)
{
P0M1 = 0; P0M0 = 0; //设置为准双向口 参考用户手册387页
P1M1 = 0; P1M0 = 0; //设置为准双向口 参考用户手册387页
P2M1 = 0; P2M0 = 0; //设置为准双向口 参考用户手册387页
P3M1 = 0; P3M0 = 0; //设置为准双向口 参考用户手册387页
P4M1 = 0; P4M0 = 0; //设置为准双向口 参考用户手册387页
P5M1 = 0; P5M0 = 0; //设置为准双向口 参考用户手册387页
P6M1 = 0; P6M0 = 0; //设置为准双向口 参考用户手册387页
P7M1 = 0; P7M0 = 0; //设置为准双向口 参考用户手册387页
P0 = 0XFF; P1 = 0XFF; P2 = 0XFF;
P3 = 0XFF; P4 = 0XFF; P5 = 0XFF;
P6 = 0XFF; P7 = 0XFF; //设置P0~P7所有I/O口为高电平
}
void Pote()
{
unsigned int i;
for(i=0;i<466;i++)
{
_nop_();
}
}
void Pote_Half()
{
unsigned int i;
for(i=0;i<189;i++)
{
_nop_();
}
}
void UART_TX(unsigned char temp)
{
unsigned char x;
unsigned char i=8;
//启始位
P31=0;
Pote();
//数据位
for(x=1;x<=8;x++)
{
P31=temp&0x01;
temp=temp>>1;
Pote();
}
//停止位
P31=0;
Pote();
P31=1;
Pote();
}
unsigned char UART_RX()
{
unsigned char y=0; //计数
unsigned char x=0; //计时
unsigned char Data=0; //保存一个八位的十六进制数据
unsigned char RX_Data[8]; //保存8位
Pote_Half();
//接收
for(y=0;y<8;y++)
{
Pote();
RX_Data[y]=P30&0x01;
}
//结束信号时间
Pote();
Pote_Half();
//数据转换
Data=(RX_Data[0]<<0)+(RX_Data[1]<<1)+(RX_Data[2]<<2)+(RX_Data[3]<<3)+(RX_Data[4]<<4)+(RX_Data[5]<<5)+(RX_Data[6]<<6)+(RX_Data[7]<<7);
P2=Data;
return Data;
}
void main()
{
unsigned char i,j;
unsigned char DAT_UART[20];
STC8H_IOinit();
while(1)
{
//串口接收到数据
if(P30==0)
{
//接收数据
for(i=0;i<20;i++)
{
DAT_UART[i]=UART_RX();
}
//发送数据
for(j=0;j<20;j++)
{
UART_TX(DAT_UART[j]);
}
}
}
}
实验现象: