一、串口通讯原理

         串口通讯(Serial Communication)是一种设备间非常常用的串行通讯方式,因为它简单便捷,大部分电子设备都支持该通讯方式,电子工程师在调试设备时也经常使用该通讯方式输出调试信息,ESP32 自有一个串口用于程序下载和 log 打印,就是这个道理。

1.串行通讯原理:

        串行通信是指使用一条数据线,将数据一位一位地依次传输,每一位数据占据一个固定的时间长度。其只需要少数几条线就可以在系统间交换信息,特别适用于计算机与计算机、计算机与外设之间的远距离通信。

python对esp32串口通信 断开 esp32 串口速度_串口

2. RS232通讯协议

        RS232通讯协议比较简单,通常遵循 96-N-8-1 格式。

        “96”表示的是通信波特率为 9600。串口通信中通常使用的是异步串口通信,即没有时钟线,所以两个设备要通信,必须要保持一致的波特率,当然,波特率常用值还有 4800、115200 等。
        “N”表示的是无校验位,由于串口通信相对更容易受到外部干扰导致传输数据出现偏差,可以在传输过程加上校验位来解决这个问题。校验方法有奇校验(odd)、偶校验(even)、0 校验(space)、1 校验(mark)以及无校验(noparity)。具体的介绍,大家可以百度下串口通信了解。
        “8”表示的是数据位数为 8 位,其数据格式在前面介绍异步通信中已讲过。当然数据位数还可以为 5、6、7 位长度。
        “1”表示的是 1 位停止位,串口通讯的一个数据包从起始信号开始,直到停止信号结束。数据包的起始信号由一个逻辑 0 的数据位表示,而数据包的停止信号可由 0.5、 1、 1.5 或 2 个逻辑 1 的数据位表示,只要双方约定一致即可。

二、API函数

  •  UART 配置函数:uart_param_config();

函数原型

esp_err_t uart_param_config

(

uart_port_t  uart_num,

const uart_config_t *uart_config

)

函数功能

UART 配置函数

参数

[in] uart_num: 串口号,取值

  •  UART_NUM_0 = 0x0, /*串口 0,下载程序端口*/
  •  UART_NUM_1 = 0x1, /*串口 1*/
  •  UART_NUM_2 = 0x2,/*串口 2*/

[in] uart_config:串口参数配置

typedef struct {
    int baud_rate; /*波特率*/
    uart_word_length_t data_bits; /*数据位*/
    uart_parity_t parity; /*校验模式*/
    uart_stop_bits_t stop_bits; /*停止位*/
    uart_hw_flowcontrol_t flow_ctrl; /*硬件流控使能位*/
} uart_config_t;

返回值

ESP_OK:成功

ESP_ERR_INVALID_ARG : 参数错误

  • UART 的 IO 映射设置函数:uart_set_pin();

函数原型

esp_err_t uart_set_pin

(

    uart_port_t uart_num,

    int tx_io_num,

    int rx_io_num,

    int rts_io_num,

    int cts_io_num

)

函数功能

UART 的IO映射函数

参数

[in] uart_num:串口号,取值

[in] tx_io_num:发送引脚

[in] rx_io_num:接收引脚

[in] rts_io_num:rts 流控引脚

[in] cts_io_num:cts 流控引脚

返回值

ESP_OK:成功

ESP_ERR_INVALID_ARG : 参数错误

  •  UART 功能安装使能函数:uart_driver_install();

函数原型

esp_err_t uart_driver_install
(
uart_port_t uart_num,
int rx_buffer_size,

int tx_buffer_size,
int queue_size,
QueueHandle_t* uart_queue,
int intr_alloc_flags
)

函数功能

UART 功能安装使能函数

参数

[in] uart_num:串口号

[in] rx_buffer_size:接收缓存大小

[in] tx_buffer_size:发送缓存大小

[in] queue_size:队列大小

[in] uart_queue:串口队列指针

[in] intr_alloc_flags:分配中断标记

返回值

ESP_OK:成功

ESP_ERR_INVALID_ARG : 参数错误

  •  UART 发送函数:uart_write_bytes();

函数原型

int uart_write_bytes

(

    uart_port_t uart_num,

    const char* src,

    size_t size

)

函数功能

UART 发送函数

参数

[in] uart_num:串口号

[in] src:发送数据指针

[in]size:发送数据大小

返回值

(-1) :参数错误

(>=0):数据已放到发送缓存

  •  UART 读取函数:uart_read_bytes();

函数原型

 int uart_read_bytes

(

uart_port_t uart_num,

uint8_t* buf,

uint32_t length,

TickType_t ticks_to_wait

)

函数功能

UART 读取函数

参数

[in] uart_num:串口号

[in] buf:接收数据指针

[in]length:接收数据最大大小

[in]ticks_to_wait:等待时间

返回值

(-1) :参数错误

(>=0):数据已放到发送缓存

 三、程序编写

         程序基于以上API函数进行编写,对于接收数据的功能,在FreeRTOS实时操作系统的基础之上,创建了一个UART数据接收任务,负责监听读取来自串口的数据。

uart.h文件内容

#ifndef COMPONENTS_UART_INCLUDE_UART_H_
#define COMPONENTS_UART_INCLUDE_UART_H_

#include <stdio.h>
#include "esp_system.h"
#include "esp_event_loop.h"
#include "esp_log.h"
#include "esp_err.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/ledc.h"
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/uart.h"
#include "driver/gpio.h"
#include "string.h"

//UART1
#define RX1_BUF_SIZE 		(1024) //接收缓冲
#define TX1_BUF_SIZE 		(512)  //发送缓冲
#define TXD1_PIN 			(GPIO_NUM_5)
#define RXD1_PIN 			(GPIO_NUM_4)

//UART2
#define RX2_BUF_SIZE 		(1024)
#define TX2_BUF_SIZE 		(512)
#define TXD2_PIN 			(GPIO_NUM_12)
#define RXD2_PIN 			(GPIO_NUM_13)

void uart_init(void); //UART初始化函数
/**
 * 数据接收任务
 */
void uart1_rx_task();
void uart2_rx_task();

#endif /* COMPONENTS_UART_INCLUDE_UART_H_ */

 uart.c文件内容

/*
 * uart.c
 *
 *  Created on: 2021年12月18日
 *      Author: wangy
 */

#include "uart.h"

void uart_init(void){
	//串口配置结构体
	uart_config_t uart1_config, uart2_config;
	/**
	 * 配置串口1
	 */
	uart1_config.baud_rate = 9600; //波特率为115200
	uart1_config.parity = UART_PARITY_DISABLE; //无校验位
	uart1_config.data_bits = UART_DATA_8_BITS; //数据位8
	uart1_config.stop_bits = UART_STOP_BITS_1; //停止位1
	uart1_config.flow_ctrl = UART_HW_FLOWCTRL_DISABLE; //硬件流控制
	uart_param_config(UART_NUM_1, &uart1_config); //配置串口1

	//IO映射-> T:IO4  R:IO5
	uart_set_pin(UART_NUM_1, TXD1_PIN, RXD1_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);

	//注册串口服务使能并设置缓冲区大小
	uart_driver_install(UART_NUM_1,RX1_BUF_SIZE * 2, TX1_BUF_SIZE * 2, 0, NULL, 0); //不使用队列


	/**
	 * 配置串口2
	 */
	uart2_config.baud_rate = 115200;					//波特率
	uart2_config.data_bits = UART_DATA_8_BITS;			//数据位
	uart2_config.parity = UART_PARITY_DISABLE;			//校验位
	uart2_config.stop_bits = UART_STOP_BITS_1;			//停止位
	uart2_config.flow_ctrl = UART_HW_FLOWCTRL_DISABLE;	//硬件流控
	uart_param_config(UART_NUM_2, &uart2_config);		//设置串口
	//IO映射-> T:IO12  R:IO13
	uart_set_pin(UART_NUM_2, TXD2_PIN, RXD2_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
	//注册串口服务即使能+设置缓存区大小
	uart_driver_install(UART_NUM_2, RX2_BUF_SIZE * 2, TX2_BUF_SIZE * 2, 0, NULL, 0);

	/**
	 * 创建两个任务,分别负责监听读取来自串口1和串口2的数据。
	 */
	//创建串口1接收任务
	xTaskCreate(uart1_rx_task, "uart1_rx_task", 1024*2, NULL, configMAX_PRIORITIES, NULL);
	//创建串口2接收任务
	xTaskCreate(uart2_rx_task, "uart2_rx_task", 1024*2, NULL, configMAX_PRIORITIES-1, NULL);
}

/**
 * 串口1数据接收任务
 */
void uart1_rx_task(){
    uint8_t* data = (uint8_t*) malloc(RX1_BUF_SIZE+1); //分配接收数据的内存
    while (1) {
        //获取串口1接收的数据
        const int rxBytes = uart_read_bytes(UART_NUM_1, data, RX1_BUF_SIZE, 10 / portTICK_RATE_MS);
        if (rxBytes > 0) {
            data[rxBytes] = 0;
			//将接收到的数据发出去
			uart_write_bytes(UART_NUM_1, (char *)data, rxBytes);
        }
    }
    free(data); //释放分配的内存。
}

/**
 * 串口2接收任务
 */
void uart2_rx_task()
{
    uint8_t* data = (uint8_t*) malloc(RX2_BUF_SIZE+1);
    while (1) {
        const int rxBytes = uart_read_bytes(UART_NUM_2, data, RX2_BUF_SIZE, 10 / portTICK_RATE_MS);
        if (rxBytes > 0) {
            data[rxBytes] = 0;
			//将接收到的数据发出去
			uart_write_bytes(UART_NUM_2, (char *)data, rxBytes);
        }
    }
    free(data);
}

         将上述程序下载到开发板,通过串口助手,可以接收到ESP32发送的数据,且ESP32也可以接收到串口助手发送的数据。

四、结束 

        本文简单介绍了串口通讯原理,并通过ESP32实现与串口助手之间的通讯。