一、DMA的介绍
(1)DMA的定义
DMA用来提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。无须CPU的干预,通过DMA数据可以快速地移动。这就节省了CPU的资源来做其他操作。
(2)DMA的三种传输方式
1、 外设到存储器
2、存储器到外设
3、存储器到存储器
二、DMA的编程步骤
1、打开时钟-----DMA1
2、初始化DMA1
字节(8bit) 半字(16bit) 全字(32bit)
-----外设基地址:SRC
-----内存基地址:DRC
-----方向:外设作为数据来源
-----数目:30
-----外设地址是否递增:递增
-----内存地址是否递增:递增
-----外设数据宽度:全字
-----内存数据宽度:全字
-----模式:正常模式
-----优先等级:中等
-----内存到内存:使能
3、使能DMA1-通道2
4、配置中断源-----利用中断提醒搬运是否完成
5、配置中断优先级
6、编写中断服务函数
等待传输完成 完成标志位
判读是否搬运成功
对比SRC和DRC中的内容是否一致
三、DMA的实现
业务需求
将SRC中的内容搬运到DRC中,利用中断的方式判读DMA是否搬运完成,搬运完成后再去判断DMA搬运是否正确。不要阻塞CPU
uint32_tSRC[30]={0x11111111,0x11311111,0x11131111,0x11111141,0x11111114,0x11112111,
0x21111111,0x11411111,0x11121111,0x11111151,0x11111115,0x11113111,
0x31111111,0x11511111,0x11151111,0x11111161,0x11111116,0x11114111,
0x41111111,0x11611111,0x11171111,0x11111171,0x11111117,0x11115111,
0x51111111,0x11711111,0x11191111,0x11111181,0x11111118,0x11116111
};
uint32_t DRC[30];
完整代码
#include "drv_usart.h"
#include "stdio.h"
#include "drv_dma.h"
#define dma_size 30
uint8_t led_flag = 0;
uint32_t SRC[dma_size]={0x11111111,0x11311111,0x11131111,0x11111141,0x11111114,0x11112111, 0x21111111,0x11411111,0x11121111,0x11111151,0x11111115,0x11113111, 0x31111111,0x11511111,0x11151111,0x11111161,0x11111116,0x11114111, 0x41111111,0x11611111,0x11171111,0x11111171,0x11111117,0x11115111, 0x51111111,0x11711111,0x11191111,0x11111181,0x11111118,0x11116111
};
uint32_t DRC[dma_size];
int main(void)
{
Usart1_Config();
// 1,选择一种管理中断的方式---整个程序只需要配置一次---写在main函数的while(1)之前
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
DMA1_Config(SRC,DRC,dma_size);
while(SET != DMA_GetFlagStatus(DMA1_FLAG_TC2));
DMA_ClearFlag(DMA1_FLAG_TC2);
if(0 == DMA_is_Ok(SRC, DRC, dma_size)){
printf("DMA_is_Ok\r\n");
}
while(1){;}
}
#ifndef __DRV_USART_H__
#define __DRV_USART_H__
#include "stm32f10x.h"
#include "stdio.h"
void Usart1_Config(void);
#endif //__DRV_USART_H__
#include "drv_usart.h"
void Usart1_Config(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
USART_InitTypeDef USART_InitStruct;
NVIC_InitTypeDef NVIC_InitStruct;
// 1,打开时钟----串口1,GPIOA,AFIO(引脚受片上外设控制时打开)
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO|RCC_APB2Periph_GPIOA|RCC_APB2Periph_USART1,ENABLE);
/*
2,初始化引脚
-----GPIO_Pin_9 TX
-----复用推挽输出
-----速度:2MHZ
-----GPIO_Pin_10 Rx
-----浮空输入
*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;//GPIO_Pin_9 TX
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽输出
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;//速度:2MHZ
GPIO_Init(GPIOA,&GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;//GPIO_Pin_10 Rx
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
GPIO_Init(GPIOA,&GPIO_InitStruct);
/*
3,初始化串口
-----波特率:115200
-----数据位数:8bit
-----奇偶校验:失能
-----停止位:1bit
-----硬件流控:失能
-----发送和接收使能
*/
USART_InitStruct.USART_BaudRate = 115200;//波特率:115200
USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//硬件流控:失能
USART_InitStruct.USART_Mode = USART_Mode_Rx|USART_Mode_Tx;//发送和接收使能
USART_InitStruct.USART_Parity = USART_Parity_No;//奇偶校验:失能
USART_InitStruct.USART_StopBits = USART_StopBits_1;//停止位:1bit
USART_InitStruct.USART_WordLength = USART_WordLength_8b;//数据位数:8bit
USART_Init(USART1,&USART_InitStruct);//
// 2,配置一个中断源----告诉NVIC哪一个事件可以打断CPU------给谁配置中断源,就写在它的配置程序中
USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);
USART_ITConfig(USART1,USART_IT_IDLE,ENABLE);//配置总线空闲的中断源
// 3,给这个中断源配置它的主优先级和次优先级(注意中断通道号不要到固件库手册中去找,去“stm32f10x.h”这个头文件的枚举中去找)------给谁配置中断优先级,就写在它的配置程序中
NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn;//中断通道号
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;//主优先级
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;// //次优先级
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;//使能此通道
NVIC_Init(&NVIC_InitStruct);
USART_ClearFlag(USART1,USART_FLAG_TC|USART_FLAG_TXE);
// 4,打开串口1
USART_Cmd(USART1,ENABLE);
}
int fputc(int ch,FILE*f)
{
USART1->DR = (uint8_t)ch;
while(0 == (USART1->SR&(1<<6)));
USART1->SR &= ~(1<<6);
return ch;
}
#ifndef __DRV_DMA_H__
#define __DRV_DMA_H__
#include "stm32f10x.h"
void DMA1_Config(uint32_t *src,uint32_t *drc,uint32_t size);
uint8_t DMA_is_Ok(uint32_t *src,uint32_t *drc,uint32_t size);
#endif //__DRV_DMA_H__
#include "drv_dma.h"
void DMA1_Config(uint32_t *src,uint32_t *drc,uint32_t size)
{
DMA_InitTypeDef DMA_InitStruct;
// 1,打开时钟-----DMA1
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
DMA_InitStruct.DMA_PeripheralBaseAddr= (uint32_t)src; //外设基地址:SRC
DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)drc; //内存基地址:DRC
DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC; //方向:外设作为数据来源
DMA_InitStruct.DMA_BufferSize = size; //数目:30
DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Enable;//外设地址是否递增:递增
DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存地址是否递增:递增
DMA_InitStruct.DMA_PeripheralDataSiz = DMA_PeripheralDataSize_Word;//外设数据宽度:全字
DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;//内存数据宽度:全字
DMA_InitStruct.DMA_Mode = DMA_Mode_Normal; //模式:正常模式
DMA_InitStruct.DMA_Priority = DMA_Priority_Medium; //优先等级:中等
DMA_InitStruct.DMA_M2M = DMA_M2M_Enable; //内存到内存:使能
// 2,初始化DMA1
DMA_Init(DMA1_Channel2,&DMA_InitStruct);
// 3,使能DMA1_通道2
DMA_Cmd(DMA1_Channel2, ENABLE);
}
uint8_t DMA_is_Ok(uint32_t *src,uint32_t *drc,uint32_t size)
{
uint8_t i,ret=0;
for(i=0;i<size;i++){
if(src[i]!=drc[i]){
ret=1;
return ret;
}
}
return ret;
}
//中断服务函数
#include "stm32f10x_it.h"
#include "drv_led.h"
#include "string.h"
extern uint8_t led_flag;
uint8_t tim6_flag=0;
#define size 10
char Usart1_data[size];
uint8_t indexs=0;
void USART1_IRQHandler(void)
{
if(SET == USART_GetITStatus(USART1,USART_IT_RXNE)){
USART_ClearITPendingBit(USART1,USART_IT_RXNE);
USART_SendData(USART1,USART_ReceiveData(USART1));
Usart1_data[indexs]=USART_ReceiveData(USART1);
indexs++;
}
if(SET == USART_GetITStatus(USART1,USART_IT_IDLE)){
// 清除标志位
USART_ReceiveData(USART1);
if(0 == strcmp("open",(const char *)Usart1_data)){
LED_CTRL(Led_r, Led_open);
}
if(0 == strcmp("close",(const char *)Usart1_data)){
LED_CTRL(Led_r, Led_close);
}
indexs = 0;
memset(Usart1_data,0,sizeof(Usart1_data));
}
}
测试结果
按下复位后