嵌入式学习日志————DMA直接存储器存取
前言
本篇内容为理论+实验方面的知识,如有问题,望指正!!
正文
参考手册的第10章——DMA控制器,第二章存储器和总线架构(存储器的映射)
1.DMA简介
DMA(Direct Memory Access)直接存储器存取
DMA可以提供外设和存储器或者存储器和存储器之间的高速数据传输,无须CPU干预,节省了CPU的资源
12个独立可配置的通道: DMA1(7个通道), DMA2(5个通道)
每个通道都支持软件触发和特定的硬件触发
STM32F103C8T6 DMA资源:DMA1(7个通道)
2.存储器映像
ROM是只读存储器,是一种非易失性、掉电不丢失的存储器
RAM是随机存储器,是一种易失性、掉电丢失额存储器
3.DMA框图
4.DMA基本结构
Flash中的数据只能读不能写入,在STM32中,使用const定义的变量存储在Flash里
DMA进行转运,有以下条件:
第一,开关控制,DMA_Cmd必须使能
第二,传输计数器必须大于零
第三,触发源必须有触发信号(软件/硬件触发)
5.DMA请求
6.数据宽度与对齐
小数据转大数据—>在高位补零
大数据转小数据—>舍弃高位,保留低位
数据宽度一样—>正常转运
7.实例(对应两个实验)
(1)数据转运+DMA
(2)ADC扫描模式+DMA
8.实验
(1)DMA数据转运
①配置步骤
RCC开启DMA时钟
调用DMA_Init,初始化各个参数(外设和存储器站点的起始地址、数据宽度、地址是否自增、方向、传输计数器、是否需要自动重装、选择触发源、通道优先级)
开关控制,DMA_Cmd给指定的通道使能
如果需要DMA的中断,那就调用DMA_ITConfig,开启中断输出,再在NVIC里配置相应的中断通道,然后写中断函数
当传输计数器自减为零后,还想给它赋值,就DMA失能->写传输计数器->DMA使能
②库函数
初始化
void DMA_Init(DMA_Channel_TypeDef* DMAy_Channelx, DMA_InitTypeDef* DMA_InitStruct);
结构体初始化
void DMA_StructInit(DMA_InitTypeDef* DMA_InitStruct);
使能
void DMA_Cmd(DMA_Channel_TypeDef* DMAy_Channelx, FunctionalState NewState);
终端输出使能
void DMA_ITConfig(DMA_Channel_TypeDef* DMAy_Channelx, uint32_t DMA_IT, FunctionalState NewState);
DMA设置当前数据寄存器:给传输计数器写数据
void DMA_SetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx, uint16_t DataNumber);
DMA获取当前数据寄存器:返回传输计数器的值
uint16_t DMA_GetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx);
获取标志位状态、清除标志位、获取中断状态、清除中断挂起位
FlagStatus DMA_GetFlagStatus(uint32_t DMAy_FLAG); void DMA_ClearFlag(uint32_t DMAy_FLAG); ITStatus DMA_GetITStatus(uint32_t DMAy_IT); void DMA_ClearITPendingBit(uint32_t DMAy_IT);
③代码
main.c
#include "stm32f10x.h" // Device header
#include "delay.h"
#include "OLED.h"
#include "MyDMA.h"uint8_t DataA[] = {0x01,0x02,0x03,0x04};
uint8_t DataB[] = {0,0,0,0};int main( void)
{OLED_Init();MyDMA_Init((uint32_t)DataA,(uint32_t)DataB,4);OLED_ShowString(1,1,"DataA");OLED_ShowString(3,1,"DataB");OLED_ShowHexNum(1,8,(uint32_t)DataA,8);OLED_ShowHexNum(3,8,(uint32_t)DataB,8);OLED_ShowHexNum(2,1,DataA[0],2);OLED_ShowHexNum(2,4,DataA[1],2);OLED_ShowHexNum(2,7,DataA[2],2);OLED_ShowHexNum(2,10,DataA[3],2);OLED_ShowHexNum(4,1,DataB[0],2);OLED_ShowHexNum(4,4,DataB[1],2);OLED_ShowHexNum(4,7,DataB[2],2);OLED_ShowHexNum(4,10,DataB[3],2);while(1){DataA[0]++;DataA[1]++;DataA[2]++;DataA[3]++;OLED_ShowHexNum(2,1,DataA[0],2);OLED_ShowHexNum(2,4,DataA[1],2);OLED_ShowHexNum(2,7,DataA[2],2);OLED_ShowHexNum(2,10,DataA[3],2);OLED_ShowHexNum(4,1,DataB[0],2);OLED_ShowHexNum(4,4,DataB[1],2);OLED_ShowHexNum(4,7,DataB[2],2);OLED_ShowHexNum(4,10,DataB[3],2);Delay_ms(1000);MyDMA_Transfer();OLED_ShowHexNum(2,1,DataA[0],2);OLED_ShowHexNum(2,4,DataA[1],2);OLED_ShowHexNum(2,7,DataA[2],2);OLED_ShowHexNum(2,10,DataA[3],2);OLED_ShowHexNum(4,1,DataB[0],2);OLED_ShowHexNum(4,4,DataB[1],2);OLED_ShowHexNum(4,7,DataB[2],2);OLED_ShowHexNum(4,10,DataB[3],2);Delay_ms(1000);}
}
MyDMA.c
#include "stm32f10x.h"uint16_t MyDMA_Size;void MyDMA_Init(uint32_t AddrA,uint32_t AddrB,uint16_t Size)
{MyDMA_Size = Size;RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE); DMA_InitTypeDef DMA_InitStructure;DMA_InitStructure.DMA_PeripheralBaseAddr = AddrA;DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;DMA_InitStructure.DMA_MemoryBaseAddr = AddrB;DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;DMA_InitStructure.DMA_BufferSize = Size;DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;DMA_InitStructure.DMA_M2M = DMA_M2M_Enable;DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;DMA_Init(DMA1_Channel1,&DMA_InitStructure);DMA_Cmd(DMA1_Channel1,DISABLE);
}void MyDMA_Transfer(void)
{DMA_Cmd(DMA1_Channel1,DISABLE);DMA_SetCurrDataCounter(DMA1_Channel1,MyDMA_Size); DMA_Cmd(DMA1_Channel1,ENABLE);while (DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET);DMA_ClearFlag(DMA1_FLAG_TC1);
}
MyDMA.h
#ifndef __MYDMA_H
#define __MYDMA_Hvoid MyDMA_Init(uint32_t AddrA,uint32_t AddrB,uint16_t Size);
void MyDMA_Transfer(void);#endif
(2)DMA+AD多通道
开启DMA触发信号:连接ADC与DMA
void ADC_DMACmd(ADC_TypeDef* ADCx, FunctionalState NewState);
代码
main.c
#include "stm32f10x.h" // Device header
#include "delay.h"
#include "OLED.h"
#include "AD.h"int main(void)
{OLED_Init();AD_Init();OLED_ShowString(1,1,"AD0:");OLED_ShowString(2,1,"AD1:");OLED_ShowString(3,1,"AD2:");OLED_ShowString(4,1,"AD3:");while(1){OLED_ShowNum(1,5,AD_Value[0],4);OLED_ShowNum(2,5,AD_Value[1],4);OLED_ShowNum(3,5,AD_Value[2],4);OLED_ShowNum(4,5,AD_Value[3],4);Delay_ms(100);}
}
AD.c
#include "stm32f10x.h"uint16_t AD_Value[4];void AD_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);RCC_ADCCLKConfig(RCC_PCLK2_Div6);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStructure);ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_55Cycles5);ADC_RegularChannelConfig(ADC1,ADC_Channel_1,2,ADC_SampleTime_55Cycles5);ADC_RegularChannelConfig(ADC1,ADC_Channel_2,3,ADC_SampleTime_55Cycles5);ADC_RegularChannelConfig(ADC1,ADC_Channel_4,4,ADC_SampleTime_55Cycles5);//初始化ADCADC_InitTypeDef ADC_InitStructure;ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;ADC_InitStructure.ADC_ScanConvMode = ENABLE;ADC_InitStructure.ADC_NbrOfChannel = 4;ADC_Init(ADC1,&ADC_InitStructure);DMA_InitTypeDef DMA_InitStructure;DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)AD_Value;DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;DMA_InitStructure.DMA_BufferSize = 4;DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;DMA_Init(DMA1_Channel1,&DMA_InitStructure);DMA_Cmd(DMA1_Channel1,ENABLE);ADC_DMACmd(ADC1,ENABLE);ADC_Cmd(ADC1,ENABLE);//对ADC进行校准ADC_ResetCalibration(ADC1); //软件触发while (ADC_GetResetCalibrationStatus(ADC1)==SET);ADC_StartCalibration(ADC1);while (ADC_GetCalibrationStatus(ADC1)==SET);ADC_SoftwareStartConvCmd(ADC1,ENABLE);
}
AD.h
#ifndef __AD_H
#define __AD_Hextern uint16_t AD_Value[4];void AD_Init(void);#endif
距离返校还有7天!!!