? ? ? ? 参考:STM32 FIFO RINGBUFF:https://blog.csdn.net/rou252051452/article/details/118607713
1.概述
? ? ? ? 本文实现如下功能:
- 通用串口驱动
- FIFO结合STM32的DMA实现串口数据的收发。
2.串口驱动相关结构体定义
串口参数结构体
typedef struct
{
USART_TypeDef* Uartx; //指向物理串口的结构体
UartIOCfg CfgIO; //串口收发管脚配置结构体
USART_InitTypeDef CfgUart; //串口参数结构体
NVIC_InitTypeDef CfgNvic; //串口中断结构体
UartCfgRx CfgRx; //串口接收DMA配置结构体
UartCfgTx CfgTx; //串口发送DMA配置结构体
}SerialTypeDef;
串口IO配置结构体
typedef struct
{
GPIO_TypeDef* TxPort; //发送端口
GPIO_TypeDef* RxPort; //接收端口
INT16U TxPin; //发送管脚
INT16U RxPin; //接收管脚
}UartIOCfg;
接收DMA配置结构体
typedef struct
{
DMA_Stream_TypeDef* DmaStream; //DMA流
INT32U DmaChannel; //通道
INT16U Used; //DMA使用量
FifoTypeDef* Fifo; //流对应使用的FIFO。
INT16U FifoSize; //接收fifo的大小。
NVIC_InitTypeDef CfgNvic; //DMA中断配置结构体
}UartCfgRx;
发送DMA配置结构体
typedef struct
{
DMA_Stream_TypeDef* DmaStream; //DMA流
INT32U DmaChannel; //通道
FifoTypeDef* Fifo; //流对应使用的FIFO。
INT16U FifoSize; //发送fifo的大小。
INT8U* Buff; //发送的buff
INT16U BuffSize; //发送buff的大小
NVIC_InitTypeDef CfgNvic;
}UartCfgTx;
3.串口驱动实现
3.1 static局部函数
3.1.1串口IO初始化
/**
***************************************************
* @brief bsp_uart_IOCfg
* @note 初始化IO为复用,USART或UART
* @param serial 指向串口参数
* @retval NONE
* @data 2021.07.01
* @auth WXL
* @his 1.0.0.0 2021.07.01 WXL
* create
***************************************************
**/
static void bsp_uart_IOCfg(SerialTypeDef* serial)
{
GPIO_InitTypeDef GPIO_InitStructure;
//IO初始化
RCC_AHB1PeriphClockCmd(1<<( (((INT32U)serial->CfgIO.TxPort)-AHB1PERIPH_BASE) / 0x0400 ),ENABLE);
GPIO_InitStructure.GPIO_Pin = (1 << serial->CfgIO.TxPin);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(serial->CfgIO.TxPort , &GPIO_InitStructure);
RCC_AHB1PeriphClockCmd(1<<( ( ((INT32U)serial->CfgIO.RxPort)-AHB1PERIPH_BASE) / 0x0400 ),ENABLE);
GPIO_InitStructure.GPIO_Pin = (1 << serial->CfgIO.RxPin);
GPIO_Init(serial->CfgIO.RxPort , &GPIO_InitStructure);
//IO复用
if(serial->Uartx == USART1 || serial->Uartx == USART2 || serial->Uartx == USART3)
{
GPIO_PinAFConfig(serial->CfgIO.TxPort , serial->CfgIO.TxPin , 7);
GPIO_PinAFConfig(serial->CfgIO.RxPort , serial->CfgIO.RxPin , 7);
}
if(serial->Uartx == UART4 || serial->Uartx == UART5 || serial->Uartx == USART6)
{
GPIO_PinAFConfig(serial->CfgIO.TxPort , serial->CfgIO.TxPin , 8);
GPIO_PinAFConfig(serial->CfgIO.RxPort , serial->CfgIO.RxPin , 8);
}
}
根据串口参数结构体的内容进行相关收发IO的初始化。注意一下几点:
????????????????STM32的标准库中RCC_AHB1PeriphClockCmd参数1可设置为RCC_AHB1Periph_GPIOA到RCC_AHB1Periph_GPIOK。对应内容如下
RCC_AHB1Periph_GPIOA =?1<<( (((INT32U)serial->CfgIO.TxPort)-AHB1PERIPH_BASE) / 0x0400;
RCC_AHB1Periph_GPIOx | 值 | GPIOx | 值 | RCC_AHB1Periph_GPIOA | 0x00000001 | GPIOA | (AHB1PERIPH_BASE + 0x0000) | RCC_AHB1Periph_GPIOB | 0x00000002 | GPIOB | (AHB1PERIPH_BASE + 0x0400) | RCC_AHB1Periph_GPIOC | 0x00000004 | GPIOC | (AHB1PERIPH_BASE + 0x0800) | RCC_AHB1Periph_GPIOD | 0x00000008 | GPIOD | (AHB1PERIPH_BASE + 0x0C00) | RCC_AHB1Periph_GPIOE | 0x00000010 | GPIOE | (AHB1PERIPH_BASE + 0x1000) | RCC_AHB1Periph_GPIOF | 0x00000020 | GPIOF | (AHB1PERIPH_BASE + 0x1400) | RCC_AHB1Periph_GPIOG | 0x00000040 | GPIOG | (AHB1PERIPH_BASE + 0x1800) | RCC_AHB1Periph_GPIOH | 0x00000080 | GPIOH | (AHB1PERIPH_BASE + 0x1C00) | RCC_AHB1Periph_GPIOI | 0x00000100 | GPIOI | (AHB1PERIPH_BASE + 0x2000) | RCC_AHB1Periph_GPIOJ | 0x00000200 | GPIOJ | (AHB1PERIPH_BASE + 0x2400) | RCC_AHB1Periph_GPIOK | 0x00000400 | GPIOK | (AHB1PERIPH_BASE + 0x2800) |
? ? ? ? ? ? ? ? USART1,USART2,USART3的收发管脚均复用为功能7。UART4,UART5,USART6管脚复用功能为8需要注意。
3.1.2串口参数初始化
/**
***************************************************
* @brief bsp_usart_UartInit
* @note 串口参数初始化
* @param serial 指向串口参数
* @retval NONE
* @data 2021.07.01
* @auth WXL
* @his 1.0.0.0 2021.07.01 WXL
* create
***************************************************
**/
static void bsp_usart_UartInit(SerialTypeDef* serial)
{
//使能时钟。
if(serial->Uartx == USART1)
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
else if(serial->Uartx == USART2)
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);
else if(serial->Uartx == USART3)
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3,ENABLE);
else if(serial->Uartx == UART4)
RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART4,ENABLE);
else if(serial->Uartx == UART5)
RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART5,ENABLE);
else if(serial->Uartx == USART6)
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART6,ENABLE);
USART_Init(serial->Uartx, &serial->CfgUart);
USART_Cmd(serial->Uartx, ENABLE);
}
根据串口参数结构体中的物理串口进行相应的时钟使能,参数配置。
3.1.3中断初始化
/**
***************************************************
* @brief bsp_usart_NVICCfg
* @note 串口中断初始化
* @param serial 指向串口参数
* @retval NONE
* @data 2021.07.01
* @auth WXL
* @his 1.0.0.0 2021.07.01 WXL
* create
***************************************************
**/
static void bsp_usart_NVICCfg(SerialTypeDef* serial)
{
USART_ITConfig(serial->Uartx, USART_IT_IDLE, ENABLE);
NVIC_Init(&serial->CfgNvic);
USART_DMACmd(serial->Uartx,USART_DMAReq_Rx,ENABLE);
USART_DMACmd(serial->Uartx,USART_DMAReq_Tx,ENABLE);
}
3.1.4中断初始化
/**
***************************************************
* @brief bsp_usart_DMACfg
* @note 串口DMA配置
* @param serial 指向串口参数
* @retval 0 成功
* 1 申请内存失败
* @data 2021.07.01
* @auth WXL
* @his 1.0.0.0 2021.07.01 WXL
* create
***************************************************
**/
static INT8U bsp_usart_DMACfg(SerialTypeDef* serial)
{
DMA_InitTypeDef DMA_InitStructure;
//创建接收FIFO
serial->CfgRx.Fifo = func_fifo_Create(serial->CfgRx.FifoSize);
if(serial->CfgRx.Fifo == NULL)
return 1;
serial->CfgRx.Used = 0;
//创建发送FIFO
serial->CfgTx.Fifo = func_fifo_Create(serial->CfgTx.FifoSize);
if(serial->CfgTx.Fifo == NULL)
{
func_fifo_Free(serial->CfgRx.Fifo);
return 1;
}
//创建发送BUFF
serial->CfgTx.Buff = malloc(serial->CfgTx.BuffSize);
if(serial->CfgTx.Buff == NULL)
{
func_fifo_Free(serial->CfgRx.Fifo);
func_fifo_Free(serial->CfgTx.Fifo);
return 1;
}
//接收 DMA配置
DMA_Stream_TypeDef* stream = serial->CfgRx.DmaStream;
INT32U channle = serial->CfgRx.DmaChannel;
if(stream < DMA2_Stream0)
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1,ENABLE);
else
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE);
DMA_DeInit(stream);
while(DMA_GetCmdStatus(stream) != DISABLE)
{}
DMA_InitStructure.DMA_Channel = channle;
DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&(serial->Uartx->DR);
DMA_InitStructure.DMA_Memory0BaseAddr = (u32)serial->CfgRx.Fifo->Buf;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
DMA_InitStructure.DMA_BufferSize = serial->CfgRx.FifoSize;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
DMA_Init(stream, &DMA_InitStructure);
DMA_Cmd(stream, ENABLE);
NVIC_Init(&serial->CfgRx.CfgNvic);
DMA_Cmd(stream, ENABLE);
DMA_ITConfig(stream, DMA_IT_TC, ENABLE);
//发送
stream = serial->CfgTx.DmaStream;
channle = serial->CfgTx.DmaChannel;
if(stream < DMA2_Stream0)
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1,ENABLE);
else
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE);
DMA_DeInit(stream);
while(DMA_GetCmdStatus(stream) != DISABLE)
{}
DMA_InitStructure.DMA_Channel = channle;
DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&(serial->Uartx->DR);
DMA_InitStructure.DMA_Memory0BaseAddr = (u32)serial->CfgTx.Buff;
DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;
DMA_InitStructure.DMA_BufferSize = 0;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
DMA_Init(stream, &DMA_InitStructure);
DMA_Cmd(stream, ENABLE);
NVIC_Init(&serial->CfgTx.CfgNvic);
DMA_Cmd(stream, ENABLE);
DMA_ITConfig(stream, DMA_IT_TC, ENABLE);
return 0;
}
串口DMA初始化需要完成如下内容:
- FIFO申请:申请用于DMA接收的FIFO,申请用于DMA发送的数据缓存FIFO
- BUFF申请:申请用于DMA发送时使用的BUFF
- 接收DMA配置为循环模式,地址为FIFO中的BUFF地址,使DMA完成自动循环写入FIFO中。在DMA中断中进行FIFO无数据写入,实现数据的FIFO接收。
- 发送DMA配置,配置发送地址为上面申请的BUFF地址,发送时将发送FIFO中的内容读取到BUFF中进行DMA发送。
- DMA中断配置,使能DMA完成中断。
3.1.5接收数据回调函数
/**
***************************************************
* @brief bsp_uart_rx_callback
* @note IDLE中断和DMA完成中断回调函数。
* @param com 端口。
* par 1 DMA完成中断
* 0 IDLE中断。
* @retval NONE
* @data 2021.01.20
* @auth WXL
* @his 1.0.0.1 2021.01.20 WXL
* 新建文件
***************************************************
**/
static void bsp_uart_rx_callback(SerialTypeDef* serial,INT8U par)
{
INT16U count = 0;
INT16U used = 0;
//DMA完成中断调用。
if(par == 1)
{
count = serial->CfgRx.FifoSize - serial->CfgRx.Used;//计算本次接收元素个数。
serial->CfgRx.Used = 0; //DMA为Circular模式,中断中需要清零该变量。
}
//IDLE中断调用。
else
{
//计算本次接收元素个数。
used = serial->CfgRx.FifoSize - DMA_GetCurrDataCounter(serial->CfgRx.DmaStream);
count = used - serial->CfgRx.Used;
serial->CfgRx.Used = used;
}
//更新接收FIFO的指针。
func_fifo_PushNoData(serial->CfgRx.Fifo, count);
}
? ? ? ? 在接收DMA完成中断和串口IDLE中断中调用。计算写入量,将FIFO进行相应操作。以上函数均为局部函数,不对外开放。
3.2全局函数
3.2.1设置串口配置参数为默认
/**
***************************************************
* @brief bsp_uart_ParaSetDefault
* @note 设置串口参数未默认IDLE中断和DMA完成中断回调函数。
* @param serial 指向设置的串口
* @retval NONE
* @data 2021.01.20
* @auth WXL
* @his 1.0.0.1 2021.01.20 WXL
* 新建文件
***************************************************
**/
void bsp_uart_ParaSetDefault(SerialTypeDef* serial)
{
serial->Uartx = USART1;
serial->CfgIO.RxPin = 10;
serial->CfgIO.RxPort = GPIOA;
serial->CfgIO.TxPin = 9;
serial->CfgIO.TxPort = GPIOA;
serial->CfgUart.USART_BaudRate = 115200;
serial->CfgUart.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
serial->CfgUart.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
serial->CfgUart.USART_Parity = USART_Parity_No;
serial->CfgUart.USART_StopBits = USART_StopBits_1;
serial->CfgUart.USART_WordLength = USART_WordLength_8b;
serial->CfgNvic.NVIC_IRQChannel = USART1_IRQn;
serial->CfgNvic.NVIC_IRQChannelPreemptionPriority = 1;
serial->CfgNvic.NVIC_IRQChannelSubPriority = 1;
serial->CfgNvic.NVIC_IRQChannelCmd = ENABLE;
serial->CfgRx.DmaChannel = DMA_Channel_4;
serial->CfgRx.DmaStream = DMA2_Stream5;
serial->CfgRx.FifoSize = 200;
serial->CfgRx.CfgNvic.NVIC_IRQChannel = DMA2_Stream5_IRQn;
serial->CfgRx.CfgNvic.NVIC_IRQChannelPreemptionPriority = 1;
serial->CfgRx.CfgNvic.NVIC_IRQChannelSubPriority = 1;
serial->CfgRx.CfgNvic.NVIC_IRQChannelCmd = ENABLE;
serial->CfgTx.DmaChannel = DMA_Channel_4;
serial->CfgTx.DmaStream = DMA2_Stream7;
serial->CfgTx.FifoSize = 200;
serial->CfgTx.BuffSize = 100;
serial->CfgTx.CfgNvic.NVIC_IRQChannel = DMA2_Stream7_IRQn;
serial->CfgTx.CfgNvic.NVIC_IRQChannelPreemptionPriority = 1;
serial->CfgTx.CfgNvic.NVIC_IRQChannelSubPriority = 1;
serial->CfgTx.CfgNvic.NVIC_IRQChannelCmd = ENABLE;
}
3.2.2串口初始化
/**
***************************************************
* @brief bsp_uart_init
* @note Serial初始化,完成端口IO,中断及DMA的初始化工作
* @param serial 指向串口参数
* @retval 0 成功
* 1 申请内存失败
* @data 2021.07.01
* @auth WXL
* @his 1.0.0.0 2021.07.01 WXL
* create
***************************************************
**/
INT8U bsp_uart_init(SerialTypeDef* serial)
{
if(bsp_usart_DMACfg(serial) == 1)
return 1;
//IO初始化
bsp_uart_IOCfg(serial);
//串口参数初始化
bsp_usart_UartInit(serial);
//中断配置初始化
bsp_usart_NVICCfg(serial);
return 0;
}
3.2.3串口发送函数
/**
***************************************************
* @brief bsp_uart_Transmittx
* @note 串口发送数据
* @param serial 指向串口参数
* @retval 0 成功
* 1 DMA繁忙,未发送。
* @data 2021.07.01
* @auth WXL
* @his 1.0.0.0 2021.07.01 WXL
* create
***************************************************
**/
INT8U bsp_uart_Transmit(SerialTypeDef* Uartx)
{
INT8U count = 0;
//DMA发送繁忙,不进行操作。
if(DMA_GetCurrDataCounter(Uartx->CfgTx.DmaStream)!= 0)
return 1;
if(Uartx->CfgTx.Fifo->CntUsed == 0)
return 0;
if(Uartx->CfgTx.Fifo->CntUsed > Uartx->CfgTx.BuffSize)
{
count = Uartx->CfgTx.BuffSize;
}
else
count = Uartx->CfgTx.Fifo->CntUsed;
//将数据从FIFO中拷贝到DMA的发送缓冲区,重启DMA。
func_fifo_Pull(Uartx->CfgTx.Fifo, count, Uartx->CfgTx.Buff);
DMA_Cmd(Uartx->CfgTx.DmaStream,DISABLE);
while (DMA_GetCmdStatus(Uartx->CfgTx.DmaStream) != DISABLE)
{}
DMA_SetCurrDataCounter(Uartx->CfgTx.DmaStream,count);
DMA_Cmd(Uartx->CfgTx.DmaStream, ENABLE);
return 0;
}
3.3中断函数
3.3.1DMA发送中断
/**
***************************************************
* @brief DMA2_Stream7_IRQHandler
* @note DMA2_Stream7的中断函数。 USART1_TX
* @param NONE
* @retval NONE
* @data 2021.01.20
* @auth WXL
* @his 1.0.0.1 2021.01.20 WXL
* 新建文件
***************************************************
**/
void DMA2_Stream7_IRQHandler()
{
if(DMA_GetITStatus(DMA2_Stream7, DMA_IT_TCIF7))
{
DMA_ClearFlag(DMA2_Stream5,DMA_FLAG_FEIF7 | DMA_FLAG_DMEIF7 | DMA_FLAG_TEIF7 | DMA_FLAG_HTIF7 | DMA_FLAG_TCIF7);
bsp_uart_Transmit(&Serial1);
}
}
3.3.2DMA接收中断
/**
***************************************************
* @brief DMA2_Stream5_IRQHandler
* @note DMA2_Stream5的中断函数。 USART1_RX
* @param NONE
* @retval NONE
* @data 2021.01.20
* @auth WXL
* @his 1.0.0.1 2021.01.20 WXL
* 新建文件
***************************************************
**/
void DMA2_Stream5_IRQHandler(void)
{
if(DMA_GetITStatus(DMA2_Stream5,DMA_IT_TCIF5))
{
DMA_ClearFlag(DMA2_Stream5,DMA_FLAG_FEIF5 | DMA_FLAG_DMEIF5 | DMA_FLAG_TEIF5 | DMA_FLAG_HTIF5 | DMA_FLAG_TCIF5);
bsp_uart_rx_callback(&Serial1,1);
}
}
3.3.3串口中断
/**
***************************************************
* @brief USART1_IRQHandler
* @note 串口1中断函数,用于处理接收完成
* @param NONE
* @retval NONE
* @data 2021.01.20
* @auth WXL
* @his 1.0.0.1 2021.01.20 WXL
* 新建文件
***************************************************
**/
void USART1_IRQHandler(void)
{
INT16U data = 0;
if(USART_GetITStatus(USART1,USART_IT_IDLE) != RESET)
{
//清除IDLE标志位
data = USART1->SR;
data = USART1->DR;
bsp_uart_rx_callback(&Serial1,0);
}
}
|