前言

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. 数据库锁机制(一)——概述

    注:内容为自己的推理认知+网络,如有错误和不合理之处,敬请指出. 在多线程环境中我用使用线程锁处理并发问题,而在数据库系统中,并发问题可以细化到事务级别,而DBMS对此的处理方案就是使用锁. 为了适应 ...

  2. jfinal框架教程-学习笔记(一)

    JFinal  是基于 Java  语言的极速  WEB  + ORM  开发框架,其核心设计目标是开发迅速.代码量少.学习简单.功能强大.轻量级.易扩展.Restful.在拥有Java 语言所有优势 ...

  3. linux下/etc/passwd和/etc/shadow文件

    /etc/passwd文件中保存的是用户的账号信息,而/etc/shadow文件中保存的是用户的口令信息. 一 /etc/passwd 一个用户对应着该文件中一行记录,一行记录由若干个字段组成,字段之 ...

  4. wav文件格式分析详解

    wav文件格式分析详解 文章转载自:http://blog.csdn.net/BlueSoal/article/details/932395 一.综述    WAVE文件作为多媒体中使用的声波文件格式 ...

  5. cocoapods 终极方案

    最近各种错误, 全部刷新 再说 sudo gem install -n /usr/local/bin cocoapods $ sudo gem update --system // 先更新gem $ ...

  6. 转载:浅析Java中的final关键字

    谈到final关键字,想必很多人都不陌生,在使用匿名内部类的时候可能会经常用到final关键字.另外,Java中的String类就是一个final类,那么今天我们就来了解final这个关键字的用法.下 ...

  7. InnoDB一定会在索引中加上主键吗

    InnoDB一定会在索引中加上主键吗 http://www.penglixun.com/tech/database/will_innodb_store_pk_in_index.html

  8. spoj 694(后缀数组)

    题意:求一个字符串的不重复子串的个数. 分析:对于下标为i的位置,能够产生的前缀子串个数为len-i(下标从0开始),对于与它字典序相邻的后缀产生的子串是重复的(就是他们的最长公共前缀),所以我们要减 ...

  9. HDU5764 After a Sleepless Night 树形乱搞题

    分析(官方题解): 假设根已确定,可以发现新树若合法,需满足以下性质:根节点是n:儿子的值不大于父亲:具有相同值的节点形成一条链,并且链不会发生“分叉”(即有多个最低点).所以对于新树中有出现的值x, ...

  10. 《Python核心编程》 第四章 Python对象- 课后习题

    练习 4-1. Python对象.与所有Python对象有关的三个属性是什么?请简单的描述一下. 答:身份.类型和值: 身份:每一个对象都有一个唯一的身份标识自己,可以用id()得到.  类型:对象的 ...