基于STM32的TIM2+ADC+DMA

想想第一次接触ADC已经是两年前了,写的第一个记录的文章就是51ADC的,时间好像过的很快,又好像过的很慢,两年了还在搞ADC😂😂,那么,今天就来写一下ADC的代码吧!

控制流程:

1、初始化TIM2,定时1ms,触发ADC

2、初始化ADC,转换相应通道的值

3、初始化DMA,转运ADC的DR寄存器值到数组

1、TIM2相应代码

#include "tim2.h"


//频率: Freq=CK_PSC/ (PSC +1) /(ARR +1) 
//占空比: Duty=CCR /(ARR +1) 
//分辨率=1/(ARR +1)

/****************************************************************************************************************
定时器2初始化函数,定时1ms,用于触发ADC1数据转换
****************************************************************************************************************/
void Tim2_1ms_Init()//定时器2定时1ms
{
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//开启时钟GPIOA的时钟
    TIM_InternalClockConfig(TIM2);//选择tim2时钟为内部时钟
    
    /*72 000 000/72=1000000(1us)--0.000001*50000=50ms*/
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructurn;
    TIM_TimeBaseInitStructurn.TIM_ClockDivision=TIM_CKD_DIV1;//输入捕获滤波频率分频(暂时不需要)
    TIM_TimeBaseInitStructurn.TIM_CounterMode=TIM_CounterMode_Up;//计数模式(向上计数    更新事件(U)不会触发中断,但可以触发内部其他电路工作,可以映射到TRGO
    TIM_TimeBaseInitStructurn.TIM_Period=1000-1;//ARR(自动重装寄存器)CNT=ARR产生的中断,称为更新中断(UI),会通往NVIC
    TIM_TimeBaseInitStructurn.TIM_Prescaler=72-1;//PSC(实际预分频值=PSC+1,所以不分频要写PSC=1-1,写0为不分频,1为2分频)
    TIM_TimeBaseInitStructurn.TIM_RepetitionCounter=0;//重复计数器
    TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructurn);
    
    
    TIM_OCInitTypeDef TIM_OCInitStructure;
    TIM_OCStructInit(&TIM_OCInitStructure);//配置结构体成员默认值
    TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1;//定时器PWM模式1
    TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_Low;//输出极性为高
    TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;//定时器输出比较状态,使能
    TIM_OCInitStructure.TIM_Pulse=990;//CCR捕获寄存器值
    TIM_OC2Init(TIM2,&TIM_OCInitStructure);

    TIM_Cmd(TIM2,ENABLE);//定时器使能
}

定时器2,不需要输出PWM,不需要中断,完整代码里可以注释掉

2、ADC相应代码

#include "adc1.h"


void ADC1_Init()
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);//开启时钟APB2_adc1的时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//开启时钟GPIOA的时钟
    RCC_ADCCLKConfig(RCC_PCLK2_Div6);//配置ADC的分频为6分频:72M/6=12M(ADC最大输入时钟不能超过14M)
    
    ADC_InitTypeDef ADC_InitStructure;//单次扫描模式——TIM2_CCR触发
    ADC_InitStructure.ADC_Mode=ADC_Mode_Independent;//单ADC模式(运行模式)
    ADC_InitStructure.ADC_ContinuousConvMode=DISABLE;//单次模式(ENABLE:连续;DISABLE:单次;)
    ADC_InitStructure.ADC_ScanConvMode=ENABLE;//非扫描模式(ENABLE:扫描模式;DISABLE:非扫描模式;)
    ADC_InitStructure.ADC_DataAlign=ADC_DataAlign_Right;//右对齐(数据对齐)
    ADC_InitStructure.ADC_ExternalTrigConv=ADC_ExternalTrigConv_T2_CC2;//外部触发为TIM2_CCR2通道(触发方式)
    ADC_InitStructure.ADC_NbrOfChannel=4;//需要转换的通道数码
    ADC_Init(ADC1,&ADC_InitStructure);
    
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AIN;//模拟输入模式
    GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0|GPIO_Pin_1;
    GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
    GPIO_Init(GPIOA,&GPIO_InitStructure);
    
    
    ADC_RegularChannelConfig(ADC1,ADC_Channel_16,1,ADC_SampleTime_55Cycles5); //需要ADC的通道,通道序号1,采样周期55.5
    ADC_RegularChannelConfig(ADC1,ADC_Channel_0,2,ADC_SampleTime_55Cycles5);  //需要ADC的通道,通道序号2,采样周期55.5
    ADC_RegularChannelConfig(ADC1,ADC_Channel_17,3,ADC_SampleTime_55Cycles5); //需要ADC的通道,通道序号3,采样周期55.5
    ADC_RegularChannelConfig(ADC1,ADC_Channel_2,4,ADC_SampleTime_55Cycles5);  //需要ADC的通道,通道序号4,采样周期55.5
    
    ADC_DMACmd(ADC1,ENABLE);             //使能ADC1的DMA请求
    ADC_TempSensorVrefintCmd(ENABLE);    //启用温度传感器通道(IN16)
    ADC_ExternalTrigConvCmd(ADC1,ENABLE);//使能外部触发
    ADC_Cmd(ADC1,ENABLE);
    
    ADC_ResetCalibration(ADC1);//复位校准
    while(ADC_GetResetCalibrationStatus(ADC1)==SET);//获取复位校准状态,RESET表示复位完成
    ADC_StartCalibration(ADC1);//开始校准           //((CR2_RSTCAL)由软件置1,复位完成硬件清0)
    while(ADC_GetCalibrationStatus(ADC1)==SET);     //获取开始校准状态,RESET表示校准完成
}                                                   //((CR2_CAL)由软件置1,校准完成硬件清0)

3、DMA相应代码

void ADC1_DMAInit(uint32_t MemoryBaseAddr)//传入内存地址
{   
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
    
    DMA_DeInit(DMA1_Channel1);
    
    DMA_InitTypeDef DMA_InitSturcrute;
    DMA_InitSturcrute.DMA_PeripheralBaseAddr=(uint32_t)(&ADC1->DR);//外设基地址为TIM1的CCR1
    DMA_InitSturcrute.DMA_MemoryBaseAddr=MemoryBaseAddr;//内存基地址
    DMA_InitSturcrute.DMA_PeripheralDataSize=DMA_PeripheralDataSize_HalfWord;//外设数据宽度16位
    DMA_InitSturcrute.DMA_MemoryDataSize=DMA_MemoryDataSize_HalfWord;//内存数据宽度16位
    DMA_InitSturcrute.DMA_PeripheralInc=DMA_PeripheralInc_Disable;//外设地址不递增
    DMA_InitSturcrute.DMA_MemoryInc=DMA_MemoryInc_Enable;//内存地址自增
    DMA_InitSturcrute.DMA_DIR=DMA_DIR_PeripheralSRC;//方向((ADC_DR)外设到内存(数组))
    DMA_InitSturcrute.DMA_BufferSize=4;//传输次数(可以用DMA_SetCurrDataCounter()单独修改)
    DMA_InitSturcrute.DMA_Mode=DMA_Mode_Normal;//单次模式
    DMA_InitSturcrute.DMA_M2M=DMA_M2M_Disable;//硬件触发;(为1软件触发,为0硬件触发)
    DMA_InitSturcrute.DMA_Priority=DMA_Priority_VeryHigh;//优先级
    DMA_Init(DMA1_Channel1,&DMA_InitSturcrute);//ADC1的数据转换
    

    DMA_Cmd(DMA1_Channel1,ENABLE);
}

DMA,不需要中断,完整代码里可以注释掉

4、main函数

/************************************************************************************************
控制流程
1、使用定时器2定时1ms的更新事件,映射到TRGO触发ADC转换,每通道转换完成,触发DMA转运。
2、ADC1对应DMA1通道一,DMA不需要进入中断
3、需要时直接读数组ADC_Value[4],就可得到ADC的值
4、

注意事项:1、TIM2不需要输出PWM波形
          2、PA0为ADC1的一通道,同时为TIM2的CH2通道,如果采用TIM2_CCR触发ADC,
          应该PA0就不能ADC的模拟通道使用;————实际未验证
          
************************************************************************************************/

#include "stm32f10x.h"                  // Device header
#include "tim2.h"
#include "adc1.h"
#include "dma.h"


uint16_t ADC_Value[4];
double ADC_Value1;
short Temp;

int main(void)
{
    
	ADC1_Init();//ADC1初始化
    Tim2_1ms_Init();
    ADC1_DMAInit((uint32_t)(ADC_Value));
    /***********************************************************************************************************
    假设MCU温度为25度:25度时为1.43V; 公式:T(℃) ={( V25 - Vsense) /Avg_Slope}+25
    分变率为:3.3/4096=0.0008V;
    1.43V对应数值:1.43/0.0008=1787;
    所以:假设是25度,则 1787*(3.3/4096.0)=1.43V;
                         ((1.43-1.43)/0.0043)+25=25度
    ***********************************************************************************************************/
	while (1)
	{
        
	}
}

 初始化完成,每隔1ms,就会触发ADC进行转换。

调试问题:在使用Keil软件调试的时候,ADC用的是软件触发,程序跑着跑着就会卡在

while(ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC)==RESET);//判断是否转换完成,转换完成EOC位置1

单步调试发现EOC标志位为0.

网上找到的解释:是查看寄存器导致EOC标志位清0了(右下角)

解决办法是:不查看寄存器(103目前只有这样)

okok就这样了,完整的代码链接我放下面,里面有些没有到的代码,你只需要看ADC、TIM2、DMA的就可以了;

代码链接:https://download.csdn.net/download/m0_67272479/90921690?spm=1001.2014.3001.5501

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值