【第五章】STM32-ADC模数转换(2.AD多通道+DMA转运实验)

关联:STM32总结超全笔记【秋招自用】

【引言】

我们在上一节已经了解了ADC以及AD单通道采集的过程,那么既然有AD单通道,那么必然有AD多通道,上一节也已经铺垫了一下:

【问】如果一个规则组同时用多个通道采集数据,那么数据如何读取?

                                            --DMA--                                                     

按照规则组的顺序:上一个通道转换的数据会被下一个通道转换的数据所覆盖。

所以通道转换完毕后要及时使用DMA把数据取走。

【问】CPU不是也可以访问这条ADC采集的数据总线吗?为什么一定要DMA去做?

 CPU相当于STM32的大脑,对于数据的复制和存储这种“小事情”,完全可以让他的小助手DMA去做。

【DMA】

DMA,全称Direct Memory Access,即直接存储器访问。

DMA将数据从一个地址空间复制到另一个地址空间,提供在外设和存储器之间的高速数据传输

【DMA工作流程】

如图所示:

【DMA数据传输方式】

1.增量模式:DMA数据传输的数据源和目的地址会自动递增。

优点:可以方便的连续传输数据块

适用于:从连续内存区域读取或写入数据

2.循环模式:DMA数据传输的数据源和目的地之在达到终点后会回到起始地址。

优点:可以重复传输一定长度的数据

适用于:周期性传输数据 或 循环缓冲区

【STM32的DMA】

STM32F103具有2个DMA控制器 ,DMA1有7个通道,DMA2有5个通道。每个通道都可以配置一些外设的地址。

这是DMA1可以产生的7个通道的DMA请求 ,每个通道对应不同的外设

【例程10】AD多通道+DMA转运实验

【GPIO的配置】

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	//开启GPIOA的时钟

    /*GPIO初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);		
    //将PA0、PA1、PA2和PA3引脚初始化为模拟输入

四个引脚都配置为模拟输入

【ADC的配置】

    /*开启时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);	//开启ADC1的时钟
	
    /*设置ADC时钟*/
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);  //选择时钟6分频,ADCCLK = 72MHz / 6 = 12MHz
	
    /*规则组通道配置*/
	ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);	//规则组序列1的位置,配置为通道0
	ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_55Cycles5);	//规则组序列2的位置,配置为通道1
	ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3, ADC_SampleTime_55Cycles5);	//规则组序列3的位置,配置为通道2
	ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 4, ADC_SampleTime_55Cycles5);	//规则组序列4的位置,配置为通道3
	
	/*ADC初始化*/
	ADC_InitTypeDef ADC_InitStructure;									
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;					//独立模式,单独使用ADC1
	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_NbrOfChannel确定
	ADC_InitStructure.ADC_NbrOfChannel = 4;								//通道数为4,扫描规则组的前4个通道
	ADC_Init(ADC1, &ADC_InitStructure);									

规则组通道配置:看图即可。

多通道(4个通道)使能了连续转换扫描模式

【DMA的配置】

    uint16_t AD_Value[4];					//定义用于存放AD转换结果的全局数组
    


    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);		//开启DMA1的时钟
	
	/*DMA初始化*/
	DMA_InitTypeDef DMA_InitStructure;											
	DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;				
    //外设基地址,给定形参AddrA
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;	
    //外设数据宽度,选择半字,对应16为的ADC数据寄存器
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;			
    //外设地址自增,选择失能,始终以ADC数据寄存器为源
	DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)AD_Value;					
    //存储器基地址,给定存放AD转换结果的全局数组AD_Value
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;			
    //存储器数据宽度,选择半字,与源数据宽度对应
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;						
    //存储器地址自增,选择使能,每次转运后,数组移到下一个位置
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;							
    //数据传输方向,选择由外设到存储器,ADC数据寄存器转到数组
	DMA_InitStructure.DMA_BufferSize = 4;										
    //转运的数据大小(转运次数),与ADC通道数一致
	DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;								
    //模式,选择循环模式,与ADC的连续转换一致
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;								
    //存储器到存储器,选择失能,数据由ADC外设触发转运到存储器
	DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;						
    //优先级,选择中等
	DMA_Init(DMA1_Channel1, &DMA_InitStructure);								//将结构体变量交给DMA_Init,配置DMA1的通道1
	

DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;

指的是DMA传输的外设基地址,本实验是DMA转运ADC的数据,自然是从ADC1的DR寄存器读数据。

DMA_PeripheralDataSize_HalfWord;   

每次从外设读取或向外设写入数据时,DMA传输的数据大小为16位(2字节),

DMA_PeripheralInc_Disable;  

外设地址自增失能,因为始终要以ADC数据寄存器为源

DMA_Priority_Medium

 当多个DMA通道同时请求访问DMA控制器时,这个优先级决定了哪个通道会被优先服务。

【使能ADC和DMA,触发ADC】

    /*DMA和ADC使能*/
	DMA_Cmd(DMA1_Channel1, ENABLE);							//DMA1的通道1使能
	ADC_DMACmd(ADC1, ENABLE);								//ADC1触发DMA1的信号使能
	ADC_Cmd(ADC1, ENABLE);									//ADC1使能

    /*ADC触发*/
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);	                //软件触发ADC开始工作

【主函数】

while (1)
	{
		OLED_ShowNum(1, 5, AD_Value[0], 4);		//显示转换结果第0个数据
		OLED_ShowNum(2, 5, AD_Value[1], 4);		//显示转换结果第1个数据
		OLED_ShowNum(3, 5, AD_Value[2], 4);		//显示转换结果第2个数据
		OLED_ShowNum(4, 5, AD_Value[3], 4);		//显示转换结果第3个数据
		
		Delay_ms(100);							//延时100ms,手动增加一些转换的间隔时间
	}

把数组中的数据在OLED上显示出来

  • 12
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值