zoukankan      html  css  js  c++  java
  • STM32之DMA实例

    DMA简介:

      DMA(Direct Memory Access,直接存储器存取),是一种可以减轻CPU工作量的数据存取方式,如今被广泛的使用。它在传输数据的同时,CPU可以做其他事,比如数据运算或者响应中断等,DMA就给CPU分担了不少的工作量!

    DMA工作分析:

                                                                      

    如图,我们可以看到STM32内核,存储器,外设及DMA的连接,这些硬件最终通过各种各样的线连接到总线矩阵中,硬件结构之间的数据转移都经过总线矩阵的协调,使各个外设和谐的使用总线来传输数据。

    下面看有与没有DMA的情况下,ADC采集的数据是怎样存放到SRAM中的?

    1.如果没有DMA,CPU传输数据还要以内核作为中转站,比如要将ADC采集的数据转移到到SRAM中,这个过程是这样的:内核通过DCode经过总线矩阵协调,使用AHB把外设ADC采集的数据,然后内核,DCode再通过总线矩阵协调把数据存放到内存SRAM中。

    2.有DMA的话,DMA控制器的DMA总线与总线矩阵协调,使用AHB把外设ADC采集的数据经由DMA通道存放到SRAM中,这个数据的传输过程中,完全不需要内核的参与,也就是CPU的参与,不过DMA传输时要对DMA外设发出请求,才会触发其工作。

    下面我是通过串口通信的例子来学习DMA的!

    主函数main.c:

    1. #include <stdio.h>
    2. uint8_t sendbuff[500];
    3. uint16_t i;
    4. int main()
    5. {
    6. printf_init();
    7. dma_init();
    8. for(i=0;i<500;i++)
    9. {
    10. sendbuff[i] =0xaf;
    11. }
    12. USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE);
    13. LED3_ON;
    14.  
    15. while(1);

    这个主函数实现的功能是利用DMA把数据(数组,数组里面的存放了500个AF字符)从内存转移到外设(串口),最后通过串口传输到我们的PC上显示。为了证明DMA在搬运数据的同时,CPU还可以做其他事,于是将LED3点亮,来测试一下。主函数至于为啥加while(1),才会产生中断?还不明白。。。

    DMA配置dma.c:

    1. #include "stm32f10x.h"
    2. #include "stm32f10x_rcc.h"
    3. #include "stm32f10x_usart.h"
    4. #include "stm32f10x_dma.h"
    5. #include "misc.h"
    6. #include "dma.h"
    7. void dma_init()
    8. {
    9.  
    10. DMA_InitTypeDef DMA_InitStructure;
    11. RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
    12. NVIC_Config();
    13. /*DMA配置*/
    14. DMA_InitStructure.DMA_PeripheralBaseAddr = USART1_DR_Base;//串口数据寄存器地址
    15. DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)sendbuff; //内存地址(要传输的变量的指针)
    16. DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; //方向(从内存到外设)
    17. DMA_InitStructure.DMA_BufferSize = 500; //传输内容的大小
    18. DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址不增
    19. DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存地址自增
    20. DMA_InitStructure.DMA_PeripheralDataSize =
    21. DMA_PeripheralDataSize_Byte ; //外设数据单位
    22. DMA_InitStructure.DMA_MemoryDataSize =
    23. DMA_MemoryDataSize_Byte ; //内存数据单位
    24. DMA_InitStructure.DMA_Mode = DMA_Mode_Normal ; //DMA模式:一次传输,循环
    25. DMA_InitStructure.DMA_Priority = DMA_Priority_Medium ; //优先级:高
    26. DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //禁止内存到内存的传输
    27.  
    28. DMA_Init(DMA1_Channel4, &DMA_InitStructure); //配置DMA1的4通道
    29. DMA_Cmd(DMA1_Channel4,ENABLE);
    30. DMA_ITConfig(DMA1_Channel4,DMA_IT_TC,ENABLE);//配置DMA发送完成后产生中断
    31.  
    32. }
    33.  
    34. static void NVIC_Config(void)
    35. {
    36. NVIC_InitTypeDef NVIC_InitStructure;
    37. /*中断配置*/
    38. NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
    39.  
    40. NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel4_IRQn;
    41. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    42. NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    43. NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    44. NVIC_Init(&NVIC_InitStructure);
    45. }

    这里的串口数据寄存器地址配置是参考STM32的datasheet来设置的,USART1_DR_Base是一个宏,#define USART1_DR_Base 0x40013804,至于地址为啥是这个,请看下图:



    存储器映射图,找到USART1的基址,再看USART1数据寄存器偏移地址就可以知道串口1的地址了。


    至于在配置DMA1通道时,那里为啥是通道4,而不是其它通道,请看下图:


    dma.h:

    1. #ifndef _dma_H
    2. #define _dma_H
    3. #include "stm32f10x.h"
    4. #define USART1_DR_Base 0x40013804;
    5. extern uint8_t sendbuff[500];
    6. static void NVIC_Config(void);
    7. void dma_init(void);
    8.  
    9. #endif

    串口配置:printf.c

    1. #include "printf.h"
    2. #include "stm32f10x.h"
    3. #include "stm32f10x_rcc.h"
    4. #include "stm32f10x_gpio.h"
    5. #include "stm32f10x_usart.h"
    6. #include "misc.h"
    7.  
    8. int fputc(int ch,FILE *f)
    9. {
    10. while(USART_GetFlagStatus(USART1,USART_FLAG_TC) != SET);
    11. USART_SendData(USART1,(unsigned char)ch);
    12. while(USART_GetFlagStatus(USART1,USART_FLAG_TC) != SET);
    13. return (ch);
    14. }
    15.  
    16. void printf_init(void)
    17. {
    18. GPIO_InitTypeDef GPIO_InitStructure;
    19. USART_InitTypeDef USART_InitStructure;
    20.  
    21. /*config USART clock*/
    22. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
    23. RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
    24. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD,ENABLE);
    25. /*USART1 GPIO config*/
    26. GPIO_InitStructure.GPIO_Pin= GPIO_Pin_9;
    27. GPIO_InitStructure.GPIO_Mode= GPIO_Mode_AF_PP; //复用推挽输出
    28. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    29. GPIO_Init(GPIOA,&GPIO_InitStructure);
    30.  
    31. GPIO_InitStructure.GPIO_Pin= GPIO_Pin_10;
    32. GPIO_InitStructure.GPIO_Mode= GPIO_Mode_IN_FLOATING; //复用开漏输入
    33. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    34. GPIO_Init(GPIOA,&GPIO_InitStructure);
    35.  
    36. GPIO_InitStructure.GPIO_Pin=GPIO_Pin_3;
    37. GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
    38. GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
    39. GPIO_Init(GPIOD,&GPIO_InitStructure);
    40. /*USART1 mode Config*/
    41. USART_InitStructure.USART_BaudRate = 9600;
    42. USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    43. USART_InitStructure.USART_StopBits = USART_StopBits_1;
    44. USART_InitStructure.USART_Parity = USART_Parity_No;
    45. USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    46. USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    47. USART_Init(USART1,&USART_InitStructure);
    48. USART_Cmd(USART1,ENABLE);

    printf.h:

    1. #ifndef __printf_H
    2. #define __printf_H
    3.  
    4. #include "stm32f10x.h"
    5. #include <stdio.h>
    6. #define LED3_ON GPIO_SetBits(GPIOD,GPIO_Pin_3)
    7. #define LED3_OFF GPIO_ResetBits(GPIOD,GPIO_Pin_3)
    8. void printf_init(void);
    9. int fputc(int ch,FILE *f);
    10.  
    11. #endif

    中断代码:stm32f10x_it.c

    1. #include "stm32f10x_it.h"
    2. #include "stm32f10x.h"
    3. #include "printf.h"
    4. void DMA1_Channel4_IRQHandler(void)
    5. {
    6. if(DMA_GetFlagStatus(DMA1_FLAG_TC4)==SET)
    7. {
    8. LED3_OFF;
    9. DMA_ClearFlag(DMA1_FLAG_TC4);
    10. }
    11. }

    效果图:

  • 相关阅读:
    cas 重复登录问题解决了。但是两个页签都是已登录状态,一个页签退出,另外一个页签正常访问应用程序。会出现下面报错信息。待解决
    flask 结合cas 出现的问题
    实验场景:两个页面登录状态下,一个logout.然后重新登录,一个页面刷新
    flask 生成excel并下载
    为什么不能在shell脚本中执行source /etc/profile或者source ~/.bashrc问题?
    centos7实现ssh免秘钥分发
    Drbd+Heatbeat实现NFS共享文件存储高可用
    docker监控方案实践(cadvisor+influxdb+grafana)
    centos7.5部署ELk
    keepalived+haproxy实现高可用负载均衡
  • 原文地址:https://www.cnblogs.com/schips/p/11039791.html
Copyright © 2011-2022 走看看