stm32 UART (hal库+空闲中断实现)

使用HAL库,uart的配置与使用只需要考虑初始化和中断的编写,以及一些函数的封装就好了。

1. UART的初始化

初始化结构体UART_HandleTypeDef(如下),用来存放一些HAL库与uart相关的变量,重点是对.Init的初始化

UART_HandleTypeDef uart_handle[] = 
{
    //index:0  USART1
    {
        .Instance = USART1,
        .Init = 
        {
            .BaudRate = 115200,
            .WordLength = UART_WORDLENGTH_8B,
            .StopBits = UART_STOPBITS_1,
            .Parity = UART_PARITY_NONE,
            .Mode = UART_MODE_TX_RX,
            .HwFlowCtl = UART_HWCONTROL_NONE,
            .OverSampling = UART_OVERSAMPLING_8,
        },
        .pTxBuffPtr = NULL,
        .TxXferSize = 0,
        .TxXferCount = 0,
        .pRxBuffPtr = NULL,
        .RxXferSize = 0,
        .RxXferCount = 0,
        .hdmatx = NULL,
        .hdmarx = NULL,
        .Lock = HAL_UNLOCKED,
        .gState = HAL_UART_STATE_RESET,
        .RxState = HAL_UART_STATE_RESET,
        .ErrorCode = 0,
    }
};

BaudRate设置波特率,如果是经典值,HAL库会帮忙自动设置寄存器配置达到相应的波特率
WordLength设置字长,一般使用奇偶校验位的话就使用9bit,不使用的话就使用8bit
StopBits设置停止位,有 0.5 个、 1 个、 1.5 个和 2 个停止位,一般使用1个停止位
Parity设置是否使用奇偶校验位
Mode设置双工还是只接收或者发送
HwFlowCtl是否使用硬件流控制(还没用到过,用到了再补充)
OverSampling设置uart的采样率,使用8倍采样率,uart的发送速率最高可达fPCLK/8,而使用16倍采样率,uart的发送速率会减半,但是会更精确

2.使用中断来发送

image
使用HAL库的中断发送HAL_UART_Transmit_IT,只需要将下面函数放入对应的中断服务函数里就成

void USART1_IRQHandler(void)
{
    HAL_UART_IRQHandler(uart_config.config_head[UART1_INDEX].handle); 
}

在这里说明一下两个中断标志位的区别(之前混在一起了)
TXE标志位表示移位寄存器器为空,TC表示发送完成,之前混淆了这两个概念,移位寄存器为空并不等于发送完成了,看手册里的这张图我们就能明了两者之间的关系

3.使用空闲中断来接收数据

image
定义一个fifo来存放收到的数据(这里的发送fifo先定义着,还没用到)

/*uart fifo 结构体*/
typedef struct
{
    uint8_t *rxBuf;/*接收缓冲指针*/
    uint8_t *txBuf;/*发送缓冲指针*/
    uint16_t unTxBufSize; /* 发送缓冲区大小 */
    uint16_t unRxBufSize; /* 接收缓冲区大小 */
    uint8_t unTxWrite; /* 发送 FIFO 写索引 */
    uint8_t unTxRead; /* 发送 FIFO 读索引 */
    uint8_t unRxWrite; /* 接收 FIFO 写索引 */
    uint8_t unRxRead; /* 接收 FIFO 读索引 */
    uint16_t unRxCount;/* 接收 FIFO 使用计数 */
}uart_fifo_type;

封装一个接收开始的函数:该函数中使用HAL库帮我们使能了接收中断,以及一些错误中断。我们在后面补上使能一个空闲中断

void Usart_Receive_start(uint8_t index)
{
    HAL_UART_Receive_IT(uart_config.config_head[index-1].handle, un_rxbuf_temp, 1);
    __HAL_UART_ENABLE_IT(uart_config.config_head[0].handle,UART_IT_IDLE);
}

定义回调函数,每接收完成一个字节,就将该字节推入fifo

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    /*进入这个回调函数表示1个字节接收完成了*/
    uart_fifo_type *lcFifo_handle = uart_config.config_head[UART1_INDEX].FifoBuf;//fifo使用的句柄
    UART_HandleTypeDef *lcHal_uart_handle = uart_config.config_head[UART1_INDEX].handle;//HAL库uart句柄
    
    /*将该字节推入fifo*/
    lcHal_uart_handle->pRxBuffPtr--;
    lcFifo_handle->rxBuf[lcFifo_handle->unRxWrite] = *(lcHal_uart_handle->pRxBuffPtr);
    /* 接收 FIFO 的写指针+1 */
    if ((lcFifo_handle->unRxWrite+1) >= lcFifo_handle->unRxBufSize)
    {
        lcFifo_handle->unRxWrite = 0;
    }
    else
    {
        lcFifo_handle->unRxWrite++;
    }
    /* 统计未处理的字节个数 */
    if (lcFifo_handle->unRxCount < lcFifo_handle->unRxBufSize) 
    {
        lcFifo_handle->unRxCount++;
    }

    /*开启下一个字节的接收*/
    HAL_UART_Receive_IT(huart, un_rxbuf_temp, 1);

    
}

在中断服务函数中加上如下代码:空闲中断产生的时候,设置标志位,让应用程序来取

void USART1_IRQHandler(void)
{
    HAL_UART_IRQHandler(uart_config.config_head[UART1_INDEX].handle);

    //空闲中断标志位
    if(__HAL_UART_GET_FLAG(uart_config.config_head[UART1_INDEX].handle, UART_FLAG_IDLE) != RESET)
    {
        /*清除中断标志位*/
        __HAL_UART_CLEAR_IDLEFLAG(uart_config.config_head[UART1_INDEX].handle);
        /*设置接收结束标志位*/
        uart_config.config_head[UART1_INDEX].RecFinish = 1;
    }

}

提供给应用程序从fifo中取数据的接口

uint8_t Usart_get_data(uint8_t index,uint8_t *pData,uint16_t *length)
{
    uart_fifo_type *lcFifo_handle = uart_config.config_head[index-1].FifoBuf;//fifo使用的句柄
    uart_config_ex_type *lcUart_handle = &uart_config.config_head[index-1];
        
    if(1u == lcUart_handle->RecFinish)
    {
        HAL_NVIC_DisableIRQ(lcUart_handle->IRQ_num);
        lcUart_handle->RecFinish = 0u;
        *length = lcFifo_handle->unRxWrite - lcFifo_handle->unRxRead;
        while(lcFifo_handle->unRxRead < lcFifo_handle->unRxWrite)
        {
            *pData = lcFifo_handle->rxBuf[lcFifo_handle->unRxRead];
            lcFifo_handle->unRxRead++;
            pData++;
        }
        lcFifo_handle->unRxRead = 0;
        lcFifo_handle->unRxWrite = 0;
        HAL_NVIC_EnableIRQ(lcUart_handle->IRQ_num);
        return 0;
    }
    else
    {
        return 2;
    }
}
posted @ 2024-03-11 21:34  kefa  阅读(3005)  评论(0)    收藏  举报