STM32 串口之中断接收与中断发送 (HAL库)

2019-07-20 06:46发布

一、首先HAL库真心的不好用,明明很简单的东西弄的那么复杂哎     基础部分:
     串口初始化后:   同时开启接收与发送功能     [mw_shl_code=cpp,true]        UartHandle->Instance = USART1;
    UartHandle->Init.BaudRate   = 115200;
    UartHandle->Init.HwFlowCtl  = UART_HWCONTROL_NONE;
    UartHandle->Init.Mode       = UART_MODE_TX_RX;
    UartHandle->Init.OverSampling = UART_OVERSAMPLING_16;
        UartHandle->Init.WordLength = UART_WORDLENGTH_8B;
        UartHandle->Init.StopBits   = UART_STOPBITS_1;
        UartHandle->Init.Parity     = UART_PARITY_NONE;
        HAL_UART_Init(UartHandle);[/mw_shl_code]
     引脚初始化MspInit:
     [mw_shl_code=applescript,true]        __HAL_RCC_USART1_CLK_ENABLE();
        __HAL_RCC_GPIOA_CLK_ENABLE();
        /**USART1 GPIO Configuration
        PA9     ------> USART1_TX
        PA10     ------> USART1_RX
        */
        GPIO_InitStruct.Pin = GPIO_PIN_9|GPIO_PIN_10;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Pull = GPIO_PULLUP;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
        GPIO_InitStruct.Alternate = GPIO_AF7_USART1;
        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);[/mw_shl_code]

      中断优先级与接收中断开启:
      [mw_shl_code=cpp,true]        HAL_NVIC_SetPriority(USART1_IRQn, 3, 1);
        NVIC_EnableIRQ(USART1_IRQn);
        __HAL_UART_ENABLE_IT(UartHandle, UART_IT_RXNE);[/mw_shl_code]

     关键部分1:
     [mw_shl_code=cpp,true]// 数据结构部分

#define SERIAL_BUFFER_SIZE 128

/* 使用USING_IT_TXE条件编译,来确定是普通发送还是中断发送
* 细节见:write函数与发送中断
*/
#define USING_IT_TXE

struct Uart_Buffer{
    uint32_t buffer_head;
    uint32_t buffer_tail;
    unsigned char buffer[SERIAL_BUFFER_SIZE];
};

struct Serial
{
     UART_HandleTypeDef UartHandle;
     IRQn_Type irq;
#ifdef USING_IT_TXE
     volatile bool tx_enable;
#endif
     volatile struct Uart_Buffer rx;
#ifdef USING_IT_TXE
     volatile struct Uart_Buffer tx;
#endif
};[/mw_shl_code]
     关键部分2:
     [mw_shl_code=cpp,true]struct Serial serial1;
void init()
{
        serial1.UartHandle.Instance  = USART1;
        serial1.irq = USART1_IRQn;
        clear();
}

/*        这里是简单的循环队列:初始化部分
*/
void clear ()
{
    rx.buffer_head = rx.buffer_tail = 0;
#ifdef USING_IT_TXE
    tx.buffer_head = tx.buffer_tail = 0;
#endif
}

/*        这里是简单的循环队列:写操作
*        1、用来干什么呢,用于中断接收到数据,保存到rx.buffer[]数组中
*  2、此函数中断函数中调用
*/
void rx_int_irq (uint8_t ch)
{
    uint32_t i = (unsigned int)(rx.buffer_head + 1) % SERIAL_BUFFER_SIZE;
    if (i != rx.buffer_tail)
    {
        rx.buffer[rx.buffer_head] = ch;
        rx.buffer_head = i;
    }
}

/*        这里是简单的循环队列:数据个数查询操作
*        1、查询rx.buffer[]数组中,是否有数据
*  2、此函数用户调用
*/
uint32_t rx_available ()
{
    return (SERIAL_BUFFER_SIZE + rx.buffer_head - rx.buffer_tail) % SERIAL_BUFFER_SIZE;
}

/*        这里是简单的循环队列:读操作
*        1、从rx.buffer[]数组中读取数据
*  2、此函数用户调用
*/
uint8_t read (void)
{
    if (rx.buffer_head == rx.buffer_tail)
    {
                /* 这里只是个返回值,无实际意义,调用read函数前,
                   需要调用rx_available函数进行查询,确保有数据存在 */
        return 0xFF;
    } else
    {
        unsigned char c = rx.buffer[rx.buffer_tail];
        rx.buffer_tail = (rx.buffer_tail + 1) % SERIAL_BUFFER_SIZE;
        return c;
    }
}


/*        这里是简单的循环队列:数据个数查询操作
*        1、查询tx.buffer[]数组中,是否有数据
*  2、此函数中断调用
*/
#ifdef USING_IT_TXE
uint32_t tx_available()
{
    return (SERIAL_BUFFER_SIZE + tx.buffer_head - tx.buffer_tail) % SERIAL_BUFFER_SIZE;
}

/*        这里是简单的循环队列:读操作
*        1、从tx.buffer[]数组中读取数据写入DR寄存器
*  2、此函数中断调用
*/
uint8_t tx_int_irq (void)
{
    if (tx.buffer_head == tx.buffer_tail)
    {
        return 0xFF;
    } else
    {
        unsigned char c = tx.buffer[tx.buffer_tail];
        tx.buffer_tail = (tx.buffer_tail + 1) % SERIAL_BUFFER_SIZE;
        return c;
    }
}
#endif

/*        这里是简单的循环队列:写操作
*        1、用来干什么呢,将数据写入rx.buffer[]数组中
*  2、此函数中断函数中调用
*/
uint8_t write (uint8_t c)
{
#ifdef USING_IT_TXE
    uint32_t i = (unsigned int)(tx.buffer_head + 1) % SERIAL_BUFFER_SIZE;
    if (i != tx.buffer_tail)
    {
        tx.buffer[tx.buffer_head] = c;
        tx.buffer_head = i;
    }


    if(!tx_enable){
            __disable_irq();
                /* 发送完成的Flag,需要手动清除 */
                __HAL_UART_CLEAR_FLAG(&UartHandle, UART_FLAG_TC);
                /*
                 *  tx.buffer[]有数据后,开启发送缓冲空的中断
                 */
                __HAL_UART_ENABLE_IT(&UartHandle, USART_IT_TXE);
                tx_enable = true;
                __enable_irq();
    }

#else
        /* 普通发送的书写方式,这种方式太耗MCU资源了,不建议采用 */
    while((__HAL_UART_GET_FLAG(&UartHandle, UART_FLAG_TXE) == RESET));
    UartHandle.Instance->DR = c;
#endif
    return 1;
}[/mw_shl_code]

     关键部分3:中断函数
     /*        这里先对FLAG参数进行说明下,以免新手看不懂 *        UART_FLAG_RXNE:接收到数据的Flag,需要手动清除,否则一直中断到MCU卡死
*        UART_FLAG_TXE:发送缓冲空的Flag,往DR寄存器写数据后,硬件自动清除
*  UART_FLAG_TC: 发送完成的Flag,需要手动清除,否则一直中断到MCU卡死
*/
void USART1_IRQHandler(void)
{
    /* UART in mode Receiver -------------------------------------------------*/
    if ((__HAL_UART_GET_FLAG(&(serial1.UartHandle), UART_FLAG_RXNE) != RESET) &&
            (__HAL_UART_GET_IT_SOURCE(&(serial1.UartHandle), UART_IT_RXNE) != RESET))
    {
        serial1.rx_int_irq(serial1.UartHandle.Instance->DR & 0xff);
        /* Clear RXNE interrupt flag */
        __HAL_UART_CLEAR_FLAG(&(serial1.UartHandle), UART_FLAG_RXNE);
    }
       
#ifdef USING_IT_TXE
        /* UART in mode ransmit_ -------------------------------------------------*/
    if ((__HAL_UART_GET_FLAG(&(serial1.UartHandle), UART_FLAG_TXE) != RESET) &&
            (__HAL_UART_GET_IT_SOURCE(&(serial1.UartHandle), USART_IT_TXE) != RESET))
    {
                /*
                 * 查询tx.buffer中是否有数据:
                 *  1、有数据进行发送
                 *  2、没有数据关闭发送中断
                 */
                if(serial1.available_irq()){
                        serial1.UartHandle.Instance->DR = serial1.tx_int_irq();
                }else{
                        __HAL_UART_DISABLE_IT(&(serial1.UartHandle), USART_IT_TXE);
                        serial1.tx_enable = false;
                }
        /* Clear TC interrupt flag */
        __HAL_UART_CLEAR_FLAG(&(serial1.UartHandle), UART_FLAG_TC);
    }
#endif
}

本实例的目的是:
     1、这类数据传输怎么处理   
     2、中断发送相对于中断接收来说,需要注意是发送中断只有在需要的情况下(有数据的情况下)开启。
     3、没有注意到 2的问题情况下,在初始化部分就开启中断发送后,MCU上电就死机的现象(这里并不是死机,是死锁,因为MCU一直在相应串口中断函数,导致MCU处理不了其他问题)。


友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。