639 lines
16 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.

/**
* @file uart.c
* @author armfly
* @author Myth
* @version 0.1
* @date 2021.10.11
* @brief stm32f103x6 串口 FIFO 库
* @note 请勿更改此文件内容
*/
#include "stdio.h"
#include "systick.h"
#include "uart.h"
COM_PORT_E printf_Com = COM1;
COM_PORT_E getchar_Com = COM1;
// USART1 PA9 PA10
#define USART1_CLK_ENABLE() __HAL_RCC_USART1_CLK_ENABLE()
#define USART1_TX_GPIO_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE()
#define USART1_TX_GPIO_PORT GPIOA
#define USART1_TX_PIN GPIO_PIN_9
#define USART1_RX_GPIO_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE()
#define USART1_RX_GPIO_PORT GPIOA
#define USART1_RX_PIN GPIO_PIN_10
// USART2 PA2 PA3
#define USART2_CLK_ENABLE() __HAL_RCC_USART2_CLK_ENABLE()
#define USART2_TX_GPIO_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE()
#define USART2_TX_GPIO_PORT GPIOA
#define USART2_TX_PIN GPIO_PIN_2
#define USART2_RX_GPIO_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE()
#define USART2_RX_GPIO_PORT GPIOA
#define USART2_RX_PIN GPIO_PIN_3
//定义每个串口结构体变量
#if UART1_FIFO_EN == 1
static UART_T g_tUART1;
static uint8_t g_TxBuf1[UART1_TX_BUF_SIZE]; //发送缓冲区
static uint8_t g_RxBuf1[UART1_RX_BUF_SIZE]; //接收缓冲区
#endif
#if UART2_FIFO_EN == 1
static UART_T g_tUART2;
static uint8_t g_TxBuf2[UART2_TX_BUF_SIZE]; //发送缓冲区
static uint8_t g_RxBuf2[UART2_RX_BUF_SIZE]; //接收缓冲区
#endif
static void UART_Var_Init(void);
static void UART_Hard_Init(void);
static void UART_T_Send(UART_T *_pUART, uint8_t *_ucaBuf, uint16_t _usLen);
static uint8_t UART_T_GetChar(UART_T *_pUART, uint8_t *_pByte);
static void UART_T_IRQ(UART_T *_pUART);
/**
* @brief 初始化串口
*/
void UART_Init(void)
{
UART_Var_Init(); //初始化全局变量
UART_Hard_Init(); //配置串口的硬件参数
}
/**
* @brief 将 COM 端口号转换为 UART 指针
* @param _ucPort: 端口号 (COM1-2)
* @retval UART 指针
*/
UART_T *ComToUART(COM_PORT_E _ucPort)
{
if (_ucPort == COM1)
{
#if UART1_FIFO_EN == 1
return &g_tUART1;
#else
return 0;
#endif
}
else if (_ucPort == COM2)
{
#if UART2_FIFO_EN == 1
return &g_tUART2;
#else
return 0;
#endif
}
else
{
return 0;
}
}
/**
* @brief 将 COM 端口号转换为 USART_TypeDef*
* @param _ucPort: 端口号 (COM1-2)
* @retval USART_TypeDef*
*/
USART_TypeDef *ComToUSARTx(COM_PORT_E _ucPort)
{
if (_ucPort == COM1)
{
#if UART1_FIFO_EN == 1
return USART1;
#else
return 0;
#endif
}
else if (_ucPort == COM2)
{
#if UART2_FIFO_EN == 1
return USART2;
#else
return 0;
#endif
}
else
{
return 0;
}
}
/**
* @brief 向串口发送一组数据。数据放到发送缓冲区后立即返回,由中断服务程序在后台完成发送
* @param _ucPort: 端口号 (COM1-2)
* @param _ucaBuf: 待发送的数据缓冲区
* @param _usLen : 数据长度
*/
void UART_SendBuff(COM_PORT_E _ucPort, uint8_t *_ucaBuf, uint16_t _usLen)
{
UART_T *pUART;
pUART = ComToUART(_ucPort);
if (pUART == 0)
{
return;
}
UART_T_Send(pUART, _ucaBuf, _usLen);
}
/**
* @brief 向串口发送 1 个字节。数据放到发送缓冲区后立即返回,由中断服务程序在后台完成发送
* @param _ucPort: 端口号 (COM1-2)
* @param _ucByte: 待发送的数据
*/
void UART_SendChar(COM_PORT_E _ucPort, uint8_t _ucByte)
{
UART_SendBuff(_ucPort, &_ucByte, 1);
}
/**
* @brief 从接收缓冲区读取 1 字节,非阻塞。无论有无数据均立即返回
* @param _ucPort: 端口号 (COM1-2)
* @param _pByte: 接收到的数据指针
* @retval 0 表示无数据1 表示读取到有效字节
*/
uint8_t UART_GetChar(COM_PORT_E _ucPort, uint8_t *_pByte)
{
UART_T *pUART;
pUART = ComToUART(_ucPort);
if (pUART == 0)
{
return 0;
}
return UART_T_GetChar(pUART, _pByte);
}
/**
* @brief 从接收缓冲区读取到特定字节,阻塞,有超时
* @param _ucPort: 端口号 (COM1-2)
* @param _pBuf: 接收到的数据指针
* @param _endByte: 终止字节
* @param _timeout: 超时时间
* @retval 0 表示超时1 表示成功
*/
uint8_t UART_GetBuffUntil(COM_PORT_E _ucPort, uint8_t *_pBuf, uint8_t _endByte, uint16_t _timeout)
{
uint8_t ch;
int32_t start_t = SysTick_GetRunTime();
uint16_t i = 0;
while (1)
{
if (UART_GetChar(_ucPort, &ch) == 1)
{
if (ch == _endByte)
{
_pBuf[i] = '\0';
return 1;
}
_pBuf[i++] = ch;
}
else if (SysTick_GetRunTime() - start_t > _timeout)
{
return 0;
}
}
}
/**
* @brief 清零串口发送缓冲区
* @param _ucPort: 端口号 (COM1-2)
*/
void UART_ClearTxFIFO(COM_PORT_E _ucPort)
{
UART_T *pUART;
pUART = ComToUART(_ucPort);
if (pUART == 0)
{
return;
}
pUART->usTxWrite = 0;
pUART->usTxRead = 0;
pUART->usTxCount = 0;
}
/**
* @brief 清零串口接收缓冲区
* @param _ucPort: 端口号 (COM1-2)
*/
void UART_ClearRxFIFO(COM_PORT_E _ucPort)
{
UART_T *pUART;
pUART = ComToUART(_ucPort);
if (pUART == 0)
{
return;
}
pUART->usRxWrite = 0;
pUART->usRxRead = 0;
pUART->usRxCount = 0;
}
/**
* @brief 设置串口的波特率。无校验,收发都使能
* @param _ucPort: 端口号 (COM1-2)
* @param _BaudRate: 波特率
*/
void UART_SetBaud(COM_PORT_E _ucPort, uint32_t _BaudRate)
{
USART_TypeDef *USARTx;
USARTx = ComToUSARTx(_ucPort);
if (USARTx == 0)
{
return;
}
UART_SetUARTParam(USARTx, _BaudRate, UART_PARITY_NONE, UART_MODE_TX_RX);
}
/**
* @brief 设置串口接受中断处理函数
* @param _ucPort: 端口号 (COM1-2)
* @param Receive: 中断处理函数
*/
void UART_BindReceiveHandle(COM_PORT_E _ucPort, UARTReceiveHandler Receive)
{
UART_T *pUART;
pUART = ComToUART(_ucPort);
if (pUART == 0)
{
return;
}
pUART->Receive = Receive;
}
/**
* @brief 初始化串口相关的变量
*/
static void UART_Var_Init(void)
{
#if UART1_FIFO_EN == 1
g_tUART1.uart = USART1; //串口设备
g_tUART1.pTxBuf = g_TxBuf1; //发送缓冲区指针
g_tUART1.pRxBuf = g_RxBuf1; //接收缓冲区指针
g_tUART1.usTxBufSize = UART1_TX_BUF_SIZE; //发送缓冲区大小
g_tUART1.usRxBufSize = UART1_RX_BUF_SIZE; //接收缓冲区大小
g_tUART1.usTxWrite = 0; //发送 FIFO 写索引
g_tUART1.usTxRead = 0; //发送 FIFO 读索引
g_tUART1.usRxWrite = 0; //接收 FIFO 写索引
g_tUART1.usRxRead = 0; //接收 FIFO 读索引
g_tUART1.usRxCount = 0; //接收到的新数据个数
g_tUART1.usTxCount = 0; //待发送的数据个数
g_tUART1.Receive = 0; //接收到新数据后的回调函数
g_tUART1.Sending = 0; //正在发送中标志
#endif
#if UART2_FIFO_EN == 1
g_tUART2.uart = USART2; //串口设备
g_tUART2.pTxBuf = g_TxBuf2; //发送缓冲区指针
g_tUART2.pRxBuf = g_RxBuf2; //接收缓冲区指针
g_tUART2.usTxBufSize = UART2_TX_BUF_SIZE; //发送缓冲区大小
g_tUART2.usRxBufSize = UART2_RX_BUF_SIZE; //接收缓冲区大小
g_tUART2.usTxWrite = 0; //发送 FIFO 写索引
g_tUART2.usTxRead = 0; //发送 FIFO 读索引
g_tUART2.usRxWrite = 0; //接收 FIFO 写索引
g_tUART2.usRxRead = 0; //接收 FIFO 读索引
g_tUART2.usRxCount = 0; //接收到的新数据个数
g_tUART2.usTxCount = 0; //待发送的数据个数
g_tUART2.Receive = 0; //接收到新数据后的回调函数
g_tUART2.Sending = 0; //正在发送中标志
#endif
}
/**
* @brief 配置串口的硬件参数
* @param Instance: USART_TypeDef*
* @param BaudRate: 波特率
* @param Parity: 校验类型,奇校验或者偶校验
* @param Mode: 发送和接收模式使能
*/
void UART_SetUARTParam(USART_TypeDef *Instance, uint32_t BaudRate, uint32_t Parity, uint32_t Mode)
{
UART_HandleTypeDef UARTHandle;
UARTHandle.Instance = Instance;
UARTHandle.Init.BaudRate = BaudRate;
UARTHandle.Init.WordLength = UART_WORDLENGTH_8B;
UARTHandle.Init.StopBits = UART_STOPBITS_1;
UARTHandle.Init.Parity = Parity;
UARTHandle.Init.HwFlowCtl = UART_HWCONTROL_NONE;
UARTHandle.Init.Mode = Mode;
UARTHandle.Init.OverSampling = UART_OVERSAMPLING_16;
HAL_UART_Init(&UARTHandle);
}
/**
* @brief 配置串口的硬件参数
*/
static void UART_Hard_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
#if UART1_FIFO_EN == 1
USART1_TX_GPIO_CLK_ENABLE();
USART1_RX_GPIO_CLK_ENABLE();
USART1_CLK_ENABLE();
GPIO_InitStruct.Pin = USART1_TX_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(USART1_TX_GPIO_PORT, &GPIO_InitStruct);
GPIO_InitStruct.Pin = USART1_RX_PIN;
HAL_GPIO_Init(USART1_RX_GPIO_PORT, &GPIO_InitStruct);
//配置NVIC the NVIC for UART
HAL_NVIC_SetPriority(USART1_IRQn, 0, 1);
HAL_NVIC_EnableIRQ(USART1_IRQn);
//配置波特率、奇偶校验
UART_SetUARTParam(USART1, UART1_BAUD, UART_PARITY_NONE, UART_MODE_TX_RX);
CLEAR_BIT(USART1->SR, USART_SR_TC); //清除 TC 发送完成标志
CLEAR_BIT(USART1->SR, USART_SR_RXNE); //清除 RXNE 接收标志
SET_BIT(USART1->CR1, USART_CR1_RXNEIE); //使能 PE RX 接受中断
#endif
#if UART2_FIFO_EN == 1
USART2_TX_GPIO_CLK_ENABLE();
USART2_RX_GPIO_CLK_ENABLE();
USART2_CLK_ENABLE();
GPIO_InitStruct.Pin = USART2_TX_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(USART2_TX_GPIO_PORT, &GPIO_InitStruct);
GPIO_InitStruct.Pin = USART2_RX_PIN;
HAL_GPIO_Init(USART2_RX_GPIO_PORT, &GPIO_InitStruct);
HAL_NVIC_SetPriority(USART2_IRQn, 0, 2);
HAL_NVIC_EnableIRQ(USART2_IRQn);
UART_SetUARTParam(USART2, UART2_BAUD, UART_PARITY_NONE, UART_MODE_TX_RX);
CLEAR_BIT(USART2->SR, USART_SR_TC); //清除 TC 发送完成标志
CLEAR_BIT(USART2->SR, USART_SR_RXNE); //清除 RXNE 接收标志
SET_BIT(USART2->CR1, USART_CR1_RXNEIE); //使能 PE RX 接受中断
#endif
}
/**
* @brief 填写数据到 UART 发送缓冲区,并启动发送中断。中断处理函数发送完毕后,自动关闭发送中断
* @param _pUART
* @param _ucaBuf
* @param _usLen
*/
static void UART_T_Send(UART_T *_pUART, uint8_t *_ucaBuf, uint16_t _usLen)
{
uint16_t i;
for (i = 0; i < _usLen; i++)
{
//如果发送缓冲区已经满了,则等待缓冲区空
while (1)
{
__IO uint16_t usCount;
DISABLE_INT();
usCount = _pUART->usTxCount;
ENABLE_INT();
if (usCount < _pUART->usTxBufSize)
{
break;
}
else if (usCount == _pUART->usTxBufSize) //数据已填满缓冲区
{
if ((_pUART->uart->CR1 & USART_CR1_TXEIE) == 0)
{
SET_BIT(_pUART->uart->CR1, USART_CR1_TXEIE);
}
}
}
//将新数据填入发送缓冲区
_pUART->pTxBuf[_pUART->usTxWrite] = _ucaBuf[i];
DISABLE_INT();
if (++_pUART->usTxWrite >= _pUART->usTxBufSize)
{
_pUART->usTxWrite = 0;
}
_pUART->usTxCount++;
ENABLE_INT();
}
SET_BIT(_pUART->uart->CR1, USART_CR1_TXEIE); //使能发送中断(缓冲区空)
}
/**
* @brief 填写数据到 UART 发送缓冲区,并启动发送中断。中断处理函数发送完毕后,自动关闭发送中断
* @param _pUART
* @param _pByte
* @retval 成功返回 1失败返回 0
*/
static uint8_t UART_T_GetChar(UART_T *_pUART, uint8_t *_pByte)
{
uint16_t usCount;
DISABLE_INT();
usCount = _pUART->usRxCount;
ENABLE_INT();
//如果读和写索引相同,则返回 0
if (usCount == 0)
{
return 0;
}
else
{
*_pByte = _pUART->pRxBuf[_pUART->usRxRead]; //从串口接收 FIFO 取 1 个数据
//改写 FIFO 读索引
DISABLE_INT();
if (++_pUART->usRxRead >= _pUART->usRxBufSize)
{
_pUART->usRxRead = 0;
}
_pUART->usRxCount--;
ENABLE_INT();
return 1;
}
}
/**
* @brief 判断发送缓冲区是否为空
* @param _ucPort
* @retval 1 为空0 为不空
*/
uint8_t UART_IsTxEmpty(COM_PORT_E _ucPort)
{
UART_T *pUART;
uint8_t Sending;
pUART = ComToUART(_ucPort);
if (pUART == 0)
{
return 0;
}
Sending = pUART->Sending;
if (Sending != 0)
{
return 0;
}
return 1;
}
/**
* @brief 供中断服务程序调用,通用串口中断处理函数
* @param _pUART
*/
static void UART_T_IRQ(UART_T *_pUART)
{
uint32_t isrflags = READ_REG(_pUART->uart->SR);
uint32_t cr1its = READ_REG(_pUART->uart->CR1);
uint32_t cr3its = READ_REG(_pUART->uart->CR3);
//处理接收中断
if ((isrflags & USART_SR_RXNE) != RESET)
{
uint8_t ch;
//从串口接收数据寄存器读取数据存放到接收 FIFO
ch = READ_REG(_pUART->uart->DR);
_pUART->pRxBuf[_pUART->usRxWrite] = ch;
if (++_pUART->usRxWrite >= _pUART->usRxBufSize)
{
_pUART->usRxWrite = 0;
}
if (_pUART->usRxCount < _pUART->usRxBufSize)
{
_pUART->usRxCount++;
}
//回调函数
if (_pUART->Receive)
{
_pUART->Receive(ch);
}
}
//处理发送缓冲区空中断
if (((isrflags & USART_SR_TXE) != RESET) && (cr1its & USART_CR1_TXEIE) != RESET)
{
if (_pUART->usTxCount == 0)
{
//发送缓冲区的数据已取完时禁止发送缓冲区空中断注意此时最后1个数据还未真正发送完毕
CLEAR_BIT(_pUART->uart->CR1, USART_CR1_TXEIE);
//使能数据发送完毕中断
SET_BIT(_pUART->uart->CR1, USART_CR1_TCIE);
}
else
{
_pUART->Sending = 1;
//从发送 FIFO 取 1 个字节写入串口发送数据寄存器
_pUART->uart->DR = _pUART->pTxBuf[_pUART->usTxRead];
if (++_pUART->usTxRead >= _pUART->usTxBufSize)
{
_pUART->usTxRead = 0;
}
_pUART->usTxCount--;
}
}
//数据bit位全部发送完毕的中断
if (((isrflags & USART_SR_TC) != RESET) && ((cr1its & USART_CR1_TCIE) != RESET))
{
if (_pUART->usTxCount == 0)
{
//如果发送 FIFO 的数据全部发送完毕,禁止数据发送完毕中断
CLEAR_BIT(_pUART->uart->CR1, USART_CR1_TCIE);
_pUART->Sending = 0;
}
else
{
//正常情况下,不会进入此分支
//如果发送 FIFO 的数据还未完毕,则从发送 FIFO 取 1 个数据写入发送数据寄存器
_pUART->uart->DR = _pUART->pTxBuf[_pUART->usTxRead];
if (++_pUART->usTxRead >= _pUART->usTxBufSize)
{
_pUART->usTxRead = 0;
}
_pUART->usTxCount--;
}
}
}
#if UART1_FIFO_EN == 1
void USART1_IRQHandler(void)
{
UART_T_IRQ(&g_tUART1);
}
#endif
#if UART2_FIFO_EN == 1
void USART2_IRQHandler(void)
{
UART_T_IRQ(&g_tUART2);
}
#endif
void UART_SetprintfCom(COM_PORT_E _ucPort)
{
printf_Com = _ucPort;
}
void UART_SetgetcharCom(COM_PORT_E _ucPort)
{
getchar_Com = _ucPort;
}
//重定义 fputc 函数,用于 printf
int fputc(int ch, FILE *f)
{
UART_SendChar(printf_Com, ch);
return ch;
}
//重定义 fgetc 函数,用于 getchar
int fgetc(FILE *f)
{
uint8_t ucData;
while (UART_GetChar(getchar_Com, &ucData) == 0)
;
return ucData;
}