/* * @Author: * @Date: 2023-07-31 11:47:35 * @LastEditors: xxx * @LastEditTime: 2023-08-25 15:30:33 * @Description: LL库的串口驱动 * email: * Copyright (c) 2023 by xxx, All Rights Reserved. */ #include "uarts.h" #include "dma.h" // 清理接收中断错误标志 static void uart_clear_error(uart_t *uart) { if (uart == NULL) { return; } uart->rx_error_count = 0; if (uart->rx_err_en == TRUE && uart->rx_error != NULL) { osel_memset((uint8_t *)uart->rx_error, 0, sizeof(uarts_interupt_error_t) * uart->rxsize); } } /** * @brief 创建一个UART设备 * @param {USART_TypeDef} *huart USART总线设备句柄 * @param {BOOL} rx_dma_en 接收DMA使能标志 * @param {uint16_t} rxsize 接收缓冲区大小 * @param {rx_interrupt_cb_t} rx_cb 接收中断回调函数 * @param {BOOL} tx_dma_en 发送DMA使能标志 * @param {uint16_t} txsize 发送缓冲区大小 * @param {tx_complete_cb_t} tx_complete_cb 发送完成回调函数 * @return {*} 创建的UART设备指针 * @note: 该函数用于创建一个UART设备。它首先断言huart不为空,然后分配内存用于接收缓冲区和发送缓冲区,并设置相关标志。最后,返回创建的UART设备指针。 */ uart_t *uart_create(USART_TypeDef *huart, BOOL rx_dma_en, uint16_t rxsize, rx_interupt_cb_t rx_cb, BOOL tx_dma_en, uint16_t txsize, tx_complete_cb_t tx_complete_cb) { DBG_ASSERT(huart != NULL __DBG_LINE); // 分配内存 uart_t *uart = (uart_t *)osel_mem_alloc(sizeof(uart_t)); DBG_ASSERT(uart != NULL __DBG_LINE); uart->rx_interupt_timeout = TRUE; // 接收超时标志 uart->rx_interupt_cnt = 0; // 接收中断计数 // 设置接收回调函数 uart->rx_interupt_cb = rx_cb; // 设置接收数据大小 uart->rxsize = rxsize; // 设置发送完成回调函数 uart->tx_complete_cb = tx_complete_cb; // 设置发送数据大小 uart->txsize = txsize; // 如果接收大小大于0,则分配内存 if (rxsize > 0) { uart->rxbuf = (uint8_t *)osel_mem_alloc(rxsize); DBG_ASSERT(uart->rxbuf != NULL __DBG_LINE); } // 如果发送大小大于0,则分配内存 if (txsize > 0) { uart->txbuf = (uint8_t *)osel_mem_alloc(txsize); DBG_ASSERT(uart->txbuf != NULL __DBG_LINE); } // 设置接收DMA禁用 uart->rx_dma_en = rx_dma_en; // 设置发送DMA禁用 uart->tx_dma_en = tx_dma_en; // 设置huart uart->huart = huart; // 返回uart return uart; } /** * @brief 使能UART接收 * @param {uart_t} *uart UART设备句柄 * @param {BOOL} rx_err_en 接收错误使能标志,只能用于串口中断模式下使用 * @return {*} 操作结果 * @note: 该函数用于使能UART设备的接收功能。它首先检查UART设备的接收DMA使能标志,然后禁用接收中断,配置RX DMA并启用RX DMA通道。最后,检查UART设备的发送DMA使能标志,配置TX DMA并启用TX DMA通道。 */ void uart_recv_en(uart_t *uart, BOOL rx_err_en) { if (FALSE == uart->rx_dma_en) { uart->rx_err_en = rx_err_en; LL_USART_EnableIT_RXNE(uart->huart); // 使用接收中断处理 } else { uart->rx_err_en = FALSE; LL_USART_ClearFlag_IDLE(uart->huart); // 配置RX DMA LL_DMA_DisableChannel(uart->dma, uart->dma_tx_channel); LL_DMA_DisableChannel(uart->dma, uart->dma_rx_channel); // 配置RX DMA LL_DMA_SetPeriphAddress(uart->dma, uart->dma_rx_channel, LL_USART_DMA_GetRegAddr(uart->huart)); LL_DMA_SetMemoryAddress(uart->dma, uart->dma_rx_channel, (uint32_t)uart->rxbuf); LL_DMA_SetDataLength(uart->dma, uart->dma_rx_channel, uart->rxsize); LL_DMA_EnableIT_TC(uart->dma, uart->dma_rx_channel); LL_DMA_EnableChannel(uart->dma, uart->dma_rx_channel); LL_USART_EnableDMAReq_RX(uart->huart); LL_USART_EnableIT_IDLE(uart->huart); // 配置TX DMA LL_DMA_SetPeriphAddress(uart->dma, uart->dma_tx_channel, LL_USART_DMA_GetRegAddr(uart->huart)); // 配置内存地址 LL_DMA_SetMemoryAddress(uart->dma, uart->dma_tx_channel, (uint32_t)uart->txbuf); LL_DMA_EnableIT_TC(uart->dma, uart->dma_tx_channel); LL_USART_EnableDMAReq_TX(uart->huart); uart->tx_dma_ok = TRUE; } if (uart->rx_err_en == TRUE) { if (uart->rx_error == NULL) { uart->rx_error = (uarts_interupt_error_t *)osel_mem_alloc(sizeof(uarts_interupt_error_t) * uart->rxsize); DBG_ASSERT(uart->rx_error != NULL __DBG_LINE); } LL_USART_EnableIT_PE(uart->huart); // 使能奇偶校验错误中断 // 使能帧错误中断 // 使能帧错误中断 // 使能溢出错误中断 // LL_USART_EnableIT_ERROR 可以使能上面3个中断 /** * When set, Error Interrupt Enable Bit is enabling interrupt generation in case of a framing * error, overrun error or noise flag (FE=1 or ORE=1 or NF=1 in the USARTx_ISR register). */ LL_USART_EnableIT_ERROR(uart->huart); } } /** * @brief 释放UART设备资源 * @param {uart_t} *uart UART设备句柄 * @return {*} 操作结果 * @note: 该函数用于释放UART设备的接收缓冲区、发送缓冲区和UART设备本身。 */ void uart_free(uart_t *uart) { if (uart != NULL) { if (uart->rxbuf != NULL) { osel_mem_free(uart->rxbuf); } if (uart->rx_error != NULL) { osel_mem_free(uart->rx_error); } if (uart->txbuf != NULL) { osel_mem_free(uart->txbuf); } osel_mem_free(uart); } } /** * @brief 设置波特率 * @param {uart_t} *uart * @param {uint32_t} baudrate 波特率 * @return {*} * @note 可以在超频后串口数据重新通讯 */ void uart_set_baudrate(USART_TypeDef *uart, uint32_t baudrate) { LL_USART_SetBaudRate(uart, SystemCoreClock, LL_USART_OVERSAMPLING_16); } /** * @brief 发送数据 * @param {uart_t} *uart UART设备句柄 * @param {uint8_t} *data 要发送的数据 * @param {uint16_t} len 要发送的数据长度 * @return {*} 操作结果 * @note: 该函数用于发送数据。首先检查UART设备的发送DMA使能标志,然后禁用发送中断,配置TX DMA并启用TX DMA通道。最后,发送数据直到发送缓冲区满或发送中断发生。 */ void uart_send_data(uart_t *uart, uint8_t *data, uint16_t len) { DBG_ASSERT(uart != NULL __DBG_LINE); DBG_ASSERT(data != NULL __DBG_LINE); DBG_ASSERT(len > 0 __DBG_LINE); uint16_t count = 0; if (TRUE == uart->tx_dma_en) { uart->tx_dma_ok = FALSE; osel_memcpy(uart->txbuf, data, len); // 拷贝数据到发送缓冲区 LL_DMA_DisableChannel(uart->dma, uart->dma_tx_channel); // 配置 DMA 源地址 LL_DMA_SetMemoryAddress(uart->dma, uart->dma_tx_channel, (uint32_t)uart->txbuf); // 配置数据长度 LL_DMA_SetDataLength(uart->dma, uart->dma_tx_channel, len); // 使能DMA STREAM 也就是发送数据 LL_DMA_EnableChannel(uart->dma, uart->dma_tx_channel); // 等待DMA发送完成 while (uart->tx_dma_ok == FALSE) { if (count++ >= 2000) { return; } } } else { count = 0; for (uint16_t i = 0; i < len; i++) { count = 0; LL_USART_TransmitData8(uart->huart, data[i]); while (!LL_USART_IsActiveFlag_TXE(uart->huart)) { if (count++ >= 0xFE) { count = 0; continue; } } } while (!LL_USART_IsActiveFlag_TC(uart->huart)) { if (count++ >= 0xFE) { count = 0; continue; } } if (uart->tx_complete_cb != NULL) { uart->tx_complete_cb(); } LL_USART_ClearFlag_TC(uart->huart); } } /** * @brief UART接收超时定时器 * * 当UART接收超时定时器触发时,调用此函数处理UART接收超时事件。 * * @param uart UART对象指针 */ void uart_rx_timeout_timer(uart_t *uart) { // DBG_ASSERT(uart != NULL __DBG_LINE); if (uart == NULL) { return; } if (uart->rx_dma_en == FALSE && uart->rx_interupt_timeout == FALSE) // 中断方式 { if (uart->rx_interupt_cnt++ == RX_TIMEOUT_MSEC) { uart->rx_interupt_timeout = TRUE; if (uart->rx_interupt_cb != NULL && uart->rx_index > 0) { uart->rx_interupt_cb(uart->uart_index, uart->rxbuf, uart->rx_index); } uart_data_storage_reset(uart); } } } /** * @brief UART接收完成回调函数 * * 当UART接收完成时调用此函数。 * * @param uart UART设备指针 */ void uart_rx_cd_callback(uart_t *uart) { if (uart == NULL) { return; } if (uart->rx_cd_en == FALSE) { return; } if (uart->rx_dma_en == FALSE) // 中断方式 { if (uart->rx_interupt_cb != NULL && uart->rx_index > 0) { uart->rx_interupt_cb(uart->uart_index, uart->rxbuf, uart->rx_index); } uart_data_storage_reset(uart); } } /** * @brief 获取UART通信错误计数 * * 获取UART设备的接收错误计数。 * * @param uart UART设备指针 * * @return uint16_t 返回接收错误计数,如果uart为NULL,则返回0 */ uint16_t uart_get_error_count(uart_t *uart) { if (uart == NULL) { return 0; } return uart->rx_error_count; } /** * @brief 获取UART通信中的错误信息 * * 从UART设备中获取接收错误信息和错误计数。 * * @param uart UART设备指针 * @param count 用于存储错误计数的指针 * * @return 如果存在错误,则返回错误类型指针;否则返回NULL */ uarts_interupt_error_t *uart_get_error(uart_t *uart) { if (uart == NULL) { return NULL; } if (uart->rx_error_count > 0) { return uart->rx_error; } else { return NULL; } } /** * @brief 重置UART数据存储 * * 将UART接收到的数据缓冲区重置,并清除错误状态。 * * @param uart UART结构体指针 */ void uart_data_storage_reset(uart_t *uart) { if (uart == NULL) { return; } uart->rx_index = 0; uart_clear_error(uart); } /** * @brief 接收中断回调函数 * @param {uart_t} *uart UART设备句柄 * @return {*} 操作结果 * @note: 该函数用于处理接收中断。首先检查接收DMA使能标志,然后禁用接收中断,配置RX DMA并启用RX DMA通道。当接收到数据时,将数据复制到接收缓冲区,并调用接收中断回调函数。当接收缓冲区满时,关闭RX DMA通道并重置接收索引。 */ void uart_reception_callback(uart_t *uart) { // DBG_ASSERT(uart != NULL __DBG_LINE); if (uart == NULL) { return; } if (LL_USART_IsEnabledIT_RXNE(uart->huart) && LL_USART_IsActiveFlag_RXNE(uart->huart)) { if (uart->rx_index >= uart->rxsize) { uart_data_storage_reset(uart); } uart->rxbuf[uart->rx_index++] = LL_USART_ReceiveData8(uart->huart); uart->rx_interupt_cnt = 0; uart->rx_interupt_timeout = FALSE; // 数据交给超时中断处理 :uart_rx_timeout_timer } else if (LL_USART_IsEnabledIT_IDLE(uart->huart) && LL_USART_IsActiveFlag_IDLE(uart->huart)) { if (uart->rx_dma_en == TRUE) { uart->rx_index = uart->rxsize - LL_DMA_GetDataLength(uart->dma, uart->dma_rx_channel); if (uart->rx_cd_en == FALSE) { LL_DMA_DisableChannel(uart->dma, uart->dma_rx_channel); if (uart->rx_interupt_cb != NULL && (uart->rx_index > 0 && uart->rx_index <= uart->rxsize)) { uart->rx_interupt_cb(uart->uart_index, uart->rxbuf, uart->rx_index); osel_memset(uart->rxbuf, 0, uart->rxsize); } LL_DMA_SetDataLength(uart->dma, uart->dma_rx_channel, uart->rxsize); // 这个不能少 先关闭DMA才能重新设置长度 LL_DMA_EnableChannel(uart->dma, uart->dma_rx_channel); } } uart->rx_index = 0; LL_USART_ClearFlag_IDLE(uart->huart); } if (LL_USART_IsEnabledIT_TC(uart->huart) && LL_USART_IsActiveFlag_TC(uart->huart)) { if (uart->tx_complete_cb != NULL) { uart->tx_complete_cb(); } LL_USART_ClearFlag_TC(uart->huart); } if (uart->rx_err_en == TRUE) { uarts_interupt_error_e err = UART_NO_ERROR; if (LL_USART_IsEnabledIT_PE(uart->huart) && LL_USART_IsActiveFlag_PE(uart->huart)) { err = UART_PARITY_ERROR; LL_USART_ClearFlag_PE(uart->huart); // 清除奇偶校验错误标志 } if (LL_USART_IsActiveFlag_FE(uart->huart) && LL_USART_IsActiveFlag_FE(uart->huart)) { err = UART_FRAME_ERROR; LL_USART_ClearFlag_FE(uart->huart); // 清除帧错误标志 } if (LL_USART_IsActiveFlag_NE(uart->huart) && LL_USART_IsActiveFlag_NE(uart->huart)) { // err = UART_NOISE_ERROR; LL_USART_ClearFlag_NE(uart->huart); // 清除噪声错误标志 } if (LL_USART_IsActiveFlag_ORE(uart->huart) && LL_USART_IsActiveFlag_ORE(uart->huart)) { err = UART_OVERRUN_ERROR; LL_USART_ClearFlag_ORE(uart->huart); // 清除溢出错误标志 } if (err != UART_NO_ERROR && uart->rx_error != NULL) { uint16_t index = uart->rx_index - 1; uart->rx_error[uart->rx_error_count].index = index; uart->rx_error[uart->rx_error_count].err = err; uart->rx_error_count++; } } } /** * @brief 用于处理串口DMA接收中断的回调函数 * @param {uart_t} *uart - 串口对象 * @return {*} 无 * @note: */ void uart_dma_reception_callback(uart_t *uart) { // 检查输入参数是否为空 DBG_ASSERT(uart != NULL __DBG_LINE); uart->tx_dma_ok = TRUE; // 禁用串口DMA的发送通道 LL_DMA_DisableChannel(uart->dma, uart->dma_tx_channel); // 清除发送中断标志位 DMA_CLEAR_FLAG_TC_CHANNEL(uart->dma, uart->dma_tx_channel); // 使能发送中断,用于关闭发送使能引脚 LL_USART_EnableIT_TC(uart->huart); // 使能发送中断,用于关闭发送使能引脚 // 清除传输错误标志 DMA_CLEAR_FLAG_TE_CHANNEL(uart->dma, uart->dma_tx_channel); }