【STM32】定时器TIM触发ADC采样,DMA搬运到内存

发布者:朝霞暮雨最新更新时间:2020-02-19 来源: eefocus关键字:STM32  定时器  TIM  触发  ADC采样  DMA  内存 手机看文章 扫描二维码
随时随地手机看文章

TIM+ADC+DMA原理

一般情况下,当我们需要进行采样的时候,需要用到ADC。例如:需要对某个信号进行定时采样(也就是隔一段时间,比如说2ms)。


本文提供的解决方案是:使用ADC的定时器触发ADC单次转换的功能,然后使用DMA进行数据的搬运!


这样只要设置好定时器的触发间隔,就能实现ADC定时采样转换的功能(即采样速率),然后可以在程序的死循环中一直检测DMA转换完成标志,然后进行数据的读取,或者使能DMA转换完成中断,这样每次转换完成就会产生中断。


主要需要解决的一个问题:定时器触发ADC采样,如何实现?


定时器触发ADC采样,是属于外部触发转换的一种方式。在《STM32中文参考手册》中,找到了关于这部分的内容:

配合上ADC外设的框图:

可以看出,STM32的ADC1和ADC2用于规则通道的外部触发可以有以上6个事件信号,本文使用TIM2_CH2触发ADC1。


ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_CC2;    //使用外部触发模式

ADC_ExternalTrigConvCmd(ADC1, ENABLE); //设置外部触发模式使能


对于ADC的配置不太熟悉的,可以参考博文:【STM32】ADC的基本原理、寄存器(超基础、详细版)、

【STM32】ADC库函数、一般步骤详解(实例:内部温度传感器实验)


同时注意一下外部触发的触发条件:当外部触发信号被选为ADC规则或注入转换时,只有它的上升沿可以启动转换。


如何有上升沿呢?定时器配置为PWM输出模式,这是重点。通过调用TIM_OC2Init(Tim2, & TIM_OCInitStructure),完成对TIM2_CH2的PWM配置。


TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式1

TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能

TIM_OCInitStructure.TIM_Pulse = 1000;

TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //输出极性:TIM输出比较极性低

TIM_OC2Init(TIM2, & TIM_OCInitStructure); //初始化外设TIM2_CH2


对于PWM的配置不太熟悉的,可以参考博文:【STM32】通用定时器的PWM输出(实例:PWM输出)、【STM32】通用定时器的基本原理(实例:定时器中断



其次,就是DMA将采样的数据由ADC1外设搬运到内存中。


配置DMA的外设地址和内存地址,并设置方向为从外设到内存即可。

可以看到ADC1可以作为DMA1的外设请求信号,那么ADC1的地址在哪里呢?

根据ADC1寄存器组的起始地址,找到偏移值:

最终得到ADC1_DR_Address=0x4001244C。


DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address; //ADC1地址

DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&ADC_ConvertedValue; //内存地址

DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //方向(从外设到内存)


对于DMA的配置不太熟悉的,可以参考博文:STM32】DMA基本原理、寄存器、库函数(DMA一般步骤)



STM32全部源码

本文采用的外设为:TIM2_CH2外部触发PA6(ADC1_CH6)采样,通过DMA1搬运到内存。


#include "adc.h"


volatile uint16_t ADC_ConvertedValue; //ADC采样的数据

#define ADC1_DR_Address    ((u32)0x4001244C) //ADC1的地址


//TIM2配置,arr为重加载值,psc为预分频系数

void TIM2_Init(u16 arr,u16 psc)

{

TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;

TIM_OCInitTypeDef TIM_OCInitStructure;

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //时钟使能


//定时器TIM2初始化

TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值

TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值

TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim

TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式

TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位

TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式1

TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能

TIM_OCInitStructure.TIM_Pulse = 1000;

TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //输出极性:TIM输出比较极性低

TIM_OC2Init(TIM2, & TIM_OCInitStructure); //初始化外设TIM2_CH2

TIM_Cmd(TIM2, ENABLE); //使能TIMx

TIM_CtrlPWMOutputs(TIM2, ENABLE); 

}


//DMA1配置

void DMA1_Init()

{

DMA_InitTypeDef DMA_InitStructure;

NVIC_InitTypeDef NVIC_InitStructure;


RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);   //使能ADC1通道时钟

//DMA1初始化

DMA_DeInit(DMA1_Channel1);

DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address; //ADC1地址

DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&ADC_ConvertedValue; //内存地址

DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //方向(从外设到内存)

DMA_InitStructure.DMA_BufferSize = 1; //传输内容的大小

DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址固定

DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable; //内存地址固定

DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord ; //外设数据单位

DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord ;    //内存数据单位

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);  //配置DMA1

DMA_ITConfig(DMA1_Channel1,DMA_IT_TC, ENABLE); //使能传输完成中断


NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel1_IRQn;

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;

NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;

NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

NVIC_Init(&NVIC_InitStructure);

DMA_Cmd(DMA1_Channel1,ENABLE);

}


//GPIO配置,PA6

void GPIO_Init()

{

GPIO_InitTypeDef GPIO_InitStructure;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);   //使能GPIOA时钟


//PA6 作为模拟通道输入引脚   

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;

GPIO_Init(GPIOA, &GPIO_InitStructure);  

}


void Adc_Init(){

ADC_InitTypeDef ADC_InitStructure;

TIM2_Init(30000,7199); //72000000/7200=10000Hz,每3s采集一次

DMA1_Init();

GPIO_Init();


RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);   //使能ADC1通道时钟


//ADC1初始化

ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //独立ADC模式

ADC_InitStructure.ADC_ScanConvMode = DISABLE;  //关闭扫描方式

ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //关闭连续转换模式

ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_CC2;    //使用外部触发模式

ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //采集数据右对齐

ADC_InitStructure.ADC_NbrOfChannel = 1; //要转换的通道数目

ADC_Init(ADC1, &ADC_InitStructure);

RCC_ADCCLKConfig(RCC_PCLK2_Div6); //配置ADC时钟,为PCLK2的6分频,即12Hz

ADC_RegularChannelConfig(ADC1, ADC_Channel_6, 1, ADC_SampleTime_239Cycles5); //配置ADC1通道6为239.5个采样周期 

//使能ADC、DMA

ADC_DMACmd(ADC1,ENABLE);

ADC_Cmd(ADC1,ENABLE);

 

ADC_ResetCalibration(ADC1); //复位校准寄存器

while(ADC_GetResetCalibrationStatus(ADC1)); //等待校准寄存器复位完成

 

ADC_StartCalibration(ADC1); //ADC校准

while(ADC_GetCalibrationStatus(ADC1)); //等待校准完成

ADC_ExternalTrigConvCmd(ADC1, ENABLE); //设置外部触发模式使能

}


//中断处理函数

void  DMA1_Channel1_IRQHandler(void)

{

if(DMA_GetITStatus(DMA1_IT_TC1)!=RESET){

//中断处理代码

    printf("The current value =%d rn",ADC_ConvertedValue);

DMA_ClearITPendingBit(DMA1_IT_TC1);

}

}


主程序中只需要调用Adc_Init(),然后空循环即可。此时串口调试助手,就会每隔3秒把ADC_ConvertedValue的值打印出来了。

关键字:STM32  定时器  TIM  触发  ADC采样  DMA  内存 引用地址:【STM32】定时器TIM触发ADC采样,DMA搬运到内存

上一篇:stm32 TIM2定时器触发ADC、DMA采样
下一篇:STM32 TIM触发ADC 定时采样-笔记

推荐阅读最新更新时间:2024-11-16 23:45

STM32 CAN总线传输波特率的计算
本人用的单片机是STM32F407,其它型号的单片机类似,可做参考! 一、标准CAN协议位时序概念 由于CAN属于异步通讯,没有时钟信号线,连接在同一个总线网络中的各个节点会像串口异步通讯那样,节点间使用约定好的波特率进行通讯。 同时,CAN还使用“位同步”的方式来抗干扰、吸收误差,实现对总线电平信号进行正确的采样,确保通讯正常。 为了实现这个位同步,CAN协议将每个位的时序分解为四段:SS段、PTS段、PSB1段、PBS2段。同时定义最小的时间单位:Tq,四个段的长度用x个Tq表示,加起来就是一个位的时序。 用一个图来表示可能会形象一点,如图一个位的时序就是19Tq。 以上就是CAN标准协议定义的位时序,而S
[单片机]
<font color='red'>STM32</font> CAN总线传输波特率的计算
利用单片机定时器实现信号采样和PWM控制
PWM控制方式广泛应用于各种控制系统中,但对脉冲宽度的调节一般采用硬件来实现。如使用PWM控制器或在系统中增加PWM电路 等,则本钱高、响应速度慢,而且PWM控制器与系统之间存在兼容题目。另外,控制系统中的信号采样通常是由A/D转换器来完成,因此检测精度要求较高时,调理电路复杂,而且因A/D的位数高,从而使设计的系统本钱居高不下。 本文以应用于温度控制系统为例,先容利用MOTOROLA公司生产的新型单片机MSP430F413内的定时器Time_A设计可以用时间量进行温度采样以及实现PWM调节的方法。为了可在使用少量外围电路的情况下实现控制系统的高精度丈量和控制,一方面用时间量采样,在省往1片A/D的情况下得到12位的高精度;另
[单片机]
利用单片机<font color='red'>定时器</font>实现信号采样和PWM控制
STM32五大新品齐亮相,砸重金积极扩产能!
漫长疫情之后,ST今年在北京的首场发布会,重磅推出了多款产品,覆盖了STM32产品家族的全线产品。 “这些产品发布代表着ST将进入一个新时代——云连接、智能边缘的时代,”ST微控制器和数字IC产品部MDG亚太区、物联网/人工智能创新中心及数字营销副总裁朱利安表示,在新的时代,更多的人工智能算法或智能化将运行在设备端,由于联网设备的增加,边缘侧安全识别、验证、数据保护也将越发关键。为此,ST在微控制器和数字产品的远景规划中,也将智能、安全、互联作为重点关注对象。 之所以做这样的战略布局,ST也是在第一线感受到了市场的变化。朱利安介绍,在32位通用MCU潜在市场中,工业市场份额将从2021年的的52%增长到2026年的65%,
[单片机]
<font color='red'>STM32</font>五大新品齐亮相,砸重金积极扩产能!
STM32之如何封装自己的lib库
似乎stm32刚开始出来的时候就是用的l.ib库,后来就开源了。 网上看了一些网友做的lib库,自己也弄了一下,其实也挺简单。 以下封装lib库都是基于stm32的3.0固件库 1、打开一个工程,将除固件库以外的其他文件删掉,如下: 2、在option下进行设置,如下: 来个特写: 3、将编译好的lib库拷贝到另外一个工程,添加进去,并将工程下库的c文件全部删除(h文件必须保留) 4、编译,如下: 注意,只需将stm32_lib_30.lib文件add进去就可以了,没必要在主文件里面include之类的。 否则编译的时候就会出错(重复包含),如下:
[单片机]
<font color='red'>STM32</font>之如何封装自己的lib库
stm32正点原子学习笔记(29)独立看门狗实验
iwdg.c 1 #include iwdg.h 2 3 void iwdg_init(uint8_t IWDG_Prescaler,uint16_t Reload) 4 { 5 IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable); 6 IWDG_SetPrescaler(IWDG_Prescaler); 7 IWDG_SetReload(Reload); 8 IWDG_ReloadCounter(); 9 IWDG_Enable(); 10 11 } iwdg.h 1 #ifndef __IWDG_H 2
[单片机]
<font color='red'>stm32</font>正点原子学习笔记(29)独立看门狗实验
8051单片机(STC89C52)以定时器中断模式实现两倒计时器异步计时
相较单倒计时器对timer0初值的设定, 此处将中断处理程序触发时间间隔改为5ms, 目的是给人在视觉上以更好的体验(设定为10ms会明显感觉到闪烁). #include STC89C5xRC.H unsigned char code DIG_CODE = {0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f};//对应数码管显示0~9 int tcount = 0;//记录经过的5ms区间段个数 int sec1 = 15, sec2 = 25;//倒计时初始值 void T0_INT() interrupt 1 //timer0中断, 每5m
[单片机]
8051单片机(STC89C52)以<font color='red'>定时器</font>中断模式实现两倒计时器异步计时
基于STM32内置ADC实现简易示波器的程序设计与实现
  做一个数字采样示波器一直是我长久以来的愿望,不过毕竟这个目标难度比较大,涉及的方面实在太多,模拟前端电路、高速ADC、单片机、CPLD/FPGA、通讯、上位机程序、数据处理等等,不是一下子就能成的,慢慢一步步来呗,呵呵,好歹有个目标,一直在学习各方面的知识,也有动力:)由于高速ADC涉及到采样后的数据存储问题,大量的数据涌入使得单片机无法承受,因此通常需要用外部高速RAM加CPLD配合,或者干脆用大容量的 FPGA做数据存储处理等,然后通知单片机将数据发送出去。这部分实在是难度比较大,电路非常复杂,自己是有心无力啊,还得慢慢地技术积累。。。   正好ST新推出市场的以CORTEX-M3为核心的STM32,内部集成了2个1M
[测试测量]
基于<font color='red'>STM32</font>内置ADC实现简易示波器的程序设计与实现
内存价格终于反弹 业内持不同意见
在5月18-25日,DRAM内存价格终于出现了反弹,业界分析家对该现象是标志着大范围的调整开始还是仅仅是一个短期现象持有不同意见。 据DRAMeXchange的数据显示,从5月18日开始,包括eTT在内各种频率型号的512MB DDR2内存现货价格据呈上升势头。在所有类型当中,eTT压低内存升幅最大,达到16%。 一些业内人士担心DRAM现货市场的价格反弹将会影响DRAM合同价格的调整。来自DRAM内存厂商的消息称现还没有收到电脑厂商需求急剧变化的反馈,而来自内存模组厂的消息称,由于DRAM厂商拒绝为戴尔等电脑厂商提供更低的内存报价是这次价格上升的主要原因。现在,eTT内存芯片1.35美金的价格已经几乎接近DRAM厂商的成本价。 D
[焦点新闻]
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved