motor_f407/User/system/bsp/uarts.c

487 lines
15 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* @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_USART_DMA_REG_DATA_RECEIVE));
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_USART_DMA_REG_DATA_TRANSMIT));
// 配置内存地址
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, baudrate);
}
/**
* @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);
}