分享一个自己写的STM32的DMA串口收发的例子,来抛砖引玉……

2019-12-20 21:42发布

本帖最后由 654705188 于 2015-7-16 09:24 编辑

#ifndef _RDCOM_H_
#define _RDCOM_H_
#include "driver_about.h"
//////////////////////////////////////////////////////////////////////
#define RDCOMDEBUG            0

#define RDCOM_MODE            RDCOM_DMA//USART收发数据的方式-查询、中断、DMA

#define RDCOMUSART            USART1  //串口号
#define RDCOM_RCC_APBnPeriphClockCmd()  RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE)//打开串口时钟
//配置IO口前别忘了开启IO时钟
#define RDCOMRX_PORT                             GPIOA
#define RDCOMRX_PIN                             GPIO_Pin_10//RX
#define RDCOMTX_PORT                             GPIOA
#define RDCOMTX_PIN                             GPIO_Pin_9//TX

////////////////////中断方式下需要配置////////////////////////////////
#define RDCOMUSART_IRQ                  USART1_IRQn       //串口接收中断
#define RDCOMUSART_IRQHandler           USART1_IRQHandler //串口接收中断服务
////////////////////DMA方式下需要配置////////////////////////////////
#define RDCOMPerph_Addr                 (u32)(&(USART1->DR)) //串口发送寄存器作为目的地址
#define RDCOM_RCC_AHBPeriphClockCmd()   RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE)//DMA时钟

#define RDCOMDMAn_Channeln_Send         DMA1_Channel4//DMA通道TX选择
#define RDCOMDMAn_IT_TC_Send            DMA1_IT_TC4//传输完成中断
#define RDCOMDMAn_IT_TE_Send            DMA1_IT_TE4//传输错误中断
#define RDCOMDMAn_IT_GL_Send            DMA1_IT_GL4//全部中断
#define RDCOMDMAn_Chn_IRQn_Send         DMA1_Channel4_IRQn
#define RDCOMUSART_DMAHandler_Send      DMA1_Channel4_IRQHandler//DMA中断服务

#define RDCOMDMAn_Channeln_Recv         DMA1_Channel5//DMA通道RX选择
#define RDCOMDMAn_FLAG_GL_Recv          DMA1_FLAG_GL5//DMA传输的所有标志
#define RDCOMDMAn_IT_TC_Recv            DMA1_IT_TC5//传输完成中断
#define RDCOMDMAn_IT_TE_Recv            DMA1_IT_TE5//传输错误中断
#define RDCOMDMAn_IT_GL_Recv            DMA1_IT_GL5//全部中断
#define RDCOMDMAn_Chn_IRQn_Recv         DMA1_Channel5_IRQn
#define RDCOMUSART_DMAHandler_Recv      DMA1_Channel5_IRQHandler//DMA中断服务
/////////////////// 以下不需要修改////////////////////////////////////
#define RDCOM_NORMAL          0         //USART收发数据的方式的选项
#define RDCOM_INT             1
#define RDCOM_DMA             2

s8 RDCOMUsartInit(u32 baudrate);//串口初始化,输入波特率,只需要调用此函数即可完成初始化
//////////////////查询方式下:mode=RDCOM_NORMAL/////////////////
#if(RDCOM_MODE==RDCOM_NORMAL)
u8 RDCOMnormal_Send(u8 *data,u8 len);
u8 RDCOMnormal_Receive(u8 *data,u16 timeout_ms);
u8 RDCOMnormal_Receive_Char(u8 *data,char ch,u16 timeout_ms);
u8 RDCOMnormal_Receive_Len(u8 *data,u8 len,u16 timeout_ms);
#endif
//////////////////中断方式下:mode=RDCOM_INT/////////////////
#if(RDCOM_MODE==RDCOM_INT)
void RDCOMUSART_IRQHandler(void);//中断接收服务
#endif
//////////////////DMA方式下 :mode=RDCOM_DMA/////////////////
#if(RDCOM_MODE==RDCOM_DMA)
u8   RDCOMDMA_Send(u8 *Send_Data,u8 len);//通过DMA发送数据
void RDCOMUSART_DMAHandler(void);//USART的DMA发送完成或错误中断服务
#endif


////////////////////////////////////////////////////////////////////////
extern s8 RDCOM_Send(u8 *sdata,u8 len,OS_TICK timeout);
extern s8 RDCOM_Recv(u8 *rdata,u8 *len,OS_TICK timeout);
extern s8 RDCOM_SendRecv(u8 *send_data,u8 send_len,u8 *recv_data,u8 *recv_len,OS_TICK timeout);//有延时的发送和接收数据
#endif


#include "rdcom.h"

#if(RDCOM_MODE==RDCOM_DMA)
static CPU_TS ts=0;  //存放发送消息时的时间戳OS_TS_GET()
static OS_ERR err;   //返回的错误信息
OS_SEM RDCOMSend_Sem;
OS_SEM RDCOMRecv_Sem;
        
#endif

void RDCOMGPIO_Config(void)//串口IO口配置
{
        GPIO_InitTypeDef GPIO_InitStructure;
        
        GPIO_InitStructure.GPIO_Pin = RDCOMRX_PIN;//
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;//2M
        GPIO_Init(RDCOMRX_PORT,&GPIO_InitStructure);//

        GPIO_InitStructure.GPIO_Pin = RDCOMTX_PIN;//
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;//2M
        GPIO_Init(RDCOMTX_PORT, &GPIO_InitStructure);//
}

void RDCOMUSART_Config(u32 baudrate)//串口初始化
{
        USART_InitTypeDef USART_InitStructure;
        RDCOM_RCC_APBnPeriphClockCmd();
        
        USART_StructInit(&USART_InitStructure);//将结构体设置为缺省状态,9600bps,8数据位,1停止位,不校验,硬件流控制失能
        USART_InitStructure.USART_BaudRate =baudrate;//波特率设置
        USART_InitStructure.USART_WordLength = USART_WordLength_8b;//一帧数据的宽度设置为8bits
        USART_InitStructure.USART_StopBits = USART_StopBits_1;//在帧结尾传输1个停止位
        USART_InitStructure.USART_Parity = USART_Parity_No;//奇偶失能模式,无奇偶校验
        USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//发送/接收使能
        USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//硬件流控制失能
        USART_Init(RDCOMUSART, &USART_InitStructure);//设置串口
#if(RDCOM_MODE==RDCOM_INT)
        USART_ITConfig(RDCOMUSART, USART_IT_RXNE, ENABLE);//打开接收中断
#endif
        USART_Cmd(RDCOMUSART, ENABLE);//打开串口
        USART_GetFlagStatus(RDCOMUSART, USART_FLAG_TC);
        //发送数据前先清除标志位,否则第1字节数据会丢失
}
///////////////////查询方式下///////////////////////////////////
#if(RDCOM_MODE==RDCOM_NORMAL)
u8 RDCOMnormal_Send(u8 *data,u8 len)
{
        u8 i=0;
        for(i=0;i<len;i++)
                {
                USART_SendData(RDCOMUSART, data);//发送数据
                while(USART_GetFlagStatus(RDCOMUSART, USART_FLAG_TC) == RESET);//等待数据发送完毕
                }
        return i;
}
u8 RDCOMnormal_Receive(u8 *data,u16 timeout_ms)
{
        u32 timeout=timeout_ms*740,timeout_count=0;
        u8 i=0;
        while(timeout_count<timeout)//等待时间设置
        {
                if (USART_GetFlagStatus(RDCOMUSART, USART_FLAG_RXNE) != RESET)  //判断接收标志;查询方式的USART接收
                {
                data[i++]=USART_ReceiveData(RDCOMUSART);
                timeout_count=0;
                }
                else
                {
                timeout_count++;
                }               
        }
        return i;
}

u8 RDCOMnormal_Receive_Char(u8 *data,char ch,u16 timeout_ms)//ms
{
        u32 timeout=timeout_ms*740,timeout_count=0;        
        u8 i=0;
        while(timeout_count<timeout)//等待时间设置600000=800ms
        {
                if (USART_GetFlagStatus(RDCOMUSART, USART_FLAG_RXNE) != RESET)  //判断接收标志;查询方式的USART接收
                {
                data[i++]=USART_ReceiveData(RDCOMUSART);
                timeout_count=0;
                if(data[i-1]==ch)
                        break;
                }
                else
                {
                timeout_count++;
                }               
        }
        return i;
}
u8 RDCOMnormal_Receive_Len(u8 *data,u8 len,u16 timeout_ms)
{
        u32 timeout=timeout_ms*740,timeout_count=0;        
        u8 i=0;
        while(timeout_count<timeout)//等待时间设置
        {
                if (USART_GetFlagStatus(RDCOMUSART, USART_FLAG_RXNE) != RESET)  //判断接收标志;查询方式的USART接收
                {
                data[i++]=USART_ReceiveData(RDCOMUSART);
                timeout_count=0;
                if(i>=len)
                        break;
                }
                else
                {
                timeout_count++;
                }               
        }
        return i;
}
#endif
///////////////////中断方式下////////////////////////////////////////////////////////////////////////
#if(RDCOM_MODE==RDCOM_INT)
void RDCOMUSART_IRQHandler(void)//中断方式的USART接收
{
        if(USART_GetITStatus(RDCOMUSART, USART_IT_RXNE) != RESET) //判断发生接收中断
        {//USART_ClearITPendingBit(RDCOMUSART,  USART_IT_RXNE);    //清除中断标志,读接收到的数据时自动清除
               
         //USART_SendData(RDCOMUSART, USART_ReceiveData(RDCOMUSART));//发送数据
         //while(USART_GetFlagStatus(RDCOMUSART, USART_FLAG_TC) == RESET);//等待数据发送完毕
        }
}
void RDCOMNVIC_Config(void)//配置中断
{
        NVIC_InitTypeDef NVIC_InitStructure;
        //NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);//选择中断分组1
        NVIC_InitStructure.NVIC_IRQChannel = RDCOMUSART_IRQ;//选择串口中断
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;//抢占式中断优先级设置为0
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;//响应式中断优先级设置为3
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能中断
        NVIC_Init(&NVIC_InitStructure);
}
#endif
///////////////////DMA方式下////////////////////////////////////////////////////////////////////////
#if(RDCOM_MODE==RDCOM_DMA)
u8 RDCOMDMA_Send(u8 *send_data,u8 len)
{  
     DMA_InitTypeDef DMA_InitStructure;
      //if(RDCOM_DMASend_Finish!=1)//发送未完成
      //return 0;
      //RDCOM_DMASend_Finish=0;//DMA发送完成的标志,1为发送完成
     USART_GetFlagStatus(RDCOMUSART,USART_FLAG_ORE);//清溢出标志USART_FLAG_NE,USART_FLAG_FE,USART_FLAG_PE
     USART_ReceiveData(RDCOMUSART);//清缓存
        
     RDCOM_RCC_AHBPeriphClockCmd();
     DMA_DeInit(RDCOMDMAn_Channeln_Send);//复位DMA-TX通道
     DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)RDCOMPerph_Addr;//外设地址
     DMA_InitStructure.DMA_MemoryBaseAddr     = (u32)send_data;//内存地址,自己开辟的数组
     DMA_InitStructure.DMA_DIR                = DMA_DIR_PeripheralDST;//外设作为数据传输的目的地
     DMA_InitStructure.DMA_BufferSize         = len;//传输的数据长度,单位在下边设置

     DMA_InitStructure.DMA_PeripheralInc      = DMA_PeripheralInc_Disable;//设置DMA外设递增模式禁止
     DMA_InitStructure.DMA_MemoryInc          = DMA_MemoryInc_Enable; //设置DMA内存递增模式使能
     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优先级
     DMA_InitStructure.DMA_M2M                = DMA_M2M_Disable;//内存到内存禁止
     DMA_Init(RDCOMDMAn_Channeln_Send, &DMA_InitStructure);//配置完成后,启动DMA通道
     USART_DMACmd(RDCOMUSART,USART_DMAReq_Tx,ENABLE);//使能USART的发送DMA请求
     DMA_Cmd(RDCOMDMAn_Channeln_Send, ENABLE);
     DMA_ITConfig(RDCOMDMAn_Channeln_Send, DMA_IT_TC, ENABLE); //DMA传输完成
   return len;
}
void RDCOMDMANVIC_Config_Send(void)//配置中断
{
        NVIC_InitTypeDef NVIC_InitStructure;
        //NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);//选择中断分组1
        NVIC_InitStructure.NVIC_IRQChannel = RDCOMDMAn_Chn_IRQn_Send;//选择DMA中断
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;//抢占式中断优先级设置为0
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;//响应式中断优先级设置为3
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能中断
        NVIC_Init(&NVIC_InitStructure);
}
void RDCOMUSART_DMAHandler_Send(void)//USART的DMA发送完成或错误中断
{
        OSIntEnter();
        
         if(DMA_GetITStatus(RDCOMDMAn_IT_TC_Send) != RESET) //判断发生DMA发送完成中断
        {  
                //RDCOM_DMASend_Finish=1;//DMA发送完成的标志,1为发送完成
                OSSemPost(&RDCOMSend_Sem,OS_OPT_POST_1,&err);//发送完成信号
                DMA_ClearITPendingBit(RDCOMDMAn_IT_GL_Send);    //清除全部中断标志
        }         

        OSIntExit();
}



u8 RDCOMDMA_Recv(u8 *recv_data,u8 len)
{
     DMA_InitTypeDef DMA_InitStructure;
      //if(RDCOM_DMARecv_Finish!=1)//接收未完成
      //return 0;
      //RDCOM_DMARecv_Finish=0;//DMA接收完成的标志,1为接收完成
     USART_GetFlagStatus(RDCOMUSART,USART_FLAG_ORE);//清溢出标志USART_FLAG_NE,USART_FLAG_FE,USART_FLAG_PE
     USART_ReceiveData(RDCOMUSART);//清缓存
        
     RDCOM_RCC_AHBPeriphClockCmd();
     DMA_DeInit(RDCOMDMAn_Channeln_Recv);//复位DMA-RX通道
     DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)RDCOMPerph_Addr;//外设地址
     DMA_InitStructure.DMA_MemoryBaseAddr     = (u32)recv_data;//内存地址,自己开辟的数组
     DMA_InitStructure.DMA_DIR                = DMA_DIR_PeripheralSRC;//外设作为数据传输的数据源
     DMA_InitStructure.DMA_BufferSize         = len;//传输的数据长度,单位在下边设置

     DMA_InitStructure.DMA_PeripheralInc      = DMA_PeripheralInc_Disable;//设置DMA外设递增模式禁止
     DMA_InitStructure.DMA_MemoryInc          = DMA_MemoryInc_Enable; //设置DMA内存递增模式使能
     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优先级
     DMA_InitStructure.DMA_M2M                = DMA_M2M_Disable;//内存到内存禁止
     DMA_Init(RDCOMDMAn_Channeln_Recv, &DMA_InitStructure);//配置完成后,启动DMA通道
     USART_DMACmd(RDCOMUSART,USART_DMAReq_Rx,ENABLE);//使能USART的接收DMA请求
     DMA_Cmd(RDCOMDMAn_Channeln_Recv, ENABLE);
     DMA_ITConfig(RDCOMDMAn_Channeln_Recv, DMA_IT_TC, ENABLE); //DMA传输完成
   return len;
}
void RDCOMDMANVIC_Config_Recv(void)//配置中断
{
        NVIC_InitTypeDef NVIC_InitStructure;
        //NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);//选择中断分组1
        NVIC_InitStructure.NVIC_IRQChannel = RDCOMDMAn_Chn_IRQn_Recv;//选择DMA中断
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;//抢占式中断优先级设置为0
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;//响应式中断优先级设置为2
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能中断
        NVIC_Init(&NVIC_InitStructure);
}
void RDCOMUSART_DMAHandler_Recv(void)//USART的DMA接收完成或错误中断
{
        OSIntEnter();
        USART_DMACmd(RDCOMUSART,USART_DMAReq_Rx,DISABLE);//禁能USART的接收DMA请求
        if(DMA_GetITStatus(RDCOMDMAn_IT_TC_Recv) != RESET) //判断发生DMA接收完成中断
        {
                //RDCOM_DMARecv_Finish=1;//DMA接收完成的标志,1为接收完成        
                OSSemPost(&RDCOMRecv_Sem,OS_OPT_POST_1,&err);//发送完成信号
                DMA_ClearITPendingBit(RDCOMDMAn_IT_GL_Recv);        //清除全部中断标志
        }
               
        OSIntExit();
}

#endif
////////////////////////////////////////////////////////////////////////////////////////////////////        
s8 RDCOMUsartInit(u32 baudrate)
{
        RDCOMUSART_Config(baudrate);//串口初始化,与第二句顺序互换是避免复位后发送一个FF
        RDCOMGPIO_Config();//串口IO口配置        
#if(RDCOM_MODE==RDCOM_INT)
        RDCOMNVIC_Config();//配置中断
#endif
#if(RDCOM_MODE==RDCOM_DMA)
        RDCOMDMANVIC_Config_Send();
        RDCOMDMANVIC_Config_Recv();
        OSSemCreate(&RDCOMSend_Sem,(char *)"RDCOMSend Sem", 0, &err);
        OSSemCreate(&RDCOMRecv_Sem,(char *)"RDCOMRecv Sem", 0, &err);
#endif
        return ERR_NO;
        }
/**************************************************************************************
        **********************以下为RDCOM设置处理函数******************************************
***************************************************************************************/
s8 RDCOM_Send(u8 *sdata,u8 len,OS_TICK timeout)//OS_CFG_TICK_RATE_HZ
{
        if(len>0)//有发送数据
        {
                OSSemSet(&RDCOMSend_Sem,0,&err);//清发送信号量
                RDCOMDMA_Send((u8 *)sdata,len);//开始发送
                OSSemPend(&RDCOMSend_Sem,timeout,OS_OPT_PEND_BLOCKING,&ts,&err);//信号量等待
                USART_DMACmd(RDCOMUSART,USART_DMAReq_Tx,DISABLE);//禁能USART的发送DMA请求
                if(err!=OS_ERR_NONE) return ERR_ERROR;//发送数据时出错
        }
        return ERR_NO;
}

s8 RDCOM_Recv(u8 *rdata,u8 *len,OS_TICK timeout)
{
        s8 re=0;
        memset(rdata,0,DATAPKG_MAXLEN);
        OSSemSet (&RDCOMRecv_Sem,0,&err);//清接收信号量
        RDCOMDMA_Recv(rdata,DATAPKG_XORMINLEN);//开始接收
        OSSemPend(&RDCOMRecv_Sem,timeout,OS_OPT_PEND_BLOCKING,&ts,&err);//信号量等待
        if(err!=OS_ERR_NONE)
        {
                return ERR_ERROR;
        }
        re=Check_Data_Pkghead(rdata);
        if(re==ERR_NO)//数据头正确
        {
          RDCOMDMA_Recv(DATARECV(rdata)->DATA,DATARECV_DLEN(rdata)+DATAPKG_MINLEN-DATAPKG_XORMINLEN);//开始接收
          OSSemPend(&RDCOMRecv_Sem,timeout,OS_OPT_PEND_BLOCKING,&ts,&err);//信号量等待
                if(err==OS_ERR_NONE)
                {
                    *len=DATARECV_DLEN(rdata)+DATAPKG_MINLEN;
                    return ERR_NO;
                }
                if(err==OS_ERR_TIMEOUT)//第二次接收超时
                {
                  *len=DATAPKG_XORMINLEN;
                  return ERR_NO;
                }
        
        }
        //头错误
        {        
          USART_DMACmd(RDCOMUSART,USART_DMAReq_Rx,DISABLE);//禁能USART的接收DMA请求
          return re;
        }
}

s8 RDCOM_SendRecv(u8 *send_data,u8 send_len,u8 *recv_data,u8 *recv_len,OS_TICK timeout)//有延时的发送和接收数据
{
        s8 re=0;
        memset(recv_data,0,DATAPKG_MAXLEN);
        OSSemSet (&RDCOMRecv_Sem,0,&err);//清接收信号量
        
        if(send_len>0)//有发送数据
        {
                OSSemSet(&RDCOMSend_Sem,0,&err);//清发送信号量
                RDCOMDMA_Send((u8 *)send_data,send_len);//开始发送
                OSSemPend(&RDCOMSend_Sem,timeout,OS_OPT_PEND_BLOCKING,0,&err);//信号量等待
                if(err!=OS_ERR_NONE) return ERR_ERROR;
        }
        
        RDCOMDMA_Recv(recv_data,DATAPKG_MAXLEN);//开始接收
        OSSemPend(&RDCOMRecv_Sem,timeout,OS_OPT_PEND_BLOCKING,&ts,&err);//信号量等待100ms
        if(err==OS_ERR_NONE)
        {
                *recv_len=DATARECV_DLEN(recv_data)+DATAPKG_MINLEN;
                return ERR_NO;
        }
        if(err==OS_ERR_TIMEOUT)//第二次接收超时
        {
                *recv_len=DATAPKG_XORMINLEN;
                return ERR_NO;
        }
        
        //错误
        {               
               USART_DMACmd(RDCOMUSART,USART_DMAReq_Rx,DISABLE);//禁能USART的接收DMA请求
               return re;
        }        
}
友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
该问题目前已经被作者或者管理员关闭, 无法添加新回复
14条回答
styleno1
1楼-- · 2019-12-21 16:38
串口的代码,接口映射选项有限,简化到只指明UART?即可。
另,个人不喜欢满屏注释的代码。
654705188
2楼-- · 2019-12-21 22:24
 精彩回答 2  元偷偷看……
whatcanitbe
3楼-- · 2019-12-22 04:18
能提供有操作 系统和无操作系统的例程吗  ?
lhaoyue
4楼-- · 2019-12-22 09:33
MARK STM32的DMA串口
feibagezib
5楼-- · 2019-12-22 12:25
MARK下 STM32的DMA
yick
6楼-- · 2019-12-22 13:00
谢谢分享~~~~~~~

一周热门 更多>