ESP32-C3入门教程 基础篇(三、UART模块 — 与Enocean无线模块串口通信)

2023-11-09

测试第三课,ESP32-C3的串口通信测试
老样子,使用Enocean无线模块和ESP32-C3进行串口通信。

前言

接下来的ESP32-C3 功能测试都是基于自己设计的开发板:

自己画一块ESP32-C3 的开发板(第一次使用立创EDA)(PCB到手)

开发环境是乐鑫官方的 ESP-IDF, 基于VScode插件搭建好的:

ESP32-C3 VScode开发环境搭建(基于乐鑫官方ESP-IDF——Windows和Ubuntu双环境)

1、UART示例测试

在开发板上面,我们使用了串口0烧录,串口1连接 Enocean 无线通讯模块,所以我们使用UART1测试,引脚为GPIO4、GPIO5:
在这里插入图片描述
在做UART测试之前,还是先得看一下官方的介绍,因为UART的示例比较多:
在这里插入图片描述
通过官方的示例说明,我们选择带串口收发的例子uart_async_rxtxtasks
在这里插入图片描述

1.1 UART 基础测试

根据上面选择的官方的示例新建的工程,是有收也有发。

因为这里是基础测试,需要做的只是把程序中定义的两个引脚通过飞线短接起来,便可以观察到示例的现象:
在这里插入图片描述
在这里插入图片描述
然后工程直接编译烧录,就可以,效果如下:
在这里插入图片描述

1.2 与Enocean无线模块串口通信测试

上面的例子只是串口自己接受发送的简单示例,在实际应用中通常是和其他的传感器,模块进行通信,数据交互,那么这里我就使用自己常用的 Enocean 无线模块,来做一次测试,首先当然得对示例代码进行一定的修改。

在开发板上只需要把预留好的跳帽,连接好即可。

涉及到串口的使用,所以建议在看这个例子前面对 下面 ESP32-C3 UART 相关知识先了解一下。

先测试接收,把发送任务去掉,接收函数稍微修改一下:
在这里插入图片描述
测试结果如下:
(一个无线开关的报文 21 bytes,预留的读取时间为 6ms ,即便这样,也可能在 6ms 内读取到了2个开关的报文,但是不会有错误,这个后期在数据处理的时候需要注意一下)
在这里插入图片描述
然后继续测试一下给 无线模块发送消息,对发送任务稍作修改:
在这里插入图片描述

接收函数不变,测试结果如下:
在这里插入图片描述

最后还是简单写一个解析函数,测试目的,就没有管代码规范了,就是上电把 ID 读出来,先来看一下效果,下面再放一下测试代码:
在这里插入图片描述
上述示例把 Enocean 模块的ID 读取出来,做到这一步,基本上 ESP32 -C3 和 Enocean 的通信没什么大问题了,如果需要想 在 STM32上面那样有完整的驱动,还是得一点一点的打磨移植一下,后续应该会有机会使用 ESP32 -C3 做些项目,到时候会花时间完善一下驱动。

下面把最后测试的示例代码放上(代码是以测试为目的,没有把读取ID的部分代码写成函数,有点不太规范):

/* UART asynchronous example, that uses separate RX and TX tasks

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_log.h"
#include "driver/uart.h"
#include "string.h"
#include "driver/gpio.h"
#include "Datadef.h"

#define proccrc8(u8CRC, u8Data)      (u8CRC8Table[u8CRC^u8Data])

const uint8 u8CRC8Table[256] = { 
							0x00, 0x07, 0x0e, 0x09, 0x1c, 0x1b, 0x12, 0x15, 
							0x38, 0x3f, 0x36, 0x31, 0x24, 0x23, 0x2a, 0x2d, 
							0x70, 0x77, 0x7e, 0x79, 0x6c, 0x6b, 0x62, 0x65, 
							0x48, 0x4f, 0x46, 0x41, 0x54, 0x53, 0x5a, 0x5d, 
							0xe0, 0xe7, 0xee, 0xe9, 0xfc, 0xfb, 0xf2, 0xf5, 
							0xd8, 0xdf, 0xd6, 0xd1, 0xc4, 0xc3, 0xca, 0xcd, 
							0x90, 0x97, 0x9e, 0x99, 0x8c, 0x8b, 0x82, 0x85, 
							0xa8, 0xaf, 0xa6, 0xa1, 0xb4, 0xb3, 0xba, 0xbd, 
							0xc7, 0xc0, 0xc9, 0xce, 0xdb, 0xdc, 0xd5, 0xd2, 
							0xff, 0xf8, 0xf1, 0xf6, 0xe3, 0xe4, 0xed, 0xea, 
							0xb7, 0xb0, 0xb9, 0xbe, 0xab, 0xac, 0xa5, 0xa2, 
							0x8f, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9d, 0x9a, 
							0x27, 0x20, 0x29, 0x2e, 0x3b, 0x3c, 0x35, 0x32, 
							0x1f, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0d, 0x0a, 
							0x57, 0x50, 0x59, 0x5e, 0x4b, 0x4c, 0x45, 0x42, 
							0x6f, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7d, 0x7a, 
							0x89, 0x8e, 0x87, 0x80, 0x95, 0x92, 0x9b, 0x9c, 
							0xb1, 0xb6, 0xbf, 0xb8, 0xad, 0xaa, 0xa3, 0xa4, 
							0xf9, 0xfe, 0xf7, 0xf0, 0xe5, 0xe2, 0xeb, 0xec, 
							0xc1, 0xc6, 0xcf, 0xc8, 0xdd, 0xda, 0xd3, 0xd4, 
							0x69, 0x6e, 0x67, 0x60, 0x75, 0x72, 0x7b, 0x7c, 
							0x51, 0x56, 0x5f, 0x58, 0x4d, 0x4a, 0x43, 0x44, 
							0x19, 0x1e, 0x17, 0x10, 0x05, 0x02, 0x0b, 0x0c, 
							0x21, 0x26, 0x2f, 0x28, 0x3d, 0x3a, 0x33, 0x34, 
							0x4e, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5c, 0x5b, 
							0x76, 0x71, 0x78, 0x7f, 0x6A, 0x6d, 0x64, 0x63, 
							0x3e, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2c, 0x2b, 
							0x06, 0x01, 0x08, 0x0f, 0x1a, 0x1d, 0x14, 0x13, 
							0xae, 0xa9, 0xa0, 0xa7, 0xb2, 0xb5, 0xbc, 0xbb, 
							0x96, 0x91, 0x98, 0x9f, 0x8a, 0x8D, 0x84, 0x83, 
							0xde, 0xd9, 0xd0, 0xd7, 0xc2, 0xc5, 0xcc, 0xcb, 
							0xe6, 0xe1, 0xe8, 0xef, 0xfa, 0xfd, 0xf4, 0xf3 
						};

static const int RX_BUF_SIZE = 200;
static const uint8_t CO_RD_VERSION[] = {0x55,0x00,0x01,0x00,0x05,0x70,0x03,0x09};
static STATES_GET_PACKET  u8state = GET_SYNC_STATE;
u32 u32MyId = 0;
uint8 Read_pt = 0;
// uint8_t data[RX_BUF_SIZE] = {0};
// uint8_t Enocean_Data = 0;       //数据长度记录   
// uint8_t EnoceanState;           //接收完成标志

#define TXD_PIN (GPIO_NUM_4)
#define RXD_PIN (GPIO_NUM_5)

void init(void) {
    const uart_config_t uart_config = {
        .baud_rate = 57600,
        .data_bits = UART_DATA_8_BITS,
        .parity = UART_PARITY_DISABLE,
        .stop_bits = UART_STOP_BITS_1,
        .flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
        .source_clk = UART_SCLK_APB,
    };
    // We won't use a buffer for sending data.
    uart_driver_install(UART_NUM_1, RX_BUF_SIZE * 2, 0, 0, NULL, 0);
    uart_param_config(UART_NUM_1, &uart_config);
    uart_set_pin(UART_NUM_1, TXD_PIN, RXD_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
}


// int sendData(const char* logName, const char* data)
// {
//     const int len = strlen(data);
//     const int txBytes = uart_write_bytes(UART_NUM_1, data, len);
//     // ESP_LOGI(logName, "Wrote %d bytes", txBytes);
//     return txBytes;
// }

static void tx_task(void *arg)
{
    // static const char *TX_TASK_TAG = "TX_TASK";
    // esp_log_level_set(TX_TASK_TAG, ESP_LOG_INFO);
    while (1) {
        printf("get my ID when poweron!\r\n");
        // sendData(TX_TASK_TAG, "Hello world");
        uart_write_bytes(UART_NUM_1, CO_RD_VERSION, sizeof(CO_RD_VERSION));
        // vTaskDelay(2000 / portTICK_PERIOD_MS);
        vTaskDelete(NULL);
    }
}

static void rx_task(void *arg)
{
    RETURN_TYPE ReturnCode = 0;
    static uint8 u8Count = 0;
    
    uint8 u8CRC = 0;
	// uint8  ERROR_FLAG = 0;
	uint32 CHIP_ID = 0;

    static const char *RX_TASK_TAG = "RX_TASK";
    esp_log_level_set(RX_TASK_TAG, ESP_LOG_INFO);
    uint8_t* data = (uint8_t*) malloc(RX_BUF_SIZE+1);
    
    while (1) {
        const int rxBytes = uart_read_bytes(UART_NUM_1, data, RX_BUF_SIZE, 6 / portTICK_RATE_MS);
        if (rxBytes > 0) { //rxBytes是 uart_read_bytes 返回的长度数据,           
            data[rxBytes] = 0; //推测读取的时候使用的是 rxBytes ++ ,所以这最后一个数据是数据后面一个不需要的,所以赋值0
            // ESP_LOGI(RX_TASK_TAG, "Read %d bytes: '%s'", rxBytes, data);
            ESP_LOGI(RX_TASK_TAG, "Read %d bytes", rxBytes);
            // printf("%x %x %x %x %x %x %x %x %x\r\n",data[0],data[1],data[2],data[3],data[4],data[5],data[6],data[7],data[8]);
            if((rxBytes >= 38)&&(u32MyId == 0)){
                while(Read_pt != rxBytes){
                    switch(u8state){
				    case GET_SYNC_STATE:               // Waiting for packet sync byte 0x55
                        if (data[Read_pt++] == ESP3_SYNC_CODE){	                          					
                            u8state = GET_HEADER_STATE;
                            u8Count = 0;
                            u8CRC = 0; 
                        }
                        // printf("111\r\n"); 
					break;
       			    case GET_HEADER_STATE:              // Read the header bytes
                        u8CRC = proccrc8(u8CRC,data[Read_pt++]);
                        u8Count++;
                        // printf("222\r\n"); 
                        if(u8Count == ESP3_Header_Bytes){
                            u8state = CHECK_CRC8H_STATE;
                            u8Count = 0;
                        }
					break;
				    case CHECK_CRC8H_STATE:                        
                        if(u8CRC == data[Read_pt++]){
                            u8state = GET_DATA_STATE;
                            ReturnCode = Enocean_OK;
                        }
                        // printf("333\r\n"); 
                        // else
                        //     ERROR_FLAG = 1;
					break;
				    case GET_DATA_STATE:
                        if(ReturnCode == Enocean_OK){
                            Read_pt = 15;                       //ID Info start from the 10th data of the data filed
                            
                            for(u8Count=0;u8Count<4;u8Count++)
                            {
                                CHIP_ID <<= 8;
                                CHIP_ID |= data[Read_pt++];
                            }
                            u32MyId = CHIP_ID;
                            Read_pt = 0;
	                        u8state = GET_SYNC_STATE;
                        }
                        // else
                        //     ERROR_FLAG = 1;
					break;
				    default:break;
		            } 
                    if(u8state == GET_SYNC_STATE)break;//跳出while循环  
                }
            }
            ESP_LOG_BUFFER_HEXDUMP(RX_TASK_TAG, data, rxBytes, ESP_LOG_INFO);
            printf("my ID is :0x %x\r\n",u32MyId);
        }
        vTaskDelay(1);//不加延时不会释放CPU使用权,会导致错误,其他任务的看门口报错
    }
    free(data);  
}

void app_main(void)
{
    init();
    xTaskCreate(rx_task, "uart_rx_task", 1024*2, NULL, configMAX_PRIORITIES, NULL);
    xTaskCreate(tx_task, "uart_tx_task", 1024*2, NULL, configMAX_PRIORITIES-1, NULL);
}

2、ESP32-C3 UART 基础知识说明

对于ESP32-C3 UART的介绍,同样在乐鑫的官网有很详细的说明,官方链接如下:

乐鑫官方ESP32-C3 UART部分说明

根据官方文档,ESP32-C3 UART使用步骤:

  1. 设置参数,使用uart_config_t结构体可以统一设置:
    在这里插入图片描述
    例如示例中:
    在这里插入图片描述
    当然,出了使用结构体设置串口的参数,也可以使用函数单独对某一个参数进行设置:
    在这里插入图片描述

  2. 设置收发引脚,使用uart_set_pin函数设置:
    在这里插入图片描述
    后两个参数是硬件流控,如果不需要,则可以设置为UART_PIN_NO_CHANGE,例如示例中:
    在这里插入图片描述

  3. 设置串口,使用uart_driver_install函数进行 install: 在这里插入图片描述参数分别为:串口号,接收缓冲区,发送缓冲区,队列长度(消息长度),中断回调函数(没有写NULL),中断标志位。
    在示例中使用:
    在这里插入图片描述

  4. 完成前面3步就完成了UART的基本配置,接下来就可以进行收发了,
    使用uart write bytes()Tx FIFO buffer里面写数据,就可以发送数据;
    在这里插入图片描述
    使用uart read bytes()Rx FIFO buffer里读数据,就是接收数据;
    在这里插入图片描述
    其他的工作由 FSM(UART controller’s finite state machine)完成 。

    具体的使用可以查看手册上面Running UART Communication章节。

在这里插入图片描述
在上面的例子中,我们只测试了基本的收发,好像没有使用到和中断相关的函数。这个后期如果有需要再来更新。

对于 ESP32-C3 UART 其他的一些功能,比如 485 接口发送等,具体在官方文档有详细的说明,我们在需要使用的时候可以先看文档,然后找到对应的例程,进行更改,和本博文的收发示例一样,学习一定得多动手尝试。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

ESP32-C3入门教程 基础篇(三、UART模块 — 与Enocean无线模块串口通信) 的相关文章

随机推荐

  • 爬虫技术-字体反爬

    文本混淆章节 1 文本混淆简介 简单而言就是利用前端技术干扰 页面可以正常展示 而使用爬虫下载后无法提取正常的数据 1 1 常见的干扰方式 字体反爬 2 字体反爬 2 1 字体反爬简介 在 CSS3 之前 Web 开发者必须使用用户计算机上
  • Vue3+Vite+Ts的Axios企业级封装+本地存储封装

    前言 实际项目开发中 向后台发请求 vue相关项目 都是用axios 不会用ajax 不会为了ajax单独去引入jquery axios官网 http www axios js com 第一步 下载依赖axios npm i axios s
  • 关于java内访问流式接口处理

    我找了一圈 没找到合适得方法 知识面得欠缺 会让遇到问题没有办法 最后联系百度智能才知道怎么做 给百度智能AI一个好评 我也是才知道一般的写法都是OKhttp OkHttp 会阻塞直到所有响应都回来后才会整体返回 建议您用原生HttpURL
  • idea搭建Spark开发环境

    最近在公司换了一个项目组 做Spark项目的 于是开始学Spark 版本
  • 基于epoll的聊天室程序

    epoll相对于poll和select这两个多路复用的I O模型更加的高效 epoll的函数很简单 麻烦的地方在于水平出发和边沿触发 用张图来说明下 ET 边沿 只是在状态反转时触发 比如从不可读到可读 而LT 水平 就是如果可读 就会一直
  • ELK 企业级日志分析系统 ELFK

    一 概述 ELK平台是一套完整的日志集中处理解决方案 将 ElasticSearch Logstash 和 Kiabana 三个开源工具配合使用 完成更强大的用户对日志的查询 排序 统计需求 使用ELK日志文件系统的原因 日志主要包括系统日
  • mysql中索引利用情况(explain用法)

    使用explain查看 如下 1 首先创建表test 语句如下 create table test a int b varchar 10 c varchar 10 2 在表中的a b都创建索引 先后顺序是a b create index i
  • 在使用vue ui创建新项目时出现问题Cannot read property ‘indexOf‘ of undefined

    问题描述 在使用vue ui创建新项目时出现问题 Cannot read property indexOf of undefined 原因分析 主要是自己的版本太低了 node npm和vue cil的版本太低了 查看node版本 node
  • 信息安全技术网络安全等级保护基本要求GB/T 22239一2019(第二级安全要求)

    安全保护能力 第二级安全保护能力 应能够防护免受来自外部小型组织的 拥有少量资源的威胁源发起恶意攻击 一般的自然灾难 以及其他相当危害程度的威胁所造成的重要资源损害 能够发现重要的安全漏洞和处置安全事件 在自身遭到损害后 能够在一段时间内恢
  • computer vision一些术语-目标识别、目标检测、目标分割、语义分割等

    object recognition目标识别 object detection目标检测 Object Segmentation目标分割 Image Segmentation图像分割 semantic segmentation语义分割 ins
  • 【计算机视觉

    文章目录 一 分割 语义相关 6篇 1 1 Semantic Image Synthesis via Class Adaptive Cross Attention 1 2 Semi supervised Domain Adaptation
  • 定时器输出PWM波

    定时器输出PWM波 一 STM32专栏目录 二 Linux专栏目录 三 Android专栏目录 注意 该文是以配置寄存器的方式 从寄存器层面讲解如何配置定时器输出PWM波形 概述 该文主要介绍了PWM的基本概念 PWM名词介绍 定时器为何能
  • IntelliJ IDEA2018.1、2017.3激活方法

    IntelliJ IDEA2018 1 2017 3激活 以下内容转自 天命的博客 中的一篇文章 不是我写的 亲测有用 http idea java sx 简单快捷 由于JetBrains封杀 大部分激活服务器已经不能使用 如果以上方法无效
  • vue中的二次确认用装饰器封装

    在 Vue 中编写装饰器可以通过使用 decorator 的方式 以简洁的方式实现对某个方法的扩展或者功能增强 下面是一个示例装饰器实现 在调用某个方法之前弹出确认框 import MessageBox from element ui ex
  • 基于conda安装GPU版本的pytorch

    安装cuda 从官网上下载或者从清华镜像下载都可以 安装对应Ubuntu系统和nvidia驱动匹配的cuda系统 安装cuda conda install cudatoolkit 10 1 安装Cudnn 安装cudnn conda ins
  • Seata概念以及简单应用

    文章目录 SpringCloud Alibaba Seata处理分布式事务 1 分布式事务问题由来 2 什么是Seata 3 分布式事务的相关概念 4 Seata Server的安装和使用 4 1 Seata修改配置文件 4 2seata的
  • 关于 TCP 窗口满/TCP 零窗口

    TCP window full 是指发送方发送的数据已经达到的接收窗口的上限 然后停止发送 等待新的接收窗口的通知 此时接收端返回TCP零窗口 表示接收端窗口为0 从抓包中可以看出 当清除缓存后窗口不再为0时 接收端更新其窗口大小 在这种情
  • ubuntu16.04修改网卡名称enp2s0为eth0

    1 sudo nano etc default grub 找到GRUB CMDLINE LINUX 改为GRUB CMDLINE LINUX net ifnames 0 biosdevname 0 然后sudo grub mkconfig
  • 去掉标题栏的方法(使用requestWindowFeature(Window.FEATURE_NO_TITLE);为什么失效)

    去掉标题栏的方法 使用requestWindowFeature Window FEATURE NO TITLE 为什么失效 使用requestWindowFeature Window FEATURE NO TITLE 隐藏标题栏失效的原因
  • ESP32-C3入门教程 基础篇(三、UART模块 — 与Enocean无线模块串口通信)

    测试第三课 ESP32 C3的串口通信测试 老样子 使用Enocean无线模块和ESP32 C3进行串口通信 目录 前言 1 UART示例测试 1 1 UART 基础测试 1 2 与Enocean无线模块串口通信测试 2 ESP32 C3