DMA说简单点就是一个搬运工,把数据从源地址搬到目的地址。于是就有了两种方式:存储器到存储器 和 存储器到外设;再简单点,存储器到存储器其实也算是存储器到外设嘛。
1. 存储器flash 读到 存储器SRAM 传输 实验代码
在flash中定义好需要传输的数据,在SRAM中定义好接收flash数据的变量。然后比较传输前后数据是否一致,用一个led来指示。
main.c
#include "stm32f10x.h" // Device header
#include "DMA.h"
#include "led.h"
// const关键字 把数组buffer定义为常量类型,作为源 。表示数据存储在内部的flash中
const uint32_t SRC_buffer[BUFF_SIZE] = {
0x01020304,0x05060708,0x090A0B0C,0x0D0E0F10,
0x11121314,0x15161718,0x191A1B1C,0x1D1E1F20,
0x21222324,0x25262728,0x292A2B2C,0x2D2E2F30,
0x31323334,0x35363738,0x393A3B3C,0x3D3E3F40,
};
// 定义DMA传输的目标存储器,存储在内部SRAM
uint32_t DST_buffer[BUFF_SIZE];
int main (void)
{
uint8_t status = 0;
DMA_init();
LED_GPIO_Init();
status = Buffercmp(SRC_buffer,DST_buffer,BUFF_SIZE);
if(status == 1)// 外设地址内容被读到目的地址内容无差错,则灯亮
{
GPIO_ResetBits(GPIOC,GPIO_Pin_13);
}
else
{
GPIO_SetBits( GPIOC,GPIO_Pin_13);
}
while(1)
{
}
}
DMA.c
#include "DMA.h"
/*
@brief DMA Init structure definition
typedef struct
{
uint32_t DMA_PeripheralBaseAddr; //!< 外设地址.
uint32_t DMA_MemoryBaseAddr; //!< 存储器地址.
uint32_t DMA_DIR; //!< 传输方向.
uint32_t DMA_BufferSize; //!< 传输数目.
uint32_t DMA_PeripheralInc; //!< 外设地址增量模式.
uint32_t DMA_MemoryInc; //!< 存储器地址增量模式.
uint32_t DMA_PeripheralDataSize; //!< 外设数据宽度.
uint32_t DMA_MemoryDataSize; //!< 存储器数据宽度.
uint32_t DMA_Mode; //!< 模式选择.
uint32_t DMA_Priority; //!< 通道优先级.
uint32_t DMA_M2M; //!< 存储器到存储器模式.
}DMA_InitTypeDef;
*/
void DMA_init(void)
{
DMA_InitTypeDef DMA_InitStruct;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)SRC_buffer;
DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)DST_buffer;
DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC;
// 外设作为源(也就是外设读到存储器)
DMA_InitStruct.DMA_BufferSize = BUFF_SIZE;
DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Enable;
DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;//四个字节
DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
DMA_InitStruct.DMA_Mode = DMA_Mode_Normal;
DMA_InitStruct.DMA_Priority = DMA_Priority_High;
DMA_InitStruct.DMA_M2M = DMA_M2M_Enable;
DMA_Init( DMA1_Channel1, & DMA_InitStruct);// DMA1的通道1
DMA_ClearFlag(DMA1_FLAG_TC1);// 清除 通道1的发送完成标志
DMA_Cmd( DMA1_Channel1, ENABLE);//使能DMA
}
/**
* @brief 比较外设地址 与 目的地址 的内容是否一样,
* 以此来检验外设地址的数据经过DMA传输到目的地址的情况
*
* @param 外设地址(也就是源地址)、目的地址、数据大小。
* @retval 数据前后一致返回1,不是就返回0.
*/
uint8_t Buffercmp(const uint32_t* SRC_buff,uint32_t* DST_buff, uint32_t BUFF_size )
{
while( BUFF_size -- )
{
if(*SRC_buff != * DST_buff)
return 0;
SRC_buff++;
DST_buff++;
}
return 1;
}
DMA.h
#ifndef _DMA_H_
#define _DMA_H_
#include "stm32f10x.h" // Device header
#define BUFF_SIZE 16
extern const uint32_t SRC_buffer[BUFF_SIZE];
extern uint32_t DST_buffer[BUFF_SIZE];
void DMA_init(void);
uint8_t Buffercmp(const uint32_t* SRC_buff,uint32_t* DST_buff, uint32_t BUFF_size );
#endif
led.c
#include "stm32f10x.h" // Device header
void LED_GPIO_Init(void)
{
GPIO_InitTypeDef LED_GPIO_InitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
LED_GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
LED_GPIO_InitStruct.GPIO_Pin = GPIO_Pin_13;
LED_GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init( GPIOC, & LED_GPIO_InitStruct);
GPIO_SetBits( GPIOC,GPIO_Pin_13);
}
【注意:】这次实验遇到一个报错:
.\Objects\project.axf: Error: L6200E: Symbol DST_buffer multiply defined (by main.o and dma.o).
大致意思就是:在main.c 和 dma.c 文件中有DST_buffer 变量重定义。只要想办法不重定义就ok了码嘛,于是我就想到用extern关键字。
我就寻思着,刚开始我把这两个源地址数组和目的地址数组都放在dma.h文件里面并且在main.c文件里面include了怎么还会报这样的错呢?原因我现在也还没有相通,或许以后应该就会明白了。于是我的解救办法就是把这两个buffer都放在main.c里面定义,在dma.h里面用extern关键字修饰,就可以解决这个报错。
2. 存储器 读到 外设 传输
DMA读取数据串口打印。
我的理解:
我们可以把DMA想象成一个搬运工,把我们在存储器(内存)存放的一个数组(有500个元素)的数据运输到外设(串口的数据寄存器,用来发送/接收数据的寄存器),然后串口的数据寄存器里面有数据就会使串口在硬件层面上发送数据(应该)。于是串口就打印信息了。
main.c
#include "stm32f10x.h" // Device header
#include "DMA.h"
#include "myusart.h"
uint8_t SendBuffer[Send_SIZE];
int main (void)
{
uint16_t i =0;
for(i = 0;i<Send_SIZE;i++)
{
SendBuffer[i] = '0';
}
MyUsart_init();
USART_DMA_init();
USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE);// 串口的发送DMA使能
while(1)
{
}
}
DAM.c
#include "DMA.h"
/*
@brief DMA Init structure definition
typedef struct
{
uint32_t DMA_PeripheralBaseAddr; //!< 外设地址.
uint32_t DMA_MemoryBaseAddr; //!< 存储器地址.
uint32_t DMA_DIR; //!< 传输方向.
uint32_t DMA_BufferSize; //!< 传输数目.
uint32_t DMA_PeripheralInc; //!< 外设地址增量模式.
uint32_t DMA_MemoryInc; //!< 存储器地址增量模式.
uint32_t DMA_PeripheralDataSize; //!< 外设数据宽度.
uint32_t DMA_MemoryDataSize; //!< 存储器数据宽度.
uint32_t DMA_Mode; //!< 模式选择.
uint32_t DMA_Priority; //!< 通道优先级.
uint32_t DMA_M2M; //!< 存储器到存储器模式.
}DMA_InitTypeDef;
*/
void USART_DMA_init(void)
{
DMA_InitTypeDef DMA_InitStruct1;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
DMA_InitStruct1.DMA_PeripheralBaseAddr = (uint32_t)USART_DR_ADDR; // 外设地址就是串口的数据寄存器(DR寄存器)
DMA_InitStruct1.DMA_MemoryBaseAddr = (uint32_t)SendBuffer;
DMA_InitStruct1.DMA_DIR = DMA_DIR_PeripheralDST;
DMA_InitStruct1.DMA_BufferSize = Send_SIZE;
DMA_InitStruct1.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStruct1.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStruct1.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;//一个字节,八位
DMA_InitStruct1.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStruct1.DMA_Mode = DMA_Mode_Normal;
DMA_InitStruct1.DMA_Priority = DMA_Priority_High;
DMA_InitStruct1.DMA_M2M = DMA_M2M_Disable;
DMA_Init( DMA1_Channel4, & DMA_InitStruct1);// DMA1的通道4--->串口1 的发送端
DMA_ClearFlag(DMA1_FLAG_TC4);// 清除 通道1的发送完成标志
//一定要注意这里到底是DMA1还是DMA2,我就是一直因为这里迟迟不出现象。。
DMA_Cmd( DMA1_Channel4, ENABLE);//使能DMA
}
DAM.h
#ifndef _DMA_H_
#define _DMA_H_
#include "stm32f10x.h" // Device header
#define Send_SIZE 500
#define USART_DR_ADDR (USART1_BASE + 0x04)
// 串口数据寄存器
extern uint8_t SendBuffer[Send_SIZE];
void USART_DMA_init(void);
#endif
myusart.c
#include "myusart.h"
void MyUsart_init(void)
{
GPIO_InitTypeDef MyUsart_GPIO_InitStruct ;
USART_InitTypeDef MyUSART_InitStruct;
// 这两个结构体的定义一定要放在时钟的之前,否者编译器警告
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 ,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOA,ENABLE);
// usart1 Tx PA9
MyUsart_GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP ;
MyUsart_GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
MyUsart_GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, & MyUsart_GPIO_InitStruct);
//usart1 Rx PA10
MyUsart_GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING ;
MyUsart_GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
GPIO_Init(GPIOA, & MyUsart_GPIO_InitStruct);
// 初始化串口2
MyUSART_InitStruct.USART_BaudRate = 115200 ;// 波特率
MyUSART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
MyUSART_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;// 收和发模式
MyUSART_InitStruct.USART_Parity = USART_Parity_No;// 无奇偶校验位
MyUSART_InitStruct.USART_StopBits = USART_StopBits_1;// 一位停止位
MyUSART_InitStruct.USART_WordLength = USART_WordLength_8b;// 字长为8位的数据模式
USART_Init(USART1, &MyUSART_InitStruct);
USART_Cmd(USART1, ENABLE);// 使能串口2
}
// 发送一个字符
void Usart_send_byte(USART_TypeDef* USARTx,uint16_t Data)
{
USART_SendData(USARTx,Data);
while( USART_GetFlagStatus(USARTx,USART_FLAG_TXE) == RESET );
}
// 发送字符串,遇到字符串结尾标志‘\0’结束
void Usart_send_string(USART_TypeDef* USARTx,char *arr)
{
uint16_t i = 0;
do
{
Usart_send_byte(USARTx,*(arr + i));
i++;
}while(*(arr + i) != '\0');
while( USART_GetFlagStatus(USARTx,USART_FLAG_TC) == RESET );
}
//重定向 printf
int fputc(int ch,FILE *f)
{
USART_SendData(USART1,(uint8_t)ch);
while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);
return (ch);
}
//重定向 输入
int fgetc(FILE *f)
{
while(USART_GetFlagStatus(USART1,USART_FLAG_RXNE) == RESET);
return (int)USART_ReceiveData(USART1);
}
myusart.h
#ifndef __MYUSART_H_
#define __MYUSART_H_
#include "stm32f10x.h" // Device header
#include <stdio.h>
void MyUsart_init(void);
void Usart_send_byte(USART_TypeDef* USARTx,uint16_t Data);
void Usart_send_string(USART_TypeDef* USARTx,char *arr);
int fputc(int ch,FILE *f);
int fgetc(FILE *f);
#endif
再见!!!我那逝去的青春~~~