STM32F103---DMA

本文介绍了DMA(直接内存访问)的基本概念,包括其三种传输方式,详细讲解了DMA的编程步骤,并通过一个实例展示了如何使用DMA将SRC数据搬运到DRC,利用中断检测搬运完成和准确性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、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));	
	}
}

测试结果

按下复位后 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值