目录
1.1 STM32 DMA简介
1.2 STM32 DMA的操作
1.DMA的初始化
2. 初始化代码
3. 主函数代码
本文将向大家介绍 STM32 的 DMA。(如有错误,欢迎批评指正) 在本章中,我们将利用 STM32 的 DMA 来实现ADC1通道1内数据传送,并在 TFTLCD 模块上显示传送数据AD的电压值。本章分为以下学习目标:
1.了解什么是DMA
2.懂得操作STM32的DMA
DMA,全称为: Direct Memory Access,即直接存储器访问, DMA 传输将数据从一个地址空间复制到另外一个地址空间。 当 CPU 初始化这个传输动作,传输动作本身是由DMA 控制器来实行和完成。典型的例子就是移动一个外部内存的区块到芯片内部更快的内存区。像是这样的操作并没有让处理器工作拖延,反而可以被重新排程去处理其他的工作。 DMA 传输对于高效能嵌入式系统算法和网络是很重要的。DMA 传输方式无需 CPU 直接控制传输,也没有中断处理方式那样保留现场和恢复现场的过程,通过硬件为 RAM 与 I/O 设备开辟一条直接传送数据的通路, 能使 CPU 的效率大为提高。
STM32 最多有 2 个 DMA 控制器( DMA2 仅存在大容量产品中), DMA1 有 7 个通道。 DMA2 有 5个通道。每个通道专门用来管理来自于一个或多个外设对存储器访问的请求。还有一个仲裁起来协调各个 DMA 请求的优先权。
STM32的DMA有以下一些特性:
STM32F103ZET6 有两个 DMA 控制器, DMA1 和 DMA2, 本章我们仅针对 DMA1 进行介绍。
从外设( TIMx、 ADC、 SPIx、 I2Cx 和 USARTx)产生的 DMA 请求,通过逻辑或输入到DMA 控制器,这就意味着同时只能有一个请求有效。外设的 DMA 请求,可以通过设置相应的外设寄存器中的控制位,被独立地开启或关闭。
接下来我们来看一下,DMA 1主要有哪些通道:
这里解释一下上面说的逻辑或,例如通道 1 的几个 DMA1 请求( ADC1、 TIM2_CH3、 TIM4_CH1),这几个是通过逻辑或到通道 1 的,这样我们在同一时间,就只能使用其中的一个。其他通道也是类似的。
接下来我们来看一下,DMA 2主要有哪些通道:
从上图我们看出 STM32 的 DMA 很多,但大致操作时一样的,这里我们主要讲述ADC DMA 的操作方式。
DMA配置通道的过程为:
不过呢,我们使用 V3.5 库函数操作的话,我们只需要调用 DMA_Init()函数就可以了。
DMA_Init()函数有两个输入参数:
1) DMA_Channelx:用来选择要初始化的通道参数。我们这里以ADC1为例,查看上
面的 DMA1 的通道表,我们知道ADC 1的 DMA 通道是是通道 1,所以打开ADC1的通道:DMA1_Channel1。
2) DMA_InitStruct:这个是用来设置 DAM 参数的结构,它一共有 11 个成员,它的成员如下:
a) 最高优先级
b) 高优先级
c) 中优先级
d) 低优先级 我们这里只使用一个 DMA,随便设置就好。
void dma_init() //DMA初始化
{
//结构体定义
GPIO_InitTypeDef GPIO_InitStructure;
ADC_InitTypeDef ADC_InitStructure;
DMA_InitTypeDef DMA_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO|RCC_APB2Periph_ADC1,ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
RCC_ADCCLKConfig(RCC_PCLK2_Div6);//12M 最大14M 设置ADC时钟(ADCCLK)
ADC_DeInit(ADC1);
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1;//ADC //1
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AIN; //模拟输入
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure); //A
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfChannel = 1;
ADC_Init(ADC1, &ADC_InitStructure);
//设置指定ADC的规则组通道,设置它们的转化顺序和采样时间
ADC_RegularChannelConfig(ADC1,ADC_Channel_1,1,ADC_SampleTime_239Cycles5); //1
//内部温度传感器是在ADC1通道16的。
// ADC_RegularChannelConfig(ADC1,ADC_Channel_16,1,ADC_SampleTime_239Cycles5);
// ADC_TempSensorVrefintCmd(ENABLE);//打开内部温度传感器使能
ADC_DMACmd(ADC1,ENABLE);//将ADC与DMA链接在一起
ADC_Cmd(ADC1,ENABLE);
ADC_ResetCalibration(ADC1);//重置指定的ADC的校准寄存器
while(ADC_GetResetCalibrationStatus(ADC1));//获取ADC重置校准寄存器的状态
ADC_StartCalibration(ADC1);//开始指定ADC的校准状态
while(ADC_GetCalibrationStatus(ADC1));//获取指定ADC的校准程序
ADC_SoftwareStartConvCmd(ADC1, ENABLE);//使能或者失能指定的ADC的软件转换启动功能
DMA_DeInit(DMA1_Channel1);
DMA_InitStructure.DMA_PeripheralBaseAddr =(u32)&ADC1->DR;//DMA外设地址
DMA_InitStructure.DMA_MemoryBaseAddr=(u32)&adc_data;//DMA内存地址
DMA_InitStructure.DMA_DIR=DMA_DIR_PeripheralSRC;//外设作为数据传输的来源
DMA_InitStructure.DMA_BufferSize=1;//指定DMA通道的DMA缓存的大小
DMA_InitStructure.DMA_PeripheralInc=DMA_PeripheralInc_Disable;//外设地址寄存器递增
DMA_InitStructure.DMA_MemoryInc=DMA_PeripheralInc_Enable;//内存地址寄存器递增
DMA_InitStructure.DMA_PeripheralDataSize=DMA_PeripheralDataSize_HalfWord;//外设数据宽度16
DMA_InitStructure.DMA_MemoryDataSize=DMA_MemoryDataSize_HalfWord;//存储数据宽度16
DMA_InitStructure.DMA_Mode=DMA_Mode_Circular;//工作在循环缓存模式
DMA_InitStructure.DMA_Priority=DMA_Priority_High;//DMA通道x拥有高优先级
DMA_InitStructure.DMA_M2M=DMA_M2M_Disable;//DMA通道x没有设置为内存到内存传输
DMA_Init(DMA1_Channel1,&DMA_InitStructure); //ADC1在DMA1通道1内
DMA_Cmd(DMA1_Channel1,ENABLE);//使能DMA1
}
int main()
{
u16 value;
u8 i,j,dat[6];
float ad;
dma_init(); //DMA初始化
TFT_Init(); //TFT彩屏初始化
LED_Init(); //端口初始化
printf_init(); //printf初始化
TFT_ClearScreen(BLACK); //清屏
GUI_Show12ASCII(10,10,"This is a ADC1-DMA1 Check!",YELLOW,BLACK);
GUI_Show12ASCII(10,27,"PA1 is AD Input!",YELLOW,BLACK);
GUI_Show12ASCII(10,100,"The AD Volage is:",YELLOW,BLACK);
while(1)
{
value=0;
for(i=0;i<10;i++)//读取10次的AD数值取其平均数较为准确
{
value=value+adc_data[0];
}
delay_ms(500);
value=value/10;
ad=value*3.3/4095;
value=(u16)(ad*100);
dat[0]=value/100+0x30;
dat[1]='.';
dat[2]=value%100/10+0x30;
dat[3]=value%100%10+0x30;
dat[4]='V';
dat[5]='\0';
GUI_Show12ASCII(160,100,dat,YELLOW,BLACK);
if(j>1)
{
j=0;
GPIO_SetBits(GPIOC,GPIO_Pin_0);
}
else
{
j++;
GPIO_ResetBits(GPIOC,GPIO_Pin_0);
}
}
}