ESP8266--阿里云SDK开发--FreeRTOS--学习笔记(一)串口中断服务函数

 从阿里物联网套件上下载ESP8266SDK,然后构建ESP8266开发环境;我个人还是喜欢Oracle VM Virtual Box 安装ESP8266_Lubuntu。然后设置一个共享文件夹,就可以在linux环境下编译然后在windows环境下下载了。
 阿里云的这个SDK是RTOS版本的,它的OS是FreeRTOS,而我没有学过FreeRTOS;还好我学过uCosIII,我估计实时操作系统应该实现的机制都是差不多的吧。

可供学习的精华

 从SDK的串口uart.c中可以看到,例程里面它用到了消息队列的方法,把串口中断服务函数中的数据传到任务里面去。

首先定义一个事件结构体:
结构里面的event成员就可以设置枚举中的事件,而para就把串口传来的字符串数据放进去。

typedef _os_event_t_ {

    uint32 event;
    uint8* para;

} os_event_;

并且枚举一些可供选择的事件名:

enum {

    UART_EVENT_RX_CHAR,
    UART_EVENT_RX_MAX

};

作为设置ESP8266 RTOS版本的串口中断配置,第一步当然是配置串口中断函数(记得改名字)

void
user_uart_init(void)
{
    UART_WaitTxFifoEmpty(UART0);
    UART_WaitTxFifoEmpty(UART1);

    UART_ConfigTypeDef uart_config;
    uart_config.baud_rate    = BIT_RATE_9600;
    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 = 100; // 设置100个字符就溢出
    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);
}

第二步就需要定义中断服务函数即发送消息的地方:(名字要一致)

LOCAL void
user_uart0_rx_intr_handler(void *para)
{
    os_evet_t e;
    uint8 RcvChar;
    uint8 uart_no = UART0;//UartDev.buff_uart_no;
    uint8 fifo_len = 0;
    uint8 buf_idx = 0;
    uint8 fifo_tmp[128] = {0};

    portBASE_TYPE xHigherPriorityTaskWoken;

    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++;
            }

            WRITE_PERI_REG(UART_INT_CLR(UART0), UART_RXFIFO_FULL_INT_CLR);
        } else if (UART_RXFIFO_TOUT_INT_ST == (uart_intr_status & UART_RXFIFO_TOUT_INT_ST)) {
            printf("tout\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++;
            }*/

            while (buf_idx < fifo_len) {
                fifo_tmp[buf_idx] = READ_PERI_REG(UART_FIFO(UART0)) & 0xFF; // 把串口fifo里面的数据循环取出来并放到字符数组里面去
                buf_idx++;
            }
            fifo_tmp[buf_idx] = '\0';
            WRITE_PERI_REG(UART_INT_CLR(UART0), UART_RXFIFO_TOUT_INT_CLR);

            e.event = UART_EVENT_RX_CHAR; // 设置好事件,便于任务查看
            e.para = fifo_tmp;            // 把串口接收到的数据放到结构体中去

            // 使用带串口中断保护的消息队列发送函数,这个函数只能用于中断服务函数中,其他地方绝对不能使用!!
            xQueueSendFromISR(xQueueUart, (void *)&e, &xHigherPriorityTaskWoken);
            portEND_SWITCHING_ISR(xHigherPriorityTaskWoken);

        } else if (UART_TXFIFO_EMPTY_INT_ST == (uart_intr_status & UART_TXFIFO_EMPTY_INT_ST)) {
            printf("empty\n\r");
            WRITE_PERI_REG(UART_INT_CLR(uart_no), UART_TXFIFO_EMPTY_INT_CLR);
            CLEAR_PERI_REG_MASK(UART_INT_ENA(UART0), UART_TXFIFO_EMPTY_INT_ENA);
        } else {
            //skip
        }

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

然后在任务里面就不停的查询事件

LOCAL void
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("%s", e.param);
                    break;

                default:
                    break;
            }
        }
    }

    vTaskDelete(NULL);
}

总结:
 这种方法真的是很好,当配置好串口中断,串口来数据之后就马上执行串口中断服务函数;在中断服务函数中设置事件,让任务进行处理。最后,要提醒的是:uart.c中那些例程是不能用的,需要重新在其他地方编写而且就算是偷懒,照抄也要把中断的配置函数(void uart_init_new(void))和中断服务函数(LOCAL void uart0_rx_intr_handler(void *para))的名字改变!!切记啊,不然你会发现你写的程序你已经失去了对它的控制!!!无穷的“tout”和“full”谁遇到谁知道!!

猜你喜欢

转载自blog.csdn.net/qq_28877125/article/details/79933433