487 lines
15 KiB
C
487 lines
15 KiB
C
/*
|
||
* @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);
|
||
}
|