之前有过一篇文章,不过那个只支持1个串口,而stm32f103大容量有5个串口,写这一篇的目的正是为了支持所有串口,可通过宏定义USE_USARTx进行设置使用串口的情况。关于基础部分,主要是原理的讲解可参考:https://blog.csdn.net/qq_40831436/article/details/115071656?spm=1001.2014.3001.5501
串口初始化讲解
初始化说明:5个串口并非所有串口都支持DMA传输,由下表所知,只有串口1-4具备DMA,而串口5无,所以,本程序中,串口1-4接收数据使用DMA传输,而串口5使用接收字节中断。
以下以串口1初始化为例:通过宏定义USE_USART1条件编译串口1初始化
#if USE_USART1
/*串口x使用到的全局变量初始化*/
USARTx_Var_Init(&U1RxBuff,&g_U1RxFra);
/**打开串口时钟**/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
//配置接收发送引脚
USARTx_AFPP_GPIO_Config(USART1TX_GPIO_CLK,USART1TX_GPIO_PORT,USART1TX_GPIO_Pin);
USARTx_FloatIN_GPIO_Config(USART1RX_GPIO_CLK,USART1RX_GPIO_PORT,USART1RX_GPIO_Pin);
//串口工作模式配置
USARTx_WorkMode_Config(USART1,115200,USART_WordLength_8b,USART_StopBits_1,USART_Parity_No);
//串口中断配置
USARTx_NVIC_Config(USART1_IRQn,1,1);
// 配置串口空闲中断
USART_ClearITPendingBit(USART1,USART_IT_IDLE);
USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);
// 使能串口
USART_Cmd(USART1, ENABLE);
//串口接收DMA配置
USARTx_RxDMA_Config(USART1,DMA1_Channel5,RCC_AHBPeriph_DMA1,(USART1_BASE+0x04),(uint32_t)U1RxBuff.rxarr,UxRXBUFFSIZE,DMA_Priority_Low);
#endif
通过初始化之后,串口1接收缓冲区为:U1RxBuff.rxarr,即通过USARTx_RxDMA_Config函数进行配置。
而U1RxBuff定义如下:
#if USE_USART1
_USARTxRXBUFF U1RxBuff; //定义串口接收缓冲区
_FRAMEATTRI g_U1RxFra; //定义帧记录指针缓冲区,非真指针
#endif
同样是通过条件编译,每个串口具备独立的串口接收缓冲区,互不干涉。
串口接收讲解
接收具备两种情况,一种是USART1-4,DMA接收,不用CPU干预,只需等待接收完成,进入空闲中断。而在空闲中断中的处理如下:
1、得到当前帧写入的结束地址 pRxBuff->wp;
2、将当前帧的起始地址 rp 赋值给帧记录队列 rpx进行记录
3、将当前帧的结束地址 wp 赋值给帧记录队列 wpx 进行记录
4、帧记录队列+1,等待前台处理数据
5、将 wp 赋值给 rp,一边记录下一个帧
/**************************************
*函 数 名: USARTxIDLE_IRQ
*功 能: 串口x空闲中断调用函数;仅串口1-4
*入口参数: pUSARTx--USARTx
DMAy_Channelx--串口通道
pRxBuff--串口接收缓冲区
pUxFra--串口接收帧记录缓冲区
*返 回 值: 无
**************************************/
static void USARTx_IDLE_IRQ(USART_TypeDef * pUSARTx,DMA_Channel_TypeDef* DMAy_Channelx,_USARTxRXBUFF *pRxBuff,_FRAMEATTRI *pUxRxFra)
{
uint16_t trnum=0;
//清除空闲中断标志位
pUSARTx->SR;
pUSARTx->DR;
//手册虽然说这个寄存器在DMA循环模式的时候,清0之后会自动恢复为最大接收缓冲区,但加入这一步以防万一
if(DMAy_Channelx->CNDTR == 0)
{
trnum = UxRXBUFFSIZE;
}
else
{
trnum = DMAy_Channelx->CNDTR&0xffff;
}
pRxBuff->wp = UxRXBUFFSIZE-trnum; //得到最新帧的结束地址
pUxRxFra->fraddr[pUxRxFra->nextfra].rpx = pRxBuff->rp; //最新帧的起始地址
pUxRxFra->fraddr[pUxRxFra->nextfra].wpx = pRxBuff->wp; //最新帧的结束地址
pUxRxFra->nextfra = (pUxRxFra->nextfra+1)%FRADDRMAX; //g_Fra.nextfra的值被限制再0,1....(FRADDRMAX-1)
pRxBuff->rp = pRxBuff->wp; //最新帧的起始与结束地址记录完,等待下一次记录
}
之后再空闲中断中调用 USARTxIDLE_IRQ()–调用–>USARTx_IDLE_IRQ()
/**************************************
*函 数 名: USARTxIDLE_IRQ
*功 能: 串口x空闲中断调用函数;串口1-5
*入口参数: Com_USARTx--定义好的串口通道
*返 回 值: 无
*说 明:串口5除了这个之外还要调用串口接收字节处理
**************************************/
void USARTxIDLE_IRQ(uint8_t Com_USARTx)
{
switch(Com_USARTx)
{
#if USE_USART1
case Com_USART1:
USARTx_IDLE_IRQ(USART1,USART1_RxDMA_CHANNEL,&U1RxBuff,&g_U1RxFra);
break;
#endif
#if USE_USART2
case Com_USART2:
USARTx_IDLE_IRQ(USART2,USART2_RxDMA_CHANNEL,&U2RxBuff,&g_U2RxFra);
break;
#endif
#if USE_USART3
case Com_USART3:
USARTx_IDLE_IRQ(USART3,USART3_RxDMA_CHANNEL,&U3RxBuff,&g_U3RxFra);
break;
#endif
#if USE_USART4
case Com_USART4:
USARTx_IDLE_IRQ(UART4,USART4_RxDMA_CHANNEL,&U4RxBuff,&g_U4RxFra);
break;
#endif
#if USE_USART5
case Com_USART5:
UART5_IDLE_IRQ(&U5RxBuff,&g_U5RxFra);
break;
#endif
default:
break;
}
}
接收的第二种情况是无DMA接收,需要CPU进行干预,则通过利用串口接收中断:
串口接收字节数据处理如下:
/**************************************
*函 数 名: USART5_RXNE_IRQ
*功 能: 串口5接收一个字节
*入口参数: pRxBuff--串口接收缓冲区
*返 回 值: 无
*说 明:
**************************************/
static void USART5_RXNE_IRQ(_USARTxRXBUFF *pRxBuff)
{
pRxBuff->rxarr[pRxBuff->wp] = USART_ReceiveData(UART5);
pRxBuff->wp = (pRxBuff->wp+1)%UxRXBUFFSIZE; //pRxBuff->wp的值被限制再0,1....(UxRXBUFFSIZE-1)
}
在串口接收中断中通过调用 USART5RXNE_IRQ()–调用–>USART5_RXNE_IRQ()接收数据。
/**************************************
*函 数 名: USART5RXNE_IRQ
*功 能: 串口5接收字节中断调用函数;串口5专用
*入口参数: 无
*返 回 值: 无
*说 明: 在接收字节中断服务函数中调用
**************************************/
void USART5RXNE_IRQ(void)
{
USART_ClearITPendingBit(UART5,USART_IT_RXNE);
USART5_RXNE_IRQ(&U5RxBuff);
}
对于无DMA的空闲中断中调用的函数处理如下:
/**************************************
*函 数 名: UART5_IDLE_IRQ
*功 能: 串口5空闲中断处理
*入口参数: pRxBuff--串口接收缓冲区
pUxFra--串口接收帧记录缓冲区
*返 回 值: 无
*说 明:
**************************************/
static void UART5_IDLE_IRQ(_USARTxRXBUFF *pRxBuff,_FRAMEATTRI *pUxRxFra)
{
UART5->SR;
UART5->DR;
pUxRxFra->fraddr[pUxRxFra->nextfra].rpx = pRxBuff->rp; //最新帧的起始地址
pUxRxFra->fraddr[pUxRxFra->nextfra].wpx = pRxBuff->wp; //最新帧的结束地址
pUxRxFra->nextfra = (pUxRxFra->nextfra+1)%FRADDRMAX; //g_Fra.nextfra的值被限制再0,1....(FRADDRMAX-1)
pRxBuff->rp = pRxBuff->wp; //最新帧的起始与结束地址记录完,等待下一次记录
}
在中断中调用情况如下:
// 串口中断服务函数
#if USE_USART1
void USART1_IRQHandler(void)
{
if(USART_GetITStatus(USART1,USART_IT_IDLE)!=RESET)
{
USARTxIDLE_IRQ(Com_USART1);
}
}
#endif
// 串口中断服务函数
#if USE_USART2
void USART2_IRQHandler(void)
{
if(USART_GetITStatus(USART2,USART_IT_IDLE)!=RESET)
{
USARTxIDLE_IRQ(Com_USART2);
}
}
#endif
// 串口中断服务函数
#if USE_USART3
void USART3_IRQHandler(void)
{
if(USART_GetITStatus(USART3,USART_IT_IDLE)!=RESET)
{
USARTxIDLE_IRQ(Com_USART3);
}
}
#endif
// 串口中断服务函数
#if USE_USART4
void UART4_IRQHandler(void)
{
if(USART_GetITStatus(UART4,USART_IT_IDLE)!=RESET)
{
USARTxIDLE_IRQ(Com_USART4);
}
}
#endif
#if USE_USART5
// 串口中断服务函数
void UART5_IRQHandler(void)
{
if(USART_GetITStatus(UART5,USART_IT_RXNE)!=RESET)
{
USART5RXNE_IRQ();
}
if(USART_GetITStatus(UART5,USART_IT_IDLE)!=RESET)
{
USARTxIDLE_IRQ(Com_USART5);
}
}
#endif
数据读取讲解
数据读取可参考先前文章,先判断是否有新的数据帧产生,即通过 currfra与 nextfra两个变量进行记录,其中nextfra变量在串口空闲中断中会+1,而当当前帧数据被应用层取出时:currfra+1。当currfra == nextfra时,说明所有的数据帧都已读取完毕。
从串口接收缓冲区中读取当前处理帧数据主要分为两种情况
一种是rpx<wpx,直接读取即可;
一种是wpx<rpx,说明接收时发生了接收缓冲区队列翻转情况,需要先读取:第一部分为rpx-(UxRXBUFFSIZE-1)的数据,第二部分为0-wpx的数据。
/**************************************
*函 数 名: GetAFraFromUxRxBuff
*功 能: 获取一帧数据
*入口参数: pbuff--获取一帧数据的数组
psize--获取的数目
pRxBuff--串口x缓冲区
pUxFra--串口x帧记录
*返 回 值: rtflg--0代表没有获取数据,1代表获取到数据
*说 明: 本函数没有进行地址超出判断,一定要确保你的数据帧的空间小于你定下的 pbuff 的空间
**************************************/
static uint8_t GetAFraFromUxRxBuff(uint8_t *pbuff,uint8_t *psize,_USARTxRXBUFF *pRxBuff,_FRAMEATTRI *pUxFra)
{
uint8_t rtflg=0; //返回值
uint16_t fralen=0; //帧长度
if(pUxFra->currfra != pUxFra->nextfra) //如果为真,说明有未处理的帧
{
/*根据每帧的帧属性(起始与结束地址)在串口接收缓冲区主体中获取一帧数据*/
if(pUxFra->fraddr[pUxFra->currfra].rpx<pUxFra->fraddr[pUxFra->currfra].wpx)
{
//本帧写入的起始地址小于写入的结束地址,直接使用写入结束地址-写入起始地址,计算出本帧的长度
fralen = pUxFra->fraddr[pUxFra->currfra].wpx-pUxFra->fraddr[pUxFra->currfra].rpx;
for((*psize)=0;(*psize)<fralen;(*psize)++)
{
//从串口接收缓冲区主体中取出本帧的数据,索引为:pUxFra->fraddr[pUxFra->currfra].rpx+(*psize)
pbuff[(*psize)] = pRxBuff->rxarr[pUxFra->fraddr[pUxFra->currfra].rpx+(*psize)];
}
//数据取出之后,本帧的.rpx(本帧起始的写入地址)=本帧的.wpx(本帧写入的结束地址)
pUxFra->fraddr[pUxFra->currfra].rpx=pUxFra->fraddr[pUxFra->currfra].wpx;
}
else
{
//本帧写入的起始地址大于写入的结束地址,说明数据在缓冲区中进行了溢出翻转,分两部分读取
//第一部分为rpx-(UxRXBUFFSIZE-1)的数据,第二部分为0-wpx的数据
for((*psize)=0;pUxFra->fraddr[pUxFra->currfra].rpx<UxRXBUFFSIZE;pUxFra->fraddr[pUxFra->currfra].rpx++)
{
//读取第一部分数据
pbuff[(*psize)] = pRxBuff->rxarr[pUxFra->fraddr[pUxFra->currfra].rpx];
(*psize)++;
}
pUxFra->fraddr[pUxFra->currfra].rpx = 0;
//读取第二部分数据
while(pUxFra->fraddr[pUxFra->currfra].rpx<pUxFra->fraddr[pUxFra->currfra].wpx)
{
pbuff[(*psize)] = pRxBuff->rxarr[pUxFra->fraddr[pUxFra->currfra].rpx];
(*psize)++;
pUxFra->fraddr[pUxFra->currfra].rpx++;
}
}
//当前帧数据读取结束,currfra(当前处理的帧+1)----0..(FRADDRMAX-1)
pUxFra->currfra = (pUxFra->currfra+1)%FRADDRMAX;
//读取到数据,返回值赋1
rtflg = 1;
}
return rtflg;
}
而应用层实际调用函数为:GetAFraFromComx()–调用–>GetAFraFromUxRxBuff()
/**************************************
*函 数 名: GetAFraFromComx
*功 能: 从COM口获取一帧数据
*入口参数: Com_USARTx--定义好的COM口,Com_USART1..5
pbuff--获取一帧数据的数组
psize--获取的数目
*返 回 值: rtflg--0代表没有获取数据,1代表获取到数据
*说 明:
**************************************/
uint8_t GetAFraFromComx(uint8_t Com_USARTx,uint8_t *pbuff,uint8_t *psize)
{
uint8_t rtflg=0; //返回值
switch(Com_USARTx)
{
#if USE_USART1
case Com_USART1:
rtflg = GetAFraFromUxRxBuff(pbuff,psize,&U1RxBuff,&g_U1RxFra);
break;
#endif
#if USE_USART2
case Com_USART2:
rtflg = GetAFraFromUxRxBuff(pbuff,psize,&U2RxBuff,&g_U2RxFra);
break;
#endif
#if USE_USART3
case Com_USART3:
rtflg = GetAFraFromUxRxBuff(pbuff,psize,&U3RxBuff,&g_U3RxFra);
break;
#endif
#if USE_USART4
case Com_USART4:
rtflg = GetAFraFromUxRxBuff(pbuff,psize,&U4RxBuff,&g_U4RxFra);
break;
#endif
#if USE_USART5
case Com_USART5:
rtflg = GetAFraFromUxRxBuff(pbuff,psize,&U5RxBuff,&g_U5RxFra);
break;
#endif
default:
break;
}
return rtflg;
}
而判断是否有新的未处理的数据帧可通过以下函数,(一般在OS中才使用本函数),裸机可直接通过调用GetAFraFromComx()函数判断。
/**************************************
*函 数 名: JudgeUxFrameDataNum
*功 能: 判断是否有未处理的数据帧
*入口参数: pUxFra--串口x帧记录
*返 回 值: rtflg--0代表没有数据,1代表有数据需要处理
*说 明:
**************************************/
static uint8_t JudgeUxFrameDataNum(_FRAMEATTRI *pUxFra)
{
uint8_t rtflg=0;
if(pUxFra->currfra != pUxFra->nextfra) //如果为真,说明有未处理的帧
{
rtflg = 1;
}
else
{
rtflg = 0;
}
return rtflg;
}
/**************************************
*函 数 名: JudgeUxFraIsNull
*功 能: 判断当前COM口是否还有未处理的数据
*入口参数: Com_USARTx--定义好的COM口,Com_USART1..5
*返 回 值: rtflg--0代表没有数据,1代表有数据需要处理
*说 明:
**************************************/
uint8_t JudgeUxFraIsNull(uint8_t Com_USARTx)
{
uint8_t rtflg=0; //返回值
switch(Com_USARTx)
{
#if USE_USART1
case Com_USART1:
rtflg = JudgeUxFrameDataNum(&g_U1RxFra);
break;
#endif
#if USE_USART2
case Com_USART2:
rtflg = JudgeUxFrameDataNum(&g_U2RxFra);
break;
#endif
#if USE_USART3
case Com_USART3:
rtflg = JudgeUxFrameDataNum(&g_U3RxFra);
break;
#endif
#if USE_USART4
case Com_USART4:
rtflg = JudgeUxFrameDataNum(&g_U4RxFra);
break;
#endif
#if USE_USART5
case Com_USART5:
rtflg = JudgeUxFrameDataNum(&g_U5RxFra);
break;
#endif
default:
break;
}
return rtflg;
}
使用说明
简单使用本库只需知道一下宏定义与调用以下几个函数即可。
一、宏定义:
想要使用哪个串口只需将 USE_USARTx 宏定义为1即可
而要是使用中断优先级分组则将 NeedSet_NVICprio 宏定义为1,默认分组2
/*串口使用情况,置1代表使用此串口*/
#define USE_USART1 1
#define USE_USART2 1
#define USE_USART3 1
#define USE_USART4 1
#define USE_USART5 1
#define NeedSet_NVICprio 1 //1:需要设置中断优先级分组,默认分组2 0:不需要设置中断优先级分组
二、使用函数说明:
void USARTx_Config(void); //串口初始化配置
void USARTxIDLE_IRQ(uint8_t Com_USARTx); //串口空闲中断处理函数,空闲中断调用
void USART5RXNE_IRQ(void); //串口5接收字节中断处理,串口5专用,串口5接收字节中断调用
void Usart_SendArray( USART_TypeDef * pUSARTx, uint8_t *array, uint16_t num); //串口发送函数
uint8_t GetAFraFromComx(uint8_t Com_USARTx,uint8_t *pbuff,uint8_t *psize); //从COM1..5口获取一帧数据
uint8_t JudgeUxFraIsNull(uint8_t Com_USARTx); //判断COM1..5口是否还有未处理数据
完整代码
bsp_usart_dma.h
/*******************************************************
*设计:陈文德
*版本:V1.0
*版本:V1.1版本:5串口全部支持
*说明:通过设置USE_USART1..5,可以选择使用哪些串口
通过设置NeedSet_NVICprio可以选择是否进行中断优先级分组,默认分组2
*******************************************************/
#ifndef __USARTDMA_H
#define __USARTDMA_H
#include "stm32f10x.h"
#include <stdio.h>
/*串口使用情况,置1代表使用此串口*/
#define USE_USART1 0
#define USE_USART2 1
#define USE_USART3 0
#define USE_USART4 0
#define USE_USART5 0
#define NeedSet_NVICprio 1 //1:需要设置中断优先级分组,默认分组2 0:不需要设置中断优先级分组
/*给5个串口编号*/
#define Com_USART1 1
#define Com_USART2 2
#define Com_USART3 3
#define Com_USART4 4
#define Com_USART5 5
#ifdef USE_USART1
/*串口1发送引脚定义*/
#define USART1TX_GPIO_CLK RCC_APB2Periph_GPIOA
#define USART1TX_GPIO_PORT GPIOA
#define USART1TX_GPIO_Pin GPIO_Pin_9
/*串口1接收引脚定义*/
#define USART1RX_GPIO_CLK RCC_APB2Periph_GPIOA
#define USART1RX_GPIO_PORT GPIOA
#define USART1RX_GPIO_Pin GPIO_Pin_10
#define USART1_RxDMA_CHANNEL DMA1_Channel5
#endif
#ifdef USE_USART2
/*串口2发送引脚定义*/
#define USART2TX_GPIO_CLK RCC_APB2Periph_GPIOA
#define USART2TX_GPIO_PORT GPIOA
#define USART2TX_GPIO_Pin GPIO_Pin_2
/*串口2接收引脚定义*/
#define USART2RX_GPIO_CLK RCC_APB2Periph_GPIOA
#define USART2RX_GPIO_PORT GPIOA
#define USART2RX_GPIO_Pin GPIO_Pin_3
#define USART2_RxDMA_CHANNEL DMA1_Channel6
#endif
#ifdef USE_USART3
/*串口3发送引脚定义*/
#define USART3TX_GPIO_CLK RCC_APB2Periph_GPIOB
#define USART3TX_GPIO_PORT GPIOB
#define USART3TX_GPIO_Pin GPIO_Pin_10
/*串口3接收引脚定义*/
#define USART3RX_GPIO_CLK RCC_APB2Periph_GPIOB
#define USART3RX_GPIO_PORT GPIOB
#define USART3RX_GPIO_Pin GPIO_Pin_11
#define USART3_RxDMA_CHANNEL DMA1_Channel3
#endif
#ifdef USE_USART4
/*串口4发送引脚定义*/
#define USART4TX_GPIO_CLK RCC_APB2Periph_GPIOC
#define USART4TX_GPIO_PORT GPIOC
#define USART4TX_GPIO_Pin GPIO_Pin_10
/*串口4接收引脚定义*/
#define USART4RX_GPIO_CLK RCC_APB2Periph_GPIOC
#define USART4RX_GPIO_PORT GPIOC
#define USART4RX_GPIO_Pin GPIO_Pin_11
#define USART4_RxDMA_CHANNEL DMA2_Channel3
#endif
#ifdef USE_USART5
/*串口5发送引脚定义*/
#define USART5TX_GPIO_CLK RCC_APB2Periph_GPIOC
#define USART5TX_GPIO_PORT GPIOC
#define USART5TX_GPIO_Pin GPIO_Pin_12
/*串口5接收引脚定义*/
#define USART5RX_GPIO_CLK RCC_APB2Periph_GPIOD
#define USART5RX_GPIO_PORT GPIOD
#define USART5RX_GPIO_Pin GPIO_Pin_2
#endif
#define UxRXBUFFSIZE 512 //接收缓冲区的大小
/**串口接收缓冲区**/
typedef struct __USARTxRXBUFF
{
uint16_t wp; //当前接收帧在接收缓冲区所处的写地址
uint16_t rp; //当前接收帧在接收缓冲区所处的读地址
uint8_t rxarr[UxRXBUFFSIZE]; //接收缓冲区实体
}_USARTxRXBUFF;
/**帧地址结构体**/
typedef struct __FRAMEADDR
{
uint16_t wpx; //本帧写地址的索引
uint16_t rpx; //本帧读地址的索引
}_FRAMEADDR;
#define FRADDRMAX 10 //最多能记录的帧
/**帧属性结构体**/
typedef struct __FRAMEATTRI
{
_FRAMEADDR fraddr[FRADDRMAX]; //每帧的地址,即通过wpx记录wp, rpx记录rp。队列主体
uint8_t currfra; //当前处理帧。0-(FRADDRMAX-1)
uint8_t nextfra; //下一个帧。0-(FRADDRMAX-1)
}_FRAMEATTRI;
void USARTx_Config(void); //串口初始化配置
void USARTxIDLE_IRQ(uint8_t Com_USARTx); //串口空闲中断处理函数,空闲中断调用
void USART5RXNE_IRQ(void); //串口5接收字节中断处理,串口5专用,串口5接收字节中断调用
void Usart_SendArray( USART_TypeDef * pUSARTx, uint8_t *array, uint16_t num); //串口发送函数
uint8_t GetAFraFromComx(uint8_t Com_USARTx,uint8_t *pbuff,uint8_t *psize); //从COM1..5口获取一帧数据
uint8_t JudgeUxFraIsNull(uint8_t Com_USARTx); //判断COM1..5口是否还有未处理数据
#endif /* __USARTDMA_H */
bsp_usart_dma.c
/*******************************************************
*设计:陈文德
*版本:V1.0
*版本:V1.1版本:增加USART1..5
*******************************************************/
#include "bsp_usart_dma.h"
#if USE_USART1
_USARTxRXBUFF U1RxBuff; //定义串口接收缓冲区
_FRAMEATTRI g_U1RxFra; //定义帧记录指针缓冲区,非真指针
#endif
#if USE_USART2
_USARTxRXBUFF U2RxBuff; //定义串口接收缓冲区
_FRAMEATTRI g_U2RxFra; //定义帧记录指针缓冲区,非真指针
#endif
#if USE_USART3
_USARTxRXBUFF U3RxBuff; //定义串口接收缓冲区
_FRAMEATTRI g_U3RxFra; //定义帧记录指针缓冲区,非真指针
#endif
#if USE_USART4
_USARTxRXBUFF U4RxBuff; //定义串口接收缓冲区
_FRAMEATTRI g_U4RxFra; //定义帧记录指针缓冲区,非真指针
#endif
#if USE_USART5
_USARTxRXBUFF U5RxBuff; //定义串口接收缓冲区
_FRAMEATTRI g_U5RxFra; //定义帧记录指针缓冲区,非真指针
#endif
/**************************************
*函 数 名: USARTx_Var_Init
*功 能: 串口x相关全局变量初始化
*入口参数: pRxBuff--接收缓冲区
pUxFra--帧记录缓冲区
*返 回 值: 无
**************************************/
static void USARTx_Var_Init(_USARTxRXBUFF *pRxBuff,_FRAMEATTRI *pUxRxFra)
{
pRxBuff->rp = 0;
pRxBuff->wp = 0;
pUxRxFra->currfra = 0;
pUxRxFra->nextfra = 0;
}
/**************************************
*函 数 名: USART_NVIC_Config
*功 能: 串口中断配置
*入口参数: usartx_irqn--中断号
preeprio--抢占优先级
subprio--子优先级
*返 回 值: 无
**************************************/
static void USARTx_NVIC_Config(uint8_t usartx_irqn,uint8_t preeprio,uint8_t subprio)
{
NVIC_InitTypeDef NVIC_InitStructure;
/* 配置USART为中断源 */
NVIC_InitStructure.NVIC_IRQChannel = usartx_irqn;
/* 抢断优先级*/
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = preeprio;
/* 子优先级 */
NVIC_InitStructure.NVIC_IRQChannelSubPriority = subprio;
/* 使能中断 */
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
/* 初始化配置NVIC */
NVIC_Init(&NVIC_InitStructure);
}
/**************************************
*函 数 名: USARTx_AFPP_GPIO_Config
*功 能: 复用推挽输出引脚配置
*入口参数: RCC_APB2Periph--APB2时钟
GPIOx--端口
GPIO_Pinx--引脚
*返 回 值: 无
**************************************/
static void USARTx_AFPP_GPIO_Config(uint32_t RCC_APB2Periph,GPIO_TypeDef* GPIOx,uint16_t GPIO_Pinx)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph, ENABLE);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pinx;
GPIO_Init(GPIOx, &GPIO_InitStructure);
}
/**************************************
*函 数 名: USARTx_FloatIN_GPIO_Config
*功 能: 浮空输入引脚配置
*入口参数: RCC_APB2Periph--APB2时钟
GPIOx--端口
GPIO_Pinx--引脚
*返 回 值: 无
**************************************/
static void USARTx_FloatIN_GPIO_Config(uint32_t RCC_APB2Periph,GPIO_TypeDef* GPIOx,uint16_t GPIO_Pinx)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph, ENABLE);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pinx;
GPIO_Init(GPIOx, &GPIO_InitStructure);
}
/**************************************
*函 数 名: USARTx_WorkMode_Config
*功 能: 串口工作模式配置
*入口参数: USARTx--串口
BaudRate--波特率
WordLength--字长
StopBits--停止位
Parity--优先级
*返 回 值: 无
**************************************/
static void USARTx_WorkMode_Config(USART_TypeDef* USARTx,uint32_t BaudRate,uint16_t WordLength,uint16_t StopBits,uint16_t Parity)
{
USART_InitTypeDef USART_InitStructure;
// 配置串口的工作参数
// 配置波特率
USART_InitStructure.USART_BaudRate = BaudRate;
// 配置 针数据字长
USART_InitStructure.USART_WordLength = WordLength;
// 配置停止位
USART_InitStructure.USART_StopBits = StopBits;
// 配置校验位
USART_InitStructure.USART_Parity = Parity ;
// 配置硬件流控制
USART_InitStructure.USART_HardwareFlowControl =
USART_HardwareFlowControl_None;
// 配置工作模式,收发一起
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
// 完成串口的初始化配置
USART_Init(USARTx, &USART_InitStructure);
}
/**************************************
*函 数 名: USARTx_RxDMA_Config
*功 能: 串口x接收DMA配置
*入口参数: USARTx--串口
DMAy_Channelx--DMA通道
RCC_DMAx--时钟
PeriBsAddr--外设地址
MemBsAddr--存储器地址
buffsize--缓冲区大小
prio--DMA优先级
*返 回 值: 无
**************************************/
static void USARTx_RxDMA_Config(USART_TypeDef* USARTx,DMA_Channel_TypeDef* DMAy_Channelx,uint32_t RCC_DMAx,\
uint32_t PeriBsAddr,uint32_t MemBsAddr,uint32_t buffsize,uint32_t prio)
{
DMA_InitTypeDef DMA_InitStructure;
// 开启DMA时钟
RCC_AHBPeriphClockCmd(RCC_DMAx, ENABLE);
DMA_InitStructure.DMA_PeripheralBaseAddr = PeriBsAddr;
DMA_InitStructure.DMA_MemoryBaseAddr = MemBsAddr; //串口接收基地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = buffsize; //接收缓冲区的大小
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 = prio;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMAy_Channelx, &DMA_InitStructure);
// 使能DMA
DMA_Cmd (DMAy_Channelx,ENABLE);
USART_DMACmd(USARTx,USART_DMAReq_Rx,ENABLE);
}
/**************************************
*函 数 名: USARTx_Config
*功 能: 串口初始化
*入口参数: 无
*返 回 值: 无
*说 明: 根据USE_USARTx定义初始化串口
**************************************/
void USARTx_Config(void)
{
#if NeedSet_NVICprio
NVIC_SetPriorityGrouping(NVIC_PriorityGroup_2);
#endif
#if USE_USART1
/*串口x使用到的全局变量初始化*/
USARTx_Var_Init(&U1RxBuff,&g_U1RxFra);
/**打开串口时钟**/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
//配置接收发送引脚
USARTx_AFPP_GPIO_Config(USART1TX_GPIO_CLK,USART1TX_GPIO_PORT,USART1TX_GPIO_Pin);
USARTx_FloatIN_GPIO_Config(USART1RX_GPIO_CLK,USART1RX_GPIO_PORT,USART1RX_GPIO_Pin);
//串口工作模式配置
USARTx_WorkMode_Config(USART1,115200,USART_WordLength_8b,USART_StopBits_1,USART_Parity_No);
//串口中断配置
USARTx_NVIC_Config(USART1_IRQn,1,1);
// 配置串口空闲中断
USART_ClearITPendingBit(USART1,USART_IT_IDLE);
USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);
// 使能串口
USART_Cmd(USART1, ENABLE);
//串口接收DMA配置
USARTx_RxDMA_Config(USART1,DMA1_Channel5,RCC_AHBPeriph_DMA1,(USART1_BASE+0x04),(uint32_t)U1RxBuff.rxarr,UxRXBUFFSIZE,DMA_Priority_Low);
#endif
#if USE_USART2
/*串口x使用到的全局变量初始化*/
USARTx_Var_Init(&U2RxBuff,&g_U2RxFra);
/**打开串口时钟**/
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);
//配置接收发送引脚
USARTx_AFPP_GPIO_Config(USART2TX_GPIO_CLK,USART2TX_GPIO_PORT,USART2TX_GPIO_Pin);
USARTx_FloatIN_GPIO_Config(USART2RX_GPIO_CLK,USART2RX_GPIO_PORT,USART2RX_GPIO_Pin);
//串口工作模式配置
USARTx_WorkMode_Config(USART2,115200,USART_WordLength_8b,USART_StopBits_1,USART_Parity_No);
//串口中断配置
USARTx_NVIC_Config(USART2_IRQn,1,1);
// 配置串口空闲中断
USART_ClearITPendingBit(USART2,USART_IT_IDLE);
USART_ITConfig(USART2, USART_IT_IDLE, ENABLE);
// 使能串口
USART_Cmd(USART2, ENABLE);
//串口接收DMA配置
USARTx_RxDMA_Config(USART2,DMA1_Channel6,RCC_AHBPeriph_DMA1,(USART2_BASE+0x04),(uint32_t)U2RxBuff.rxarr,UxRXBUFFSIZE,DMA_Priority_Low);
#endif
#if USE_USART3
/*串口x使用到的全局变量初始化*/
USARTx_Var_Init(&U3RxBuff,&g_U3RxFra);
/**打开串口时钟**/
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3,ENABLE);
//配置接收发送引脚
USARTx_AFPP_GPIO_Config(USART3TX_GPIO_CLK,USART3TX_GPIO_PORT,USART3TX_GPIO_Pin);
USARTx_FloatIN_GPIO_Config(USART3RX_GPIO_CLK,USART3RX_GPIO_PORT,USART3RX_GPIO_Pin);
//串口工作模式配置
USARTx_WorkMode_Config(USART3,115200,USART_WordLength_8b,USART_StopBits_1,USART_Parity_No);
//串口中断配置
USARTx_NVIC_Config(USART3_IRQn,1,1);
// 配置串口空闲中断
USART_ClearITPendingBit(USART3,USART_IT_IDLE);
USART_ITConfig(USART3, USART_IT_IDLE, ENABLE);
// 使能串口
USART_Cmd(USART3, ENABLE);
//串口接收DMA配置
USARTx_RxDMA_Config(USART3,DMA1_Channel3,RCC_AHBPeriph_DMA1,(USART3_BASE+0x04),(uint32_t)U3RxBuff.rxarr,UxRXBUFFSIZE,DMA_Priority_Low);
#endif
#if USE_USART4
/*串口x使用到的全局变量初始化*/
USARTx_Var_Init(&U4RxBuff,&g_U4RxFra);
/**打开串口时钟**/
RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART4,ENABLE);
//配置接收发送引脚
USARTx_AFPP_GPIO_Config(USART4TX_GPIO_CLK,USART4TX_GPIO_PORT,USART4TX_GPIO_Pin);
USARTx_FloatIN_GPIO_Config(USART4RX_GPIO_CLK,USART4RX_GPIO_PORT,USART4RX_GPIO_Pin);
//串口工作模式配置
USARTx_WorkMode_Config(UART4,115200,USART_WordLength_8b,USART_StopBits_1,USART_Parity_No);
//串口中断配置
USARTx_NVIC_Config(UART4_IRQn,1,1);
// 配置串口空闲中断
USART_ClearITPendingBit(UART4,USART_IT_IDLE);
USART_ITConfig(UART4, USART_IT_IDLE, ENABLE);
// 使能串口
USART_Cmd(UART4, ENABLE);
//串口接收DMA配置
USARTx_RxDMA_Config(UART4,DMA2_Channel3,RCC_AHBPeriph_DMA2,(UART4_BASE+0x04),(uint32_t)U4RxBuff.rxarr,UxRXBUFFSIZE,DMA_Priority_Low);
#endif
#if USE_USART5
/*串口x使用到的全局变量初始化*/
USARTx_Var_Init(&U5RxBuff,&g_U5RxFra);
/**打开串口时钟**/
RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART5,ENABLE);
//配置接收发送引脚
USARTx_AFPP_GPIO_Config(USART5TX_GPIO_CLK,USART5TX_GPIO_PORT,USART5TX_GPIO_Pin);
USARTx_FloatIN_GPIO_Config(USART5RX_GPIO_CLK,USART5RX_GPIO_PORT,USART5RX_GPIO_Pin);
//串口工作模式配置
USARTx_WorkMode_Config(UART5,115200,USART_WordLength_8b,USART_StopBits_1,USART_Parity_No);
//串口中断配置
USARTx_NVIC_Config(UART5_IRQn,1,1);
// 配置串口接收中断
USART_ClearITPendingBit(UART5,USART_IT_RXNE);
USART_ITConfig(UART5, USART_IT_RXNE, ENABLE);
//配置串口空闲中断
USART_ClearITPendingBit(UART5,USART_IT_IDLE);
USART_ITConfig(UART5, USART_IT_IDLE, ENABLE);
// 使能串口
USART_Cmd(UART5, ENABLE);
//串口5无DMA传输
#endif
}
/**************************************
*函 数 名: Usart_SendByte
*功 能: 串口发送一个字节
*入口参数: pUSARTx--USART1..3,UART4/5
ch--要发送的字节
*返 回 值: 无
*说 明:
**************************************/
void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch)
{
/* 发送一个字节数据到USART */
USART_SendData(pUSARTx,ch);
/* 等待发送数据寄存器为空 */
while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
}
/**************************************
*函 数 名: Usart_SendArray
*功 能: 串口发送8位数组
*入口参数: pUSARTx--USART1..3,UART4/5
array--要发送的数组指针
num--要发送的数目
*返 回 值: 无
*说 明:
**************************************/
void Usart_SendArray( USART_TypeDef * pUSARTx, uint8_t *array, uint16_t num)
{
uint8_t i;
for(i=0; i<num; i++)
{
/* 发送一个字节数据到USART */
Usart_SendByte(pUSARTx,array[i]);
}
/* 等待发送完成 */
while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET);
}
/**************************************
*函 数 名: Usart_SendString
*功 能: 串口发送字符串
*入口参数: pUSARTx--USART1..3,UART4/5
str--要发送的字符串指针
*返 回 值: 无
*说 明:
**************************************/
void Usart_SendString( USART_TypeDef * pUSARTx, char *str)
{
unsigned int k=0;
do
{
Usart_SendByte( pUSARTx, *(str + k) );
k++;
} while(*(str + k)!='\0');
/* 等待发送完成 */
while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET)
{}
}
/***************** 发送一个16位数 **********************/
void Usart_SendHalfWord( USART_TypeDef * pUSARTx, uint16_t ch)
{
uint8_t temp_h, temp_l;
/* 取出高八位 */
temp_h = (ch&0XFF00)>>8;
/* 取出低八位 */
temp_l = ch&0XFF;
/* 发送高八位 */
USART_SendData(pUSARTx,temp_h);
while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
/* 发送低八位 */
USART_SendData(pUSARTx,temp_l);
while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
}
///重定向c库函数printf到串口,重定向后可使用printf函数
int fputc(int ch, FILE *f)
{
/* 发送一个字节数据到串口 */
USART_SendData(USART1, (uint8_t) ch);
/* 等待发送完毕 */
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
return (ch);
}
///重定向c库函数scanf到串口,重写向后可使用scanf、getchar等函数
int fgetc(FILE *f)
{
/* 等待串口输入数据 */
while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET);
return (int)USART_ReceiveData(USART1);
}
/**************************************
*函 数 名: USARTxIDLE_IRQ
*功 能: 串口x空闲中断调用函数;仅串口1-4
*入口参数: pUSARTx--USARTx
DMAy_Channelx--串口通道
pRxBuff--串口接收缓冲区
pUxFra--串口接收帧记录缓冲区
*返 回 值: 无
**************************************/
static void USARTx_IDLE_IRQ(USART_TypeDef * pUSARTx,DMA_Channel_TypeDef* DMAy_Channelx,_USARTxRXBUFF *pRxBuff,_FRAMEATTRI *pUxRxFra)
{
uint16_t trnum=0;
//清除空闲中断标志位
pUSARTx->SR;
pUSARTx->DR;
//手册虽然说这个寄存器在DMA循环模式的时候,清0之后会自动恢复为最大接收缓冲区,但加入这一步以防万一
if(DMAy_Channelx->CNDTR == 0)
{
trnum = UxRXBUFFSIZE;
}
else
{
trnum = DMAy_Channelx->CNDTR&0xffff;
}
pRxBuff->wp = UxRXBUFFSIZE-trnum; //得到最新帧的结束地址
pUxRxFra->fraddr[pUxRxFra->nextfra].rpx = pRxBuff->rp; //最新帧的起始地址
pUxRxFra->fraddr[pUxRxFra->nextfra].wpx = pRxBuff->wp; //最新帧的结束地址
pUxRxFra->nextfra = (pUxRxFra->nextfra+1)%FRADDRMAX; //g_Fra.nextfra的值被限制再0,1....(FRADDRMAX-1)
pRxBuff->rp = pRxBuff->wp; //最新帧的起始与结束地址记录完,等待下一次记录
}
#if USE_USART5
/**************************************
*函 数 名: USART5_RXNE_IRQ
*功 能: 串口5接收一个字节
*入口参数: pRxBuff--串口接收缓冲区
*返 回 值: 无
*说 明:
**************************************/
static void USART5_RXNE_IRQ(_USARTxRXBUFF *pRxBuff)
{
pRxBuff->rxarr[pRxBuff->wp] = USART_ReceiveData(UART5);
pRxBuff->wp = (pRxBuff->wp+1)%UxRXBUFFSIZE; //pRxBuff->wp的值被限制再0,1....(UxRXBUFFSIZE-1)
}
/**************************************
*函 数 名: UART5_IDLE_IRQ
*功 能: 串口5空闲中断处理
*入口参数: pRxBuff--串口接收缓冲区
pUxFra--串口接收帧记录缓冲区
*返 回 值: 无
*说 明:
**************************************/
static void UART5_IDLE_IRQ(_USARTxRXBUFF *pRxBuff,_FRAMEATTRI *pUxRxFra)
{
UART5->SR;
UART5->DR;
pUxRxFra->fraddr[pUxRxFra->nextfra].rpx = pRxBuff->rp; //最新帧的起始地址
pUxRxFra->fraddr[pUxRxFra->nextfra].wpx = pRxBuff->wp; //最新帧的结束地址
pUxRxFra->nextfra = (pUxRxFra->nextfra+1)%FRADDRMAX; //g_Fra.nextfra的值被限制再0,1....(FRADDRMAX-1)
pRxBuff->rp = pRxBuff->wp; //最新帧的起始与结束地址记录完,等待下一次记录
}
/**************************************
*函 数 名: USART5RXNE_IRQ
*功 能: 串口5接收字节中断调用函数;串口5专用
*入口参数: 无
*返 回 值: 无
*说 明: 在接收字节中断服务函数中调用
**************************************/
void USART5RXNE_IRQ(void)
{
USART_ClearITPendingBit(UART5,USART_IT_RXNE);
USART5_RXNE_IRQ(&U5RxBuff);
}
#endif
/**************************************
*函 数 名: USARTxIDLE_IRQ
*功 能: 串口x空闲中断调用函数;串口1-5
*入口参数: Com_USARTx--定义好的串口通道
*返 回 值: 无
*说 明:串口5除了这个之外还要调用串口接收字节处理
**************************************/
void USARTxIDLE_IRQ(uint8_t Com_USARTx)
{
switch(Com_USARTx)
{
#if USE_USART1
case Com_USART1:
USARTx_IDLE_IRQ(USART1,USART1_RxDMA_CHANNEL,&U1RxBuff,&g_U1RxFra);
break;
#endif
#if USE_USART2
case Com_USART2:
USARTx_IDLE_IRQ(USART2,USART2_RxDMA_CHANNEL,&U2RxBuff,&g_U2RxFra);
break;
#endif
#if USE_USART3
case Com_USART3:
USARTx_IDLE_IRQ(USART3,USART3_RxDMA_CHANNEL,&U3RxBuff,&g_U3RxFra);
break;
#endif
#if USE_USART4
case Com_USART4:
USARTx_IDLE_IRQ(UART4,USART4_RxDMA_CHANNEL,&U4RxBuff,&g_U4RxFra);
break;
#endif
#if USE_USART5
case Com_USART5:
UART5_IDLE_IRQ(&U5RxBuff,&g_U5RxFra);
break;
#endif
default:
break;
}
}
/**************************************
*函 数 名: GetAFraFromUxRxBuff
*功 能: 获取一帧数据
*入口参数: pbuff--获取一帧数据的数组
psize--获取的数目
pRxBuff--串口x缓冲区
pUxFra--串口x帧记录
*返 回 值: rtflg--0代表没有获取数据,1代表获取到数据
*说 明: 本函数没有进行地址超出判断,一定要确保你的数据帧的空间小于你定下的 pbuff 的空间
**************************************/
static uint8_t GetAFraFromUxRxBuff(uint8_t *pbuff,uint8_t *psize,_USARTxRXBUFF *pRxBuff,_FRAMEATTRI *pUxFra)
{
uint8_t rtflg=0; //返回值
uint16_t fralen=0; //帧长度
if(pUxFra->currfra != pUxFra->nextfra) //如果为真,说明有未处理的帧
{
/*根据每帧的帧属性(起始与结束地址)在串口接收缓冲区主体中获取一帧数据*/
if(pUxFra->fraddr[pUxFra->currfra].rpx<pUxFra->fraddr[pUxFra->currfra].wpx)
{
//本帧写入的起始地址小于写入的结束地址,直接使用写入结束地址-写入起始地址,计算出本帧的长度
fralen = pUxFra->fraddr[pUxFra->currfra].wpx-pUxFra->fraddr[pUxFra->currfra].rpx;
for((*psize)=0;(*psize)<fralen;(*psize)++)
{
//从串口接收缓冲区主体中取出本帧的数据,索引为:pUxFra->fraddr[pUxFra->currfra].rpx+(*psize)
pbuff[(*psize)] = pRxBuff->rxarr[pUxFra->fraddr[pUxFra->currfra].rpx+(*psize)];
}
//数据取出之后,本帧的.rpx(本帧起始的写入地址)=本帧的.wpx(本帧写入的结束地址)
pUxFra->fraddr[pUxFra->currfra].rpx=pUxFra->fraddr[pUxFra->currfra].wpx;
}
else
{
//本帧写入的起始地址大于写入的结束地址,说明数据在缓冲区中进行了溢出翻转,分两部分读取
//第一部分为rpx-(UxRXBUFFSIZE-1)的数据,第二部分为0-wpx的数据
for((*psize)=0;pUxFra->fraddr[pUxFra->currfra].rpx<UxRXBUFFSIZE;pUxFra->fraddr[pUxFra->currfra].rpx++)
{
//读取第一部分数据
pbuff[(*psize)] = pRxBuff->rxarr[pUxFra->fraddr[pUxFra->currfra].rpx];
(*psize)++;
}
pUxFra->fraddr[pUxFra->currfra].rpx = 0;
//读取第二部分数据
while(pUxFra->fraddr[pUxFra->currfra].rpx<pUxFra->fraddr[pUxFra->currfra].wpx)
{
pbuff[(*psize)] = pRxBuff->rxarr[pUxFra->fraddr[pUxFra->currfra].rpx];
(*psize)++;
pUxFra->fraddr[pUxFra->currfra].rpx++;
}
}
//当前帧数据读取结束,currfra(当前处理的帧+1)----0..(FRADDRMAX-1)
pUxFra->currfra = (pUxFra->currfra+1)%FRADDRMAX;
//读取到数据,返回值赋1
rtflg = 1;
}
return rtflg;
}
/**************************************
*函 数 名: JudgeUxFrameDataNum
*功 能: 判断是否有未处理的数据帧
*入口参数: pUxFra--串口x帧记录
*返 回 值: rtflg--0代表没有数据,1代表有数据需要处理
*说 明:
**************************************/
static uint8_t JudgeUxFrameDataNum(_FRAMEATTRI *pUxFra)
{
uint8_t rtflg=0;
if(pUxFra->currfra != pUxFra->nextfra) //如果为真,说明有未处理的帧
{
rtflg = 1;
}
else
{
rtflg = 0;
}
return rtflg;
}
/**************************************
*函 数 名: GetAFraFromComx
*功 能: 从COM口获取一帧数据
*入口参数: Com_USARTx--定义好的COM口,Com_USART1..5
pbuff--获取一帧数据的数组
psize--获取的数目
*返 回 值: rtflg--0代表没有获取数据,1代表获取到数据
*说 明:
**************************************/
uint8_t GetAFraFromComx(uint8_t Com_USARTx,uint8_t *pbuff,uint8_t *psize)
{
uint8_t rtflg=0; //返回值
switch(Com_USARTx)
{
#if USE_USART1
case Com_USART1:
rtflg = GetAFraFromUxRxBuff(pbuff,psize,&U1RxBuff,&g_U1RxFra);
break;
#endif
#if USE_USART2
case Com_USART2:
rtflg = GetAFraFromUxRxBuff(pbuff,psize,&U2RxBuff,&g_U2RxFra);
break;
#endif
#if USE_USART3
case Com_USART3:
rtflg = GetAFraFromUxRxBuff(pbuff,psize,&U3RxBuff,&g_U3RxFra);
break;
#endif
#if USE_USART4
case Com_USART4:
rtflg = GetAFraFromUxRxBuff(pbuff,psize,&U4RxBuff,&g_U4RxFra);
break;
#endif
#if USE_USART5
case Com_USART5:
rtflg = GetAFraFromUxRxBuff(pbuff,psize,&U5RxBuff,&g_U5RxFra);
break;
#endif
default:
break;
}
return rtflg;
}
/**************************************
*函 数 名: JudgeUxFraIsNull
*功 能: 判断当前COM口是否还有未处理的数据
*入口参数: Com_USARTx--定义好的COM口,Com_USART1..5
*返 回 值: rtflg--0代表没有数据,1代表有数据需要处理
*说 明:
**************************************/
uint8_t JudgeUxFraIsNull(uint8_t Com_USARTx)
{
uint8_t rtflg=0; //返回值
switch(Com_USARTx)
{
#if USE_USART1
case Com_USART1:
rtflg = JudgeUxFrameDataNum(&g_U1RxFra);
break;
#endif
#if USE_USART2
case Com_USART2:
rtflg = JudgeUxFrameDataNum(&g_U2RxFra);
break;
#endif
#if USE_USART3
case Com_USART3:
rtflg = JudgeUxFrameDataNum(&g_U3RxFra);
break;
#endif
#if USE_USART4
case Com_USART4:
rtflg = JudgeUxFrameDataNum(&g_U4RxFra);
break;
#endif
#if USE_USART5
case Com_USART5:
rtflg = JudgeUxFrameDataNum(&g_U5RxFra);
break;
#endif
default:
break;
}
return rtflg;
}
中断服务函数中:
// 串口中断服务函数
#if USE_USART1
void USART1_IRQHandler(void)
{
if(USART_GetITStatus(USART1,USART_IT_IDLE)!=RESET)
{
USARTxIDLE_IRQ(Com_USART1);
}
}
#endif
// 串口中断服务函数
#if USE_USART2
void USART2_IRQHandler(void)
{
if(USART_GetITStatus(USART2,USART_IT_IDLE)!=RESET)
{
USARTxIDLE_IRQ(Com_USART2);
}
}
#endif
// 串口中断服务函数
#if USE_USART3
void USART3_IRQHandler(void)
{
if(USART_GetITStatus(USART3,USART_IT_IDLE)!=RESET)
{
USARTxIDLE_IRQ(Com_USART3);
}
}
#endif
// 串口中断服务函数
#if USE_USART4
void UART4_IRQHandler(void)
{
if(USART_GetITStatus(UART4,USART_IT_IDLE)!=RESET)
{
USARTxIDLE_IRQ(Com_USART4);
}
}
#endif
#if USE_USART5
// 串口中断服务函数
void UART5_IRQHandler(void)
{
if(USART_GetITStatus(UART5,USART_IT_RXNE)!=RESET)
{
USART5RXNE_IRQ();
}
if(USART_GetITStatus(UART5,USART_IT_IDLE)!=RESET)
{
USARTxIDLE_IRQ(Com_USART5);
}
}
#endif
main测试函数:
uint8_t parr[100],psize;
int main(void)
{
BSP_Init();
SysTick_Config(SystemCoreClock / 1000);
while(1)
{
if(GetAFraFromComx(Com_USART1,parr,&psize))
{
if(psize != 0)
{
Usart_SendArray(USART1,parr,psize);
}
}
if(GetAFraFromComx(Com_USART2,parr,&psize))
{
if(psize != 0)
{
Usart_SendArray(USART2,parr,psize);
}
}
if(GetAFraFromComx(Com_USART3,parr,&psize))
{
if(psize != 0)
{
Usart_SendArray(USART3,parr,psize);
}
}
if(GetAFraFromComx(Com_USART4,parr,&psize))
{
if(psize != 0)
{
Usart_SendArray(UART4,parr,psize);
}
}
if(GetAFraFromComx(Com_USART5,parr,&psize))
{
if(psize != 0)
{
Usart_SendArray(UART5,parr,psize);
}
}
}
}
兴起再写DMA发送队列
题外话
以上的都是会产生空闲中断的数据帧格式,如果数据帧无空闲,而是通过帧头帧尾判断的话,以下代码为串口接收处理参考。
通过帧头帧尾判断数据帧,无需配置空闲中断,而在串口接收中断中调用:
//通过帧头帧尾判断数据帧
void USART1RXNE_IRQ(void)
{
static uint8_t afracnt=0; //单帧数据计数
uint8_t ch;
USART_ClearITPendingBit(USART1,USART_IT_RXNE);
afracnt++;
ch = USART_ReceiveData(DEBUG_USARTx)&0xff;
RxBuff.rxarr[RxBuff.wp] = ch;
RxBuff.wp=(RxBuff.wp+1)%RXBUFFSIZE;
if((ch == 0xf5)||(afracnt==50)) //到达本帧帧尾或者这帧数据超过50个字节
{
afracnt = 0;
g_Fra.fraddr[g_Fra.nextfra].rpx = RxBuff.rp; //最新帧的起始地址
g_Fra.fraddr[g_Fra.nextfra].wpx = RxBuff.wp; //最新帧的结束地址
g_Fra.nextfra = (g_Fra.nextfra+1)%FRADDRMAX; //g_Fra.nextfra的值被限制再0,1....(FRADDRMAX-1)
RxBuff.rp = RxBuff.wp; //最新帧的起始与结束地址记录完,等待下一次记录
}
}
中断中为:
/****************************************************
函数名称 : USART1_IRQHandler
功 能 : 判断通过串口接收到的数据,执行相应的命令
参 数 : 无
返 回 值 : 无
****************************************************/
void USART1_IRQHandler(void) //串口接收中断服务函数
{
if(USART_GetITStatus(USART1,USART_IT_RXNE) != RESET)
{
USART1RXNE_IRQ();
}
if ( USART_GetITStatus( USART1, USART_IT_IDLE ) != RESET ) //数据帧接收完毕
{
// USART1IDLE_IRQ();
}
}
其余与上述一致。