OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 12524|回复: 17

急!急!急!STM32F4 ADC采样速率远大于输入信号频率怎么办?

[复制链接]

1

主题

7

帖子

0

精华

新手上路

积分
31
金钱
31
注册时间
2015-5-27
在线时间
0 小时
发表于 2015-5-27 09:51:28 | 显示全部楼层 |阅读模式
5金钱
我用STM32F407做胎儿心率信号检测,输入信号频率很低(2Hz左右),ADC采样必须采集几个周期(约几秒钟),但板子自带的ADC采样速率太快(最低几十KHz),而我只需要采样4096个FFT计算(做自相关算法),我开始采用的是ADC+DMA方式,但这样采集完4096个点根本不到一个周期,想用数据抽取的方式,感觉数据量有太大,难以存储,也没必要,想请问有没有做过的人有的方法?(莫非要用定时器控制)

最佳答案

查看完整内容[请看2#楼]

回复【8楼】jessepinkman: --------------------------------- DMA是不依赖CPU的数据搬运工,一般是这么用。他就是个搬运工,你把它指向ADC的话,那就是ADC出一个结果它搬一次,他搬多快取决于ADC数据出得有多快。ADC数据出得快的话,用DMA就很有效率,因为他不消耗CPU的处理时间,搬完了再一次性处理,节约了很多CPU时间。低速你也可以用DMA,但是就得控制ADC以缓慢的速度采样,不能让其自动连续采样,得用软件触发或者定时器触发 ...
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

1

主题

192

帖子

0

精华

高级会员

Rank: 4

积分
597
金钱
597
注册时间
2014-4-14
在线时间
104 小时
发表于 2015-5-27 09:51:29 | 显示全部楼层
回复【8楼】jessepinkman:
---------------------------------
DMA是不依赖CPU的数据搬运工,一般是这么用。他就是个搬运工,你把它指向ADC的话,那就是ADC出一个结果它搬一次,他搬多快取决于ADC数据出得有多快。ADC数据出得快的话,用DMA就很有效率,因为他不消耗CPU的处理时间,搬完了再一次性处理,节约了很多CPU时间。低速你也可以用DMA,但是就得控制ADC以缓慢的速度采样,不能让其自动连续采样,得用软件触发或者定时器触发,但这样低的采样频率数据量很小,CPU完全有充足的时间可以采样后软件取一下就行,关掉DMA定时器什么的还省了很多系统资源,用DMA就有点划不来。
回复

使用道具 举报

58

主题

6294

帖子

1

精华

资深版主

Rank: 8Rank: 8

积分
11545
金钱
11545
注册时间
2014-4-1
在线时间
1315 小时
发表于 2015-5-27 10:08:52 | 显示全部楼层
好奇怪,法拉利必须跑200迈以上吗?
回复

使用道具 举报

17

主题

342

帖子

0

精华

高级会员

Rank: 4

积分
544
金钱
544
注册时间
2014-4-24
在线时间
20 小时
发表于 2015-5-27 11:16:45 | 显示全部楼层
回复【2楼】xuande:
---------------------------------
居然有人抱怨ADC的速度太快,我也是醉了。
回复

使用道具 举报

1

主题

192

帖子

0

精华

高级会员

Rank: 4

积分
597
金钱
597
注册时间
2014-4-14
在线时间
104 小时
发表于 2015-5-27 11:17:55 | 显示全部楼层
这么低的频率用DMA干啥?
回复

使用道具 举报

58

主题

499

帖子

4

精华

金牌会员

Rank: 6Rank: 6

积分
1920
金钱
1920
注册时间
2013-11-18
在线时间
268 小时
发表于 2015-5-27 13:18:15 | 显示全部楼层
用定时器来触发ADC采样,你想调多少采样率都可以,我都抱怨ADC的速度太慢了,哈哈
已经放下多年的FPGA,要重新再拾起来,却是如此的陌生
回复

使用道具 举报

5

主题

424

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1020
金钱
1020
注册时间
2014-9-4
在线时间
199 小时
发表于 2015-5-27 13:49:34 | 显示全部楼层
[mw_shl_code=c,true]*********************************************************************************************************[/mw_shl_code] [mw_shl_code=c,true]* 函 数 名: InitDSO * 功能说明: 对示波器通道1进行初始化配置。主要完成GPIO的配置、ADC的配置、DMA配置、TIM配置。 * 形 参:无 * 返 回 值: 无 ********************************************************************************************************* */ void InitDSO(void) { GPIO_InitTypeDef GPIO_InitStructure; DMA_InitTypeDef DMA_InitStructure; ADC_InitTypeDef ADC_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; /* 初始化时钟 */ RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1|RCC_APB2Periph_ADC2, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC | RCC_APB2Periph_AFIO, ENABLE); RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE); RCC_ADCCLKConfig(RCC_PCLK2_Div2); //ADCCLK = PCLK2/2 = 36MHz /*初始化 PC0 PC1的ADC输入引脚*/ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; GPIO_Init(GPIOC, &GPIO_InitStructure); /*初始化DMA*/ DMA_DeInit(DMA1_Channel1); /* 复位DMA1寄存器到缺省状态 */ DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address; /* 选择ADC1的数据寄存器作为源 */ ////////////////////ADC1_DR_Address DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&g_DSO.buffer; /* 目标地址 */ ////////////////////g_DSO.buffer DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; /* 设置DMA传输方向,外设(ADC)作为源 */ DMA_InitStructure.DMA_BufferSize = SAMPLE_COUNT; /* 设置缓冲区大小 */ DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; /* 外设地址不自增 */ DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; /* 存储器地址需要自增 */ DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word; /* 选择外设传输单位:32bit */ DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word; /* 选择内存传输单位:32bit */ DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; /* 无需循环模式 */ DMA_InitStructure.DMA_Priority = DMA_Priority_High; /* 选择DMA优先级 */ DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; /* DMA传输类型,不是内存到内存 */ DMA_Init(DMA1_Channel1, &DMA_InitStructure); DMA_Cmd(DMA1_Channel1, ENABLE); ADC_DeInit(ADC1); ADC_DeInit(ADC2); /* ADC1 配置 ------------------------------------------------------*/ ADC_InitStructure.ADC_Mode = ADC_Mode_FastInterl; ADC_InitStructure.ADC_ScanConvMode = DISABLE; ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC3; ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; ADC_InitStructure.ADC_NbrOfChannel = 1; ADC_Init(ADC1, &ADC_InitStructure); ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 1, ADC_SampleTime_1Cycles5); ADC_ExternalTrigConvCmd(ADC1, ENABLE); /* ADC2 配置 ------------------------------------------------------*/ ADC_InitStructure.ADC_Mode = ADC_Mode_FastInterl; ADC_InitStructure.ADC_ScanConvMode = DISABLE; ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; ADC_InitStructure.ADC_NbrOfChannel = 1; ADC_Init(ADC2, &ADC_InitStructure); ADC_RegularChannelConfig(ADC2, ADC_Channel_10, 1, ADC_SampleTime_1Cycles5); ADC_ExternalTrigConvCmd(ADC2, ENABLE); /* 使能 ADC1 */ ADC_DMACmd(ADC1, ENABLE); ADC_Cmd(ADC1, ENABLE); ADC_ResetCalibration(ADC1); while(ADC_GetResetCalibrationStatus(ADC1)); ADC_StartCalibration(ADC1); while(ADC_GetCalibrationStatus(ADC1)); /* 使能 ADC2 */ ADC_Cmd(ADC2, ENABLE); ADC_ResetCalibration(ADC2); while(ADC_GetResetCalibrationStatus(ADC2)); ADC_StartCalibration(ADC2); while(ADC_GetCalibrationStatus(ADC2)); /*初始化定时器1 作为ADC的触发源*/ TIM_Cmd(TIM1, DISABLE); TIM_TimeBaseStructInit(&TIM_TimeBaseStructure); //初始化定时器1的寄存器为复位值 TIM_TimeBaseStructure.TIM_Period = 72000000 / g_DSO.SampleFreq; //ARR自动重装载寄存器周期的值(定时时间)到设置频率后产生个更新或者中断(也是说定时时间到) TIM_TimeBaseStructure.TIM_Prescaler = 0; //PSC时钟预分频数 例如:时钟频率=TIM1CLK/(时钟预分频+1) TIM_TimeBaseStructure.TIM_ClockDivision = 0x0; //CR1->CKD时间分割值 TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //CR1->CMS[1:0]和DIR定时器模式 向上计数 TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //CCMR2在向上计数时,一旦TIMx_CNT<TIMx_CCR1时通道1为有效电平,否则为无效电平 TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //CCER 输出使能 TIM_OCInitStructure.TIM_Pulse = TIM_TimeBaseStructure.TIM_Period / 2;//CCR3同计数器TIMx_CNT的比较,并在OC3端口上产生输出信号 TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //CCER输出极性设置 高电平有效 TIM_OC3Init(TIM1, &TIM_OCInitStructure); //TIM_OC3PreloadConfig(TIM1, TIM_OCPreload_Enable); //CMR2 设置预装载使能 更新事件产生时写入有效 //TIM_ARRPreloadConfig(TIM1, ENABLE); //CR1 设置ARR自动重装 更新事件产生时写入有效 TIM_Cmd(TIM1, ENABLE); TIM_CtrlPWMOutputs(TIM1, ENABLE); //使能PWM 输出 }[/mw_shl_code]
[mw_shl_code=c,true]******************************************************************************************* * 函数名: SetSampRate * 输 入: freq : 采样频率 单位Hz * 输 出: * 功 能:修改采样频率 配置定时器1 通道CC2, CC2作为ADC1的触发源 说 明:TIMxCLK = 72 MHz, 如果Prescaler =N(不分频) 那么TIMx counter clock = 72 MHz/(N+1) 计算公式: PWM输出频率 = TIMx counter clock /(ARR + 1) 在TIM8和TIM1需要设置TIM_RepetitionCounter(TIM1_RCR)=0,每次向上溢出都产生更新事件 当外部触发信号被选为ADC规则或注入转换时,只有它的上升沿可以启动转换 *********************************************************************************************/ void SetSampRate(uint32_t freq) { TIM1->ARR=72000000 / freq; //设定计数器自动重装值 }[/mw_shl_code]
[mw_shl_code=c,true]/******************************************************************************* * 函数名: StartADC * 输 入: 无 * 输 出: 无 * 功 能:开启ADC采样 这里全部直接寄存器操作 加快速度 *******************************************************************************/ void StartADC(void) { DMA1_Channel1->CPAR=ADC1_DR_Address; //DMA1 外设地址 DMA1_Channel1->CMAR=(uint32_t)&g_DSO.buffer; //DMA1,存储器地址 DMA1_Channel1->CNDTR=SAMPLE_COUNT; //DMA1,传输数据量 DMA1_Channel1->CCR=0X00000000; //复位 DMA1_Channel1->CCR|=0<<4; //从外设来读 DMA1_Channel1->CCR|=0<<5; //普通模式 DMA1_Channel1->CCR|=0<<6; //外设地址非增量模式 DMA1_Channel1->CCR|=1<<7; //存储器增量模式 DMA1_Channel1->CCR|=2<<8; //外设数据宽度为32位 DMA1_Channel1->CCR|=2<<10; //存储器数据宽度32位 DMA1_Channel1->CCR|=2<<12; //高优先级 DMA1_Channel1->CCR|=0<<14; //非存储器到存储器模式 DMA1_Channel1->CCR|=1; //开启DMA传输 //TIM1->CR1|=1; //开启定时器1 }[/mw_shl_code]
[mw_shl_code=c,true]/******************************************************************************* * 函数名: StopADC * 输 入: 无 * 输 出: 无 * 功 能:暂停ADC采样 ********************************************************************************/ void StopADC(void) { DMA1_Channel1->CCR&=0xffffffe; //关闭DMA传输 //TIM1->CR1&=0xffffffe; //关闭定时器1 }[/mw_shl_code]




https://github.com/WZTENG
回复

使用道具 举报

1

主题

7

帖子

0

精华

新手上路

积分
31
金钱
31
注册时间
2015-5-27
在线时间
0 小时
 楼主| 发表于 2015-5-27 14:58:06 | 显示全部楼层
我是STM32新手,很多东西都不懂,谢谢楼上各位回复,请问哪位大神有没有相关代码,供参考一下,感激不尽!
回复

使用道具 举报

1

主题

7

帖子

0

精华

新手上路

积分
31
金钱
31
注册时间
2015-5-27
在线时间
0 小时
 楼主| 发表于 2015-5-27 15:00:17 | 显示全部楼层
回复【4楼】翼间:
---------------------------------
DMA一般只使用于高采样率数据传送么?
回复

使用道具 举报

1

主题

7

帖子

0

精华

新手上路

积分
31
金钱
31
注册时间
2015-5-27
在线时间
0 小时
 楼主| 发表于 2015-5-27 15:02:28 | 显示全部楼层
回复【5楼】mzwhhwj:
---------------------------------
谢谢回复!定时器可以控制ADC每隔一段时间只采样一个数据么,比如说定时器可以定时1ms只采样一个数据吗?望回复,再次感谢!
回复

使用道具 举报

1

主题

7

帖子

0

精华

新手上路

积分
31
金钱
31
注册时间
2015-5-27
在线时间
0 小时
 楼主| 发表于 2015-5-27 15:06:37 | 显示全部楼层
由于我的输入信号频率太低了(2Hz左右),需要ADC的采样频率很低,最好几百赫兹,我就想知道怎么把STM32自带的ADC的采样率降下来,还望懂的大神不吝赐教,感激不尽!
回复

使用道具 举报

1

主题

192

帖子

0

精华

高级会员

Rank: 4

积分
597
金钱
597
注册时间
2014-4-14
在线时间
104 小时
发表于 2015-5-27 15:16:08 | 显示全部楼层
回复【10楼】jessepinkman:
---------------------------------
连续采样最低就是239.5个CLK,想再低,关掉连续转换,用定时器触发单次转换。
回复

使用道具 举报

58

主题

499

帖子

4

精华

金牌会员

Rank: 6Rank: 6

积分
1920
金钱
1920
注册时间
2013-11-18
在线时间
268 小时
发表于 2015-5-27 15:16:41 | 显示全部楼层
回复【9楼】jessepinkman:
---------------------------------
只要定时能定到1MS,那就没得问题
已经放下多年的FPGA,要重新再拾起来,却是如此的陌生
回复

使用道具 举报

1

主题

192

帖子

0

精华

高级会员

Rank: 4

积分
597
金钱
597
注册时间
2014-4-14
在线时间
104 小时
发表于 2015-5-27 15:17:24 | 显示全部楼层
或者你干脆就连续转换,但是不取值,由得ADC数据寄存器不断覆盖,定时去取个值。
回复

使用道具 举报

1

主题

7

帖子

0

精华

新手上路

积分
31
金钱
31
注册时间
2015-5-27
在线时间
0 小时
 楼主| 发表于 2015-5-27 15:21:07 | 显示全部楼层
回复【11楼】翼间:
---------------------------------
谢谢回复,那你觉得我这种情况:输入信号频率太低了(2Hz左右),需要ADC的采样频率很低,最好几百赫兹(低于1KHz),该怎么设置ADC采样呢?多有打扰,望见谅,再次感谢回复!
回复

使用道具 举报

1

主题

192

帖子

0

精华

高级会员

Rank: 4

积分
597
金钱
597
注册时间
2014-4-14
在线时间
104 小时
发表于 2015-5-27 15:24:34 | 显示全部楼层
回复【15楼】jessepinkman:
---------------------------------
假如你对采样时间要求精确,设个定时器,定时触发ADC进行采样。
回复

使用道具 举报

1

主题

7

帖子

0

精华

新手上路

积分
31
金钱
31
注册时间
2015-5-27
在线时间
0 小时
 楼主| 发表于 2015-5-27 15:32:10 | 显示全部楼层
回复【16楼】翼间:
---------------------------------
谢谢
回复

使用道具 举报

1

主题

3

帖子

0

精华

新手上路

积分
42
金钱
42
注册时间
2017-3-27
在线时间
5 小时
发表于 2019-1-25 14:58:33 | 显示全部楼层
可以用TIM控制ADC的采样时间,在定时器里面进行ADC的采集,这样可以控制很低的采样频率。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-6-9 15:00

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表