ESP8266-FreeRTOS-串口中断(一)

31 篇文章 12 订阅

 首先在乐鑫官方的参考文档里面只有简单的几句话:

官方参考文档里有关串口中断的描述

 ESP8266_RTOS_SDK 默认使⽤ UART0 打印调试信息,默认波特率为 74880。用户可以在 user_init 中⾃定义初始化 UART,参考 uart_init_new 实现。UART 驱动示例: \ESP8266_RTOS_SDK\driver_lib\driver\uart.c
 以初始化 UART0 为例。定义 UART 参数:

UART_ConfigTypeDef  uart_config;

uart_config.baud_rate   =   BIT_RATE_74880;

uart_config.data_bits   =   UART_WordLength_8b;

uart_config.parity      =   USART_Parity_None;

uart_config.stop_bits   =   USART_StopBits_1;

uart_config.flow_ctrl   =   USART_HardwareFlowControl_None;

uart_config.UART_RxFlowThresh   =   120;

uart_config.UART_InverseMask    =   UART_None_Inverse;

UART_ParamConfig(UART0, &uart_config);

注册 UART 中断处理函数,使能 UART 中断:

UART_IntrConfTypeDef    uart_intr;

编程示例

uart_intr.UART_IntrEnMask = UART_RXFIFO_TOUT_INT_ENA | UART_FRM_ERR_INT_ENA | UART_RXFIFO_FULL_INT_ENA | UART_TXFIFO_EMPTY_INT_ENA;

uart_intr.UART_RX_FifoFullIntrThresh = 10;

uart_intr.UART_RX_TimeOutIntrThresh = 2;

uart_intr.UART_TX_FifoEmptyIntrThresh = 20;

UART_IntrConfig(UART0, &uart_intr);

UART_SetPrintPort(UART0);

UART_intr_handler_register(uart0_rx_intr_handler);

ETS_UART_INTR_ENABLE();

然后就没有更多的解释了!

从这篇博客里看到:http://blog.sina.com.cn/s/blog_ed2c83c30102xc0o.html

  • 串口经常会随机的发来数据,而且数据大小未知;
  • 对收到的数据如何存储;
  • 如何确定是将一条完整的数据发送出去了。

要达到上述三点要求,其实现思路大致如下:

  • 对于随机数据的接收,要依靠中断来实现,在中断的结构体中设置UART_RXFIFO_FULL_INT_ENA 和UART_RXFIFO_TOUT_INT_ENA,然后据此设定相应门限值。
UART_IntrConfTypeDef uart_intr;
uart_intr.UART_IntrEnMask = UART_FRM_ERR_INT_ENA | UART_RXFIFO_FULL_INT_ENA | UART_RXFIFO_TOUT_INT_ENA;
uart_intr.UART_RX_FifoFullIntrThresh = 63;
uart_intr.UART_RX_TimeOutIntrThresh = 20;
uart_intr.UART_TX_FifoEmptyIntrThresh = 20;
UART_IntrConfig(UART0, &uart_intr);
  • 一次性接收到63字节(实际测试似乎是64字节)会产生UART_RXFIFO_FULL_INT_ST 中断。
  • 如果接收到数据小于63字节,并且在规定时间没有收到数据,就会产生UART_RXFIFO_TOUT_INT_ST中断。(esp8266并不需要自己来实现中断函数,而是通过注册一个中断回调函数来实现)
  • 由于接收数据比较快,所以要先缓存下来再处理,我在这里采用一个循环队列来实现缓存,最初,我是逐个字符往队列里存储,但是终端回调函数发送数据快,逐个字符存储处理太慢,于是就将其改成了用memcpy()函数来往队列里存储。(注意这里要合理估算,确保下一个数据发过来之前前一数据已被任务处理完成)
  • 对于存储下来的数据,设计了一个定时任务,并在队列里设计了一个查找函数,每隔一段时间对队列里面的数据进行查询,查找到最后一个’\n’字符串,返回这段数据长度,然后封装成json数据发送出去。这里用循环队列实现要避免重复发送数据,而且不用擦除数据,可以直接覆盖原有数据。
    队列结构体如下:
    typedef struct{
    u8* p_o;
    u8* volatile p_r;
    u8* volatile p_w;
    volatile u32 fill_cnt;
    u32 size;
    }RINGBUF;

最终搞出来了!!

它的思想就是:
- 首先注册中断服务函数,并注册完成之后马上初始化一个队列(便于将数据在任务之间传递)和开始一个串口任务
- 编写中断服务函数,在中断服务函数中判断条件,可以设置为串口数据溢出时就把数据发送到队列
- 在串口任务中只要一检测到队列中有数据发送就马上用串口打印出来。

user_main.c(删除了一些不必要的代码)

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "esp_common.h"
#include "esp_wifi.h"
#include "uart.h"
#include "apps/sntp.h"

#include "iot_export.h"
#include "aliyun_port.h"
#include "aliyun_config.h"
#include "ota.h"
#include "mqtt.h"

int got_ip_flag = 0;
ota_info_t *p_ota_info = NULL;

enum {
    UART_EVENT_RX_CHAR,
    UART_EVENT_MAX
};

typedef struct _os_event_ {
    uint32 event;
    uint32 param;
} os_event_t;


xTaskHandle xUartTaskHandle;
xQueueHandle xQueueUart;

LOCAL STATUS
uart_tx_one_char(uint8 uart, uint8 TxChar)
{
    while (true) {
        uint32 fifo_cnt = READ_PERI_REG(UART_STATUS(uart)) & (UART_TXFIFO_CNT << UART_TXFIFO_CNT_S);

        if ((fifo_cnt >> UART_TXFIFO_CNT_S & UART_TXFIFO_CNT) < 126) {
            break;
        }
    }

    WRITE_PERI_REG(UART_FIFO(uart) , TxChar);
    return OK;
}


// 串口处理任务  步骤:3
LOCAL void
user_uart_task(void *pvParameters)
{
    os_event_t e;

    for (;;) {
        if (xQueueReceive(xQueueUart, (void *)&e, (portTickType)portMAX_DELAY)) {
            switch (e.event) {
                case UART_EVENT_RX_CHAR:
                    printf("%c", e.param); // 打印一个字符
                    break;

                default:
                    break;
            }
        }
    }

    vTaskDelete(NULL);
}



// 串口中断服务函数 步骤:2
LOCAL void
user_uart0_rx_intr_handler(void *para)
{
    os_event_t e;
    portBASE_TYPE xHigherPriorityTaskWoken;

    uint8 RcvChar;
    uint8 uart_no = UART0;//UartDev.buff_uart_no;
    uint8 fifo_len = 0;
    uint8 buf_idx = 0;
    uint8 fifo_tmp[128] = {0};

    uint32 uart_intr_status = READ_PERI_REG(UART_INT_ST(uart_no)) ;

    while (uart_intr_status != 0x0) {
        if (UART_FRM_ERR_INT_ST == (uart_intr_status & UART_FRM_ERR_INT_ST)) {
            //printf("FRM_ERR\r\n");
            WRITE_PERI_REG(UART_INT_CLR(uart_no), UART_FRM_ERR_INT_CLR);
        } else if (UART_RXFIFO_FULL_INT_ST == (uart_intr_status & UART_RXFIFO_FULL_INT_ST)) { //  串口数据溢出
            //printf("*************full\r\n");
            fifo_len = (READ_PERI_REG(UART_STATUS(UART0)) >> UART_RXFIFO_CNT_S)&UART_RXFIFO_CNT;
            buf_idx = 0;

/*            while (buf_idx < fifo_len) {
                uart_tx_one_char(UART0, READ_PERI_REG(UART_FIFO(UART0)) & 0xFF);
                buf_idx++;
            } */
            RcvChar = READ_PERI_REG(UART_FIFO(uart_no)) & 0xFF; // 将一个字符拷贝过去


            WRITE_PERI_REG(UART_INT_CLR(UART0), UART_RXFIFO_FULL_INT_CLR);

            // 设置为串口接收溢出时就将数据传到队列
            e.event = UART_EVENT_RX_CHAR;
            e.param = RcvChar;

            xQueueSendFromISR(xQueueUart, (void *)&e, &xHigherPriorityTaskWoken); // 将数据发送到队列一次只有一次字符
            portEND_SWITCHING_ISR(xHigherPriorityTaskWoken);


        }  
        uart_intr_status = READ_PERI_REG(UART_INT_ST(uart_no)) ;
    }
}

// 串口中断初始化 步骤:1
void
user_uart_init(void)
{
    UART_WaitTxFifoEmpty(UART0);
    UART_WaitTxFifoEmpty(UART1);

    UART_ConfigTypeDef uart_config;
    uart_config.baud_rate    = BIT_RATE_115200;
    uart_config.data_bits    = UART_WordLength_8b;
    uart_config.parity       = USART_Parity_None;
    uart_config.stop_bits    = USART_StopBits_1;
    uart_config.flow_ctrl    = USART_HardwareFlowControl_None;
    uart_config.UART_RxFlowThresh = 120;
    uart_config.UART_InverseMask = UART_None_Inverse;
    UART_ParamConfig(UART0, &uart_config);

    UART_IntrConfTypeDef uart_intr;
    uart_intr.UART_IntrEnMask = UART_RXFIFO_TOUT_INT_ENA | UART_FRM_ERR_INT_ENA | UART_RXFIFO_FULL_INT_ENA | UART_TXFIFO_EMPTY_INT_ENA;
    uart_intr.UART_RX_FifoFullIntrThresh = 10; // 设置10个字符就溢出
    uart_intr.UART_RX_TimeOutIntrThresh = 10;
    uart_intr.UART_TX_FifoEmptyIntrThresh = 20;
    UART_IntrConfig(UART0, &uart_intr);

    UART_SetPrintPort(UART0);
    UART_intr_handler_register(user_uart0_rx_intr_handler, NULL);
    ETS_UART_INTR_ENABLE();

    // 中断设置完成以后就马上初始化队列和创建串口处理任务
    xQueueUart = xQueueCreate(32, sizeof(os_event_t));
    xTaskCreate(user_uart_task, (uint8 const *)"uTask", 512, NULL, 5, &xUartTaskHandle);
}

void user_init(void)
{
    user_uart_init();
}


FreeRTOS是一个开源的实时操作系统内核,被广泛应用于嵌入式系统中。ESP32是一款具有双核处理器和Wi-Fi功能的芯片,通过使用ESP-IDF开发框架可以进行软件开发。在ESP32-IDF开发中,使用FreeRTOS的消息队列可以实现不同任务之间的通信。 在ESP32开发中,可以通过中断服务程序(Interrupt Service Routine,ISR)来发送消息到消息队列,并在任务中通过接收方法响应。 首先,我们需要创建一个全局的消息队列句柄,可以使用xQueueCreate函数来创建一个消息队列。例如,可以使用以下代码创建一个大小为10的消息队列: xQueueHandle messageQueue = xQueueCreate(10, sizeof(int)); 然后,在中断服务程序中,可以使用xQueueSendFromISR方法将消息发送到消息队列中。例如,可以使用以下代码将一个整数值发送到消息队列中: int value = 100; xQueueSendFromISR(messageQueue, &value, NULL); 在任务中,可以使用xQueueReceive方法从消息队列中接收消息并进行响应。例如,可以使用以下代码从消息队列中接收一个整数值并打印出来: int receivedValue; xQueueReceive(messageQueue, &receivedValue, portMAX_DELAY); printf("Received value: %d\n", receivedValue); 需要注意的是,在接收消息时,可以通过指定第三个参数来设置等待时间。例如,使用portMAX_DELAY表示无限等待,即直到接收到消息为止。 通过以上步骤,我们可以实现在ESP32开发中使用FreeRTOS消息队列进行中断服务消息发送与响应。这种方式可以实现不同任务之间的通信和同步,提高系统的并发性和实时性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值