串口协议简介
串口通讯(Serial Communication)是一种设备间非常常用的串行通讯方式,因为它简单便捷,大部分电子设备都支持该通讯方式,电子工程师在调试设备时也经常使用该通讯方式输出调试信息,ESP32 自有一个串口用于程序下载和 log 打印,就是这个道理。
在计算机科学里,大部分复杂的问题都可以通过分层来简化。如芯片被分为内核层和片上外设;对于通讯协议,我们也以分层的方式来理解,最基本的是把它分为物理层和协议层。物理层规定通讯系统中具有机械、电子功能部分的特性,确保原始数据在物理媒体的传输。协议层主要规定通讯逻辑,统一收发双方的数据打包、解包标准。简单来说物理层规定我们用嘴巴还是用肢体来交流,协议层则规定我们用中文还是英文来交流
物理层
串口通讯的物理层有很多标准及变种,我们主要讲解 RS-232 标准 ,RS-232 标准主要规定了信号的用途、通讯接口以及信号的电平标准。使用 RS-232 标准的串口设备间常见的
通讯结构如下
在上面的通讯方式中,两个通讯设备的“DB9 接口”之间通过串口信号线建立起连接,串口信号线中使用“RS-232 标准”传输数据信号。由于 RS-232 电平标准的信号不能直接被控制器直接识别,所以这些信号会经过一个“电平转换芯片”转换成控制器能识别的“TTL校准”的电平信号,才能实现通讯。
协议层
串口通讯的数据包由发送设备通过自身的 TXD 接口传输到接收设备的 RXD 接口。在串口通讯的协议层中,规定了数据包的内容,它由启始位、主体数据、校验位以及停止位组成,通讯双方的数据包格式要约定一致才能正常收发数据,其组成如下:
1.波特率
文中主要讲解的是串口异步通讯,异步通讯中由于没有时钟信号(如前面讲解的 DB9接口中是没有时钟信号的),所以两个通讯设备之间需要约定好波特率,即每个码元的长度,以便对信号进行解码,上图中用虚线分开的每一格就是代表一个码元。常见的波特率为 4800、9600、115200 等
2.通讯的起始和停止信号
串口通讯的一个数据包从起始信号开始,直到停止信号结束。数据包的起始信号由一个逻辑 0 的数据位表示,而数据包的停止信号可由 0.5、1、1.5 或 2 个逻辑 1 的数据位表示,只要双方约定一致即可。
3.有效数据
在数据包的起始位之后紧接着的就是要传输的主体数据内容,也称为有效数据,有效数据的长度常被约定为 5、6、7 或 8 位长
4.数据校验
在有效数据之后,有一个可选的数据校验位。由于数据通信相对更容易受到外部干扰导致传输数据出现偏差,可以在传输过程加上校验位来解决这个问题。校验方法有奇校验(odd)、偶校验(even)、0 校验(space)、1 校验(mark)以及无校验(noparity)。在无校验的情况下,数据包中不包含校验位
硬件设计及原理
设备连接
UART1 | 功能 | 映射 ESP32 的引脚 | TTL转串口引脚 |
---|---|---|---|
TX | 发送 | 1 | RX |
RX | 接收 | 3 | TX |
3V3 | 供电 | VCC | |
GND | 接地 | GND |
若使用的实验板 UART 的连接方式或引脚不一样,只需根据工程修改引脚即可,程序的控制原理相同。
软件设计
#include <stdio.h>
#include "freertos/FreeRTOS.h"//freertos系统文件
#include "freertos/task.h"//任务创建
#include "driver/uart.h"//串口驱动
#include "driver/gpio.h"//gpio(引脚,输入输出)
#include "sdkconfig.h"
#define Uart_1 UART_NUM_0 //选择串口端口
#define Uart1_rate 115200 //设置串口波特率
#define Uart1_data UART_DATA_8_BITS//数据位
#define Uart1_parity UART_PARITY_DISABLE//奇偶校验
#define Uart1_stop UART_STOP_BITS_1 //停止位
#define Uart1_ctrl UART_HW_FLOWCTRL_DISABLE//流控
#define Uart1_flow 122//硬件RTS阀值
#define BUF_SIZE 1024
void uart_init()
{
//配置UART相关参数
uart_config_t uart1_config =
{
.baud_rate = Uart1_rate,
.data_bits = Uart1_data,
.parity = Uart1_parity,
.stop_bits = Uart1_stop,
.flow_ctrl = Uart1_ctrl,
.rx_flow_ctrl_thresh = Uart1_flow,
};
//串口参数配置 串口号、串口配置参数
ESP_ERROR_CHECK(uart_param_config(Uart_1,&uart1_config));
//安装串口驱动 串口编号、接收buff、发送buff、事件队列、分配中断的标志
ESP_ERROR_CHECK(uart_set_pin(Uart_1,1,3,UART_PIN_NO_CHANGE,UART_PIN_NO_CHANGE));
//安装串口驱动 串口编号、接收buff、发送buff、事件队列、分配中断的标志
ESP_ERROR_CHECK(uart_driver_install(Uart_1,1024 *2,1024*2,0,NULL,0));
// 申请临时内存
uint8_t *data = (uint8_t *) malloc(BUF_SIZE);
while (1) {
// Read data from the UART 接收到串口数据
int len = uart_read_bytes(Uart_1, data, BUF_SIZE, 20 / portTICK_PERIOD_MS);
// Write data back to the UART 发送串口数据
uart_write_bytes(Uart_1, (const char *) data, len);
}
}
void app_main(void)
{
xTaskCreate(uart_init, "uart_echo_task", 1024*2, NULL,10, NULL); //创建串口应声虫任务
}
评论