前言

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. 通过CSS禁止Chrome自动为输入框添加橘黄色边框,修改/禁止 chrome input边框颜色,

    1   /*Chrome浏览器 点击input 黄色边框 禁用*/ .NoOutLine:focus{outline: none} <asp:TextBox ID="txtTeleph ...

  2. CORS 跨域 实现思路及相关解决方案

    本篇包括以下内容: CORS 定义 CORS 对比 JSONP CORS,BROWSER支持情况 主要用途 Ajax请求跨域资源的异常 CORS 实现思路 安全说明 CORS 几种解决方案 自定义CO ...

  3. Azure SQL 数据库新服务级别现已正式发布

    T.K.Ranga Rengarajan   2014 年 9 月 10 日上午 11:00 我们很高兴地宣布,新的 SQL 数据库服务级被基本.标准和高级级别现已正式发布.这些服务级别中含有内置且可 ...

  4. Java [Leetcode 110]Balanced Binary Tree

    题目描述: Given a binary tree, determine if it is height-balanced. For this problem, a height-balanced b ...

  5. 域名下Web项目重定向出现DNS域名解析错误问题

    问题: 项目使用的是阿里云的ESC,前几天为IP地址添加了域名 发现发送正常请求时跳转没问题,但发送重定向请求时,页面就会出现DNS域名解析错误的情况 1.我在Tomcat的server.xml中配置 ...

  6. Spring各jar包的作用(转载)

    spring.jar是包含有完整发布的单个jar 包,spring.jar中包含除了spring-mock.jar里所包含的内容外其它所有jar包的内容,因为只有在开发环境下才会用到 spring-m ...

  7. Javascript Utils.js

    \ var Utils ={//字符串格式化StringFormat: function () {if (arguments.length == 0)return null;var str = arg ...

  8. 打通ssh的方法

    为了实现密码免输入,可以在安全的内网环境中打通ssh.linux和cygwin均可,步骤如下: 由A机去B机.在A生成密钥:ssh-keygen -t rsa,一路回车 将A的~/.ssh/id_rs ...

  9. HDU 5119 Happy Matt Friends

    Happy Matt Friends Time Limit: 6000/6000 MS (Java/Others) Memory Limit: 510000/510000 K (Java/Others ...

  10. 《Python核心编程》 第七章 映射和集合类型 - 习题

    课后习题 7–1. 字典方法.哪个字典方法可以用来把两个字典合并到一起? 答: dict1 = {' :' python' } dict2 = {' :"hello" } dict ...