ESP32 的 UART
简介
通用异步接收器/发送器 (UART) 属于一种硬件功能,通过使用 RS232、RS422、RS485 等常见异步串行通信接口来处理通信时序要求和数据帧。UART 是实现不同设备之间全双工或半双工数据交换的一种常用且经济的方式。
ESP32 芯片有 3 个 UART 控制器(也称为端口),每个控制器都有一组相同的寄存器以简化编程并提高灵活性。
每个 UART 控制器可以独立配置波特率、数据位长度、位顺序、停止位位数、奇偶校验位等参数。所有具备完整功能的 UART 控制器都能与不同制造商的 UART 设备兼容,并且支持红外数据协会 (IrDA) 定义的标准协议。
配置步骤
配置
配置通信
首先先配置通信的参数。
有两种配置方法,一种是一次性全部配完,另一种是分开一个个配置。
我们先看一次性配置。
首先引入头文件#include <driver/uart.h>
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; //硬件流控方式
uint8_t rx_flow_ctrl_thresh; //硬件流控阈值
union {
uart_sclk_t source_clk; //时钟源
//deprecated vt. 强烈反对, 抨击; 对...表示不赞成;
//下面带有__attribute__((deprecated))的use_ref_tick意味着某代码已过时,不推荐再使用。
bool use_ref_tick __attribute__((deprecated)); //已过时,请改用source_clk选择时钟源。
};
} uart_config_t;
第一个是波特率
,直接传入数字即可。
第二个数据位
,可选的有UART_DATA_5_BITS
、UART_DATA_6_BITS
、UART_DATA_7_BITS
、UART_DATA_8_BITS
。
第三个是校验位
,一般就是上面示例中的无校验
。
第四个是停止位
,可选的有UART_STOP_BITS_1
(1 个停止位)、UART_STOP_BITS_1_5
(1.5 个停止位,仅适用于 5 数据位)和 UART_STOP_BITS_2
(2 个停止位)。
第五个是硬件流控制
,一般不使用,选择UART_HW_FLOWCTRL_DISABLE
(禁用流控制)即可。
第六个是RTS的阈值
按照官方给的配置为122
即可,不配置就行。
即
#define Baud_Rate (9600)//波特率
#define Uart_Num UART_NUM_2 //串口通信x 即UART1,UART2...
uart_config_t uart_config_InitStructure=
{
.baud_rate=Baud_Rate,//波特率
.data_bits=UART_DATA_8_BITS,//数据位 8位
.flow_ctrl=UART_HW_FLOWCTRL_DISABLE, //流控制 失能
.parity=UART_PARITY_DISABLE,//奇偶校验位 无校验
.stop_bits=UART_STOP_BITS_1,//停止位 1位
.rx_flow_ctrl_thresh=122,
.source_clk = UART_SCLK_DEFAULT//配置时钟,默认时钟
};
uart_param_config(Uart_Num,&uart_config_InitStructure);//设置UsartNumx x 为 0 1 2 以及把上面的参数配置进来
配置引脚
分配引脚的方法是调用函数uart_set_pin()
一般来说我们最后两个参数是不需要的,给-1就行。
#define Uart_Num UART_NUM_2 //串口通信x 即UART1,UART2...
#define Uart_Tx_Pin_Num GPIO_NUM_17 //tx引脚
#define Uart_Rx_Pin_Num GPIO_NUM_16//rx引脚
uart_set_pin(Uart_Num,Uart_Tx_Pin_Num,Uart_Rx_Pin_Num,-1,-1); //设置引脚,tx为17,rx为16
安装驱动程序
#define Uart_Num UART_NUM_2 //串口通信x 即UART1,UART2...
#define Uart_RxBuffer_Size (1024)//接收缓存区大小
#define Uart_TxBuffer_Size (1024)//发送缓冲区大小
uart_driver_install(Uart_Num,Uart_RxBuffer_Size,Uart_TxBuffer_Size,0,NULL,0);//安装uart
发送数据
#define Uart_Num UART_NUM_2 //串口通信x 即UART1,UART2...
/**
* @description: 串口发送一个字节
* @param {uint8_t} byte 要发送的字节
* @return {*}
*/
void Uart_SendByte(uint8_t byte)
{
uart_write_bytes(Uart_Num,&byte,sizeof(byte));
}
接收数据
这个就比较麻烦,stm32接收数据时可以用中断,但这个没提供相关中断函数,但可以用freertos的轮询查询
读取使用uart_read_bytes()
函数
#define Uart_RxBuffer_Size (1024)//接收缓存区大小
#define Uart_task_size (1024)//任务栈大小
xTaskCreatePinnedToCore(Uart_ReceiveData_Task, "Uart_ReceiveData_Task", Uart_task_size*2, NULL, configMAX_PRIORITIES-1, &Uart_RxData_Task_Handel, 1);//创建接收任务
/**
* @description: 串口接收数据
* @return {*}
*/
void Uart_ReceiveData_Task()
{
uint8_t* receiveDataBuffer = (uint8_t*) malloc(Uart_RxBuffer_Size + 1);//创建接收空间
int receiveSize;//用于判断发是否接收到了数据
while(1)
{
receiveSize =uart_read_bytes(Uart_Num,receiveDataBuffer,Uart_RxBuffer_Size,(10/portTICK_PERIOD_MS));//接收数据,10ms超时
if(receiveSize>0)//接收到了数据
{
receiveDataBuffer[receiveSize]=0;//将数据的最后一位置0 表示结束
}
}
free(receiveDataBuffer);//释放缓冲区
vTaskDelete(Uart_RxData_Task_Handel);//删除任务
}
整体代码测试
main.c
/*
* @Author: i want to 舞动乾坤
* @Date: 2024-07-24 12:23:38
* @LastEditors: i want to 舞动乾坤
* @LastEditTime: 2024-07-25 13:00:15
* @FilePath: \uart_serial\main\main.c
* @Description:
*
* Copyright (c) 2024 by i want to 舞动乾坤, All Rights Reserved.
*/
#include <stdio.h>
#include "myUart.h"
#include "string.h"
#include "esp_log.h"
#include <freertos/FreeRTOS.h>
#include <freeRtos/task.h>
uint8_t ReceiveDataFlag=0;
void app_main(void)
{
Uart_Init();
Uart_Printf("你好,世界\r\n");
Uart_SendNumber(12345,5);
Uart_SendString("\r\n");
Uart_SendString("hahahaha\r\n");
while (1)
{
/* code */
ReceiveDataFlag=Uart_GetRxFlag();//判断是否接收到数据
if(ReceiveDataFlag)
{
ESP_LOGI("main.c","receive data is %s",receiveDataBuffer);
}
vTaskDelay(10/portTICK_PERIOD_MS);
}
}
myUart.c
/*
* @Author: i want to 舞动乾坤
* @Date: 2024-07-24 12:27:08
* @LastEditors: i want to 舞动乾坤
* @LastEditTime: 2024-07-25 12:55:27
* @FilePath: \uart_serial\main\myUart.c
* @Description:
*
* Copyright (c) 2024 by i want to 舞动乾坤, All Rights Reserved.
*/
#include <freertos/FreeRTOS.h>
#include <freeRtos/task.h>
#include <driver/uart.h>
#include <driver/gpio.h>
#include <stdio.h>
#include <stdarg.h>
#include "myUart.h"
#include "esp_log.h"
#define Baud_Rate (9600)//波特率
#define Uart_Num UART_NUM_2 //串口通信x 即UART1,UART2...
#define Uart_Tx_Pin_Num GPIO_NUM_17 //tx引脚
#define Uart_Rx_Pin_Num GPIO_NUM_16//rx引脚
#define Uart_RxBuffer_Size (1024)//接收缓存区大小
#define Uart_TxBuffer_Size (1024)//发送缓冲区大小
#define Uart_task_size (1024)//任务栈大小
TaskHandle_t Uart_RxData_Task_Handel=NULL;//接收数据的任务句柄
void Uart_ReceiveData_Task();//接收数据的任务
uint8_t receiveDataBuffer[Uart_RxBuffer_Size]={0};//接收数据的buffer
uint8_t recdeiveDataFlag=0;//接收数据的flag标志位
void Uart_Init()
{
uart_config_t uart_config_InitStructure=
{
.baud_rate=Baud_Rate,//波特率
.data_bits=UART_DATA_8_BITS,//数据位 8位
.flow_ctrl=UART_HW_FLOWCTRL_DISABLE, //流控制 失能
.parity=UART_PARITY_DISABLE,//奇偶校验位 无校验
.stop_bits=UART_STOP_BITS_1,//停止位 1位
.rx_flow_ctrl_thresh=122,
.source_clk = UART_SCLK_DEFAULT//配置时钟
};
uart_param_config(Uart_Num,&uart_config_InitStructure);//设置UsartNumx x 为 0 1 2
uart_set_pin(Uart_Num,Uart_Tx_Pin_Num,Uart_Rx_Pin_Num,-1,-1); //设置引脚,tx为17,rx为16
uart_driver_install(Uart_Num,Uart_RxBuffer_Size,Uart_TxBuffer_Size,0,NULL,0);//安装uart
xTaskCreatePinnedToCore(Uart_ReceiveData_Task, "Uart_ReceiveData_Task", Uart_task_size*2, NULL, configMAX_PRIORITIES-1, &Uart_RxData_Task_Handel, 1);//创建接收任务
}
/**
* @description: 串口接收数据
* @return {*}
*/
void Uart_ReceiveData_Task()
{
int receiveSize;//用于判断发是否接收到了数据
while(1)
{
receiveSize =uart_read_bytes(Uart_Num,receiveDataBuffer,Uart_RxBuffer_Size,(100/portTICK_PERIOD_MS));//接收数据,100ms扫描一次
if(receiveSize>0)//接收到了数据
{
receiveDataBuffer[receiveSize]=0;//将数据的最后一位置0 表示结束
recdeiveDataFlag=1;//置标志位,证明接收到了数据
ESP_LOGI("myUart.c","receive data is %s",receiveDataBuffer);
uart_flush(Uart_Num); //清空缓冲区
}
}
vTaskDelete(Uart_RxData_Task_Handel);//删除任务
}
/**
* @description: 是否接收到数据的标志位
* @return {*} 1:串口已经接收到数据,0:串口暂时未接收到数据
*/
uint8_t Uart_GetRxFlag(void)
{
if(recdeiveDataFlag==1)
{
recdeiveDataFlag=0;
return 1;
}
return 0;
}
/**
* @description: 串口发送一个字节
* @param {uint8_t} byte 要发送的字节
* @return {*}
*/
void Uart_SendByte(uint8_t byte)
{
uart_write_bytes(Uart_Num,&byte,sizeof(byte));
}
/**
* @description: 通过USART发送一个数组
* @param {uint8_t} *Array 数组的首地址
* @param {uint16_t} Length 数组的长度 用于判断数组是否结束
* @return {*}
*/
void Uart_SendArray(uint8_t *Array,uint16_t Length)
{
uint16_t i;
for(i=0;i<Length;i++)
{
Uart_SendByte(Array[i]);
}
}
/**
* @description: 通过USART发送字符串
* @param {char} *String 要发送的字符串
* @return {*}
*/
void Uart_SendString(char *String)
{
uint8_t i;
for(i=0;String[i]!='\0';i++)
{
Uart_SendByte(String[i]);
}
}
/**
* @description: x的y次方函数
* @param {uint32_t} x 要转换的数字
* @param {uint32_t} y 次方
* @return {返回x的y次方}
*/
uint32_t Uart_Pow(uint32_t x,uint32_t y)
{
uint32_t result=1;
while(y--)
{
result*=x;
}
return result;
}
/**
* @description: 通过USART发送一个数字
* @param {uint32_t} Number 要发送的数字
* @param {uint8_t} Length 发送数字的长度
* @example 12345 分别取出某一位 :
万:12345 /10000 %10=1 ; 千:12345 /1000%10=2 ; 百:12345 /100%10=3; 十:12345/10%10=4 ; 个:12345/1%10=5
总结下来 取某位 即:数字除以10的x次方%10
* @return {*}
*/
void Uart_SendNumber(uint32_t Number,uint8_t Length)
{
uint8_t i;
for(i=0;i<Length;i++)
{
Uart_SendByte(Number / Uart_Pow(10, Length - i - 1) % 10 + '0') ;//+'0'是数字ASCII表对应数字0的基地址(偏移量)
}
}
int fputc(int ch ,FILE *f)
{
Uart_SendByte(ch);
return ch;
}
/**
* @description: 重定向到Uart上,使用Uart可输出至串口
* @param {char} *format
* @return {*}
*/
void Uart_Printf(char *format, ...)//可变参数
{
char String[100];
va_list arg;
va_start(arg,format);
vsprintf(String,format,arg);
va_end(arg);
Uart_SendString(String);
}
myUart.h
/*
* @Author: i want to 舞动乾坤
* @Date: 2024-07-24 12:27:21
* @LastEditors: i want to 舞动乾坤
* @LastEditTime: 2024-07-25 12:56:15
* @FilePath: \uart_serial\main\myUart.h
* @Description:
*
* Copyright (c) 2024 by i want to 舞动乾坤, All Rights Reserved.
*/
#ifndef _MYUART__H
#define _MYUART__H
#define Uart_RxBuffer_Size (1024)//接收缓存区大小
extern uint8_t receiveDataBuffer[Uart_RxBuffer_Size];//接收数据的buffer
void Uart_Init();
void Uart_SendByte(uint8_t byte);
void Uart_SendString(char *String);
uint8_t Uart_GetRxFlag(void);
void Uart_SendNumber(uint32_t Number,uint8_t Length);
void Uart_Printf(char *format, ...);//可变参数
#endif
效果:
参考大佬文章
1.【快速上手ESP32(基于ESP-IDF&VSCode)】05-UART串口通信
2. ESP32 之 ESP-IDF 教学(九)—— 串口通信(UART)