zoukankan      html  css  js  c++  java
  • STM32 ADC多通道规则采样和注入采样


    layout: post
    tags: [STM32]
    comments: true

    什么是ADC?

    Analog to Digital Converter,将模拟信号转换成数字的模数转换器,后面可能还会接触到DAC,恰恰相反,是将数字信号转换成模拟信号。具体的原理可以自行找搜索引擎,可以得到更好的答案。

    STM32 ADC的特性

    参考手册给出ADC的功能十分丰富,具体如下:

    • 12 bit分辨率,量化到0-4096的范围;
    • 转换结束、注入转换结束和发生模拟看门狗事件时产生中断
    • 单次和连续转换模式
    • 从通道0到通道n的自动扫描模式
    • 自校准
    • 带内嵌数据一致性的数据对齐
    • 采样间隔可以按通道分别编程
    • 规则转换和注入转换均有外部触发选项
    • 间断模式
      本文只讨论规则采样和注入采样,并给出具体的代码实现,更多细节还需要参考《STM32参考手册》

    采样模式

    • 规则采样:相当于软件触发采样,可以在程序里主动调用规则采样去读取具体的ADC值,同样
    • 注入采样:相当于中断,所以需要具体的触发源,比如外部的信号可以触发注入采样,ADC转换成功之后,便会触发ADC中断,在中断服务子程序中,就可以读取ADC值;

    触发源可以是外部信号,也可以是定时器的触发信号;标准库中注入模式的触发信号如下所示;
    在这里插入图片描述
    注入组的外部触发信号

    /** @defgroup ADC_external_trigger_sources_for_injected_channels_conversion 
      * @{
      */
    
    #define ADC_ExternalTrigInjecConv_T2_TRGO           ((uint32_t)0x00002000) /*!< For ADC1 and ADC2 */
    #define ADC_ExternalTrigInjecConv_T2_CC1            ((uint32_t)0x00003000) /*!< For ADC1 and ADC2 */
    #define ADC_ExternalTrigInjecConv_T3_CC4            ((uint32_t)0x00004000) /*!< For ADC1 and ADC2 */
    #define ADC_ExternalTrigInjecConv_T4_TRGO           ((uint32_t)0x00005000) /*!< For ADC1 and ADC2 */
    #define ADC_ExternalTrigInjecConv_Ext_IT15_TIM8_CC4 ((uint32_t)0x00006000) /*!< For ADC1 and ADC2 */
    
    #define ADC_ExternalTrigInjecConv_T1_TRGO           ((uint32_t)0x00000000) /*!< For ADC1, ADC2 and ADC3 */
    #define ADC_ExternalTrigInjecConv_T1_CC4            ((uint32_t)0x00001000) /*!< For ADC1, ADC2 and ADC3 */
    #define ADC_ExternalTrigInjecConv_None              ((uint32_t)0x00007000) /*!< For ADC1, ADC2 and ADC3 */
    
    #define ADC_ExternalTrigInjecConv_T4_CC3            ((uint32_t)0x00002000) /*!< For ADC3 only */
    #define ADC_ExternalTrigInjecConv_T8_CC2            ((uint32_t)0x00003000) /*!< For ADC3 only */
    #define ADC_ExternalTrigInjecConv_T8_CC4            ((uint32_t)0x00004000) /*!< For ADC3 only */
    #define ADC_ExternalTrigInjecConv_T5_TRGO           ((uint32_t)0x00005000) /*!< For ADC3 only */
    #define ADC_ExternalTrigInjecConv_T5_CC4            ((uint32_t)0x00006000) /*!< For ADC3 only */
    

    规则组的外部触发信号

    #define ADC_ExternalTrigConv_T1_CC1                ((uint32_t)0x00000000) /*!< For ADC1 and ADC2 */
    #define ADC_ExternalTrigConv_T1_CC2                ((uint32_t)0x00020000) /*!< For ADC1 and ADC2 */
    #define ADC_ExternalTrigConv_T2_CC2                ((uint32_t)0x00060000) /*!< For ADC1 and ADC2 */
    #define ADC_ExternalTrigConv_T3_TRGO               ((uint32_t)0x00080000) /*!< For ADC1 and ADC2 */
    #define ADC_ExternalTrigConv_T4_CC4                ((uint32_t)0x000A0000) /*!< For ADC1 and ADC2 */
    #define ADC_ExternalTrigConv_Ext_IT11_TIM8_TRGO    ((uint32_t)0x000C0000) /*!< For ADC1 and ADC2 */
    
    #define ADC_ExternalTrigConv_T1_CC3                ((uint32_t)0x00040000) /*!< For ADC1, ADC2 and ADC3 */
    #define ADC_ExternalTrigConv_None                  ((uint32_t)0x000E0000) /*!< For ADC1, ADC2 and ADC3 */
    
    #define ADC_ExternalTrigConv_T3_CC1                ((uint32_t)0x00000000) /*!< For ADC3 only */
    #define ADC_ExternalTrigConv_T2_CC3                ((uint32_t)0x00020000) /*!< For ADC3 only */
    #define ADC_ExternalTrigConv_T8_CC1                ((uint32_t)0x00060000) /*!< For ADC3 only */
    #define ADC_ExternalTrigConv_T8_TRGO               ((uint32_t)0x00080000) /*!< For ADC3 only */
    #define ADC_ExternalTrigConv_T5_CC1                ((uint32_t)0x000A0000) /*!< For ADC3 only */
    #define ADC_ExternalTrigConv_T5_CC3                ((uint32_t)0x000C0000) /*!< For ADC3 only */
    

    从参考手册中可以看到ADC的框架,注入通道转换成功之后,标志位JEOC使能,然后JEOCIE中断位被使能,最终触发ADC中断;
    在这里插入图片描述

    采样时间

    标准库中对于采样周期的定义;

    #define ADC_SampleTime_1Cycles5                    ((uint8_t)0x00)
    #define ADC_SampleTime_7Cycles5                    ((uint8_t)0x01)
    #define ADC_SampleTime_13Cycles5                   ((uint8_t)0x02)
    #define ADC_SampleTime_28Cycles5                   ((uint8_t)0x03)
    #define ADC_SampleTime_41Cycles5                   ((uint8_t)0x04)
    #define ADC_SampleTime_55Cycles5                   ((uint8_t)0x05)
    #define ADC_SampleTime_71Cycles5                   ((uint8_t)0x06)
    #define ADC_SampleTime_239Cycles5                  ((uint8_t)0x07)
    

    TCONV = 采样时间+ 12.5个周期

    ADC_InjectedChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_1Cycles5);
    

    ADCCLK=14MHz,采样时间为1.5周期(ADC_SampleTime_1Cycles5)

    采样时间TCONV = 1.5 + 12.5 = 14周期 = 1μs

    代码实现

    基于ST标准库V3.5,实现了ADC规则采样和注入采样两种模式;

    current.h

    #ifndef CURRENT_H
    #define CURRENT_H
    #include <stdint.h>
    
    #define RES_IA	1024	//采样电阻A
    #define RES_IB	1024	//采样电阻B
    
    /**
     * Ia--->PA0/ADC0
     *	|------------>PA2/ADC2
     * Ib--->PA1/ADC1
     *	|------------>PA3/ADC3
     */
    
    void cur_fbk_init(void);
    
    uint16_t cur_fbk_get_Ia(void);
    uint16_t cur_fbk_get_Ia_avl(uint8_t sample_times);
    uint16_t cur_fbk_get_Ib(void);
    uint16_t cur_fbk_get_Ib_avl(uint8_t sample_times);
    
    uint16_t get_inject_ia(void);
    uint16_t get_inject_ib(void);
    
    
    int16_t cur_fbk_get_theta(void);
    
    #endif
    
    

    current.c

    #include "current.h"
    #include "stm32f10x.h"
    #include "stm32f10x_gpio.h"
    #include "stm32f10x_adc.h"
    #include "stm32f10x_rcc.h"
    #include <stdint.h>
    
    #if 1
    #define IA_CHANNEL_DRI ADC_Channel_0
    #define IB_CHANNEL_DRI ADC_Channel_1
    #else
    #define IA_CHANNEL_DRI ADC_Channel_2
    #define IB_CHANNEL_DRI ADC_Channel_3
    #endif
    
    volatile uint16_t Ia_val = 0;
    volatile uint16_t Ib_val = 0;
    
    static void cur_fbk_irq_init(void){
    
    	NVIC_InitTypeDef NVIC_InitStructure;
    
    	/* Configure and enable ADC interrupt */
    	NVIC_InitStructure.NVIC_IRQChannel = ADC1_2_IRQn;
    
    	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    	NVIC_Init(&NVIC_InitStructure);
    
    }
    
    static void cur_fbk_adc_init(void){
    	
    	ADC_DeInit(ADC1);
    	ADC_InitTypeDef ADC_InitStructure;
    	/* ADC1 configuration ------------------------------------------------------*/
    	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
    	ADC_InitStructure.ADC_ScanConvMode = ENABLE;
    	ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
    	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1;
    	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
    	ADC_InitStructure.ADC_NbrOfChannel = 2;
    
    	/* Set injected sequencer length */
    	ADC_InjectedSequencerLengthConfig(ADC1, 2);
    	/* ADC1 injected channel Configuration */ 
    	ADC_InjectedChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_71Cycles5);
    	ADC_InjectedChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_71Cycles5);
    	//ADC_InjectedChannelConfig(ADC1, ADC_Channel_2, 1, ADC_SampleTime_71Cycles5);
    	//ADC_InjectedChannelConfig(ADC1, ADC_Channel_3, 1, ADC_SampleTime_71Cycles5);	
    	/* ADC1 injected external trigger configuration */
    	//ADC_ExternalTrigInjectedConvConfig(ADC1, ADC_ExternalTrigInjecConv_T1_CC4);
    	ADC_ExternalTrigInjectedConvConfig(ADC1, ADC_ExternalTrigInjecConv_None);
    
    	//ADC_SetInjectedOffset(ADC1, ADC_InjectedChannel_1,2048);
    	//ADC_SetInjectedOffset(ADC1, ADC_InjectedChannel_2,2048);
    	
    	/* Enable automatic injected conversion start after regular one */
    	ADC_AutoInjectedConvCmd(ADC1, ENABLE);
    	
    	/* Enable ADC1 DMA */
    	ADC_DMACmd(ADC1, ENABLE);
    	
    	/* Enable ADC1 external trigger */ 
    	ADC_ExternalTrigConvCmd(ADC1, ENABLE);
    
    	
    	ADC_Init(ADC1, &ADC_InitStructure);
    	ADC_Cmd(ADC1, ENABLE);
    	
    	ADC_ResetCalibration(ADC1);
    	while(ADC_GetResetCalibrationStatus(ADC1)){
    		/**
    			TODO
                timeout_detect
    		*/
    		
    	}
    	ADC_StartCalibration(ADC1);
    	while(ADC_GetCalibrationStatus(ADC1)){
    		/**
    			TODO
                timeout_detect
    		*/
    	}
    
    	ADC_ITConfig(ADC1, ADC_IT_JEOC, ENABLE);
    }
    
    static void cur_fbk_rcc_init(void){
    
    	/* ADCCLK = PCLK2/6 */
    	RCC_ADCCLKConfig(RCC_PCLK2_Div6); 
    	  
    	/* Enable peripheral clocks ------------------------------------------------*/
    	/* Enable DMA1 and DMA2 clocks */
    	//RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1 | RCC_AHBPeriph_DMA2, ENABLE);
    
    	/* Enable ADC1 and GPIOA clocks */
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOA, ENABLE);
    }
    
    static void cur_fbk_pin_init(void){
    
    	GPIO_InitTypeDef GPIO_InitStructure;
    	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
    	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
    	GPIO_Init(GPIOA, &GPIO_InitStructure);		
      
    }
    
    static uint16_t cur_fbk_get_ad_val(uint8_t channel){
    	ADC_RegularChannelConfig(ADC1, channel, 1, ADC_SampleTime_239Cycles5 );
    	ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能软件转换功能
    	//等待转换结束
    	/*
     		EOC:转换结束位 (End of conversion)
    		该位由硬件在(规则或注入)通道组转换结束时设置,由软件清除或由读取ADC_DR时清除
    		0:转换未完成;
    		1:转换完成。 		
    	 */
    	while((ADC1->SR & ADC_FLAG_EOC) != ADC_FLAG_EOC){}
    	//while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC )){}
    	
    	return ADC_GetConversionValue(ADC1); //返回最近一次 ADC1 规则组的转换结果
    }
    	
    void cur_fbk_init(void){
    	cur_fbk_rcc_init();
    	cur_fbk_pin_init();
    	cur_fbk_irq_init();
    	cur_fbk_adc_init();
    }
    
    uint16_t cur_fbk_get_Ia_avl(uint8_t sample_times){
    	uint32_t Ia_sum = 0;
    	int8_t i;
    	for(; i < sample_times; i++){
    		Ia_sum+=cur_fbk_get_Ia();
    	}
    	return (uint16_t)(Ia_sum/sample_times);
    }
    
    uint16_t cur_fbk_get_Ib_avl(uint8_t sample_times){
    	uint32_t Ib_sum = 0;
    	int8_t i = 0;
    	for(;i < sample_times; i++){
    		Ib_sum+=cur_fbk_get_Ib();
    	}
    	return (uint16_t)(Ib_sum/sample_times);
    }
    
    
    uint16_t cur_fbk_get_Ia(void){
    	return cur_fbk_get_ad_val(IA_CHANNEL_DRI);
    }
    
    uint16_t cur_fbk_get_Ib(void){
    	return cur_fbk_get_ad_val(IB_CHANNEL_DRI);
    }
    
    int16_t cur_fbk_get_theta(void){
    	return 0;
    }
    
    /******************************************************************************/
    /*            STM32F10x Peripherals Interrupt Handlers                        */
    /******************************************************************************/
    
    /**
      * @brief  This function handles ADC1 and ADC2 global interrupts requests.
      * @param  None
      * @retval None
      */
    void ADC1_2_IRQHandler(void)
    {
    
    	Ia_val = ADC_GetInjectedConversionValue(ADC1, ADC_InjectedChannel_1);
    	Ib_val = ADC_GetInjectedConversionValue(ADC1, ADC_InjectedChannel_2);
    	/* Clear ADC1 JEOC pending interrupt bit */
    	ADC_ClearITPendingBit(ADC1, ADC_IT_JEOC);
    }
    
    
    uint16_t get_inject_ia(void){
    	return Ia_val;
    }
    
    uint16_t get_inject_ib(void){
    	return Ib_val;
    }
    
    
  • 相关阅读:
    Internet protocol optimizer
    SQl常用语句总结(持续更新……)
    让 步( 写的太好了!)
    让 步( 写的太好了!)
    让 步( 写的太好了!)
    $.ajax()参数详解及标准写法
    $.ajax()参数详解及标准写法
    $.ajax()参数详解及标准写法
    JQuery函数attr()和prop()的区别
    公司来了个傻员工,改变了所有聪明的员工
  • 原文地址:https://www.cnblogs.com/unclemac/p/12783371.html
Copyright © 2011-2022 走看看