1基础知识点
DMA
DMA(Direct Memory Access),即直接内存存储,在一些数据的传输中,如串口、SPI等,采用DMA方式,传输过程不需要CPU参与,可用让CPU有更多的时间处理其他的事情。

STM32F4的DMA通道选择如下:

接下来的程序思路如下:

2编程要点
2.1DMA发送
2.1.1串口DMA发送配置
由于是发送不定长的数据,先不需要配置发送的长度,在每次的发送时,再配置。

//=======================================//串口DMA发送配置//=======================================void dma_uart_tx_init(){    DMA_InitTypeDef  DMA_InitStructure;    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE);//DMA2时钟使能    DMA_DeInit(Uart_Tx_DMAStream);//使用----->DMA2_Stream7    while (DMA_GetCmdStatus(Uart_Tx_DMAStream) != DISABLE){}//等待DMA可配置    /* 配置 DMA Stream */    DMA_InitStructure.DMA_Channel            = DMA_Channel_4;              //通道选择    DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&USART1->DR;           //目的:DMA外设地址    DMA_InitStructure.DMA_Memory0BaseAddr    = (u32)SendBuff;              //源:DMA存储器0地址    DMA_InitStructure.DMA_DIR                = DMA_DIR_MemoryToPeripheral; //方向:存储器到外设模式    //DMA_InitStructure.DMA_BufferSize       = BUF_SIZE;                   //长度:数据传输量(先不配置)    DMA_InitStructure.DMA_PeripheralInc      = DMA_PeripheralInc_Disable;  //外设非增量模式    DMA_InitStructure.DMA_MemoryInc          = DMA_MemoryInc_Enable;       //存储器增量模式    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;//外设数据长度:8位    DMA_InitStructure.DMA_MemoryDataSize     = DMA_MemoryDataSize_Byte;    //存储器数据长度:8位    DMA_InitStructure.DMA_Mode               = DMA_Mode_Normal;            //使用普通模式    DMA_InitStructure.DMA_Priority           = DMA_Priority_Medium;        //DMA优先级:中等优先级    DMA_InitStructure.DMA_FIFOMode           = DMA_FIFOMode_Disable;       //FIFO模式    DMA_InitStructure.DMA_FIFOThreshold      = DMA_FIFOThreshold_Full;     //FIFO大小    DMA_InitStructure.DMA_MemoryBurst        = DMA_MemoryBurst_Single;     //存储器单次传输    DMA_InitStructure.DMA_PeripheralBurst    = DMA_PeripheralBurst_Single; //外设单次传输    DMA_Init(Uart_Tx_DMAStream, &DMA_InitStructure);//初始化DMA Stream    //中断配置    DMA_ITConfig(Uart_Tx_DMAStream,DMA_IT_TC,ENABLE);  //配置DMA发送完成后产生中断    NVIC_InitTypeDef NVIC_InitStructure;    NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream7_IRQn;//    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=7;//抢占优先级8    NVIC_InitStructure.NVIC_IRQChannelSubPriority =0;       //子优先级0    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;         //IRQ通道使能    NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器    USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE);  //使能串口1的DMA发送    DMA_Cmd (Uart_Tx_DMAStream,DISABLE);//先不要使能DMA!}

2.1.2DMA发送完成中断
DMA发送完成后,触发DMA发送完成中断,这里可用释放自定义的DMA发送完成信号量,表明下次的DMA传输可用进行。

//=======================================//DMA发送完成中断服务程序//=======================================void DMA2_Stream7_IRQHandler(void){    BaseType_t xHigherPriorityTaskWoken;    //printf("ooooooo\r\n");    if(DMA_GetITStatus(Uart_Tx_DMAStream,DMA_IT_TCIF7)!= RESET) //检查DMA传输完成中断 DMA_IT_TCIF7    {        DMA_ClearITPendingBit(Uart_Tx_DMAStream,DMA_IT_TCIF7);        //printf("dma tx ok\r\n");        if(uartDMATCSemaphore!=NULL)        {            //释放二值信号量            xSemaphoreGiveFromISR(uartDMATCSemaphore,&xHigherPriorityTaskWoken);    //释放DMA传输完成二值信号量        }        portYIELD_FROM_ISR(xHigherPriorityTaskWoken);//如果需要的话进行一次任务切换    }}

2.1.3DMA发送函数接口

//=======================================//串口DMA发送函数//=======================================void uart_DMA_send(u8 *str,u16 ndtr){    u8 i;    u8 *p=str;    while(xSemaphoreTake(uartDMATCSemaphore,2)!=pdTRUE);//获取信号量,等待DMA发送可用    DMA_Cmd(Uart_Tx_DMAStream, DISABLE);                      //关闭DMA传输    while (DMA_GetCmdStatus(Uart_Tx_DMAStream) != DISABLE){}    //确保DMA可以被设置      DMA_SetCurrDataCounter(Uart_Tx_DMAStream,ndtr);          //数据传输量    for(i=0;i<ndtr;i++){        SendBuff[i]=*p++;}    DMA_Cmd(Uart_Tx_DMAStream, ENABLE);                      //开启DMA传输}

2.2DMA接收
2.2.1串口DMA接收配置
需要配置一个接收地址和一个接收长度,用于DMA接收数据的暂存。

//=======================================//串口DMA接收配置//=======================================void dma_uart_rx_init(){    DMA_InitTypeDef  DMA_InitStructure;    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE);//DMA2时钟使能    DMA_DeInit(Uart_Rx_DMAStream);//使用----->DMA2_Stream5    while (DMA_GetCmdStatus(Uart_Rx_DMAStream) != DISABLE){}//等待DMA可配置    /* 配置 DMA Stream */    DMA_InitStructure.DMA_Channel            = DMA_Channel_4;              //通道选择    DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&USART1->DR;           //源:DMA外设地址    DMA_InitStructure.DMA_Memory0BaseAddr    = (u32)ReceiveBuff;           //目的:DMA存储器0地址    DMA_InitStructure.DMA_DIR                = DMA_DIR_PeripheralToMemory; //方向:外设到存储器模式    DMA_InitStructure.DMA_BufferSize         = BUF_SIZE;                   //长度:数据传输量    DMA_InitStructure.DMA_PeripheralInc      = DMA_PeripheralInc_Disable;  //外设非增量模式    DMA_InitStructure.DMA_MemoryInc          = DMA_MemoryInc_Enable;       //存储器增量模式    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;//外设数据长度:8位    DMA_InitStructure.DMA_MemoryDataSize     = DMA_MemoryDataSize_Byte;    //存储器数据长度:8位    DMA_InitStructure.DMA_Mode               = DMA_Mode_Normal;            //使用普通模式    DMA_InitStructure.DMA_Priority           = DMA_Priority_Medium;        //DMA优先级:中等优先级    DMA_InitStructure.DMA_FIFOMode           = DMA_FIFOMode_Disable;       //FIFO模式    DMA_InitStructure.DMA_FIFOThreshold      = DMA_FIFOThreshold_Full;     //FIFO大小    DMA_InitStructure.DMA_MemoryBurst        = DMA_MemoryBurst_Single;     //存储器单次传输    DMA_InitStructure.DMA_PeripheralBurst    = DMA_PeripheralBurst_Single; //外设单次传输    DMA_Init(Uart_Rx_DMAStream, &DMA_InitStructure);//初始化DMA Stream    USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE);  //使能串口1的DMA接收    DMA_Cmd (Uart_Rx_DMAStream,ENABLE);//使能          }

2.2.2串口空闲中断
串口空闲中断的作用与上一篇FreeRTOS例程3-串口中断接收不定长的数据与二值信号量的使用介绍的一样,都是在发送完一串字符后被触发,这次由于使用了DMA接收,所以接收的数据在DMA缓冲区,且接收的数据长度可用根DMA接收通道的总长度与剩余长度的差值来计算,将接收的数据复制出来使用即可,同时释放自定义的串口空闲信号量,以便其它任务可用及时获取串口接收到的数据。

//=======================================//串口1空闲中断服务程序,用于DMA接收//=======================================void USART1_IRQHandler(void)                    {    uint8_t data;//接收数据暂存变量    BaseType_t xHigherPriorityTaskWoken;    if(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET)//空闲中断{        data = USART1->SR;        data = USART1->DR;        DMA_Cmd(Uart_Rx_DMAStream,DISABLE);//关闭DMA接收        while (DMA_GetCmdStatus(Uart_Rx_DMAStream) != DISABLE){}    //确保DMA可以被设置        rx_cnt = BUF_SIZE - DMA_GetCurrDataCounter(Uart_Rx_DMAStream);//得到真正接收数据个数          DMA_SetCurrDataCounter(Uart_Rx_DMAStream,BUF_SIZE);//重新设置接收数据个数            //printf("rx_cnt:%d\r\n",rx_cnt);        memcpy(rxbuf,ReceiveBuff,rx_cnt);//先复制出来,防止下次的数据来了之后将其覆盖        DMA_ClearFlag(Uart_Rx_DMAStream,DMA_FLAG_TCIF5 | DMA_FLAG_FEIF5 | DMA_FLAG_DMEIF5 | DMA_FLAG_TEIF5 | DMA_FLAG_HTIF5);//这里的各种标志还没搞懂        DMA_Cmd(Uart_Rx_DMAStream,ENABLE); //开启DMA接收        if(uartRxIDLESemaphore!=NULL)        {            //printf("nnnnnnn\r\n");            //释放二值信号量            xSemaphoreGiveFromISR(uartRxIDLESemaphore,&xHigherPriorityTaskWoken);//释放串口空闲中断二值信号量        }        portYIELD_FROM_ISR(xHigherPriorityTaskWoken);//如果需要的话进行一次任务切换}}

2.3串口配置与测试任务
2.3.1串口配置
基础的GPIO配置,以及串口空闲中断配置,并调用上面的串口DMA发送与接收配置。

//=======================================//串口配置//=======================================void uart_init(u32 bound){    //GPIO端口设置    GPIO_InitTypeDef GPIO_InitStructure;    USART_InitTypeDef USART_InitStructure;    NVIC_InitTypeDef NVIC_InitStructure;    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE); //使能GPIOA时钟    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//使能USART1时钟    //串口1对应引脚复用映射    GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1); //GPIOA9复用为USART1    GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1); //GPIOA10复用为USART1    //USART1端口配置    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_9 | GPIO_Pin_10; //GPIOA9与GPIOA10    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF;             //复用功能    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;         //速度50MHz    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;            //推挽复用输出    GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP;             //上拉    GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化PA9,PA10    //USART1 初始化设置    USART_InitStructure.USART_BaudRate            = bound;                         //波特率设置    USART_InitStructure.USART_WordLength          = USART_WordLength_8b;           //字长为8位数据格式    USART_InitStructure.USART_StopBits            = USART_StopBits_1;              //一个停止位    USART_InitStructure.USART_Parity              = 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); //初始化串口1    //DMA Config    dma_uart_tx_init();//串口DMA发送配置    dma_uart_rx_init();//串口DMA接收配置    USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);//开启串口空闲中断    //Usart1 NVIC 配置    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;       //串口1中断通道    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=6; //抢占优先级8    NVIC_InitStructure.NVIC_IRQChannelSubPriority =0;       //子优先级0    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;         //IRQ通道使能    NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器    USART_Cmd(USART1, ENABLE);  //使能串口1}

2.3.2测试任务
创建DMA发送完成信号量和串口空闲信号量,并先释放DMA发送完成信号量,用于第一次DMA发送时获取信号量。然后测试两条DMA发送不定长字符串,最后测试DMA接收不定长字符串。

//打印任务函数(测试任务)void print_task(void *pvParameters){    //创建二值信号量    uartDMATCSemaphore = xSemaphoreCreateBinary();    uartRxIDLESemaphore = xSemaphoreCreateBinary();    xSemaphoreGive(uartDMATCSemaphore);    u8 str1[]="ma nong ai xue xi\r\n";    uart_DMA_send(str1,sizeof(str1));    u8 str2[]="xxpcb.github.io\r\n";    uart_DMA_send(str2,sizeof(str2));    BaseType_t err = pdFALSE;    while(1)    {        err=xSemaphoreTake(uartRxIDLESemaphore,5);  //获取信号量        if(err==pdTRUE)                         //获取信号量成功        {              uart_DMA_send("receive:",sizeof("receive:"));            uart_DMA_send(rxbuf,rx_cnt);            uart_DMA_send("\r\n",sizeof("\r\n"));            rx_cnt=0;        }    }}

3实验结果
通过串口助手,可以先接收到DMA发送的两个字符串(第一条hello是测试串口的,不是DMA发的),然后通过串口调试助手发送两次nice to meet you,测试DMA接收。

helloma nong ai xue xixxpcb.github.ioreceive:nice to meet youreceive:nice to meet you

完整工程代码已保存至GitHub:https://github.com/xxpcb/FreeRTOS-STM32F407-examples

©著作权归作者所有:来自51CTO博客作者mb5fdb1021b5992的原创作品,如需转载,请注明出处,否则将追究法律责任

更多相关文章

  1. 安卓串口中InputStream数据接收不完整
  2. Android跨进程通信IPC之2——Linux的跨进程通信详解
  3. Android(安卓)USB转串口编程
  4. android串口的使用
  5. 串口-关于vendor-id和product-id的查找
  6. Android(安卓)串口通讯集成
  7. Android(安卓)与蓝牙串口通讯
  8. [RK3288]串口开发之JNI环境搭建(基于android8.1使用android studi
  9. Android串口开发初体验(windows开发环境)

随机推荐

  1. android 小部件 AndroidManifest.xml
  2. Android自学笔记(番外篇):全面搭建Linux环境
  3. Android(安卓)Intent
  4. android 2.3 电量管理
  5. QT Creator 构建android apk失败问题
  6. Linux 下进行Android开发环境搭建
  7. android TabWidget 位置
  8. Android练习—修改背景颜色
  9. Android 中支持的几种传感器
  10. 【android】shape的使用