干货

关于STM32F03X串口空闲中断+DMA的应用

分类名:拆解日期:2018-11-29作者:懒猫爱飞
分享到
微博
QQ
微信
LinkedIn


AuthorMark Xu

前言

偶然得到一块STM32F030的开发板,业余时间手痒痒,写点小程序练练手,一般调试时,用串口比较多,结合网上的资料,整理了一下串口中断与DMA的结合方式,测试了一下,用来起还不错,发送与接收都是用的是DMA方式,当然如果有其它紧急任务需要用到DMA可以让你其它任务,这里暂时不讨论其它。

概述

为了测试方便,暂时没有用到硬件流摈,也没有用到循环队列存储数据,只做了一下简单的测试,STM32F03X某些型号有两个串口,而我这块开发板正好是有两个串口,故两个串口都把它用上了^_^
因为属于简单的应用,说白了就是串口初始化及一些简单的小程序,而又因为是事后结合代码作的总结,故实验结果没有贴上来,如果日后有机会,可以再补齐。关于串口的API主要包括两个源文件Dsk_Usart.c及Dsk_Usart.h

Dsk_Usart.c源文件

该源文件中主要包括的是串口的配置及一些简单的API,关于串口的配置主要涉及下面几个:

// 最多可以接收到字节数
  #define  USART_RXD_BUF_SIZE                    (64)
  // 最多可以发送64字节
  #define  USART_TXD_BUF_SIZE                    (64)
  // 最多可以打印64个字符
  #define DEBUG_PRINT_BUF_LEN                 (64)
  // 串口2缓存大小
  #define  USART2_RX_BUF_SIZE                   (32)
  #define  USART2_TX_BUF_SIZE                    (32)
  
  // 发送方式配置
  // 查询方式/DMA中断方式
  #define  UART_SNED_REQ                      (0)
  #define  UART_SEND_DMA                     (1)
  #define  USART1_RXD_MODE                   UART_SEND_DMA
  #define  USART2_RXD_MODE                   UART_SNED_REQ
  
  
接收缓存与发射缓存根据MCU的RAM而定吧,这个没有什么限制,只要够用就好。发送方式一般用查询方式或用DMA方式,查询方式对于发送少量数据没的应用没有什么影响,如果单次发送的数据量比较大,最好用DMA配合中断的方式,这样不浪费MCU的资源而且不会造成信息阻塞。
该源文件中关于USART的函数有以下几个:
  •   void Uart1_Gpio_Init(void)  - 端口初始化的,注意是否进行了重新映射

  •   void Uart1_DMA_Init(void)  - DMA通道初始化,注意是否进行了重新映射

  •   void UART1_Api_Init(void)  - 串口1初始化函数

  •    void USART1_IRQHandler(void) –串口1中断函数,函数名要与启动文件中的名称一致

  •    void Uart1_Data_Process(pRevprocessDat) – 算是接收到的数据

  •   void Uart1_Send_Bytes(uint8_t*dat,uint16_t len) –通过串口1发送数据

  •    int Debug_Printf(const char*fmt, ...) – 把串口1发送函数包装成了类似printf的函数,方便调试打印

  •    void Uart2_Gpio_Init(void) – 与串口11类似

  •   void Uart2_DMA_Init(void) –DMA初始化,与串口1类似

  •    void UART2_Api_Init(void) – 串口2初始化化,与串口1类似

  •   void USART2_IRQHandler(void) –串口2中断,与串口1类似

  •   void Uart2_Data_Process(pRevprocessDat) – 处理接收数据

  • void Uart2_Send_Bytes(uint8_t*dat,uint16_t len) – 串口2发送数据


串口1与串口2的初始化函数及发送接收函数类似,本来可以整理成数组方式,比如把端口,DMA通道等加到相应的数组中,就可以同时初始化,可以节省一些代码空间,代码也可以更整洁一些。由于我开始只做了串口1的测试,没有做串口2的实验,后面想着既然实验就把两个都测试了,于是为了偷懒,直接复制了一份代码,这个不是好习惯,当然这样也代码也更清楚。好吧,不找理由,下次如果项目上用到,直接整合到一块^_^
下面是Dsk_Usart.c的源文件,我用的是3.5版本的库文件,如果您与我的版本不一样,注意修改一下寄存器或修改相关调用的库函数。
/*********************************************************************************
* Copyright (C) 2000-2018 Mark Xu WorkStudio,All Rights Reserved.
*
* File  : Dsk_Uart.c
*
* Brief : Uart api Module
*
* Author :Mark Xu
*
* Remark :None
---------------------------------------------------------------------------------
* Modify Content: NULL
*
* Modify Date  : xxxx/xx/xx/     xx:xx
*
* Modify staff :xx xx
**********************************************************************************/
#define DSKUART_IMPLATION


/*********************************************************************************
* Include
**********************************************************************************/
#include
#include
#include
#include
#include

#include"stm32f0xx_Lib.h"
#include"Dsk_Lib.h"


/*********************************************************************************
* Macros
**********************************************************************************/
// 最多可以接收到字节数
#defineUSART_RXD_BUF_SIZE                   (64)
// 最多可以发送64字节
#defineUSART_TXD_BUF_SIZE                   (64)
// 最多可以打印128个字符
#defineDEBUG_PRINT_BUF_LEN                  (64)
// 串口2缓存大小
#defineUSART2_RX_BUF_SIZE                   (32)
#defineUSART2_TX_BUF_SIZE                   (32)

// 发送方式配置
// 查询方式/DMA中断方式
#define  UART_SNED_REQ                       (0)
#define  UART_SEND_DMA                       (1)
#define  USART1_RXD_MODE                     UART_SEND_DMA
#define  USART2_RXD_MODE                     UART_SNED_REQ

/*********************************************************************************
* Variables
**********************************************************************************/
// The receive data buffer
static uint8_trev_buff[USART_RXD_BUF_SIZE]      = {0};
static uint8_tdatBuff[USART_RXD_BUF_SIZE]       = {0};
static uint8_trevUart2[USART_RXD_BUF_SIZE]      = {0};
static uint8_tdatUart2Buff[USART_RXD_BUF_SIZE]  = {0};
static uint8_t sendBuff[DEBUG_PRINT_BUF_LEN]= {0};
static uint8_tsend2Buff[USART2_TX_BUF_SIZE] = {0};


// TRUE - been receiveddata    FALSE - no data received
static uint8_t revFlag      = FALSE;
static uint8_t revUart2Flag= FALSE;


/*********************************************************************************
* Name  : Uart1_Gpio_Init
*
* Brief : Initialized the uart1 port.
*
* Input : None
*
* Output : None
*
* Remark :PA9-TX,PA10-RX,banudrate=9600
***********************************************************************************/
void Uart1_Gpio_Init(void)
{
         GPIO_InitTypeDef        GPIO_InitStructure;
         USART_InitTypeDef     USART_InitStructure;
         NVIC_InitTypeDef   NVIC_InitStructure;

         // 开启串口时钟及端口时钟
         RCC_APB2PeriphClockCmd(USART1_RCC, ENABLE);
    RCC_AHBPeriphClockCmd(USART1_PORT_RCC,ENABLE);
    GPIO_PinAFConfig(USART1_MAP_PORT,UART1_TXD_PINSOURCE, UART1_AF_TXD);
    GPIO_PinAFConfig(USART1_MAP_PORT,UART1_RXD_PINSOURCE, UART1_AF_RXD);

      // 配置TX端口
         GPIO_InitStructure.GPIO_Pin  = (UART1_TXD_PIN | UART1_RXD_PIN);
         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_OType =GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP;
         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
         GPIO_Init(USART1_MAP_PORT, &GPIO_InitStructure);

         // 配置串口模式
         USART_InitStructure.USART_BaudRate   = USART1_BAUDRATE;
         USART_InitStructure.USART_WordLength = USART_WordLength_8b;
         USART_InitStructure.USART_StopBits   = USART_Parity_No;
         USART_InitStructure.USART_HardwareFlowControl =USART_HardwareFlowControl_None;
         USART_InitStructure.USART_Mode = (USART_Mode_Rx |USART_Mode_Tx);
         USART_Init(USART1,&USART_InitStructure);

    // TXE发送中断,TC传输完成中断,RXNE接收中断,PE奇偶错误中断,可以是多个
    USART_ITConfig(USART1,USART_IT_TC,DISABLE);
    // 设置总线空闲产生中断
   USART_ITConfig(USART1,USART_IT_IDLE,ENABLE);
    // 必须先清除IDLE中断,否则会一直进IDLE中断
    USART1->ICR |= 1<<4;
    USART_ITConfig(USART1, USART_IT_RXNE,DISABLE);

    // 开启串口DMA接收
    USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE);
    // 开启串口1
    USART_Cmd(USART1,ENABLE);

    // 中断配置
    NVIC_InitStructure.NVIC_IRQChannel         = USART1_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPriority= 2;
    NVIC_InitStructure.NVIC_IRQChannelCmd      = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}

/*********************************************************************************
* Name  : Uart1_DMA_Init
*
* Brief : Initialized the uart1 DMA.
*
* Input : None
*
* Output : None
*
* Remark : None
***********************************************************************************/
void Uart1_DMA_Init(void)
{
         DMA_InitTypeDef DMA_Initstruct;

         // 开启DMA时钟
         RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
    //DMA1通道3配置
    DMA_DeInit(USART1_RXD_MA_CHANNEL);

         // DMA配置
         // 外设地址
         DMA_Initstruct.DMA_PeripheralBaseAddr      = (uint32_t)(&USART1->RDR);
    // 内存地址
         DMA_Initstruct.DMA_MemoryBaseAddr      =(uint32_t)rev_buff;
    // DMA传输方向单向
         DMA_Initstruct.DMA_DIR                  =DMA_DIR_PeripheralSRC;
    // 设置DMA在传输时缓冲区的长度
         DMA_Initstruct.DMA_BufferSize              =USART_RXD_BUF_SIZE;
    // 设置DMA的外设递增模式,一个外设
         DMA_Initstruct.DMA_PeripheralInc             =DMA_PeripheralInc_Disable;
    // 设置DMA的内存递增模式
         DMA_Initstruct.DMA_MemoryInc            =DMA_MemoryInc_Enable;
    // 外设数据字长
         DMA_Initstruct.DMA_PeripheralDataSize   = DMA_PeripheralDataSize_Byte;
    // 内存数据字长
         DMA_Initstruct.DMA_MemoryDataSize        =DMA_MemoryDataSize_Byte;
    // 设置DMA的传输模式
         DMA_Initstruct.DMA_Mode                                   =DMA_Mode_Normal;
    // 设置DMA的优先级别
         DMA_Initstruct.DMA_Priority                  = DMA_Priority_High; //DMA_Priority_VeryHigh;
         // 设置DMA的2个memory中的变量互相访问
         DMA_Initstruct.DMA_M2M                      =DMA_M2M_Disable;
   DMA_Init(USART1_RXD_MA_CHANNEL,&DMA_Initstruct);
//               USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE);
    // 开启DMA
          DMA_Cmd(USART1_RXD_MA_CHANNEL,ENABLE);

#if( USART1_RXD_MODE ==UART_SEND_DMA )
    // DMA1通道TXD配置
    DMA_DeInit(USART1_TXD_MA_CHANNEL);
    // 外设地址,串口1
    DMA_Initstruct.DMA_PeripheralBaseAddr =(uint32_t)(&USART1->TDR);
    // 发送内存地址
    DMA_Initstruct.DMA_MemoryBaseAddr     = (uint32_t)sendBuff;
    // 外设为传送数据目的地,即发送数据
    DMA_Initstruct.DMA_DIR =DMA_DIR_PeripheralDST;
    // 发送长度为0,即未有快递需要发送
    DMA_Initstruct.DMA_BufferSize = 0;
    // 初始化
    DMA_Init(USART1_TXD_MA_CHANNEL, &DMA_Initstruct);
    // 使能串口发送完成中断
    USART_ITConfig(USART1, USART_IT_TC,ENABLE);
          DMA_Cmd(USART1_TXD_MA_CHANNEL,ENABLE);
    // 使能DMA串口发送和接受请求
    USART_DMACmd(USART1,(USART_DMAReq_Tx|USART_DMAReq_Rx), ENABLE);
#endif /* USART1_RXD_MODE */

}

/*********************************************************************************
* Name  : UART1_Api_Init
*
* Brief : Initialized the uart1
*
* Input : None
*
* Output : None
*
* Remark : None
***********************************************************************************/
void UART1_Api_Init(void)
{
         Uart1_Gpio_Init();
         Uart1_DMA_Init();
}

/*********************************************************************************
* Name  : USART1_IRQHandler
*
* Brief : USART1 interrupt process handle
*
* Input : None
*
* Output : None
*
* Remark : None
***********************************************************************************/
void USART1_IRQHandler(void)
{
         uint8_t len = 0;

         if(USART_GetITStatus(USART1,USART_IT_IDLE) != RESET)
         {
        USART1->ICR |= 1<<4; // 清除中断
            DMA_Cmd(USART1_RXD_MA_CHANNEL,DISABLE);

             len =USART_RXD_BUF_SIZE - DMA_GetCurrDataCounter(USART1_RXD_MA_CHANNEL);

             if(len >USART_RXD_BUF_SIZE)
                                              len= USART_RXD_BUF_SIZE;

            memcpy(datBuff,rev_buff,len);

                   // 重新设置接收数据个数
                   USART1_RXD_MA_CHANNEL->CNDTR =USART_RXD_BUF_SIZE;
                   // 开启DMA
                   DMA_Cmd(USART1_RXD_MA_CHANNEL,ENABLE);

                   revFlag = TRUE;
         }

#if( USART1_RXD_MODE ==UART_SEND_DMA )
         // 全部数据发送完成,产生该标记
  if(USART_GetITStatus(USART1,USART_IT_TC)!=RESET)
  {
                    // 清除完成标记
    USART_ClearITPendingBit(USART1,USART_IT_TC);
    DMA_Cmd(USART1_TXD_MA_CHANNEL, DISABLE);
                   // 清除数据长度
    USART1_TXD_MA_CHANNEL->CNDTR = 0;
                   DMA_Cmd(USART1_TXD_MA_CHANNEL, ENABLE);
         }
#endif /* USART1_RXD_MODE */
}

/*********************************************************************************
* Name  : Uart1_Data_Process
*
* Brief : process the received data
*
* Input : processDat - the process function point
*
* Output : None
*
* Remark : None
***********************************************************************************/
void Uart1_Data_Process(pRevprocessDat)
{
         if(!revFlag)
                   return;
         revFlag = FALSE;

    processDat(datBuff);

}


/*********************************************************************************
* Name  : Uart1_Send_Bytes
*
* Brief : process the received data
*
* Input : dat - the data buffer
*
*          len - the count of data to be send
*
* Output : None
*
* Remark : None
***********************************************************************************/
voidUart1_Send_Bytes(uint8_t *dat,uint16_t len)
{
#if  0//( USART1_RXD_MODE == UART_SNED_REQ )
                   while(len--)
                   {
                            USART_SendData(USART1, *dat++);
                            /* Loop until the end of transmission */
                            while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);
                   }
#else
                       // 判断长度是否有效
   if(!len)
      return ;
   while(DMA_GetCurrDataCounter(USART1_TXD_MA_CHANNEL));

  // 检查DMA发送通道内是否还有数据
  if(dat)
    memcpy(sendBuff, dat,(len >DEBUG_PRINT_BUF_LEN ? DEBUG_PRINT_BUF_LEN:len));

  // DMA发送数据-要先关 设置发送长度 开启DMA
  DMA_Cmd(USART1_TXD_MA_CHANNEL, DISABLE);
  // 设置发送长度
  USART1_TXD_MA_CHANNEL->CNDTR = len;
  // 启动DMA发送
  DMA_Cmd(USART1_TXD_MA_CHANNEL, ENABLE);
#endif /**/
}

/*********************************************************************************
* Name  : Debug_Printf
*
* Brief : print the debug information
*
* Input : *fmt - the print data type
*
* Output : None
*
* Remark : just like theprintf() function
***********************************************************************************/
int Debug_Printf(const char*fmt, ...)
{
        uint32_t ulLen = 0;

#ifdef UART_DEBUG_PRINT

        va_list ap;
        // 开辟缓冲区
        // char *pBuf = (char*)OS_MALLOC(DEBUG_PRINT_BUF_LEN);
        char *pBuf = (char*)malloc(DEBUG_PRINT_BUF_LEN);
        va_start(ap,fmt);

        if(pBuf != NULL)
        {
            // 用虚拟打印函数实现
            ulLen = vsprintf(pBuf,fmt,ap);

            va_end(ap);
            // 从串口输出
            Uart1_Send_Bytes((uint8_t *)pBuf,ulLen );
            // 释放存储空间
            //OS_FREE(pBuf);
            free(pBuf);
        }

#endif /* UART_DEBUG_PRINT*/

        return ulLen;
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////
// USART2 相关API - 主要用于级联信号
/////////////////////////////////////////////////////////////////////////////////////////////////////////

/*********************************************************************************
* Name  : Uart2_Gpio_Init
*
* Brief : Initialized the uart2 port.
*
* Input : None
*
* Output : None
*
* Remark :PA3-TX,PA2-RX,banudrate=9600
***********************************************************************************/
void Uart2_Gpio_Init(void)
{
         GPIO_InitTypeDef        GPIO_InitStructure;
         USART_InitTypeDef     USART_InitStructure;
         NVIC_InitTypeDef   NVIC_InitStructure;

         // 开启串口时钟及端口时钟
         RCC_APB1PeriphClockCmd(USART2_RCC,ENABLE);
    RCC_AHBPeriphClockCmd(USART2_PORT_RCC,ENABLE);
    GPIO_PinAFConfig(USART2_MAP_PORT,UART2_TXD_PINSOURCE, UART2_AF_TXD);
    GPIO_PinAFConfig(USART2_MAP_PORT,UART2_RXD_PINSOURCE, UART2_AF_RXD);

      // 配置TX端口
         GPIO_InitStructure.GPIO_Pin  = UART2_TXD_PIN | UART2_RXD_PIN;
         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_OType =GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP;
         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
         GPIO_Init(USART2_MAP_PORT, &GPIO_InitStructure);


         // 配置串口模式
         USART_InitStructure.USART_BaudRate   = USART2_BAUDRATE;
         USART_InitStructure.USART_WordLength = USART_WordLength_8b;
         USART_InitStructure.USART_StopBits   = USART_Parity_No;
         USART_InitStructure.USART_HardwareFlowControl =USART_HardwareFlowControl_None;
         USART_InitStructure.USART_Mode = USART_Mode_Rx |USART_Mode_Tx;
         USART_Init(USART2,&USART_InitStructure);

    //TXE发送中断,TC传输完成中断,RXNE接收中断,PE奇偶错误中断,可以是多个
    USART_ITConfig(USART2,USART_IT_TC,DISABLE);
    // 设置总线空闲产生中断
   USART_ITConfig(USART2,USART_IT_IDLE,ENABLE);
    USART2->ICR |= 1<<4; //必须先清除IDLE中断,否则会一直进IDLE中断
    USART_ITConfig(USART2, USART_IT_RXNE,DISABLE);

    // 开启串口DMA接收
   USART_DMACmd(USART2,USART_DMAReq_Rx,ENABLE);
    // 开启串口2
    USART_Cmd(USART2,ENABLE);


    // 中断配置
    NVIC_InitStructure.NVIC_IRQChannel         = USART2_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPriority= 2;
    NVIC_InitStructure.NVIC_IRQChannelCmd      = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}

/*********************************************************************************
* Name  : Uart2_DMA_Init
*
* Brief : Initialized the uart2 DMA.
*
* Input : None
*
* Output : None
*
* Remark : None
***********************************************************************************/
void Uart2_DMA_Init(void)
{
         DMA_InitTypeDef DMA_Initstruct;

         // 开启DMA时钟
         RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
    //DMA1通道5配置
    DMA_DeInit(USART2_RXD_DMA_CHAN);

         // DMA配置
         // 外设地址
         DMA_Initstruct.DMA_PeripheralBaseAddr      = (uint32_t)(&USART2->RDR);
    // 内存地址
         DMA_Initstruct.DMA_MemoryBaseAddr      =(uint32_t)revUart2;
    // DMA传输方向单向
         DMA_Initstruct.DMA_DIR                  =DMA_DIR_PeripheralSRC;
    // 设置DMA在传输时缓冲区的长度
         DMA_Initstruct.DMA_BufferSize              =USART_RXD_BUF_SIZE;
    // 设置DMA的外设递增模式,一个外设
         DMA_Initstruct.DMA_PeripheralInc             =DMA_PeripheralInc_Disable;
    // 设置DMA的内存递增模式
         DMA_Initstruct.DMA_MemoryInc            =DMA_MemoryInc_Enable;
    // 外设数据字长
         DMA_Initstruct.DMA_PeripheralDataSize   = DMA_PeripheralDataSize_Byte;
    // 内存数据字长
         DMA_Initstruct.DMA_MemoryDataSize        =DMA_MemoryDataSize_Byte;
    // 设置DMA的传输模式
         DMA_Initstruct.DMA_Mode                                   =DMA_Mode_Normal;
    // 设置DMA的优先级别
         DMA_Initstruct.DMA_Priority                  =DMA_Priority_High; // DMA_Priority_VeryHigh;
         // 设置DMA的2个memory中的变量互相访问
         DMA_Initstruct.DMA_M2M                      =DMA_M2M_Disable;
   DMA_Init(USART2_RXD_DMA_CHAN,&DMA_Initstruct);

    // 开启DMA
         DMA_Cmd(USART2_RXD_DMA_CHAN,ENABLE);

#if( USART2_RXD_MODE == UART_SEND_DMA)
    // DMA通道TXD配置
    DMA_DeInit(USART2_TXD_DMA_CHAN);
    // 外设地址,串口1
    DMA_Initstruct.DMA_PeripheralBaseAddr =(uint32_t)(&USART1->TDR);
    // 发送内存地址
    DMA_Initstruct.DMA_MemoryBaseAddr     = (uint32_t)send2Buff;
    // 外设为传送数据目的地,即发送数据
    DMA_Initstruct.DMA_DIR =DMA_DIR_PeripheralDST;
    // 发送长度为0,即未有快递需要发送
    DMA_Initstruct.DMA_BufferSize = 0;
    // 初始化
    DMA_Init(USART2_TXD_DMA_CHAN,&DMA_Initstruct);
    // 使能串口发送完成中断
    USART_ITConfig(USART2, USART_IT_TC,ENABLE);
          DMA_Cmd(USART2_TXD_DMA_CHAN,ENABLE);
    // 使能DMA串口发送和接受请求
    USART_DMACmd(USART2,(USART_DMAReq_Tx|USART_DMAReq_Rx), ENABLE);
#endif /* USART1_RXD_MODE */
}

/*********************************************************************************
* Name  : UART2_Api_Init
*
* Brief : Initialized the uart1
*
* Input : None
*
* Output : None
*
* Remark : None
***********************************************************************************/
void UART2_Api_Init(void)
{
         Uart2_Gpio_Init();
         Uart2_DMA_Init();
}

/*********************************************************************************
* Name  : USART2_IRQHandler
*
* Brief : USART1 interrupt process handle
*
* Input : None
*
* Output : None
*
* Remark : None
***********************************************************************************/
void USART2_IRQHandler(void)
{
         uint8_t len = 0;

         if(USART_GetITStatus(USART2,USART_IT_IDLE) != RESET)
         {
        USART2->ICR |= 1<<4; // 清除中断
            DMA_Cmd(USART2_RXD_DMA_CHAN,DISABLE);

             len =USART_RXD_BUF_SIZE - DMA_GetCurrDataCounter(USART2_RXD_DMA_CHAN);

             if(len >USART_RXD_BUF_SIZE)
                                               len =USART_RXD_BUF_SIZE;

            memcpy(datUart2Buff,revUart2,len);

                   // 重新设置接收数据个数
                   USART2_RXD_DMA_CHAN->CNDTR =USART_RXD_BUF_SIZE;
                   // 开启DMA
                   DMA_Cmd(USART2_RXD_DMA_CHAN,ENABLE);

                   revUart2Flag = TRUE;
         }

#if( USART2_RXD_MODE ==UART_SEND_DMA )
         // 全部数据发送完成,产生该标记
  if(USART_GetITStatus(USART2,USART_IT_TC)!=RESET)
  {
                    // 清除完成标记
     USART_ClearITPendingBit(USART2,USART_IT_TC);
     DMA_Cmd(USART2_TXD_DMA_CHAN,DISABLE);
                   // 清除数据长度
                USART2_TXD_DMA_CHAN->CNDTR= 0;
                   DMA_Cmd(USART2_TXD_DMA_CHAN, ENABLE);
         }
#endif /* USART1_RXD_MODE */
}

/*********************************************************************************
* Name   :Uart2_Data_Process
*
* Brief : process the received data
*
* Input : processDat - the process function point
*
* Output : None
*
* Remark : None
***********************************************************************************/
void Uart2_Data_Process(pRevprocessDat)
{
         if(!revUart2Flag)
                   return;
         revUart2Flag = FALSE;

    processDat(datUart2Buff);
}

/*********************************************************************************
* Name  : Uart2_Send_Bytes
*
* Brief : process the received data
*
* Input : dat - the data buffer
*
*          len - the count of data to be send
*
* Output : None
*
* Remark : None
***********************************************************************************/
void Uart2_Send_Bytes(uint8_t*dat,uint16_t len)
{
#if (USART2_RXD_MODE ==UART_SNED_REQ)
                   while(len--)
                   {
                            USART_SendData(USART2, *dat++);
                            /* Loop until the end of transmission */
                            while(USART_GetFlagStatus(USART2,USART_FLAG_TXE) == RESET);
                   }
#else
     // 判断长度是否有效
          if(!len)
             return ;
          while(DMA_GetCurrDataCounter(USART2_TXD_DMA_CHAN));

         // 检查DMA发送通道内是否还有数据
         if(dat)
           memcpy(send2Buff,dat,(len > USART2_TX_BUF_SIZE ? USART2_TX_BUF_SIZE:len));

         // DMA发送数据-要先关 设置发送长度 开启DMA
         DMA_Cmd(USART2_TXD_DMA_CHAN, DISABLE);
         // 设置发送长度
         USART2_TXD_DMA_CHAN->CNDTR = len;
         // 启动DMA发送
         DMA_Cmd(USART2_TXD_DMA_CHAN, ENABLE);

#endif /* */
}


本次代码是为了测试用的,所以有些地方写的比较随意,变量命名也不是太规范,这里检讨一下。串口接收到数据处理函数用的是回调函数,方便修改,在使用时,只要调用相关的函数指针即可。关于函数指针的类型,在下面Dsk_Usart.h中有定义。

Dsk_Usart.h源文件

笔者习惯一个.c文件跟一个.h文件,方便移植,也方面管理。用到哪个模块直接把两个文件一拷贝,再在相关文件中包含.h文件,就可以直接用,比较方便。有人会说有没有必要,一个.C跟一个.H,这个怎么说呢?看个人习惯吧,编程这玩意只有最适合项目工程的,没有说最好的。一般按功能模块划分,源文件感觉长了,还可以再细分不同的模块,笔者认为,加个头文件,至少不用在.c文件中调用某个函数再用extern声明那个外部函数,至少声明不会乱掉。

关于Dsk_Usart.h中主要是接口的声明,API的声明等,其源代码如下所示:

/*********************************************************************************
* Copyright (C) 2000-2018 Mark Xu Work Studio,All Rights Reserved.
*
* File   : Dsk_Uart.h
*
* Brief  : the defination and declarations for uart module
*
* Author :Mark Xu
*
* Remark :None
---------------------------------------------------------------------------------
* Modify Content: NULL
*
* Modify Date   : xxxx/xx/xx/     xx:xx
*
* Modify staff  :xx xx
**********************************************************************************/
#ifndef __DSK_UART_H__
#define __DSK_UART_H__

#ifdef __cplusplus
extern "C" {
#endif /*__cplusplus  */

#ifdef DSKUART_IMPLATION
        #define DSKUART_EXTERN
#else
        #define DSKUART_EXTERN                extern
#endif

#include "stdint.h"
#include "stm32f0xx.h"

/*********************************************************************************
* Port defination
**********************************************************************************/
// Config the uart1
#define USART1_RCC                                              RCC_APB2Periph_USART1
#define USART1_PORT_RCC                     RCC_AHBPeriph_GPIOA
#define USART1_BAUDRATE                     (9600)
#define USART1_RXD_MA_CHANNEL               DMA1_Channel3
#define USART1_MAP_PORT                     GPIOA
#define USART1_TXD_MA_CHANNEL               DMA1_Channel2
// #define USART1_MAP_PORT                     GPIOA

#define UART1_TXD_PORT                            GPIOA
#define UART1_TXD_PIN                             GPIO_Pin_9
#define UART1_TXD_PINSOURCE                       GPIO_PinSource9
#define UART1_AF_TXD                        GPIO_AF_1

#define UART1_RXD_PORT                            GPIOA
#define UART1_RXD_PIN                             GPIO_Pin_10
#define UART1_RXD_PINSOURCE                       GPIO_PinSource10
#define UART1_AF_RXD                        GPIO_AF_1

// Config the uart2
#define USART2_RCC                          RCC_APB1Periph_USART2
#define USART2_PORT_RCC                     RCC_AHBPeriph_GPIOA
#define USART2_BAUDRATE                     (9600)
#define USART2_RXD_DMA_CHAN                 DMA1_Channel5
#define USART2_TXD_DMA_CHAN                 DMA1_Channel4
#define USART2_MAP_PORT                     GPIOA

#define UART2_TXD_PORT                            GPIOA
#define UART2_TXD_PIN                             GPIO_Pin_2
#define UART2_TXD_PINSOURCE                       GPIO_PinSource2
#define UART2_AF_TXD                        GPIO_AF_1

#define UART2_RXD_PORT                            GPIOA
#define UART2_RXD_PIN                             GPIO_Pin_3
#define UART2_RXD_PINSOURCE                       GPIO_PinSource3
#define UART2_AF_RXD                        GPIO_AF_1

/*********************************************************************************
* Exported Typedef
**********************************************************************************/
// 接收命令处理函数
typedef void (*pRev)(uint8_t *);

/*********************************************************************************
* Exported Functions
**********************************************************************************/
DSKUART_EXTERN void UART1_Api_Init(void);
DSKUART_EXTERN void Uart1_Send_Bytes(uint8_t *dat,uint16_t len);
// 处理接收到的数据
DSKUART_EXTERN void Uart1_Data_Process(pRev processDat);
// 打印调试信息
DSKUART_EXTERN int Debug_Printf(const char *fmt, ...);

//---------------------------------------------------------------------------------------------------------------------------------
// 关于UART2相关API
//---------------------------------------------------------------------------------------------------------------------------------
DSKUART_EXTERN void UART2_Api_Init(void);
DSKUART_EXTERN void Uart2_Send_Bytes(uint8_t *dat,uint16_t len);
DSKUART_EXTERN void Uart2_Data_Process(pRev processDat);

#ifdef __cplusplus
}
#endif /* __cplusplus */

#endif /* __DSK_UART_H__ */

结语

本篇文档主要总结一下如果设置串口的空闲中断+DMA的应用方式,也相当于代码备份吧,具体要怎么测试,测试结果如果,这里不再说细述,如果读者有兴趣,可以自己设置相关的测试。


关键字:
阅读原文 浏览量:187 收藏:1
此内容由EEWORLD论坛网友 懒猫爱飞 原创,如需转载或用于商业用途需征 得作者同意并注明出处

上一篇: SD卡WAV音乐播放器DIY
下一篇: 旧LED灯泡拆解及改造

评论

登录 | 注册 需要登陆才可发布评论    
评论加载中......
电子工程世界版权所有 京ICP证060456号 京ICP备10001474号 电信业务审批[2006]字第258号函 京公海网安备110108001534 Copyright ? 2005-2017 EEWORLD.com.cn, Inc. All rights reserved