前言

DMA即直接内存存取。我理解它就是一个“交通部长”抑或是一个“搬运工”,协助CPU存储或读取数据。既然它的主要工作就是“搬运”数据,服务对象自然就是内存(不太严格的说法吧,STM32中Flash闪存也可成为DMA的服务对象)。

问题1   DMA传输数量寄存器DMA_CNDTRx的含义

描述

在中文版本参考手册里,寄存器DMA_CNDTRx有如下解释:

对于“指示待传输字节数目”的解释,我有些疑惑,因为在参考手册DMA主要特性中又是这么说的:可编程的数据传输数目:最大为65535.同样的,我在英文版本参考手册里也看到如下:

所以寄存器DMA_CNDTRx的内容是代表哪个意义,待传输字节数目还是待传输单位数目?

实验

设计DMA从内存搬运数据到内存,数据为u16类型,往DMA_CNDTRx里写入4. 即

u16 srcTable[TABLE_LENGTH] = {0x1234,0x2345,0x3456,0x4567};作为源数据

u16 dstTable[TABLE_LENGTH] = {0};作为目的

可以想象,如果4代表的是4个字节,则只能搬移0x1234,0x2345;否则,全部数据都被复制过去了。

 #include "STM32f10x_lib.h"
#include "stdio.h" void RCC_Configuration(void);
void GPIO_Configuration(void);
void USART_Configuration(void);
void DMA_Configuration(void); #define TABLE_LENGTH 4
u16 srcTable[TABLE_LENGTH] = {0x1234,0x2345,0x3456,0x4567};
u16 dstTable[TABLE_LENGTH] = {}; int main(void)
{
RCC_Configuration();
GPIO_Configuration();
USART_Configuration();
printf("before:0x%x,0x%x,0x%x,0x%x\r\n",dstTable[],dstTable[],dstTable[],dstTable[]);
DMA_Configuration();
while(DMA_GetFlagStatus(DMA1_FLAG_TC5) == RESET);
printf("after:0x%x,0x%x,0x%x,0x%x\r\n",dstTable[],dstTable[],dstTable[],dstTable[]);
while();
} /*RCC*/
void RCC_Configuration(void)
{
ErrorStatus HSEStartUpStatus;
/*默认状态*/
RCC_DeInit();
/*HSE使能并等待起震*/
RCC_HSEConfig(RCC_HSE_ON);
HSEStartUpStatus = RCC_WaitForHSEStartUp();
/*HSE外部高速晶振启动成功*/
if(HSEStartUpStatus == SUCCESS)
{
/*配置HCLK,PCLK1,PCLK2分频*/
RCC_HCLKConfig(RCC_SYSCLK_Div1);
RCC_PCLK2Config(RCC_HCLK_Div1);
RCC_PCLK1Config(RCC_HCLK_Div2);
/**/
FLASH_SetLatency(FLASH_Latency_2);
FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
/*配置PLL*/
RCC_PLLConfig(RCC_PLLSource_HSE_Div1,RCC_PLLMul_9);
RCC_PLLCmd(ENABLE);
while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
/*选择SYSCLK时钟源为PLL*/
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
while(RCC_GetSYSCLKSource() != 0x08);
}
/*使能外设时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA ,ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
}
/*GPIO*/
void GPIO_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/*USART1*/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;/*USART1 TXD*/
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA,&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;/*USART1 RXD*/
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA,&GPIO_InitStructure);
}
/*USART*/
void USART_Configuration(void)
{
USART_InitTypeDef USART_InitStructure; USART_InitStructure.USART_BaudRate = ;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_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(USART1,&USART_InitStructure); USART_Cmd(USART1,ENABLE);
}
int fputc(int ch, FILE *f)
{
USART_SendData(USART1,(u8)ch);
while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);
return ch;
} /*DMA*/
void DMA_Configuration(void)
{
DMA_InitTypeDef DMA_InitStructure; DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)srcTable;
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)dstTable;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC ;
DMA_InitStructure.DMA_BufferSize = ;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable ;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable ;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord ;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh ;
DMA_InitStructure.DMA_M2M = DMA_M2M_Enable ;
DMA_Init(DMA1_Channel5,&DMA_InitStructure); DMA_Cmd(DMA1_Channel5,ENABLE);
}

软件仿真结果显示,全部数据都被复制过去了

结论

参考手册对DMA_CNDTRx寄存器描述有误,其代表的是待传输单位(依赖于配置寄存器的设置,字节、半字、字)数目。

问题2   DMA的外设请求信号

描述

在DMA请求映像中,固定的几个外设的请求映像,通过或门连接到一个逻辑选择器中,选择器的输入另外还连接着软件可控的MEM2MEM位。此外逻辑选择器有一个EN使能信号。选择器出来就是DMA各个通道的请求。如下图所示。

试想,如果DMA1通道5由USART1_RX产生请求信号,然后DMA将内存上的数据搬移到内存上另外一个地方,这是否可行?

实验

将上一个实验的请求由软件触发换成USART1_RX。

u16 srcTable[TABLE_LENGTH] = {0x1234,0x2345,0x3456,0x4567};

u16 dstTable[TABLE_LENGTH] = {0};

如果可行,dstTable将变成srcTable中的值。

 #include "STM32f10x_lib.h"
#include "stdio.h" void RCC_Configuration(void);
void GPIO_Configuration(void);
void USART_Configuration(void);
void DMA_Configuration(void); #define TABLE_LENGTH 4
u16 srcTable[TABLE_LENGTH] = {0x1234,0x2345,0x3456,0x4567};
u16 dstTable[TABLE_LENGTH] = {}; int main(void)
{
RCC_Configuration();
GPIO_Configuration();
USART_Configuration();
printf("before:0x%x,0x%x,0x%x,0x%x\r\n",dstTable[],dstTable[],dstTable[],dstTable[]);
DMA_Configuration();
while(DMA_GetFlagStatus(DMA1_FLAG_TC5) == RESET);
printf("%d\r\n",DMA_GetFlagStatus(DMA1_FLAG_GL1));
printf("after:0x%x,0x%x,0x%x,0x%x\r\n",dstTable[],dstTable[],dstTable[],dstTable[]);
while();
} /*RCC*/
void RCC_Configuration(void)
{
ErrorStatus HSEStartUpStatus;
/*默认状态*/
RCC_DeInit();
/*HSE使能并等待起震*/
RCC_HSEConfig(RCC_HSE_ON);
HSEStartUpStatus = RCC_WaitForHSEStartUp();
/*HSE外部高速晶振启动成功*/
if(HSEStartUpStatus == SUCCESS)
{
/*配置HCLK,PCLK1,PCLK2分频*/
RCC_HCLKConfig(RCC_SYSCLK_Div1);
RCC_PCLK2Config(RCC_HCLK_Div1);
RCC_PCLK1Config(RCC_HCLK_Div2);
/**/
FLASH_SetLatency(FLASH_Latency_2);
FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
/*配置PLL*/
RCC_PLLConfig(RCC_PLLSource_HSE_Div1,RCC_PLLMul_9);
RCC_PLLCmd(ENABLE);
while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
/*选择SYSCLK时钟源为PLL*/
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
while(RCC_GetSYSCLKSource() != 0x08);
}
/*使能外设时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA ,ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
}
/*GPIO*/
void GPIO_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/*USART1*/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;/*USART1 TXD*/
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA,&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;/*USART1 RXD*/
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA,&GPIO_InitStructure);
}
/*USART*/
void USART_Configuration(void)
{
USART_InitTypeDef USART_InitStructure; USART_InitStructure.USART_BaudRate = ;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_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(USART1,&USART_InitStructure);
/*使能了USART1_RX的DMA请求*/
USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE); USART_Cmd(USART1,ENABLE);
}
int fputc(int ch, FILE *f)
{
USART_SendData(USART1,(u8)ch);
while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);
return ch;
} /*DMA*/
void DMA_Configuration(void)
{
DMA_InitTypeDef DMA_InitStructure; DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)srcTable;
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)dstTable;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC ;
DMA_InitStructure.DMA_BufferSize = ;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable ;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable ;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh ;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable ;
DMA_Init(DMA1_Channel5,&DMA_InitStructure); DMA_Cmd(DMA1_Channel5,ENABLE);
}

串口调试助手显示搬运前的dstTable中值全为0.在串口发送任意字符后,打印出了搬运后的数值,与srcTable中无异。

结论

由这个实验现象:DMA1通道5由USART1_RX产生请求信号,使DMA在内存上搬移数据。推广出来,该通道上其它请求信号也可以启动数据的传输。

后记

值得一提的是,DMA不仅支持内存上的数据传输,还支持外设之间,外设到内存,内存到外设的数据传输。说白了,外设、RAM、ROM都是依靠地址寻址的,对DMA来说,无所谓外设或内存,只认地址。如果你设置了ADC作为请求信号,来启动串口数据到内存的传输,而不是将ADC采样数据存放进内存。这也可行,但对我们来说,没有什么意义。

STM32实验非正式报告之DMA的更多相关文章

  1. STM32使用串口1配合DMA接收不定长数据,减轻CPU载荷

    STM32使用串口1配合DMA接收不定长数据,减轻CPU载荷 http://www.openedv.com/thread-63849-1-1.html 实现思路:采 用STM32F103的串口1,并配 ...

  2. STM32的SPI口的DMA读写[原创www.cnblogs.com/helesheng]

    SPI是我最常用的接口之一,连接管脚仅为4根:在常见的芯片间通信方式中,速度远优于UART.I2C等其他接口.STM32的SPI口的同步时钟最快可到PCLK的二分之一,单个字节或字的通信时间都在us以 ...

  3. oled的一套stm32实验2(自己的实验)

    stm32与OLED屏接口的引脚介绍: CS————GPIOD3: RST————GPIOD4: DC—————GPIOD5: D0——————GPIOD6: D1——————GPIOD7; 上是我参 ...

  4. 嵌入式02 STM32 实验10 定时器中断

    优秀文章 https://blog.csdn.net/qq_38351824/article/details/82619734 一.STM32通用定时器(TIM2.TIM3.TIM4和TIM5共四个通 ...

  5. 嵌入式02 STM32 实验08 外部中断

    一.中断 由于某个事件的发生,CPU暂停当前正在执行的程序,转而执行处理事件的一个程序.该程序执行完成后,CPU接着执行被暂停的程序.这个过程称为中断.(我正在捉泥鳅,但是我妈喊我回家吃饭,我必须回家 ...

  6. 嵌入式02 STM32 实验04跑马灯

    开学STM32 跑马灯的实验主要就是了解GPIO口的配置及使用,我这里是使用库函数进行编程,主要需要设置以下两方面: 1.使能需要使用的IO口的时钟,一共有A.B.C.D.E.F.G七组IO口 2.初 ...

  7. STM32基于HAL库通过DMA读写SDIO

    通过STM32CUBEMX生成DMA读写sdio的工程,再读写过程中总会卡死在DMA中断等待读写完成的while中,最终发现while等待的标志在SDIO的中断里置位的,而SDIO中断优先级如果小于或 ...

  8. IAR升级之后,编译stm32官方工程报错的解决办法

    IAR升级之后,打开stm32官方例程,编译时提示如下错误: Error[Pe147]: declaration is incompatible with "__nounwind __int ...

  9. oled的一套stm32实验1

    详细的oled介绍:http://blog.sina.com.cn/s/blog_57ad1bd20102wtq8.html 整理自:https://www.cnblogs.com/wp2312139 ...

随机推荐

  1. 【笨嘴拙舌WINDOWS】API

    如今,相对于大行其道的对象,服务,API概念的提出要早很多,却依然经久不衰:所谓万变不离其宗,如今很多服务(Web Services,云服务)的提供方式和API如出一辙. Windows API(Ap ...

  2. sql语句记录

    清空日志 DUMP TRANSACTION 库名 WITH NO_LOG 截断事务日志 BACKUP LOG 数据库名 WITH NO_LOG 收缩数据库 DBCC SHRINKDATABASE(数据 ...

  3. 对于随机变量的标准差standard deviation、样本标准差sample standard deviation、标准误差standard error的解释

    参考:http://blog.csdn.net/ysuncn/article/details/1749729

  4. 移动开发之浅析cocos2d-x的中文支持问题

    题记:这阵子一直在学习cocos2d-x,其跨平台的特性确实让人舒爽,引擎的框架概念也很成熟,虽然相应的第三方工具略显单薄,但也无愧是一件移动开发的利器啊,有兴趣的朋友有时间就多了解吧. 使用引擎的过 ...

  5. 【策略】HDOJ-1205-吃糖果

    [题目链接:HDOJ-1205] 思路:直接看题毫无思路... 看了别人的思路,到现在还懵懵懂懂.   只要除了数目最多的糖果以外的其他所有糖果的数目之和加1(小心这里要用int64),大于等于数目最 ...

  6. Oracle 11g AMM与ASMM切换

    现在的Oracle正在往智能化方向发展.如果我们现在找一些8i/9i时代的Oracle书籍,怎么样配置合适的数据库各内存池大小是非常重要的话题.但是进入10g之后,自动内存池调节成为一个重要Oracl ...

  7. 转载:ofstream和ifstream详细用法

    ofstream是从内存到硬盘,ifstream是从硬盘到内存,其实所谓的流缓冲就是内存空间; 在C++中,有一个stream这个类,所有的I/O都以这个“流”类为基础的,包括我们要认识的文件I/O, ...

  8. slidingmenu + fragment 左右菜单滑动

                           content_frame.xml <?xml version="1.0" encoding="utf-8" ...

  9. qt集成到vs2010

    因为要正式开始学习图形处理相关的知识了,所以要搭建开发平台.我所要用到的平台主要是vs和qt.虽然现在已经出了vs2013了,并且外观看起来特别漂亮,但是我所要用到的qt4版本中,没有对于的qtAdd ...

  10. ASP.NET MVC3 系列教程 - Razor视图引擎基础语法

    http://www.cnblogs.com/highend/archive/2011/04/09/aspnet_mvc3_razor_engine.html 4. 关于所有带"_" ...