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;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM