STM32 ADC单通道与多通道_DMA学习笔记

合集下载

STM32多通道ADC采集详解(DMA模式和非DMA模式)

STM32多通道ADC采集详解(DMA模式和非DMA模式)

STM32多通道ADC采集详解(DMA模式和非DMA模式)在非DMA模式下,ADC采集的数据是通过CPU直接读取的,采集效率相对较低,但是编程相对简单。

首先,需要初始化ADC模块的工作模式(单通道、多通道等)和采样时间。

然后,使能ADC模块,并配置所需的通道和采样时间。

接着,设置采样序列,指定要采集的通道和相应的排列顺序。

在采集数据时,首先需要设置ADC转换模式和采样时间,然后开始转换,并等待转换完成。

转换完成后,通过读取ADC_DR寄存器可以获取转换结果。

如果需要采集多个通道的数据,可以通过设置ADCSQR中的SQx位来启动下一次转换。

在DMA模式下,ADC采集的数据是通过DMA控制器传输到指定的内存区域,采集效率较高,适合数据量较大的应用场景。

与非DMA模式相比,DMA模式下的配置需要额外设置DMA控制器的工作模式(单次传输、循环传输等)和传输数据的目的地地址。

在采集数据前,需要设置DMA传输的目的地地址,并使能DMA传输。

在开启ADC转换后,DMA控制器会根据设置的目的地地址来自动传输数据,无需CPU干预。

采集完成后,CPU可以通过检查DMA传输完成标志位来判断数据是否已传输完毕。

总结:
使用非DMA模式的ADC采集相对简单而容易上手,适用于数据量较小且对实时性要求不高的应用场景。

DMA模式下的ADC采集效率更高,适用于数据量较大且对实时性要求较高的应用场景。

无论是DMA模式还是非DMA模式,都需要根据具体的应用需求来选择合适的模式。

在使用DMA模式时,还需要注意合理设置DMA传输的目的地地址和传输模式,以充分发挥DMA的优势。

STM32双ADC多路通道采样DMA传输单通道模拟看门狗设置

STM32双ADC多路通道采样DMA传输单通道模拟看门狗设置

STM32双ADC多路通道采样DMA传输单通道模拟看门狗设置vu32 ADCConvertedValue[TimsOfSample][NbrOfChannel];void DMA_Config(){DMA_InitTypeDef DMA_InitStructure;RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);NVIC_Config( DMA1_Channel1_IRQn,0,1);DMA_DeInit(DMA1_Channel1);DMA_InitStructure.DMA_BufferSize=DMABuffSize;//⼀次中断传输数据的个数,数据单位与外设或内存单位⼤⼩相同DMA_InitStructure.DMA_DIR=DMA_DIR_PeripheralSRC;//外设作为数据传输来源DMA_InitStructure.DMA_M2M=DMA_M2M_Disable;//内存到内存传输关闭DMA_InitStructure.DMA_MemoryBaseAddr=(u32)ADCConvertedValue;//存储器地址,实际就是⼀个内部的SRAM变量DMA_InitStructure.DMA_MemoryDataSize=DMA_MemoryDataSize_Word;//内存数据⼤⼩与外设数据⼤⼩相同,为32位DMA_InitStructure.DMA_MemoryInc=DMA_MemoryInc_Enable;//内存地址递增DMA_InitStructure.DMA_Mode=DMA_Mode_Circular;//循环模式⽤于处理循环缓冲区和连续的数据传输(如ADC的扫描模式)。

DMA_InitStructure.DMA_PeripheralBaseAddr=(u32)&ADC1->DR;//外设基地址:ADC数据寄存器地址DMA_InitStructure.DMA_PeripheralDataSize=DMA_PeripheralDataSize_Word;//外设数据⼤⼩为32位DMA_InitStructure.DMA_PeripheralInc=DMA_PeripheralInc_Disable;//外设地址固定DMA_InitStructure.DMA_Priority=DMA_Priority_High;//优先级⾼,只使⽤⼀个DMA时,优先级设置不影响DMA_Init(DMA1_Channel1,&DMA_InitStructure);//DMA_ITConfig(DMA1_Channel1,DMA_IT_TC,ENABLE);DMA_Cmd(DMA1_Channel1,ENABLE);}void ADC_DualModeConfig(){ADC_InitTypeDef ADC_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1|RCC_APB2Periph_ADC2,ENABLE);RCC_ADCCLKConfig(RCC_PCLK2_Div6);//ADC采样时钟分频,确保ADC的时钟不超过14MHzGPIO_Config( GPIOA,RCC_APB2Periph_GPIOA,GPIO_Mode_AIN,GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7);NVIC_Config( ADC1_2_IRQn,0,0);DMA_Config();ADC_DeInit(ADC1);ADC_InitStructure.ADC_ContinuousConvMode=ENABLE;//连续转换模式ADC_InitStructure.ADC_DataAlign=ADC_DataAlign_Right;//数据对齐⽅式右对齐ADC_InitStructure.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None;ADC_InitStructure.ADC_Mode=ADC_Mode_RegSimult;//同步规则模式ADC_InitStructure.ADC_NbrOfChannel=NbrOfChannel;//规则序列的长度ADC_InitStructure.ADC_ScanConvMode=ENABLE;//模数转换⼯作在扫描模式ADC_Init(ADC1,&ADC_InitStructure);ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_28Cycles5);ADC_RegularChannelConfig(ADC1,ADC_Channel_7,2,ADC_SampleTime_28Cycles5);ADC_RegularChannelConfig(ADC1,ADC_Channel_1,3,ADC_SampleTime_28Cycles5);ADC_InitStructure.ADC_Mode = ADC_Mode_RegSimult;ADC_InitStructure.ADC_ScanConvMode = ENABLE;ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;ADC_InitStructure.ADC_NbrOfChannel = NbrOfChannel;ADC_Init(ADC2, &ADC_InitStructure);ADC_RegularChannelConfig(ADC2, ADC_Channel_6, 1, ADC_SampleTime_28Cycles5); ADC_RegularChannelConfig(ADC2, ADC_Channel_4, 2, ADC_SampleTime_28Cycles5); ADC_RegularChannelConfig(ADC2, ADC_Channel_5, 3, ADC_SampleTime_28Cycles5); ADC_ExternalTrigConvCmd(ADC2, ENABLE);ADC_AnalogWatchdogThresholdsConfig(ADC2,0xBA3, 0x000);ADC_AnalogWatchdogSingleChannelConfig(ADC2, ADC_Channel_6);ADC_AnalogWatchdogCmd(ADC2, ADC_AnalogWatchdog_SingleRegEnable);ADC_ITConfig(ADC2, ADC_IT_AWD, ENABLE);ADC_DMACmd(ADC1,ENABLE);ADC_Cmd(ADC1,ENABLE);ADC_ResetCalibration(ADC1);while(ADC_GetResetCalibrationStatus(ADC1));ADC_StartCalibration(ADC1);while(ADC_GetCalibrationStatus(ADC1));ADC_Cmd(ADC2, ENABLE);ADC_ResetCalibration(ADC2);while(ADC_GetResetCalibrationStatus(ADC2));ADC_StartCalibration(ADC2);while(ADC_GetCalibrationStatus(ADC2));ADC_SoftwareStartConvCmd(ADC1, ENABLE);while(!DMA_GetFlagStatus(DMA1_FLAG_TC1));DMA_ClearFlag(DMA1_FLAG_TC1);}。

STM32CUBEMX配置教程(十二)STM32的定时器触发的固定频率ADC采样(使用DMA)

STM32CUBEMX配置教程(十二)STM32的定时器触发的固定频率ADC采样(使用DMA)

STM32CUBEMX配置教程(十二)STM32的定时器触发的固定频率ADC采样(使用DMA)本教程将向您展示如何使用STM32CubeMX配置定时器触发的固定频率ADC采样,并使用DMA进行数据传输。

此配置可以用于您需要按照固定频率对模拟信号进行采样的应用中。

在开始之前,请确保已安装好STM32CubeMX和相应的IDE(如Keil、IAR等),并且您已熟悉STM32CubeMX的基本使用方法。

以下是配置步骤:1. 打开STM32CubeMX,并选择您的目标MCU型号。

2. 在"Pinout & Configuration"选项卡中,配置定时器和ADC引脚。

a.选择一个定时器,并设置其时钟源和频率。

您可以选择任何一个可用的定时器来触发ADC采样。

b.配置ADC引脚,将其连接到您的模拟信号源。

3. 在"Configuration"选项卡中,配置ADC。

a.启用ADC和DMA控制器。

b.配置ADC分辨率,采样时间和采样周期。

这些参数取决于您的应用需求。

c. 在"Mode"选项中,选择"Continuous Conversion Mode"。

这样ADC将会不断地根据定时器触发进行采样。

d. 启用"DMA Continuous Requests"。

这样当ADC完成一次采样后,DMA控制器将自动将数据传输到内存中。

4. 在"NVIC Settings"选项卡中,启用DMA和ADC中断。

5. 在"Project"选项卡中,选择生成代码所需的IDE和工程路径。

然后单击"Generate Code"按钮生成代码。

现在您已成功配置了定时器触发的固定频率ADC采样,并使用DMA进行数据传输。

您可以在生成的代码中初始化和启用各个模块,并编写相应的中断处理函数来处理DMA和ADC中断。

STM32单片机的ADC多通道采样

STM32单片机的ADC多通道采样
面给出《STM32库开发实战指南》上面配套的硬件电路图
如果设计电路图的话,可以参考这种思路。
我的收获
1引脚初始化的时候需要注意
每一个ADC通道都对应一个GPIO引脚,看图中的ADC123_IN10,表示
这个引脚(PC0)可以配置为ADC1的通道10,或者是ADC2的通道10,或
者是ADC3的通道10,这个地方非常重要,涉及到后面ADC初始化时函数
STM32单片机的ADC多通道采样
一单通道采样
参考资料:
《STM32库开发实战指南》
原理性质的东西还是少讲,因为上面那本书里面讲解的很详细了,直接来
看硬件电路图
这里使用的是3362电位器(10K),即用STM32来测量PB0和GND两
端的电压,这样的电路设计比较简单也容易理解,但是存在一定的弊端,下
的调用。
比如当我们使能ADC时,我们调用的函数是
ADC_Cmd();1
如果我们使用的是ADC1,那幺上面函数的写法就应该是
ADC_Cmd(ADC1,ENABLE);1
相应的如果是ADC2,那幺上面函数的写法就是
ADC_Cmd(ADC2,ENABLE);1
另外需要注意的是,我们这里的ADC使用了DMA,所以如果你使用的
是ADC1,那幺对应DMA就应该是DMA1的通道1
如果使用的是ADC2或者是ADC3就需要自己去查找手册,看看对应的
是哪个DMA的那个通道。
在后面上传的程序中可以看到DMA初始化的时候有这幺一条语句
DMA_Init(DMA1_Channel1);1
这就是初始化DMA1的通道1
相应的
DMA_Cmd(DMA1_Channel1,ENABLE);1
就是使能DMA1的通道1

记STM32F030多通道ADCDMA读取乱序问题

记STM32F030多通道ADCDMA读取乱序问题

记STM32F030多通道ADCDMA读取乱序问题问题描述通过 uint16_t ConvData[8]保存DMA搬运的ADC转换数值,但是这个数组数值的顺序总是和ADC不是顺序对应的。

⽐如⽤7个通道的ADC,当设置ADC_InitStructure.ADC_ScanDirection = ADC_ScanDirection_Backward,是对应顺序是:0->0,1->7,2->6…7->1 ;当设置ADC_InitStructure.ADC_ScanDirection = ADC_ScanDirection_Upward,是对应顺序是:0->7,1->0,2->1…7->6 。

问题原因F0的ADC在使⽤之前需要校准。

这个7位的校准值也是放在ADC_DR中的,它也会触发DMA请求。

可以参照F0的ADC-DMA例程,先做ADC校准、然后再设置DMA,再使能ADC的DMA。

实例代码void ADC1_DMA_Init(void){GPIO_InitTypeDef GPIO_InitStructure;DMA_InitTypeDef DMA_InitStructure;ADC_InitTypeDef ADC_InitStructure;RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);GPIO_InitStructure.GPIO_Pin = 0x00ff;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;GPIO_Init(GPIOA, &GPIO_InitStructure);RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);ADC_DeInit(ADC1); //ADC恢复默认设置ADC_StructInit(&ADC_InitStructure); //初始化ADC结构ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b; //12位精度ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; //规定模式装换⼯作在连续模式ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //数据对其为右对齐ADC_InitStructure.ADC_ScanDirection = ADC_ScanDirection_Upward; // ADC_ScanDirection_Backward; //ADC的扫描⽅向ADC_Init(ADC1, &ADC_InitStructure);ADC_ChannelConfig(ADC1, ADC_Channel_0, ADC_SampleTime_239_5Cycles); /* Convert the ADC1 Channel 11 with 239.5 Cycles as sampling time */ADC_ChannelConfig(ADC1, ADC_Channel_1, ADC_SampleTime_239_5Cycles); /* Convert the ADC1 Channel 11 with 239.5 Cycles as sampling time */ADC_ChannelConfig(ADC1, ADC_Channel_2, ADC_SampleTime_239_5Cycles); /* Convert the ADC1 Channel 11 with 239.5 Cycles as sampling time */ADC_ChannelConfig(ADC1, ADC_Channel_3, ADC_SampleTime_239_5Cycles); /* Convert the ADC1 Channel 11 with 239.5 Cycles as sampling time */ADC_ChannelConfig(ADC1, ADC_Channel_4, ADC_SampleTime_239_5Cycles); /* Convert the ADC1 Channel 11 with 239.5 Cycles as sampling time */ADC_ChannelConfig(ADC1, ADC_Channel_5, ADC_SampleTime_239_5Cycles); /* Convert the ADC1 Channel 11 with 239.5 Cycles as sampling time */ADC_ChannelConfig(ADC1, ADC_Channel_6, ADC_SampleTime_239_5Cycles); /* Convert the ADC1 Channel 11 with 239.5 Cycles as sampling time */ADC_ChannelConfig(ADC1, ADC_Channel_7, ADC_SampleTime_239_5Cycles); /* Convert the ADC1 Channel 11 with 239.5 Cycles as sampling time */ADC_GetCalibrationFactor(ADC1); /* ADC Calibration */ADC_Cmd(ADC1, ENABLE); /* Enable ADCperipheral[PerIdx] */while (!ADC_GetFlagStatus(ADC1, ADC_FLAG_ADRDY)); /* Wait the ADCEN falg *///设置DMA要在校准ADC之后DMA_DeInit(DMA1_Channel1); /* DMA1 Channel1 Config */DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t) 0x40012440; //ADC1->DR; //外设地址DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t) RegularConvData_Tab; //内存地址DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //外设作为数据传输的来源DMA_InitStructure.DMA_BufferSize = 8; //DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址寄存器不变DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_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_Mode_Circular;DMA_InitStructure.DMA_Priority = DMA_Priority_High; //DMA_Priority设定DMA通道x的软件优先级DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //DMA通道x没有设置为内存到内存传输DMA_Init(DMA1_Channel1, &DMA_InitStructure);DMA_Cmd(DMA1_Channel1, ENABLE);/* DMA1 Channel1 enable */DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE);ADC_DMARequestModeConfig(ADC1, ADC_DMAMode_Circular); /* Enable ADC_DMA */ADC_DMACmd(ADC1, ENABLE);ADC_StartOfConversion(ADC1); /* ADC1 regular Software Start Conv */}。

STM32 ADC DMA 使用心得

STM32 ADC DMA 使用心得

(二)ADC循环采集六路电压,使用DMA.这次实验真的很郁闷,对DMA的不了解让我深陷误区,明白之后,让我更加佩服DMA的强大。

误区就是:从实验的目标我们知道这次是用DMA把ADC转换的数据传送到内存中的一个数组里存起来,因为是采集6个通道,这里使能了ADC的扫描模式。

一旦启动ADC,就会按顺序转换SQRX里选中的通道,问题就是我一开始以为ADC与DMA并不会协调工做,也就是ADC自己转自己的,DMA自己传自己的,这样的话内存里的数组就不是我想要的了,后来着实的研究了很长时间,在群里的一位兄弟的提醒下,我才知道,可能我想的复杂了,也许就可以在ADC转一次,然后DMA把数据传一次,Ok,经过实验得知,这个想法是正确的。

好了,说了这么多废话,开始进入正题。

这里使用了ADC1的六个规则通道分别是:CH0、CH1、CH2、CH3、CH14、CH15,分别对应的引脚为PA0、PA1、PA2、PA3、PC4、PC5。

关于ADC的配置:启动了ADC1的扫描模式,还有连续转换模式,独立工作模式(只用1个ADC),因为用的了DMA,所以也要使能DMA位,使用外部触发(SWSTART),数据为右对齐。

还有SQRX等等就不说了,这里不需要ADC中断的。

中断在DMA里。

关于DMA的配置:因为ADC请求规定在DMA1的第一个通道,所以这里使用DMA_CH1,外设地址为ADC唯一的数据寄存器(u32)&ADC1->DR,存储器地址为(u32)SendBuff数组,这个数组可以存放6个元素。

这里还有使能传输完中断(TCIF),选择从外设读取,循环模式,外设地址非增量模式,存储器地址增量模式,外设数据宽度16位,存储器地址16位,非存储器到存储器模式。

关于DMA中断函数:当DMA传输完6次数据时,TCIF位自动置位,程序进入中断服务函数,首先先关闭ADC的连续转换,我们把数组的处理都放在了这里,处理完发送到串口,通过电脑的超级终端可以看到不停变化的6个引脚电压的数据。

STM32ADC多通道转换DMA模式与非DMA模式两种方法(HAL库)

STM32ADC多通道转换DMA模式与非DMA模式两种方法(HAL库)

STM32ADC多通道转换DMA模式与⾮DMA模式两种⽅法(HAL库)⼀、⾮DMA模式(转) 说明:这个是⾃⼰刚做的时候百度出来的,不是我⾃⼰做出来的,因为感觉有⽤就保存下来做学习⽤,原⽂链接:,下⾯第⼆部分我会补充⾃⼰的DMA模式的⽅法。

Stm32 ADC 的转换模式还是很灵活,很强⼤,模式种类很多,那么这也导致很多⼈使⽤的时候没细⼼研究参考⼿册的情况下容易混淆。

不知道该⽤哪种⽅式来实现⾃⼰想要的功能。

⽹上也可以搜到很多资料,但是⼤部分是针对之前⽼版本的标准库的。

昨天帮客户解决这个问题,正好做个总结:使⽤stm32cubeMX配置⽣成多通道采集的例⼦。

软件:STM32Cumebx MDK硬件:eemaker板(基于stm32F103c8的)在百度搜索ADC多通道采集,⼤部分的都是基于采⽤dma模式才实现的。

⽽我讲的使⽤⾮dma⽅法。

⾸先有⼏个概念要搞清楚: 扫描模式(想采集多通道必须开启):是⼀次对所选中的通道进⾏转换,⽐如开了ch0,ch1,ch4,ch5。

Ch0转换完以后就会⾃动转换通道0,1,4,5直到转换完。

但是这种连续性并不是不能被打断。

这就引⼊了间断模式,可以说是对扫描模式的⼀种补充。

它可以把0,1,4,5这四个通道进⾏分组。

可以分成0,1⼀组,4,5⼀组。

也可以每个通道配置为⼀组。

这样每⼀组转换之前都需要先触发⼀次。

Stm32 ADC的单次模式和连续模式。

这两中模式的概念是相对应的。

这⾥的单次模式并不是指⼀个通道。

假如你同时开了ch0,ch1,ch4,ch5这四个通道。

单次模式转换模式下会把这四个通道采集⼀边就停⽌了。

⽽连续模式就是这四个通道转换完以后再循环过来再从ch0开始。

另外还有规则组和注⼊组的概念,因为我这个例程只⽤到了规则组,就不多介绍这两个概念,想要弄清楚请⾃⾏查阅⼿册。

下⾯进⼊正题,配置stm32cubeMX。

先使能⼏个通道,我这⾥设置为0、1、4、5.然后就要配置ADC的参数: ⽬前经过我的测试,要想⽤⾮dma和中断模式只有这样配置可以正确进⾏多通道转换:扫描模式+单次转换模式+间断转换模式(每个间断组⼀个通道)。

STM32的ADC简介_DMA方式的程序设计与实现

STM32的ADC简介_DMA方式的程序设计与实现

STM32的ADC简介_DMA方式的程序设计与实现ADC简介:ADC(Analog-to-Digital Converter,模/ 数转换器)。

也就是将模拟信号转换为数字信号进行处理,在存储或传输时,模数转换器几乎必不可少。

STM32在片上集成的ADC外设非常强大,我使用的奋斗开发板是STM32F103VET6,属于增强型的CPU,它有18个通道,可测量16个外部和2个内部信号源。

各通道的A/D转换可以单次,连续,扫描或间断模式执行,ADC的结果可以左对齐或右对齐方式存储在16位数据寄存器中。

我们以ADC规则通道转换过程来分析,如上图,所有的器件都是围绕中间的模拟至数字转换器部分展开的。

它的左端VREF+,VREF- 等ADC参考电压,ADCx_IN0 ~ADCx_IN15为ADC的输入信号通道,即某些GPIO引脚。

输入信号经过这些通道被送到ADC器件,ADC器件需要收到触发信号才开始进行转换,如EXTI外部触发,定时器触发,也可以使用软件触发。

ADC部件接受到触发信号后,在ADCCLK时钟的驱动下对输入通道的信号进行采样,并进行模数转换,其中ADCCLK是来自ADC预分频器。

ADC部件转换后的数值被保存到一个16位的规则通道数据寄存器(或注入通道数据寄存器)中,我们可以通过CPU指令或DMA把它读到内存(变量),模数转换之后,可以出发DMA请求或者触发ADC转换结束事件,如果配置了模拟看门狗,并且采集的电压大于阈值,会触发看门狗中断。

其实对于ADC采样,软件编程主要就是ADC的配置,当然我是基于DMA方式的,所以DMA的配置也是关键!话不多说看代码!主函数:main.c#include printf.h#include adc.h#include stm32f10x.hextern __IO uint16_t ADC_ConvertedValue;float ADC_ConvertedValueLocal;void Delay(__IO uint32_t nCount)。

一文带你看懂Stm32定时器+ADC+DMA进行AD采样的实现

一文带你看懂Stm32定时器+ADC+DMA进行AD采样的实现

一文带你看懂Stm32定时器+ADC+DMA进行AD采样的实现此STM32单片机为STM32F103系列的STM32的ADC有DMA功能这都毋庸置疑,也是我们用的最多的!然而,如果我们要对一个信号(比如脉搏信号)进行定时采样(也就是隔一段时间,比如说2ms),有三种方法:1、使用定时器中断每隔一定时间进行ADC转换,这样每次都必须读ADC的数据寄存器,非常浪费时间!2、把ADC设置成连续转换模式,同时对应的DMA通道开启循环模式,这样ADC就一直在进行数据采集然后通过DMA把数据搬运至内存。

但是这样做的话还得加一个定时中断,用来定时读取内存中的数据!3、使用ADC的定时器触发ADC转换的功能,然后使用DMA进行数据的搬运!这样只要设置好定时器的触发间隔,就能实现ADC定时采样转换的功能,然后可以在程序的死循环中一直检测DMA转换完成标志,然后进行数据的读取,或者使能DMA转换完成中断,这样每次转换完成就会产生中断,我是采用第二种方法。

下面上代码:我这里使用的单通道//定时器初始化voidTIM2_ConfiguraTIon(void){TIM_TImeBaseInitTypeDefTIM_TimeBaseStructure;TIM_OCInitTypeDefTIM_OCInitStructure;RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);TIM_TimeBaseStructure.TIM_Period=1999;//设置2ms一次TIM2比较的周期TIM_TimeBaseStructure.TIM_Prescaler=71;//系统主频72M,这里分频71,相当于1000K 的定时器2时钟TIM_TimeBaseStructure.TIM_ClockDivision=0x0;。

STM32单片机的ADC配置详解

STM32单片机的ADC配置详解

STM32单片机的ADC配置详解一、ADC定义将模拟量转换为数字量的过程称为模式(A/D)转换,完成这一转换的工具就是模数转换器(简称ADC),用于将模拟形式的连续信号转换为数字形式的离散信号的一类设备。

例如:把芯片的引脚上的电压读出来,把芯片集成的上的温度传感器的温度读出来!二、ADC要点1-独立模式-单通道-中断读取①、初始化ADC用到的GPIO;②、设置ADC的工作参数并初始化;③、配置ADC时钟;④、设置ADC转换通道顺序及采样时间;⑤、配置使能ADC转换完成中断,在中断内读取转换完的数据;⑥、使能ADC;⑦使能软件触发ADC转换。

三、ADC内容1)ADC数量:STM32有3个ADC,每个ADC最多有16个外部通道,ADC1和ADC2都有16个外部通道,而ADC3随CPU引脚的不同通道数也不同,一般都有8个外部通道。

2)ADC精度:ADC为12位,即模拟电压经过ADC转换后是一个12位的数字量;一般情况下ADC的输入电压范围是:0~3.3V,因此最小精度为:3.3/2^12,当数字量为X时,则有模拟量Y = (3.3 / 2^12)*X。

3)电压输入范围:ADC 输入范围为:VREF- ≤VIN ≤VREF+。

由VREF- 、VREF+ 、VDDA 、VSSA 、这四个外部引脚决定。

一般把VSSA 和VREF- 接地,把VREF+ 和VDDA 接3V3,得到ADC的输入电压范围为:0~3.3V。

4)输入通道:ADC的信号输入就是通过通道来实现的,信号通过通道输入到单片机中,单片机经过转换后,将模拟信号输出为数字信号;STM32F103的ADC多达18个通道,在F103ZET6中ADC1的通道16连接到了芯片内部的温度传感器,Vrefint (内部参照电压)连接到了通道17,ADC2 的模拟通道16 和17 连接到了内部的VSS(地)。

外部的16 个通道在转换的时候又分为规则通道和注入通道,其中规则通道最多有16路,注入通道最多有4 路。

STM32学习笔记,定时器,PWM,ADC,UART,DMA

STM32学习笔记,定时器,PWM,ADC,UART,DMA
TIM_ClearITPendingBit(TIM1, TIM_IT_CC1 ); capture = TIM_GetCapture1(TIM1); TIM_SetCompare1(TIM1, capture + 2000); //这里解释下: //将 TIM1_CCR1 的值增加 2000,使得下一个 TIM 事件也需要 2000 个脉冲, //另一种方式是清零脉冲计数器 //TIM_SetCounter(TIM2,0x0000); }
RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD,\ ENABLE); //启动 AFIO RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //启动 TIM1 RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
//Step2. GPIO 做相应设置,为 AF 输出 //PA.8/9 口设置为 TIM1 的 OC1 输出口 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure);
}
关于 TIM 的操作,要注意的是 STM32 处理器因为低功耗的需要,各模块需要分别独立开启时钟,所以, 一定不要忘记给用到的模块和管脚使能时钟,因为这个原因,浪费了我好多时间阿~~!
STM32 笔记(二)TIM 模块产生 PWM 这个是 STM32 的 PWM 输出模式,STM32 的 TIM1 模块是增强型的定时器模块,天生就是为电机控制而生,可 以产生 3 组 6 路 PWM,同时每组 2 路 PWM 为互补,并可以带有死区,可以用来驱动 H 桥。

stm32CubeMx实现单通道ADCDMA采集

stm32CubeMx实现单通道ADCDMA采集

stm32CubeMx实现单通道ADCDMA采集今天要做的是ADC单通道DMA采集实验MCU : STM32F429开发⼯具:STM32CubeMx 版本号 5.0.0实验⽬的:实现ADC1 13通道 DMA采集⼀:简介 ⾸先,我们来看⼀下STM32F4XX参考⼿册⾥关于该芯⽚的ADC功能介绍⼆:STM32CubeMx 配置配置RCC配置时钟,硬件使⽤了25MHz⽆源晶振配置ADC数据对齐⽅式为右对齐使能连续转换模式,DMA连续请求设置采样次数为 3个周期配置 DMAMode设置为Circular模式,数据宽度设置为Half Word 2个字节配置Project Manager设置好之后,点击GENERATE CODE ⽣成代码定义变量__IO uint16_t ADC_ConvertedValue[20];__IO float ADC_Volt;启动DMA数据传输HAL_ADC_Start_DMA(&hadc1, (uint32_t*)&ADC_ConvertedValue[0], 20);读取ADC数据unsigned int i = 0, ADC_Value = 0;for(i = 0; i < 20; ++i){ ADC_Value += ADC_ConvertedValue[i];}ADC_Value = ADC_Value / 20;printf("ADC Value %d\r\n",ADC_Value);printf("vol %f\r\n", ADC_Value * 3.3 / 4096);三:实验结果串⼝输出结果四:实验中遇到的问题 1最后⼀个参数⼀开始的时候写1,程序就出现了阻塞的现象。

我分析可能是因为DMA选择的是Circular模式,会⼀直产⽣中断,从⽽使得CPU都被中断抢占了,因此将数据传输长度改为了20,每次对20个数据进⾏求均值计算,然后测试没有问题。

STM32学习笔记(14)-用ADC和片内温度传感器测温

STM32学习笔记(14)-用ADC和片内温度传感器测温

STM32学习笔记(14)-用ADC和片内温度传感器测温使用内置温度传感器测量温度学习使用ADC多通道转换方式,验证温度测量的准确性,为以后的工程实践打好基础。

(1) ADC的单次与连续转换ADC转换可以在一次转换后停止,然后再次触发后进行下一次转换;也可以是持续不断地转换下去。

这个是通过设定ADC_CR2的CONT位来确定。

而在ST提供的库里面,是这样来设定的:ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;(2) ADC的扫描模式ADC的扫描模式是用来扫描一组选定的通道的,它们将会被依次转换。

这个在上一份笔记中已说明过。

那么连续转换和扫描转换之间又是什么关系呢?字面上理解,似乎它们都是持续不断地转换啊。

答案是:连续转换的层次比扫描更高,它管着扫描呢。

也就是说,对连续转换来说,它所谓的“一次转换”可并不是指的一个通道的转换结束,而是指的“一组”转换结束,当然,这个“一组”有可能只有一个通道而已。

再说得明确一些:当ADC扫描一次结束以后,如果CONT位是“1”(设定为连续转换方式),那么将继续下一轮的转换。

(3) EOC什么时候产生?我的理解应该是每个通道(Channel)转换结束时都会发生。

但这里有些问题(见下图):上面的说明中:该位由硬件在(规则或注入)通道组换结束时设置…其中有个“组”字,字面的理解似乎应该是指一次转换组的所有通道都结束后才置1?但如果是这样,那么又如何进行数据的传递呢?要知道,对于ADC1来说,它的多个通道只有一个用于数据何存的寄存器:ADC1->DR啊。

而这个问题在其他两个地方也没有说得清楚(见下图):我们前面讨论了说连续转换是针对一组转换而言的,所以这里所谓的:每个转换后EOC标志被设置,究竟是一组转换结束后呢还是一个通道结束后呢?不明确。

而在扫描模式是这么说的(见下图):这里仅说到:如果设置了DMA位,在每次EOC后…,而并没有说到什么时候会有EOC 产生?是所有扫描结束还是每个通道转换结束?而关于SCAN位又有这样的说明(见下图):注意最后的注:如果分别设置了EOCIE或JEOCIE位,只在最后一个通道转换完毕才会产生EOC或者JEOC中断。

(完整版)STM32学习之:DMA详解

(完整版)STM32学习之:DMA详解

STM32学习之:DMA详解JawSoW个人分类:STM32DMA部分我用到的相对简单,当然,可能这是新东西,我暂时还用不到它的复杂功能吧。

下面用问答的形式表达我的思路。

DMA有什么用?直接存储器存取用来提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。

无须CPU的干预,通过DMA数据可以快速地移动。

这就节省了CPU的资源来做其他操作。

有多少个DMA资源?有两个DMA控制器,DMA1有7个通道,DMA2有5个通道。

数据从什么地方送到什么地方?外设到SRAM(I2C/UART等获取数据并送入SRAM);SRAM的两个区域之间;外设到外设(ADC读取数据后送到TIM1控制其产生不同的PWM占空比);SRAM到外设(SRAM中预先保存的数据送入DAC产生各种波形);……还有一些目前还搞不清楚的。

DMA可以传递多少数据?传统的DMA的概念是用于大批量数据的传输,但是我理解,在STM32中,它的概念被扩展了,也许更多的时候快速是其应用的重点。

数据可以从1~65535个。

直接存储器存取(Direct Memory Access,DMA)是计算机科学中的一种内存访问技术。

它允许某些电脑内部的硬体子系统(电脑外设),可以独立地直接读写系统存储器,而不需绕道 CPU。

在同等程度的CPU负担下,DMA是一种快速的数据传送方式。

它允许不同速度的硬件装置来沟通,而不需要依于 CPU的大量中断请求。

【摘自Wikipedia】现在越来越多的单片机采用DMA技术,提供外设和存储器之间或者存储器之间的高速数据传输。

当 CPU 初始化这个传输动作,传输动作本身是由DMA 控制器来实行和完成。

STM32就有一个DMA控制器,它有7个通道,每个通道专门用来管理一个或多个外设对存储器访问的请求,还有一个仲裁器来协调各个DMA请求的优先权。

DMA 控制器和Cortex-M3核共享系统数据总线执行直接存储器数据传输。

当CPU和DMA同时访问相同的目标(RAM或外设)时,DMA请求可能会停止 CPU访问系统总线达若干个周期,总线仲裁器执行循环调度,以保证CPU至少可以得到一半的系统总线(存储器或外设)带宽。

STM32ADC在DMA中断模式下多通道数据采集

STM32ADC在DMA中断模式下多通道数据采集

STM32ADC在DMA中断模式下多通道数据采集在DMA中断模式下进行多通道数据采集,需要进行以下步骤:1.初始化ADC模块:使用HAL库或者标准库,初始化ADC模块,设置采样时间、分辨率、触发源等参数。

同时,还需要配置ADC的多通道模式,选择需要采集的通道。

2.配置DMA:使用HAL库或者标准库,初始化DMA模块,设置DMA通道、数据传输方向、数据传输长度等参数。

3.设置中断回调函数:配置DMA传输完成后的中断回调函数,当DMA传输完成后会触发中断,在该中断中可以进行数据处理操作。

4.开始数据采集:启动ADC和DMA,开始进行数据采集。

下面是一个使用HAL库的示例代码,实现了三个通道的数据采集,每次采集10个数据点,采集完成后会触发中断进行数据处理:```c#include "stm32f4xx_hal.h"#define DATA_SIZE 10ADC_HandleTypeDef hadc1;DMA_HandleTypeDef hdma_adc1;uint16_t adc_data[DATA_SIZE * 3]; // 保存采集到的数据uint8_t current_channel = 0; // 当前采集的通道//ADCDMA中断回调函数void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) if (current_channel == 3)//数据采集完成,进行数据处理操作// 处理adc_data中已经采集到的数据// 重置current_channelcurrent_channel = 0;} else//继续采集下一个通道的数据current_channel++;HAL_ADC_Start_DMA(&hadc1, adc_data + DATA_SIZE * current_channel, DATA_SIZE);}int main(void)HAL_Init(;//初始化ADC模块__HAL_RCC_ADC1_CLK_ENABLE(;hadc1.Instance = ADC1;hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;hadc1.Init.Resolution = ADC_RESOLUTION_12B;hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;hadc1.Init.ContinuousConvMode = DISABLE;hadc1.Init.DiscontinuousConvMode = DISABLE;hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;hadc1.Init.ExternalTrigConvEdge =ADC_EXTERNALTRIGCONVEDGE_NONE;hadc1.Init.DMAContinuousRequests = ENABLE;hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;hadc1.Init.NbrOfConversion = 3;hadc1.Init.ScanConvMode = ENABLE;HAL_ADC_Init(&hadc1);//配置ADC的通道顺序ADC_ChannelConfTypeDef sConfig;sConfig.Channel = ADC_CHANNEL_0;sConfig.Rank = 1;sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;HAL_ADC_ConfigChannel(&hadc1, &sConfig);sConfig.Channel = ADC_CHANNEL_1;sConfig.Rank = 2;HAL_ADC_ConfigChannel(&hadc1, &sConfig);sConfig.Channel = ADC_CHANNEL_2;sConfig.Rank = 3;HAL_ADC_ConfigChannel(&hadc1, &sConfig);//初始化DMA模块__HAL_RCC_DMA2_CLK_ENABLE(;hdma_adc1.Instance = DMA2_Stream0;hdma_adc1.Init.Channel = DMA_CHANNEL_0;hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE;hdma_adc1.Init.MemInc = DMA_MINC_ENABLE;hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD; hdma_adc1.Init.Mode = DMA_CIRCULAR;hdma_adc1.Init.Priority = DMA_PRIORITY_LOW;HAL_DMA_Init(&hdma_adc1);//设置DMA中断回调函数HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 0, 0);HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);__HAL_DMA_ENABLE_IT(&hdma_adc1, DMA_IT_TC);//关联ADC和DMA__HAL_LINKDMA(&hadc1, DMA_Handle, hdma_adc1);//开始数据采集HAL_ADC_Start_DMA(&hadc1, adc_data, DATA_SIZE);while (1)//业务逻辑代码}//DMA中断处理函数void DMA2_Stream0_IRQHandler(void)HAL_DMA_IRQHandler(&hdma_adc1);```通过以上代码,就可以实现多通道的数据采集。

STM32F103的ADC总结

STM32F103的ADC总结

STM32的ADC采样难点:如何确定采样周期?如何配置相关寄存器?转换时间里的12.5是怎么来的?一、基本概念:ADC转换就是输入模拟的信号量,单片机转换成数字量。

读取数字量必须等转换完成后,完成一个通道的读取叫做采样周期。

采样周期一般来说=转换时间+读取时间。

而转换时间=采样时间+12.5个时钟周期。

采样时间是你通过寄存器告诉stm32采样模拟量的时间,设置越长越精确。

STM32的ADC模块各个通道对应的IO(注意:STM32F103系列最少都拥有2个ADC,STM32F103ZET6包含有3个ADC,STM32F103ZET6内部集成了12位的逐次逼近型模拟数字转换器,它有多大18个通道,可测量16个外部和2个内部信号源。

)二、规则组和注入组STM32的ADC通道分为规则组和注入组。

因为ADC转换模块只有一个ADC功能核心,它能够支持这么多通道的数据转换,用的是分时复用的方法。

分组的目的是为了赋予特定的ADC通道优先权。

比如,ADCx_IN2被分配到规则组,ADCx_IN3被分配到注入组,在IN2通道进行数据转换的过程中,外部信号触发了IN3通道的转换,则ADC功能核心将暂停IN2的转换,转去执行IN3的转换,完成转换后在回来执行IN2的转换。

由此可知,注入组的通道具有优先转换权,可以打断规则组通道正在进行的转换。

三、STM32 ADC 采样频率的确定①、可编程的通道采样时间ADC 使用若干个ADC_CLK 周期对输入电压采样,采样周期数目可以通过ADC_SMPR1 和ADC_SMPR2 寄存器中的SMP[2:0]位而更改。

每个通道可以以不同的时间采样。

总转换时间如下计算:TCONV = 采样时间+ 12.5 个周期例如:当ADCCLK=14MHz 和1.5 周期的采样时间TCONV = 1.5 + 12.5 = 14 周期 = 1μs转换时间里的12.5是怎么来的?原子哥告诉我,ST固定死了的,咱们不用关心。

STM32超详细的讲解——AD单通道与多通道转换(DMA)

STM32超详细的讲解——AD单通道与多通道转换(DMA)

第三章AD转换本章的内容分两部分,第一是AD的单通道转换,第二是AD的多通道转换。

首先先将单通道转换。

STM32中自带的AD最大的转换频率是14MHZ,共有16个转换通道,每个转ADC123_IN10表明PC0管脚可以作为AD1,AD2,AD3的第10通道。

下面我们将PC0配置成AD1的通道10为例进行讲解。

3.1首先我们应将PC0设置成模拟输入:#include "adc.h"/*为何定义ADC1_DR_Address 为((u32)0x40012400+0x4c),因为存放AD转换结果的寄存器的地址就是0x4001244c*/#define ADC1_DR_Address ((u32)0x40012400+0x4c)/*定义变量ADC_ConvertedValue,放AD1通道10转换的数据*/__IO uint16_t ADC_ConvertedValue;static void ADC1_GPIO_Config(void){GPIO_InitTypeDef GPIO_InitStructure;/* Enable ADC1 and GPIOC clock */RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 |RCC_APB2Periph_GPIOC,ENABLE);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 ;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;GPIO_Init(GPIOC, &GPIO_InitStructure);}3.2设置完端口后下一步当然是对AD进行初始化:这里需要补充一个知识点DMA,DMA就相当与CPU的一个秘书,他的作用就是帮CPU减轻负担的。

说的再具体点就是帮CPU来转移数据的。

我们都知道,AD每次转换结束后会将转换的结果放到一个固定的寄存器里,以往我们如果想将该寄存器中的值赋给某一变量时会用到赋值语句,如果不用DMA,则赋值语句便要CPU来完成,CPU本来就要忙着处理其他事情,现在还要来解决赋值语句这么简单的问题,肯到会蛋疼。

STM32DMA传输笔记(HAL库版)

STM32DMA传输笔记(HAL库版)

STM32DMA传输笔记(HAL库版)DMA,全称为:Direct Memory Access,即直接存储器访问。

DMA传输⽅式⽆需CPU 直接控制传输,也没有中断处理⽅式那样保留现场和恢复现场的过程,通过硬件为RAM 与I/O设备开辟⼀条直接传送数据的通路,能使CPU 的效率⼤为提⾼。

⼀、DMA请求映像 STM32F10x有两个DMA控制器,使⽤DMA控制器可使数据从存储器到存储器、存储器到外设、外设到存储器。

每个控制器有若⼲通道,参考《STM32参考⼿册》,各通道请求⼀览如下图:⼆、DMA初始化 1、使能DMA时钟__HAL_RCC_DMA1_CLK_ENABLE(); //DMA1时钟使能 2、关联DMA与UART1DMA_HandleTypeDef UART1TxDMA_Handler; //DMA句柄__HAL_LINKDMA(&UART1_Handler,hdmatx,UART1TxDMA_Handler); //将DMA与USART1联系起来(发送DMA) 3、配置DMA句柄//Tx DMA配置UART1TxDMA_Handler.Instance=chx; //通道选择通道4指的是UART1TxUART1TxDMA_Handler.Init.Direction=DMA_MEMORY_TO_PERIPH; //存储器到外设/*由于是从存储器读数据给外设,所以存储器设置为增量模式,这样的话,它地址可以⾃动增加;⽽外设因为是固定的地址,所以设为⾮增量模式。

*/UART1TxDMA_Handler.Init.PeriphInc=DMA_PINC_DISABLE; //外设⾮增量模式UART1TxDMA_Handler.Init.MemInc=DMA_MINC_ENABLE; //存储器增量模式/*外设数据长度和存储器数据长度要设置⼀样的位数,其可以定义⼀次传输数据量的⼤⼩*/UART1TxDMA_Handler.Init.PeriphDataAlignment=DMA_PDATAALIGN_BYTE; //外设数据长度:8位UART1TxDMA_Handler.Init.MemDataAlignment=DMA_MDATAALIGN_BYTE; //存储器数据长度:8位UART1TxDMA_Handler.Init.Mode=DMA_NORMAL; //外设普通模式UART1TxDMA_Handler.Init.Priority=DMA_PRIORITY_MEDIUM; //中等优先级HAL_DMA_DeInit(&UART1TxDMA_Handler);HAL_DMA_Init(&UART1TxDMA_Handler); 其中 Instance 参数根据请求映像表需要⽤到哪个设备就选择相应通道; Init.Direction 可选择“存储器到外设”、“外设到存储器”、“存储器到存储器”,决定数据传输⽅向; Init.PeriphInc 参数选择外设是否增量模式,本例中使⽤外设串⼝1的发送端,所以地址是固定的,要使⽤⾮增量模式;Init.MemInc 参数选择存储器是否为增量模式,由于存储器地址是连续的,本例中要读取连续的地址区域,所以要使⽤增量模式;Init.PeriphDataAlignment和Init.MemDataAlignment 分别表⽰外设和存储器长度,两个长度要相同,否则可能读取不完全;其余参数lue。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; GPIO_Init(GPIOC, &GPIO_InitStructure); }
/* 函数名:ADC1_Mode_Config * 描述 :配置 ADC1 的工作模式为 MDA 模式 * 输入 : 无 * 输出 :无 * 调 用 :内部调用 */
static void ADC1_GPIO_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; /* Enable ADC1 and GPIOC clock */ RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOC,ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; GPIO_Init(GPIOC, &GPIO_InitStructure); }
3.2 设置完端口后下一步当然是对 AD 进行初始化: 这里需要补充一个知识点 DMA,DMA 就相当与 CPU 的一个秘书,他的作用就是帮 CPU 减轻负担的。说的再具
体点就是帮 CPU 来转移数据的。我们都知道,AD 每次转换结束后会将转换的结果放到一个固定的寄存器里,以往 我们如果想将该寄存器中的值赋给某一变量时会用到赋值语句,如果不用 DMA,则赋值语句便要 CPU 来完成,CPU 本来就要忙着处理其他事情,现在还要来解决赋值语句这么简单的问题,肯到会蛋疼。所以需要 DMA 这个秘书来 帮他解决这个问题。由于 DMA 只是个秘书,所以比较笨,你只有把任务交代清楚了她才能很好的完成任务。那么 怎样来给 DMA 吩咐任务呢,聪明的人肯定想到了,那当然是“DMA_Init(DMA1_Channel1, &DMA_InitStructure)”这 个函数啦。下面就来一步步的来给 DMA 交代任务。 /* 函数名:ADC1_Mode_Config * 描述 :配置 ADC1 的工作模式为 MDA 模式 * 输入 : 无 * 输出 :无 * 调用 :内部调用 */ static void ADC1_Mode_Config(void) { DMA_InitTypeDef DMA_InitStructure; ADC_InitTypeDef ADC_InitStructure;
/*设定寄存器地址固定*/ DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
/*设定内存地址递加,即每次 DMA 都是将该外设寄存器中的值传到两个内存空间中*/ DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
/*设定 DMA 工作再循环缓存模式,即告诉 DMA 要不停的搬运,不能偷懒*/ DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
/*设定 DMA 选定的通道软件优先级*/ DMA_InitStructure.DMA_Priority = DMA_Priority_High; DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; DMA_Init(DMA1_Channel1, &DMA_InitStructure); /* Enable DMA channel1,CPU 有好几个 DMA 秘书,现在只用 DMA1_Channel1 这个秘书*/ DMA_Cmd(DMA1_Channel1, ENABLE);
/*定义 DMA 外设基地址,即为存放转换结果的寄存器*/ DMA_InitStructure.DMA_PeripheralBaseAddr =ADC1_DR_Address;
/*定义内存基地址*/ DMA_InitStructure.DMA_MemoryBaseAddr =(u32)&ADC_ConvertedValue;
/*设定外设数据宽度*/ DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; /*设定内存的的宽度*/ DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; /*设定 DMA 工作再循环缓存模式*/ DMA_InitStructБайду номын сангаасre.DMA_Mode = DMA_Mode_Circular;
/* Enable DMA clock */ RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
/* Enable ADC1 and GPIOC clock */ RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1| RCC_APB2Periph_GPIOC,ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 |GPIO_Pin_1;
/* 将与 DMA 有关的寄存器设我初始值 */ DMA_DeInit(DMA1_Channel1);
/*定义 DMA 外设基地址, 这里的 ADC1_DR_Address 是用户自己定义的,即为存放转换结果的寄存器 ,他的作用 就是告诉 DMA 取数就到 ADC1_DR_Address 这里来取。*/ DMA_InitStructure.DMA_PeripheralBaseAddr =ADC1_DR_Address; /*定义内存基地址,即告诉 DMA 要将从 AD 中取来的数放到 ADC_ConvertedValue 中 */ DMA_InitStructure.DMA_MemoryBaseAddr =(u32)&ADC_ConvertedValue; /*定义 AD 外设作为数据传输的来源,即告诉 DMA 是将 AD 中的数据取出放到内存中,不能反过来*/ DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; /*指定 DMA 通道的 DMA 缓存的大小,即告诉 DMA 开辟几个内存空间,由于我们只取通道 10 的 AD 数据所以只需 开辟一个内存空间*/ DMA_InitStructure.DMA_BufferSize = 1; /*设定寄存器地址固定,即告诉 DMA,只从固定的一个地方取数*/ DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; /*设定内存地址固定,即每次 DMA,,只将数搬到固定的内存中*/ DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
第二部分:AD 多路采样 #include "adc.h" #define ADC1_DR_Address ((u32)0x40012400+0x4c) /*定义数组变量 ADC_ConvertedValue[2],分别放 AD1 通道 10 和 11 转换的数据*/
__IO uint16_t ADC_ConvertedValue[2]; /* * 函数名:ADC1_GPIO_Config * 描述 :使能 ADC1 和 DMA1 的时钟,设置 PC0,PC1 为模拟输入 * 输入 : 无 * 输出 :无 * 调用 :内部调用 */ static void ADC1_GPIO_Config(void) { GPIO_InitTypeDef GPIO_InitStructure;
/*设置 ADC 工作在独立模式*/ ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
/*规定 AD 转换工作在单次模式,即对一个通道采样*/ ADC_InitStructure.ADC_ScanConvMode = DISABLE
/*设定 AD 转化在连续模式*/ ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
static void ADC1_Mode_Config(void) {
DMA_InitTypeDef DMA_InitStructure; ADC_InitTypeDef ADC_InitStructure;
/* DMA channel1 configuration */ DMA_DeInit(DMA1_Channel1);
下面我们将 PC0 配置成 AD1 的通道 10 为例进行讲解。 3.1 首先我们应将 PC0 设置成模拟输入: #include "adc.h" /* 为 何 定 义 ADC1_DR_Address 为 ((u32)0x40012400+0x4c) , 因 为 存 放 AD 转 换 结 果 的 寄 存 器 的 地 址 就 是 0x4001244c*/ #define ADC1_DR_Address ((u32)0x40012400+0x4c) /*定义变量 ADC_ConvertedValue,放 AD1 通道 10 转换的数据*/ __IO uint16_t ADC_ConvertedValue;
/*设定外设数据宽度,即告诉 DMA 要取的数的大小*/ DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
/*设定内存的的宽度*/ DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
/*不使用外部促发转换*/ ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; /*采集的数据在寄存器中以右对齐的方式存放*/ ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; /*设定要转换的 AD 通道数目*/ ADC_InitStructure.ADC_NbrOfChannel = 1; ADC_Init(ADC1, &ADC_InitStructure); /*配置 ADC 时钟,为 PCLK2 的 8 分频,即 9MHz*/ RCC_ADCCLKConfig(RCC_PCLK2_Div8); /*配置 ADC1 的通道 11 为 55.5 个采样周期 */ ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 1, ADC_SampleTime_55Cycles5); /* Enable ADC1 DMA */ ADC_DMACmd(ADC1, ENABLE); /* Enable ADC1 */ ADC_Cmd(ADC1, ENABLE); /*复位校准寄存器 */ ADC_ResetCalibration(ADC1); /*等待校准寄存器复位完成 */ while(ADC_GetResetCalibrationStatus(ADC1)); /* ADC 校准 */ ADC_StartCalibration(ADC1); /* 等待校准完成*/ while(ADC_GetCalibrationStatus(ADC1)); /* 由于没有采用外部触发,所以使用软件触发 ADC 转换 */ ADC_SoftwareStartConvCmd(ADC1, ENABLE); } 配置完以上的程序,那么 AD 每转换一次,DMA 都会将转换结果搬到变量 ADC_ConvertedValue 中,而不需用每次都用赋值语句来取值 AD 转换的值。
相关文档
最新文档