STM32实验非正式报告之DMA
前言
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的更多相关文章
- STM32使用串口1配合DMA接收不定长数据,减轻CPU载荷
STM32使用串口1配合DMA接收不定长数据,减轻CPU载荷 http://www.openedv.com/thread-63849-1-1.html 实现思路:采 用STM32F103的串口1,并配 ...
- STM32的SPI口的DMA读写[原创www.cnblogs.com/helesheng]
SPI是我最常用的接口之一,连接管脚仅为4根:在常见的芯片间通信方式中,速度远优于UART.I2C等其他接口.STM32的SPI口的同步时钟最快可到PCLK的二分之一,单个字节或字的通信时间都在us以 ...
- oled的一套stm32实验2(自己的实验)
stm32与OLED屏接口的引脚介绍: CS————GPIOD3: RST————GPIOD4: DC—————GPIOD5: D0——————GPIOD6: D1——————GPIOD7; 上是我参 ...
- 嵌入式02 STM32 实验10 定时器中断
优秀文章 https://blog.csdn.net/qq_38351824/article/details/82619734 一.STM32通用定时器(TIM2.TIM3.TIM4和TIM5共四个通 ...
- 嵌入式02 STM32 实验08 外部中断
一.中断 由于某个事件的发生,CPU暂停当前正在执行的程序,转而执行处理事件的一个程序.该程序执行完成后,CPU接着执行被暂停的程序.这个过程称为中断.(我正在捉泥鳅,但是我妈喊我回家吃饭,我必须回家 ...
- 嵌入式02 STM32 实验04跑马灯
开学STM32 跑马灯的实验主要就是了解GPIO口的配置及使用,我这里是使用库函数进行编程,主要需要设置以下两方面: 1.使能需要使用的IO口的时钟,一共有A.B.C.D.E.F.G七组IO口 2.初 ...
- STM32基于HAL库通过DMA读写SDIO
通过STM32CUBEMX生成DMA读写sdio的工程,再读写过程中总会卡死在DMA中断等待读写完成的while中,最终发现while等待的标志在SDIO的中断里置位的,而SDIO中断优先级如果小于或 ...
- IAR升级之后,编译stm32官方工程报错的解决办法
IAR升级之后,打开stm32官方例程,编译时提示如下错误: Error[Pe147]: declaration is incompatible with "__nounwind __int ...
- oled的一套stm32实验1
详细的oled介绍:http://blog.sina.com.cn/s/blog_57ad1bd20102wtq8.html 整理自:https://www.cnblogs.com/wp2312139 ...
随机推荐
- Html5大文件断点续传
大文件分块 一般常用的web服务器都有对向服务器端提交数据有大小限制.超过一定大小文件服务器端将返回拒绝信息.当然,web服务器都提供了配置文件可能修改限制的大小.针对iis实现大文件的上传网上也 ...
- LeetCode Single Number (找不不重复元素)
题意:给一个序列,序列中只有1个是单个的,其他都是成对出现的.也就是序列中有奇数个元素.要求找出这个元素. 思路:成对出现用异或最好了.两个同样的数一异或就变零,剩下的,就是那个落单的. class ...
- 流媒體】jrtplib—VS2010下RTP开源协议库JRTPLIB3.9.1编译
一.JRTPLIB简介 老外用C++编写的开源RTP协议库,用来进行实时数据传输,可以运行在 Windows.Linux. FreeBSD.Solaris.Unix和VxWorks 等多种操作系统上, ...
- PL/SQL中LOOP循环控制语句
在PL/SQL中可以使用LOOP语句对数据进行循环处理,利用该语句可以循环执行指定的语句序列.常用的LOOP循环语句包含3种形式:基本的LOOP.WHILE...LOOP和FOR...LOOP. LO ...
- 搭建LAMP测试环境
LAMP:Linux+Apache+Mysql+Php,组合统称为LAMP,关于其中的独立个体,这里就不多介绍了. 1.首先准备一下软件包,如下: mysql-5.0.22.tar.gz httpd- ...
- Solr DIH以Mysql为数据源批量创建索引
演示使用solr管理后台,以mysql为数据源,批量建索引的方法 测试于:Solr 4.5.1, mmseg4j 1.9.1, Jdk 1.6.0_45, Tomcat 6.0.37 | CentOS ...
- 嵌入式 hi3518c下ramdisk文件系统与文件系统烧写以及uboot中change-the-env
NULL RAM : mkdir ramdisk_test 临时挂在点 dd if=/dev/zero of=123 bs=1k count=10000 建立空硬盘 losetup /dev/loo ...
- 【转】让apache支持中文路径或者中文文件
本帖最后由 狂人阿川 于 2013-4-12 19:13 编辑 今天在给一美国VPS客户调试他的程序的时候.发现他的网站有中文名称.貌似apache无法认识中文路径,火狐下面能下载他的文件,IE下面不 ...
- js时间日期转时间戳
var contractstarttimea='2016-01-01'; var contractendtimea='2016-05-01'; var contractstart = Date.par ...
- 转载: Vim 练级攻略
转自:http://coolshell.cn/articles/5426.html 酷壳 vim的学习曲线相当的大(参看各种文本编辑器的学习曲线),所以,如果你一开始看到的是一大堆VIM的命令分类, ...