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 ...
随机推荐
- UVa 10596 Moring Walk【欧拉回路】
题意:给出n个点,m条路,问能否走完m条路. 自己做的时候= =三下两下用并查集做了交,WA了一发-后来又WA了好几发--(而且也是判断了连通性的啊) 搜了题解= = 发现是这样的: 因为只要求走完所 ...
- Repeater 控件 当数据源没有数据的时候显示 暂无数据 的两种方式
第一种:现在前台给Repeater控件外面的div加一个runat=”server” 然后在cs后台判断数据源是否为空, 是的话就修改这个前台div的InnerText或者是InnerHtml 即可 ...
- [.NET WebAPI系列03] WebAPI Controller 中标准CRUD方法
[因] WebAPI的Controller中,一般都是下面四种方法: 对应于数据操作是我们通常所说的CRUD. C对应post,R对应Get,U对应Put,D对应Delete. 直接模仿VS 2012 ...
- php linux部署相关
http://www.itbulu.com/wdcp-php55.html http://www.wdlinux.cn/wdcp/install.html http://www.yiichina.co ...
- apache开源项目--Synapse
Apache Synapse一个易于使用.轻量级的XML与Web Services管理和集成中间件.可用于搭建SOA和ESB的基础平台.Apache Synapse支持多种标准包括:XML.XSLT. ...
- 【C#学习笔记】读access2007
using System; using System.Data.OleDb; namespace ConsoleApplication { class Program { static void Ma ...
- ViewPager+Fragment替代TabHost效果的简单示例
本示例旨在展示fragment替代tabhost的效果,具体的业务逻辑还要根据这个示例进行扩展. 效果图如下: 主Activity代码: package com.llb.view; import ja ...
- delphi 对话框初始地址InitialDir
我的电脑:SaveDialog1.InitialDir := '::{20D04FE0-3AEA-1069-A2D8-08002B30309D}';// My Computer {20D04FE0-3 ...
- Hibernate管理Session和批量操作
Hibernate管理Session Hibernate自身提供了三种管理Session对象的方法 Session对象的生命周期与本地线程绑定 Session对象的生命周期与JTA事务绑定 Hiber ...
- ISAPI在IIS7上的配置
主要介绍ISAPI的作用.ISAPI在IIS7上的配置.开发ISAPI的基本内容及使用VS 2008配置ISAPI DLL开发项目. 一.ISAPI介绍 缩写词=Internet Server App ...