ESP32-C3基于RMT的uart

ESP32-C3硬件串口资源较少, 有的项目需要较多的串口, 有一个arduino库实现了用GPIO模拟的串口, SoftwareSerial. 这里给出一个基于RMT外设的实现, 接收时避免频繁进入中断, 发送时不用禁止中断以保证发送时序.RMT(Remote Control)外设不仅可以用于红外遥控、LED灯条控制等场景,还可以通过灵活配置实现UART串行通信功能.

1. 使用方法

目前仅支持9600波特率,1位起始位, 1位停止位

#include "ks_software_serial.h"
#include "soc/usb_serial_jtag_reg.h"

#define TEST_UART_RX				2
#define TEST_UART_TX				3

class ks_software_serial_c test_uart;
static void test_uart_callback(void *data, uint32_t data_len)
{
	Serial.println("test_uart_callback");
	uint8_t *data_buf = (uint8_t *)data;
	for (int i = 0; i < data_len; i++) {
		Serial.printf("%02x ", data_buf[i]);
	}
	Serial.println();

	test_uart.write(data_buf, data_len);	// send back

	test_uart.flush();
}
void setup() {
	// initialize serial communication at 115200 bits per second:
	static const int SEND_BUF_SIZE = 1024;
	Serial.setTxBufferSize(SEND_BUF_SIZE); 	// If set to zero, driver will not use TX buffer, TX function will block task until all data have been sent out.
	
	Serial.begin ( 115200 );
	Serial.println("start");


	test_uart.begin(9600, -1, TEST_UART_RX, TEST_UART_TX, 64, KS_SWS_BASED_RMT);
	//test_uart.begin(9600, -1, TEST_UART_RX, TEST_UART_TX, 64, KS_SWS_BASED_GPIO);	// you can use gpio if rmt channel is not enough, not recommended
	//CLEAR_PERI_REG_MASK(USB_SERIAL_JTAG_CONF0_REG, USB_SERIAL_JTAG_DP_PULLUP);	// for io19

	test_uart.set_timeout_callback(test_uart_callback);

	test_uart.start_rx();
} 



void loop() {
	test_uart.perform_work();
}

测试收发
在这里插入图片描述

原理

RMT外设可以捕获到脉冲的持续时间和电平, 存放到如下的结构体中

typedef struct rmt_item32_s {
    union {
        struct {
            uint32_t duration0 :15;
            uint32_t level0 :1;
            uint32_t duration1 :15;
            uint32_t level1 :1;
        };
        uint32_t val;
    };
} rmt_item32_t;

在这里插入图片描述
如上图可以得到每个边沿的时刻. 但是最后的几位如果是高电平, 则需要手动补满一帧.

根据获取到的边沿的微秒时刻, 分析出一整包串口数据.

static size_t parse_data(uint32_t *micro, uint8_t *lvl, size_t len, uint8_t *out_byte, size_t out_max)
{
	int out_idx = 0;
	int lvl_idx = 0;
	int micro_idx = 0;
	int start_bit_idx = 0;

    // at the end of whole pack, level is high, the left bit will be lost, need manual add it back
    uint32_t tail = (micro[len - 1] - micro[0]) % (BAUD_9600_US * 10);
    // LOG_INFO("tail: %d\n", tail);
    uint16_t high_lvl_tail = BAUD_9600_US * 10 - tail;
    // LOG_INFO("high_lvl_tail: %d\n", high_lvl_tail);
    micro[len] = high_lvl_tail + micro[len - 1];
    lvl[len] = 0;


    while(micro_idx < (int)len){
        uint32_t sample_stamp = micro[micro_idx];
        sample_stamp += BAUD_9600_US / 2;	// for 9600, mid of the start bit(a clock is 104 us)
        micro_idx++;

		
        uint8_t parse_byte = 0;
        bool one_frame_lvl[10] = {0};
        int one_frame_idx = 0;
        while(sample_stamp < micro[start_bit_idx] + (BAUD_9600_US / 2) + 9 * BAUD_9600_US){	// while < stop bit mid
            bool cur_lvl = lvl[lvl_idx];
            one_frame_lvl[one_frame_idx] = cur_lvl;
            one_frame_idx++;
            sample_stamp += BAUD_9600_US;
			
            if ( sample_stamp > micro[micro_idx] ) {
                micro_idx++;
                lvl_idx++;
            }
        }


        for (int i = 1; i < 9; i++) {
            parse_byte |= one_frame_lvl[i] << (i - 1);
        }
        start_bit_idx = micro_idx;
        lvl_idx++;

		if ( out_idx < out_max ) {
			out_byte[out_idx] = parse_byte;
			out_idx++;
		}

    }

    return out_idx;

}

代码
https://github.com/kalimdorsummer/rmt_serial

  • 8
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值