yuangaoping的个人空间 https://bbs.21ic.com/?587159 [收藏] [复制] [RSS]

日志

STM32定时TIM2触发ADC采样,使用DMA保存结果

已有 621 次阅读2016-11-18 15:58 |个人分类:STM32|系统分类:单片机| STM32, TIM2, DMA, ADC采样

1.adc.h文件
//ADC-------------------------------------------------------------------------//
#ifndef __EVAL_ADC_H
        #define __EVAL_ADC_H

// Includes ------------------------------------------------------------------//
        #include "stm32f10x.h"
        #include "eval.h"
       
// Exported types ------------------------------------------------------------//
        typedef struct
        {
                GPIO_TypeDef*  io_gpio_port;
                const uint16_t io_gpio_pin;
                const uint32_t io_gpio_clk;
                const uint8_t  adc_channel;
                const uint8_t  adc_sample;
        }ADC_CONFIG_STRUCT;
// Exported constants --------------------------------------------------------//
// Exported macro ------------------------------------------------------------//
// Exported define -----------------------------------------------------------//
        #define ADC1_DR_Address    ((uint32_t)0x4001244C)
        //ADC_DR(ADC规则数据寄存器),偏移量=0x4c  ADC1(0x40012400-0x400127ff)
        //so ADC1_DR_Address=0x40012400+0x4c
       
        #define ADC_USE_DMA                        1//仅使用DMA方式,才能同时进行多路采样
        #define ADC_USE_TIM                        1//=1定时器触发扫描,=0为连续扫描
       
//ADC输入        PA0
        #define ADCn                                3
       
        #define ADC_1                                IO1 //信号1
        #define ADC_2                                IO2 //信号2
        #define ADC_3                                IO3 //信号3
#if PLATFORM_PKT
        #define ADC_1_PIN                        GPIO_Pin_1
        #define ADC_1_GPIO_PORT                GPIOA
        #define ADC_1_GPIO_CLK                RCC_APB2Periph_GPIOA  
        #define ADC_1_CHANNEL                ADC_Channel_1
        #define ADC_1_SAMPLE                ADC_SampleTime_239Cycles5
       
        #define ADC_2_PIN                        GPIO_Pin_0
        #define ADC_2_GPIO_PORT                GPIOA
        #define ADC_2_GPIO_CLK                RCC_APB2Periph_GPIOA  
        #define ADC_2_CHANNEL                ADC_Channel_0
        #define ADC_2_SAMPLE                ADC_SampleTime_239Cycles5
       
        #define ADC_3_PIN                        GPIO_Pin_2
        #define ADC_3_GPIO_PORT                GPIOA
        #define ADC_3_GPIO_CLK                RCC_APB2Periph_GPIOA  
        #define ADC_3_CHANNEL                ADC_Channel_2
        #define ADC_3_SAMPLE                ADC_SampleTime_239Cycles5
       
        #define ADC_4_PIN                        GPIO_Pin_3
        #define ADC_4_GPIO_PORT                GPIOA
        #define ADC_4_GPIO_CLK                RCC_APB2Periph_GPIOA  
        #define ADC_4_CHANNEL                ADC_Channel_3
        #define ADC_4_SAMPLE                ADC_SampleTime_239Cycles5
       
        #define ADC_5_PIN                        GPIO_Pin_1
        #define ADC_5_GPIO_PORT                GPIOA
        #define ADC_5_GPIO_CLK                RCC_APB2Periph_GPIOA  
        #define ADC_5_CHANNEL                ADC_Channel_1
        #define ADC_5_SAMPLE                ADC_SampleTime_239Cycles5
       
        #define ADC_6_PIN                        GPIO_Pin_1
        #define ADC_6_GPIO_PORT                GPIOA
        #define ADC_6_GPIO_CLK                RCC_APB2Periph_GPIOA
        #define ADC_6_CHANNEL                ADC_Channel_1
        #define ADC_6_SAMPLE                ADC_SampleTime_239Cycles5//ADC_SampleTime_7Cycles5//
#else
        #define ADC_1_PIN                        GPIO_Pin_0
        #define ADC_1_GPIO_PORT                GPIOA
        #define ADC_1_GPIO_CLK                RCC_APB2Periph_GPIOA  
        #define ADC_1_CHANNEL                ADC_Channel_0
        #define ADC_1_SAMPLE                ADC_SampleTime_239Cycles5//ADC_SampleTime_7Cycles5//
#endif
//----------------------------------------------------------------------------//
       
/*
        #define ADC16_PIN                        GPIO_Pin_0
        #define ADC16_GPIO_PORT                GPIOA
        #define ADC16_GPIO_CLK                RCC_APB2Periph_GPIOA  
        #define ADC16_CHANNEL                ADC_Channel_16//通道16(内部温度)
        #define ADC16_SAMPLE                ADC_SampleTime_239Cycles5
       
        #define ADC17_PIN                        GPIO_Pin_0
        #define ADC17_GPIO_PORT                GPIOA
        #define ADC17_GPIO_CLK                RCC_APB2Periph_GPIOA  
        #define ADC17_CHANNEL                ADC_Channel_17//通道17(内部1.2v参照电压)
        #define ADC17_SAMPLE                ADC_SampleTime_239Cycles5
*/



// External variables --------------------------------------------------------//
        extern volatile uint16_t ADC_Value[ADCn];
        extern uint16_t ADC_ConvertedValue[ADCn];
//        extern uint16_t ADC_ConvertedValue[3];
//        extern volatile uint8_t adc_dma_simple_ok;
// Exported functions ------------------------------------------------------- //
        void ADC_Configuration(void);
        uint16_t Get_ADC_Converted_Values(uint8_t channel);
        void ADC_Sample_Frequency_Set(u32 Frequency);
       
#endif
//----------------------------------------------------------------------------//
2.adc.c
/*
规则组采集多路电压时,使用DMA方式,且必须启用扫描模式,否则只能单路采样;
ADC时钟,必须<14MHz;


ADC的速度由2个参数决定,它是采样时间和转换时间之和:
TCONV = 采样时间 + 12.5个ADC时钟周期
采样时间共有8种选择:1.5、7.5、13.5、28.5、41.5、55.5、71.5和239.5;
若ADC的时钟频率=14MHz,则最高ADC的采样频率=14/(12.5+1.5)=1MHz;最低=14/(12.5+239.5)=55.56kHz
若ADC的时钟频率=12MHz,则最低ADC的采样频率=12/(12.5+239.5)=47.62kHz;
*/

// Includes ------------------------------------------------------------------//
#include "adc.h"
#include "ucos_ii.h"
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
#ifdef OS_uCOS_II_H
        OS_EVENT* adc_dma_simple_ok;//信号量
#else
        volatile uint8_t adc_dma_simple_ok = 0;
#endif
/* Extern variables ----------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/
//----------------------------------------------------------------------------//
//----------------------------------------------------------------------------//
//----------------------------------------------------------------------------//
ADC_CONFIG_STRUCT adc_struct[ADCn]        =
{
        {ADC_1_GPIO_PORT, ADC_1_PIN, ADC_1_GPIO_CLK, ADC_1_CHANNEL, ADC_1_SAMPLE},
        {ADC_1_GPIO_PORT, ADC_1_PIN, ADC_1_GPIO_CLK, ADC_Channel_Vrefint, ADC_1_SAMPLE},
        {ADC_1_GPIO_PORT, ADC_1_PIN, ADC_1_GPIO_CLK, ADC_Channel_TempSensor, ADC_1_SAMPLE},
#if 0
        {ADC_2_GPIO_PORT, ADC_2_PIN, ADC_2_GPIO_CLK, ADC_2_CHANNEL, ADC_2_SAMPLE},
        {ADC_3_GPIO_PORT, ADC_3_PIN, ADC_3_GPIO_CLK, ADC_3_CHANNEL, ADC_3_SAMPLE},
        {ADC_4_GPIO_PORT, ADC_4_PIN, ADC_4_GPIO_CLK, ADC_4_CHANNEL, ADC_4_SAMPLE},
        {ADC_5_GPIO_PORT, ADC_5_PIN, ADC_5_GPIO_CLK, ADC_5_CHANNEL, ADC_5_SAMPLE},
        {ADC_6_GPIO_PORT, ADC_6_PIN, ADC_6_GPIO_CLK, ADC_6_CHANNEL, ADC_6_SAMPLE},
#endif
};

uint16_t ADC_ConvertedValue[ADCn]        =
{
        0
};

//----------------------------------------------------------------------------//
void ADC_Configuration(void)
{
        GPIO_InitTypeDef GPIO_InitStructure;
        ADC_InitTypeDef ADC_InitStructure;

        uint8_t i;

        // Enable ADC1 clock
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_AFIO, ENABLE);

        //ADC管脚GPIO配置
        for(i = 0; i < ADCn; i++)
        {
                RCC_APB2PeriphClockCmd(adc_struct[i].io_gpio_clk, ENABLE);
                GPIO_InitStructure.GPIO_Pin = adc_struct[i].io_gpio_pin;
                GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
                GPIO_Init(adc_struct[i].io_gpio_port, &GPIO_InitStructure);
        }
        //
#if ADC_USE_DMA
{
        DMA_InitTypeDef DMA_InitStructure;
        NVIC_InitTypeDef NVIC_InitStructure;

        RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);                                                        //Enable DMA1 clock

        NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
        NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel1_IRQn;                
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
        NVIC_Init(&NVIC_InitStructure);

        DMA_DeInit(DMA1_Channel1);

        DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address;                                        //指定外设基址
        DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t) ADC_ConvertedValue;                //指定内存基址
        DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;                                                        //外设为源(外设->内存)
        DMA_InitStructure.DMA_BufferSize = ADCn;                                                                        //设置缓冲区大小
        DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;                        //外设地址固定(ADC结果)
        DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;                                                //内存地址自增(存储)
        DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;        //外设传输单位:16bit
        DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;                        //内存传输单位:16bit
        DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;                                                                //循环模式
        DMA_InitStructure.DMA_Priority = DMA_Priority_High;                                                        //选择DMA优先级
        DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;                                                                //MA传输类型,不是内存到内存

        DMA_Init(DMA1_Channel1, &DMA_InitStructure);

        DMA_Cmd(DMA1_Channel1, ENABLE);
}
#endif
        //内部温度对应16通道,无引脚,只需开启adc时钟即可.
        //内部参考电压对应17通道,无引脚,只需开启时钟.
        ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;                                //ADC独立模式
        ADC_InitStructure.ADC_ScanConvMode = DISABLE;                                        //扫描模式
        ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;                                //连续转换
        ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//不使用外部触发
        ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;                        //采集数据右对齐
        ADC_InitStructure.ADC_NbrOfChannel = 1;                                                        //转换组的通道数目
#if ADC_USE_DMA
        ADC_InitStructure.ADC_ScanConvMode = ENABLE;                                        //必须启用扫描模式
        ADC_InitStructure.ADC_NbrOfChannel = ADCn;                                                //转换组的通道数目
#endif
#if ADC_USE_TIM//使用定时器触发
        ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;                                //不使用连续转换
        ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_CC2;//定时器2触发
#endif
        ADC_Init(ADC1, &ADC_InitStructure);

        RCC_ADCCLKConfig(RCC_PCLK2_Div8);//配置ADC时钟,为PCLK2的6分频,即12MHz,时钟必须<14MHz;

        ADC_TempSensorVrefintCmd(ENABLE);//使能温度传感器

#if ADC_USE_DMA
        for(i = 0; i < ADCn; i++)
        {
                ADC_RegularChannelConfig(ADC1, adc_struct[i].adc_channel, i + 1, adc_struct[i].adc_sample);
        }

        DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE);

        ADC_DMACmd(ADC1, ENABLE);
#else
        ADC_RegularChannelConfig(ADC1, adc_struct[0].adc_channel, 1, adc_struct[0].adc_sample);
#endif

        ADC_Cmd(ADC1, ENABLE);

        //使能ADC1的复位校准寄存器
        ADC_ResetCalibration(ADC1);
        while(ADC_GetResetCalibrationStatus(ADC1));

        //开始校准ADC1
        ADC_StartCalibration(ADC1);
        while(ADC_GetCalibrationStatus(ADC1));

#if ADC_USE_TIM
        ADC_ExternalTrigConvCmd(ADC1, ENABLE);//使能ADC经外部触发启动转换功能
#endif
       
#if !ADC_USE_TIM
        ADC_SoftwareStartConvCmd(ADC1, ENABLE);        //启动上面设置好的一个通道,进行转换       
        while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);//等待EOC置位
#endif       
}
//----------------------------------------------------------------------------//
void ADC_Sample_Frequency_Set(u32 Frequency)
{
        TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
        TIM_OCInitTypeDef TIM_OCInitStructure;

        RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

        TIM_Cmd(TIM2, DISABLE);                                                                        //先停止TIM2时钟

        TIM_TimeBaseStructure.TIM_Period = 3599;                                //APR寄存器         
        TIM_TimeBaseStructure.TIM_Prescaler = 3999;//3;//(20,000 / Frequency - 1);//预分频值,用来调整频率,分频系数=1000khz/(prescaler+1)     
        TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;//向上计数
        TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;        //时钟分频
        TIM_TimeBaseStructure.TIM_RepetitionCounter = 0x00;                //溢出指定(+1)次数后产生中断

        TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);

        TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;                //
        TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;                
        TIM_OCInitStructure.TIM_Pulse = 1;                                                //!!必须<=TIM_Period!!
        TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;//

        TIM_OC2Init(TIM2, &TIM_OCInitStructure);

        TIM_Cmd(TIM2, ENABLE);
}

//----------------------------------------------------------------------------//
#if !ADC_USE_TIM
#define VOLTAGE_OVER_SAMPLING        0//过采样2^N次
uint16_t Get_ADC_Converted_Values(uint8_t channel)//提取通道电压
{
        uint32_t adc_value = 0;
        uint16_t i = 0;
       
        //通道n,规则组序号1,。。。
        ADC_RegularChannelConfig(ADC1, adc_struct[channel].adc_channel, 1, adc_struct[channel].adc_sample);
       
        ADC_Cmd(ADC1, ENABLE); //使能ADC1
       
        for(i = 0; i < (1<<VOLTAGE_OVER_SAMPLING); i++)
        {
                ADC_SoftwareStartConvCmd(ADC1, ENABLE);        //启动上面设置好的一个通道,进行转换       
                while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);//等待EOC置位
                adc_value += ADC_GetConversionValue(ADC1);        //把数据寄存器的值读走
        }
       
        return (adc_value>>VOLTAGE_OVER_SAMPLING);
}

#endif
//----------------------------------------------------------------------------//
uint16_t Get_ADC_Converted_Values0(uint8_t channel)//提取指定通道电压
{
        ADC_RegularChannelConfig(ADC1, adc_struct[channel].adc_channel, 1, adc_struct[channel].adc_sample);

        ADC_Cmd(ADC1, ENABLE);
       
#if !ADC_USE_DMA       
        ADC_SoftwareStartConvCmd(ADC1, ENABLE);        //启动上面设置好的一个通道,进行转换       
        while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);//等待EOC置位
#endif
        return ADC_GetConversionValue(ADC1);        //把数据寄存器的值读走
}
//----------------------------------------------------------------------------//
void DMA1_Channel1_IRQHandler()
{
        if(DMA_GetITStatus(DMA1_IT_TC1) != RESET)
        {
#ifdef OS_uCOS_II_H
                OSSemPost(adc_dma_simple_ok);
#else
                adc_dma_simple_ok = 1;
#endif
                DMA_ClearITPendingBit(DMA1_IT_TC1);
        }
}
//----------------------------------------------------------------------------//
3.主程序中调用
 extern OS_EVENT* adc_dma_simple_ok;        //定义信号量              
 3.1.信号量初始化
       adc_dma_simple_ok = OSSemCreate(0);
3.2.等待DMA中断,接收信号量               
                OSSemPend(adc_dma_simple_ok, 0, &err);
                if(err == OS_NO_ERR)
                {
                        //adc_dma_simple_ok = 0;
                        sprintf(tmp_str, "电压=%d,%d,%d,%d\r\n", ADC_ConvertedValue[0],ADC_ConvertedValue[1],ADC_ConvertedValue[2],adc_tmp);
                        USB_Put_Str_PC((uint8_t *)tmp_str);
                }


路过

鸡蛋

鲜花

握手

雷人

全部作者的其他最新日志

评论 (0 个评论)