ESP8266 RTOS SDK学习之 Uart串口

写在前面: 

本文章旨在总结备份、方便以后查询,由于是个人总结,如有不对,欢迎指正;另外,内容大部分来自网络、书籍、和各类手册,如若侵权请告知,马上删帖致歉。

这篇来分析一下 Uart的实现方法以及添加自己的用户代码

首先我们来看看官方已经写好的串口程序,在 uart.c文件里面,而初始化 Uart是在 uart_init_new(void)函数里面,然后我们进入里面分析一下

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

    UART_ConfigTypeDef uart0_config, uart1_config;

//    uart0_config.baud_rate    = 100000;					// 波特率
    uart0_config.baud_rate    = BIT_RATE_115200;

    uart0_config.data_bits     = UART_WordLength_8b;	// 数据位 8位
    uart0_config.parity          = USART_Parity_None;	// 奇偶校验,无
    uart0_config.stop_bits     = USART_StopBits_1;		// 一个停止位
    uart0_config.flow_ctrl      = USART_HardwareFlowControl_None;	// 硬件流控制,就是两个串口芯片的CTS_RTS引脚相连接控制收发
    uart0_config.UART_RxFlowThresh = 120;
    uart0_config.UART_InverseMask = UART_None_Inverse;
    UART_ParamConfig(UART0, &uart0_config);

//    uart1_config.baud_rate    = 100000;
    uart1_config.baud_rate    = BIT_RATE_115200;

    uart1_config.data_bits     = UART_WordLength_8b;
    uart1_config.parity          = USART_Parity_None;
    uart1_config.stop_bits     = USART_StopBits_1;
    uart1_config.flow_ctrl      = USART_HardwareFlowControl_None;
    uart1_config.UART_RxFlowThresh = 120;
    uart1_config.UART_InverseMask = UART_None_Inverse;
    UART_ParamConfig(UART1, &uart1_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_IntrEnMask = UART_RXFIFO_TOUT_INT_ENA | UART_FRM_ERR_INT_ENA | UART_TXFIFO_EMPTY_INT_ENA;
    uart_intr.UART_RX_FifoFullIntrThresh = 10;		// 设置接收 FIFO数据满时的中断阈值
    uart_intr.UART_RX_TimeOutIntrThresh = 2;		// 配置进入空闲中断的传输时间,此处表示停止传输的时间超过两个字符则触发该中断
    uart_intr.UART_TX_FifoEmptyIntrThresh = 20;		// 表示发送 FIFO里面的数据个数少于 20个进入中断
    UART_IntrConfig(UART0, &uart_intr);

    UART_SetPrintPort(UART0);	// 选择串口打印端口
    UART_intr_handler_register(uart0_rx_intr_handler,NULL);		// uart0接收中断
    ETS_UART_INTR_ENABLE();		// 使能 uart中断

    /*
    UART_SetWordLength(UART0,UART_WordLength_8b);
    UART_SetStopBits(UART0,USART_StopBits_1);
    UART_SetParity(UART0,USART_Parity_None);
    UART_SetBaudrate(UART0,74880);
    UART_SetFlowCtrl(UART0,USART_HardwareFlowControl_None,0);
    */
}

这里我稍微改了下,把 uart_config这个结构体拆分成两个,这样比较好区分,还有就是对应中断使能的宏解释如下

到这里,串口的初始化大致就这样,然后再看看中断函数处理部分

/* 串口接收数据中断函数 */
LOCAL void uart0_rx_intr_handler(void *para)
{
    /* uart0 and uart1 intr combine togther, when interrupt occur, see reg 0x3ff20020, bit2, bit0 represents
    * uart1 and uart0 respectively
    */
    uint8 RcvChar;
    uint8 uart_no = UART0;//UartDev.buff_uart_no;
    uint8 fifo_len = 0;
    uint8 buf_idx = 0;
    uint8 temp = 0;
   // uint8 fifo_tmp[128] = {0};	// 只是告诉我们这个单片机的内部 FIFO是 128字节大小

    uint32 uart_intr_status = READ_PERI_REG(UART_INT_ST(uart_no)) ;		//读取中断状态

    portBASE_TYPE xHigherPriorityTaskWoken;
    UART_Buf_Type uart_receive_data;
    memset((char*)&uart_receive_data, 0, sizeof(UART_Buf_Type));

    while (uart_intr_status != 0x0) {
    	/* 接收帧错误中断,,,可能是数据位数不对,或者接收到的数据不满8bit...等等 */
        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);	// 清除中断寄存器的 帧错误位
        }
        /* 进入 FIFO满中断 */
        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;	// 读出来内部 FIFO缓存的数据个数
            buf_idx = 0;

            while (buf_idx < fifo_len) {
                uart_tx_one_char(UART0, READ_PERI_REG(UART_FIFO(UART0)) & 0xFF);	// 把数据通过串口 0输出
                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) {
            	temp = READ_PERI_REG(UART_FIFO(UART0)) & 0xFF;		// 读取数据

            	if(uart_receive_data.len + buf_idx < USER_UART_MAX)
            	{
            		uart_receive_data.buf[uart_receive_data.len + buf_idx] = temp;
            	}
                uart_tx_one_char(UART1, temp);	// 把数据通过串口 1输出
                buf_idx++;
            }
            uart_receive_data.len += fifo_len;

            WRITE_PERI_REG(UART_INT_CLR(UART0), UART_RXFIFO_TOUT_INT_CLR);	// 清除空闲标志位

            /* 发送队列 */
            xQueueSendFromISR(xQueueCusUart, (void *)&uart_receive_data, &xHigherPriorityTaskWoken);
            portEND_SWITCHING_ISR(xHigherPriorityTaskWoken);
        }
        /* 发送 FIFO里面的数据个数少于 20个,进入中断 */
        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 STATUS uart_tx_one_char(uint8 uart, uint8 TxChar)
{
    while (true) {
    	// 首先要明确,只要发送FIFO里面有数据,串口就会不断的从 FIFO里面取数据然后发出去
        uint32 fifo_cnt = READ_PERI_REG(UART_STATUS(uart)) & (UART_TXFIFO_CNT << UART_TXFIFO_CNT_S);

        // 如果 FIFO里面 >= 126,就会一直在 while循环,实际上内部串口也在不停的从 FIFO取出来数据,然后发出去
        if ((fifo_cnt >> UART_TXFIFO_CNT_S & UART_TXFIFO_CNT) < 126) {
            break;
        }
    }

    WRITE_PERI_REG(UART_FIFO(uart) , TxChar);		// 写一个字节到串口的FIFO
    return OK;
}

/* 这个就不用多说了,用来转换打印串口 */
void UART_SetPrintPort(UART_Port uart_no)
{
    if (uart_no == 1) {
        os_install_putc1(uart1_write_char);		// 选用 USART1打印
    } else {
        os_install_putc1(uart0_write_char);		// 选用 USART0打印
    }
}

好了,分析完官方内嵌的程序后,我们自己去定义,封装一些需要用到函数,因为是 RTOS开发,自然要引用操作系统啦,不多说,代码如下,说明看注释

/*
 * bsp_uart.c
 *
 *  Created on: 2019年9月7日
 *      Author: liziyuan
 */

#include "esp_common.h"

#include "bsp_uart.h"


xQueueHandle xQueueCusUart;
xQueueHandle xQueueXmodem;

/******************************************************************************
 * FunctionName : uart0_send_data
 * Description  : uart 0数据发送
 * Parameters   : Buf ---- 数据
 * 				  Len ---- 长度
 * Returns      : none
*******************************************************************************/
void ICACHE_FLASH_ATTR uart0_send_data(uint8 *Buf, uint32 Len)
{
	uint32 i;

	for(i = 0;i < Len;i++)
	{
		WRITE_PERI_REG(UART_FIFO(UART0) , *(Buf + i));		// 写一个字节到串口 0的FIFO
	}
}

/******************************************************************************
 * FunctionName : uart_receive
 * Description  : uart数据接收处理
 * Parameters   : Buf ---- 数据
 * 				  Len ---- 长度
 * Returns      : none
*******************************************************************************/
LOCAL void ICACHE_FLASH_ATTR uart_receive(uint8 *Buf, uint32 Len)
{
	uart0_send_data(Buf, Len);    // 自收发测试,你可以在这里添加你需要执行的程序
}

/******************************************************************************
 * FunctionName : uart_task
 * Description  : uart处理任务
 * Parameters   : pvParameters
 * Returns      : none
*******************************************************************************/
LOCAL void ICACHE_FLASH_ATTR uart_task(void *pvParameters)
{
	UART_Buf_Type Uart0_data;

	while(1)
	{
		if(xQueueReceive(xQueueCusUart, (void *)&Uart0_data, (portTickType)500/*portMAX_DELAY*/))	// wait about 5sec
		{
			if(Uart0_data.len > 0)
			{
				uart_receive(Uart0_data.buf, Uart0_data.len);
			}
		}
//		vTaskDelay(50 / portTICK_RATE_MS);	 // 50 ms
	}

	vTaskDelete(NULL);
}

/******************************************************************************
 * FunctionName : user_Uart_Init
 * Description  : 串口初始化
 * Parameters   : none
 * Returns      : none
*******************************************************************************/
void ICACHE_FLASH_ATTR user_Uart_Init(void)
{
	xQueueCusUart = xQueueCreate((unsigned portBASE_TYPE)UART0_QUEUE_LENGTH, sizeof(UART_Buf_Type));	// 目标队列的句柄
//	xQueueXmodem = xQueueCreate((unsigned portBASE_TYPE)(1), sizeof(UART_Buf_Type));

	uart_init_new();	// config uart0 connection device MCU, config uart1 TX debug output

	xTaskCreate(uart_task, "uart_task", 256, NULL, tskIDLE_PRIORITY + 4, NULL);	// 串口处理任务
}


/*------------------------------- END OF FILE -------------------------------*/


/*
 * bsp_uart.h
 *
 *  Created on: 2019年9月7日
 *      Author: liziyuan
 */

#ifndef __BSP_UART_H
#define __BSP_UART_H


#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"

#include "../../../include/uart.h"

#define UART0_QUEUE_LENGTH		3
#define USER_UART_MAX			50

typedef struct
{
	uint16 len;
	uint8 buf[USER_UART_MAX];
}UART_Buf_Type;

extern xQueueHandle xQueueCusUart;
extern xQueueHandle xQueueXmodem;

void ICACHE_FLASH_ATTR user_Uart_Init(void);


#endif /* __BSP_UART_H */


/*------------------------------- END OF FILE -------------------------------*/


最后,当时在学习串口时参考的链接:https://www.cnblogs.com/yangfengwu/p/11080567.html

发布了31 篇原创文章 · 获赞 12 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_42992084/article/details/102911273