文章目录

此篇文章依旧以HC6800-MS这块开发板为例。本片文章可能涉及之前文章内容,下面是前两篇文章的链接,需要的可以看看:
【初识嵌入式之C51】成为点灯大师
【51单片机】GPIO/中断/定时器/PWM
一、硬件通信协议
在单片机上主要有四种通信协议:
UART、I2C、SPI、CAN
这篇文章主要解释串口UART这种通信协议。
二、三种通信方式
1.单工通信
a. 数据的发送方和接收方是固定的;
b. 数据的传输通过一根信号线完成;
c. 数据传输的方向是固定的,只能由发送方传给接收方。
2.半双工通信(I2C ——>VCC GND SDA SCL)
a. 通信双方既可以作为数据的发送方,也可以作为数据的接收方;
b. 数据传输通过一根信号线完成;
c. 数据传输的方向可以是双向的,但同一时刻数据的传输方向呈单一性。
3. 全双工通信
a. 通信双方既可以作为数据的发送方,也可以作为数据的接收方;
b. 数据传输通过两根信号线完成;
c. 同一时刻数据的传输方向是双向的。

三、串口UART
3.1 UART
Universal Async Receiver/Transmitter 通用异步收发器(串口) 全双工、串行
3.2 UART信号线
VCC、GND、TXD发送数据线、RXD接受数据线

3.3 串口传输数据顺序
串口通信,遵循LSB先行原则,即先发送数据的低位,再发送高位;
3.4 串行传输和并行传输
串行:通过一根信号线传输数据,按照先后次序逐个bit逐个bit去发送数据;
并行:通过多根信号线同时去传输多个bit数据
串行传输特点:
(1)传输速率慢(只有一根线逐个bit去传输数据);
(2)硬件成本低、实现简单;
(3)传输距离远,抗干扰能力强(信号线少,干扰小,不存在多根线之间的信号偏移)。
并行传输特点:
(1)传输速率快(通过多根线同时发送多个bit数据);
(2)硬件成本高,实现复杂;
(3)传输距离近,抗干扰能力弱(一般30米内,信号偏移,电磁波干扰、误差大)。
3.5 串口的通信时序
(1)空闲状态为高电平
(2)发送1bit低电平代表起始位
(3)发送8bit数据位()
(4)发送1bit校验位 低电平/高电平(奇偶校验)
(5)发送1bit高电平代表停止位

校验方法
(1)奇偶校验
奇校验:校验位为“1”,若数据位中“1”的个数加上校验位的“1”保持“1”的总个数为奇数个,则代表奇校验通过;
偶校验:校验位为“0”,若数据位中“1”的个数加上校验位的“0”保持“1”的总个数为偶数个,则代表偶校验通过;
(2)累加和校验
3.6 同步通信和异步通信
时钟线:约定双方的通信频率。
同步通信:通信双方有一根共享的时钟信号线来约定通信的频率;
异步通信:通信双方没有时钟信号线来约定通信的频率
波特率:bit per second,每秒传输的bit数量
常见波特率:2400bps 4800bps 9600bps 115200bps
3.7 串口通信参数
9600 8 N 1
9600:波特率
8:8位数据位
N:None无校验
1:1位停止位
3.8 三种数字信号的电气接口标准
电气特性 电气标准
- TTL电平
1(高电平)
0(低电平) - RS232电平 负逻辑
1(高电平):-3V——-15V;
0(低电平):3V——15V; - RS485电平 差分
1(高电平):2V-6V
0(低电平):-2V–6V
3.9 51单片机串口数据寄存器

两个缓冲区名字都叫 SBUF,地址都是 0x99;写 = 发送,读 = 接收,硬件自动区分,不是同一个存储单元。
电脑 USB → CH340 芯片(USB 转 TTL) → 51 单片机 RX 引脚 → 存入 51 的接收 SBUF
51 单片机写SBUF=dat,数据进入发送 SBUF,从 TX 引脚输出 → CH340 转 USB → 电脑接收。
3.10 串口初始化流程

tips:
data:将变量的存储位置放到芯片内部RAM(256byte)
xdata:将变量的存储位置放到芯片外部扩展RAM(64KB)
四、单片机串口应用示例
串口任务:
主机(Windows)向从机发送Modbus协议格式指令,从机解析指令,根据指令完成对应功能并向从机回复应答。
主从应答:
主机:永远是发起通信的一方,拥有对通信的绝对控制从机:无法发起通信,只能根据主机下发的指令完成对应的控制应答:从机完成对应功能后,需要给主机一个回复
TCP/IP应用层:HTTPModbus(CRC校验)
功能码:实现的具体功能
累加和校验
0x01:控制LED灯
0x02:数码管显示
0x03:蜂鸣器控制
0x04:温度采集
主机——>从机:

从机——>主机:

主机——>从机:0x01 0000 0001
从机——>主机:0x81 1000 0001
功能码最高位为数据流向位:0:数据从主机到从机 1:数据从从机到主机。
主函数:
#include <reg51.h>
#include <string.h>
#include "uart.h"
#include "delay.h"
#include "led.h"
#include "digitaltube.h"
#include "beep.h"
#include "modbus.h"
int main()
{
int ret = 0;
uart_init();
led_init();
timer0_init();
while(1)
{
if (pos != 0)
{
delay(0xAFFF);
ret = parse();
if (ret != 0)
{
do_handler(ret);
}
if (ret != 0)
{
callback();
}
pos = 0;
}
}
return 0;
}
延时:
#include <reg51.h>
#include "delay.h"
int delay(unsigned int n)
{
while(n--)
{
}
return 0;
}
串口:
#include <reg51.h>
#include "uart.h"
xdata char recv_buff[32];
unsigned int pos = 0;
void uart_recv_handler(void) interrupt 4
{
if((SCON & (1 << 0)) != 0)
{
if(pos < 32)
{
recv_buff[pos++] = SBUF;
recv_buff[pos] = 0;
}
SCON &= ~(1 << 0);
}
}
void uart_init()
{
//配置串口相关寄存器
//设置UART工作模式:8位UART波特率可变
SCON &= ~(3 << 6);
SCON |= (1 << 6);
//允许串口收发数据
SCON |= (1 << 4);
//PCON寄存器SMOD0清0,SCON寄存器指定串口工作方式
PCON &= ~(1 << 6);
//波特率翻倍
PCON |= (1 << 7);
//配置定时器1相关寄存器
//配置定时器1工作模式:8位自动重装载
TMOD &= ~(0x0f << 4);
TMOD |= (1 << 5);
//定时器初值:12MHZ:230 11.0592MHZ:232
TL1 = 230;
TH1 = 230;
//打开定时器
TCON = (1 << 6);
//中断开关
//开启CPU总中断+串口中断
IE |= (1 << 4)|(1 << 7);
}
//发送单个字符
void uart_sendchar(unsigned int dat)
{
SBUF = dat;
while((SCON & (1 << 1)) == 0);
SCON &= ~(1 << 1);
}
//发送字符串
void uart_sendstr(const char *p)
{
while(*p)
{
uart_sendchar(*p++);
}
}
//发送指定长度的数据
void uart_send_n_str(const char *p, int n)
{
while(n--)
{
uart_sendchar(*p++);
}
}
定时器:
#include <reg51.h>
#include "timer.h"
#define INI_VAL 64535
float f_beep = 200;
float t_beep = 0;
unsigned int cnt = 0;
unsigned int ini_val = 0;
void calcu_val()
{
t_beep = 1 / f_beep;
cnt = t_beep / 0.000001;
ini_val = 65535 - (int)cnt;
}
void timer0_handler(void) interrupt 1
{
TL0 = ini_val;
TH0 = (ini_val >> 8);
P2 ^= (1 << 1);
}
void timer0_init()
{
TMOD &= (0xf0 << 0);
TMOD |= (1 << 0);
// t_beep = 1 / f_beep;
// cnt = t_beep / 0.000001;
// ini_val = 65535 - (int)cnt;
calcu_val();
TL0 = ini_val;
TH0 = (ini_val >> 8);
IE |= (1 << 1) | (1 << 7);
TCON |= (1 << 4);
}
modbus:
#include <reg51.h>
#include <string.h>
#include "modbus.h"
#include "led.h"
#include "digitaltube.h"
#include "beep.h"
#include "uart.h"
#define DEV_ADDRES 0x01
//recv_buff:0xAA 0x01 0x01 0x00 0x42 0xEE 0xBB
unsigned char parse(void)
{
unsigned char sum = 0;
int i = 0;
int ret = 0;
if((unsigned char)recv_buff[0] == 0xAA && (unsigned char)recv_buff[6] == 0xBB)
{
if((unsigned char)recv_buff[1] == DEV_ADDRES)
{
for(i = 0; i < 5; ++i)
{
sum += recv_buff[i];
}
if((unsigned char)recv_buff[5] == sum)
{
ret = (unsigned char)recv_buff[2];
}
}
}
return ret;
}
void do_handler(unsigned char n)
{
switch(n)
{
case 0x01:
led_on(recv_buff[4]);
break;
case 0x02:
digiter_show(recv_buff[4]);
break;
case 0x03:
beep_on((unsigned char)recv_buff[4]);
break;
case 0x04:
break;
default:
break;
}
}
//recv_buff:0xAA 0x01 0x81 0x00 0x42 0xEE 0xBB
void callback(void)
{
unsigned char sum = 0;
int i = 0;
unsigned char send_buff[10];
memcpy(send_buff, recv_buff, 7);
send_buff[2] |= (1 << 7);
if((unsigned char)send_buff[0] == 0xAA && (unsigned char)send_buff[6] == 0xBB)
{
if((unsigned char)send_buff[1] == DEV_ADDRES)
{
for(i = 0; i < 5; ++i)
{
sum += send_buff[i];
}
send_buff[5] = sum;
// if((unsigned char)send_buff[5] == sum)
// {
// uart_send_n_str(send_buff, 7);
// }
}
}
uart_send_n_str(send_buff, 7);
}
led:
#include <reg51.h>
#include "led.h"
void led_init(void)
{
P2 = 0xff;
}
void led_all_on()
{
P2 = 0x00;
}
void led_all_off()
{
P2 = 0xff;
}
void led_on(unsigned int n)
{
P2 = ~n;
}
void led_nor()
{
P2 ^= 0xff;
}
数码管:
#include <reg51.h>
#include "digitaltube.h"
#include "delay.h"
int digitaltube_on(unsigned int n, unsigned int num)
{
P1 &= ~(0xdf << 0);
P1 |= (1 << (n-1));
switch(num)
{
//0011 1111
case 0: P0 = 0x3f; break;
//0000 0110
case 1: P0 = 0x06; break;
//0101 1011
case 2: P0 = 0x5b; break;
//0100 1111
case 3: P0 = 0x4f; break;
//0110 0110
case 4: P0 = 0x66; break;
//0110 1101
case 5: P0 = 0x6d; break;
//0111 1101
case 6: P0 = 0x7d; break;
//0000 0111
case 7: P0 = 0x07; break;
//0111 1111
case 8: P0 = 0x7f; break;
//0110 1111
case 9: P0 = 0x6f; break;
}
return 0;
}
void digiter_show(unsigned int n)
{
int i = 1;int tmp = 0;
while(n)
{
tmp = n % 10;
n /= 10;
digitaltube_on(i, tmp);
++i;
delay(100);
}
}
蜂鸣器:
#include <reg51.h>
#include "timer.h"
void beep_on(unsigned char ret)
{
if(0x01 == ret)
{
f_beep = 400;
calcu_val();
}else if(0x02 == ret)
{
f_beep = 600;
calcu_val();
}else if(0x03 == ret)
{
f_beep = 800;
calcu_val();
}else if(0x04 == ret)
{
f_beep = 1000;
calcu_val();
}else if(0x05 == ret)
{
f_beep = 1200;
calcu_val();
}
}
转载自 CSDN-专业IT技术社区
原文链接:https://blog.csdn.net/2403_89702754/article/details/165212580




