STM32 LCD-ADC-DMA实验

目录

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

1.1 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有以下一些特性:

  • 每个通道都直接连接专用的硬件 DMA 请求,每个通道都同样支持软件触发。这些功能通过软件来配置。
  • 在七个请求间的优先权可以通过软件编程设置(共有四级:很高、高、中等和低),假如 在相等优先权时由硬件决定(请求 0 优先于请求 1,依此类推) 。
  • 独立的源和目标数据区的传输宽度(字节、半字、全字),模拟打包和拆包的过程。源和 目标地址必须按数据传输宽度对齐。
  • 支持循环的缓冲器管理。
  • 每个通道都有 3 个事件标志(DMA 半传输, DMA 传输完成和 DMA 传输出错),这 3 个 事件标志逻辑或成为一个单独的中断请求。
  • 存储器和存储器间的传输。
  • 外设和存储器,存储器和外设的传输
  • 闪存、 SRAM、外设的 SRAM、 APB1 APB2 和 AHB 外设均可作为访问的源和目标。
  • 可编程的数据传输数目:最大为 65536

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 的操作方式。

1.2 STM32 DMA的操作

1.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 个成员,它的成员如下:

  • DMA_PeripheralBaseAddr:外设基地址,也就是你 DMA 要传输数据 的目的地,我们是要使用到ADC1,所以设置为ADC1 的地址:(u32)&ADC1->DR(注意是地址)。
  • DMA_MemoryBaseAddr:内存基地址,也就是你 DMA 要搬运数据的存放的首地址,如果你是定义了一组数组发送,那么就设置为你的数据首地址。
  • DMA_BufferSize:DMA 通道的缓存大小。即设置一次传输数据量的大小,这个很容易理解。
  • DMA_PeripheralInc:是否使能外设地址递增。我们直接使用ADC1的 发 送 寄 存 器 , 地 址 不 递 增 , 所 以 设 置 为 : DMA_PeripheralInc_Disable。
  • DMA_MemoryInc:是否使能内存地址递增。我们发送的数据发送完 一 位 , 肯 定 是 发 送 下 一 位 , 所 以 这 里 肯 定 要 使 能 : DMA_MemoryInc_Enable。
  • DMA_PeripheralDataSize:设置外设数据宽度,ADC保存数组内数据是16位,所以设置为:DMA_PeripheralDataSize_HalfWord。
  • MA_MemoryDataSize : 内 存 数 据 宽 度 , 这 里 也 设 置 为 :DMA_MemoryDataSize_HalfWord。
  • DMA_Mode:传送模式。DMA 的工作模式又两种,第 1 种是工作在 循环缓存模式,也就是将数据一直重复发送,第 2 种工作在正常缓存模式,也就是只将数据传送一次。我们这里设置为循环缓存模式:DMA_Mode_Circular。
  • DMA_Priority:DMA 的传输顺序是有优先级的,当有两个 DMA 同时使用的时候, 优先级高的先发送。 而 DMA 的等级一共有四种等级:

                a) 最高优先级
                b) 高优先级
                c) 中优先级
                d) 低优先级 我们这里只使用一个 DMA,随便设置就好。

  • DMA_M2M : 是 否 使 用 存 储 器 到 存 储 器 模 式 , 我 们 设 置 为 DMA_M2M_Disable。

2. 初始化代码

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
}

3. 主函数代码

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

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

I am Supreme

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值