STM32-ADC规则通道和注入通道混合使用

STM32F407规则采样和注入采样混合使用和数据处理

  • 1.ADC
    • 1.1相关IO口、DMA宏定义(adc.h)
    • 1.2ADC IO 口的配置(adc.c)
    • 1.3DMA配置(adc.c)
      • 1.3.1DMA中断配置(adc.c)
    • 1.4ADC配置(adc.c)
      • 1.4.1ADC规则采样与注入采样配置(adc.c)
  • 2.TIM配置(adc.c)
  • 3.滤波函数(adc.c)
  • 4.中断服务函数(stm32f4xx_it.c)
  • 5.在while循环中运行VALUE_PRINTF函数(main.c)

代码基于野火的ADC采样例程进一步修改而来。
代码最后实现功能:
1.软件触发规则采样,(ADC1两个规则通道,ADC2两个规则通道)DMA中断进行数据处理;
2.TIM1-CH4上升沿外部触发注入采样(ADC1一个注入通道,ADC2一个注入通道);
3.对采集的数据进行简单的均值滤波;
4.串口打印采集电压数据。

代码的注释以及说明都标注出来了!

1.ADC

1.1相关IO口、DMA宏定义(adc.h)

/*=====================通道1 IO======================*/
// PB0 通过调帽接电位器
// ADC IO宏定义
#define RHEOSTAT_ADC_GPIO_PORT1    GPIOB
#define RHEOSTAT_ADC_GPIO_PIN1     GPIO_Pin_0
#define RHEOSTAT_ADC_GPIO_CLK1     RCC_AHB1Periph_GPIOB
#define RHEOSTAT_ADC_CHANNEL1     ADC_Channel_8
/*=====================通道2 IO ======================*/
// PA6 悬空,可用杜邦线接3V3或者GND来实验
// ADC IO宏定义
#define RHEOSTAT_ADC_GPIO_PORT2    GPIOB
#define RHEOSTAT_ADC_GPIO_PIN2     GPIO_Pin_1
#define RHEOSTAT_ADC_GPIO_CLK2     RCC_AHB1Periph_GPIOB
#define RHEOSTAT_ADC_CHANNEL2     ADC_Channel_9


/*=====================注入通道 IO ======================*/
// PA6 悬空,可用杜邦线接3V3或者GND来实验
// ADC IO宏定义
#define RHEOSTAT_ADC_GPIO_PORT3    GPIOA
#define RHEOSTAT_ADC_GPIO_PIN3     GPIO_Pin_6
#define RHEOSTAT_ADC_GPIO_CLK3     RCC_AHB1Periph_GPIOA
#define RHEOSTAT_ADC_CHANNEL3     ADC_Channel_6
/*=====================注入通道 IO ======================*/
// PA1 悬空,可用杜邦线接3V3或者GND来实验
// ADC IO宏定义
#define RHEOSTAT_ADC_GPIO_PORT4    GPIOA
#define RHEOSTAT_ADC_GPIO_PIN4     GPIO_Pin_1	
#define RHEOSTAT_ADC_GPIO_CLK4     RCC_AHB1Periph_GPIOA
#define RHEOSTAT_ADC_CHANNEL4     ADC_Channel_1

// ADC 序号宏定义
#define RHEOSTAT_ADC1             ADC1
#define RHEOSTAT_ADC1_CLK         RCC_APB2Periph_ADC1

// PB1 通过调帽接光敏电阻
// ADC IO宏定义
#define RHEOSTAT_ADC2_GPIO_PORT1    GPIOA
#define RHEOSTAT_ADC2_GPIO_PIN1     GPIO_Pin_3
#define RHEOSTAT_ADC2_GPIO_CLK1     RCC_AHB1Periph_GPIOA
#define RHEOSTAT_ADC2_CHANNEL1     ADC_Channel_3
/*=====================ADC2    通道2 IO ======================*/
// PA1 悬空,可用杜邦线接3V3或者GND来实验
// ADC IO宏定义
#define RHEOSTAT_ADC2_GPIO_PORT2    GPIOA
#define RHEOSTAT_ADC2_GPIO_PIN2     GPIO_Pin_4
#define RHEOSTAT_ADC2_GPIO_CLK2     RCC_AHB1Periph_GPIOA
#define RHEOSTAT_ADC2_CHANNEL2     ADC_Channel_4

#define RHEOSTAT_ADC2             ADC2
#define RHEOSTAT_ADC2_CLK         RCC_APB2Periph_ADC2


// ADC CDR寄存器宏定义,ADC转换后的数字值则存放在这里
#define RHEOSTAT_ADC_CDR_ADDR    ((uint32_t)0x40012308)

// ADC DMA 通道宏定义,这里我们使用DMA传输
#define RHEOSTAT_ADC_DMA_CLK      RCC_AHB1Periph_DMA2
#define RHEOSTAT_ADC_DMA_CHANNEL  DMA_Channel_0
#define RHEOSTAT_ADC_DMA_STREAM   DMA2_Stream0

// 这个N是为了滤波使用的
#define N 20


// ADC 中断宏定义
#define Rheostat_ADC_IRQ            ADC_IRQn
#define Rheostat_ADC_INT_FUNCTION   ADC_IRQHandler

void Rheostat_Init(void);
void ADC1_External_T1_CC4_Init(void);     //这个是为了触发ADC注入采样
void VALUE_PRINTF(void);                  //用串口打印函数转换结果
void FILTER(void);                        //简单的平均值滤波
u16 GetVolt(u16 advalue);   
#endif /* __BSP_ADC_H */

1.2ADC IO 口的配置(adc.c)

static void Rheostat_ADC_GPIO_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	
	// 使能 GPIO 时钟
	RCC_AHB1PeriphClockCmd(RHEOSTAT_ADC_GPIO_CLK1, ENABLE);
	RCC_AHB1PeriphClockCmd(RHEOSTAT_ADC_GPIO_CLK2, ENABLE);
	RCC_AHB1PeriphClockCmd(RHEOSTAT_ADC_GPIO_CLK3, ENABLE);
	RCC_AHB1PeriphClockCmd(RHEOSTAT_ADC_GPIO_CLK4, ENABLE);
	RCC_AHB1PeriphClockCmd(RHEOSTAT_ADC2_GPIO_CLK1, ENABLE);
	RCC_AHB1PeriphClockCmd(RHEOSTAT_ADC2_GPIO_CLK2, ENABLE);
		
	// 配置 IO
	GPIO_InitStructure.GPIO_Pin = RHEOSTAT_ADC_GPIO_PIN1;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;	    
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ; //不上拉不下拉
	GPIO_Init(RHEOSTAT_ADC_GPIO_PORT1, &GPIO_InitStructure);
	
		// 配置 IO
	GPIO_InitStructure.GPIO_Pin = RHEOSTAT_ADC_GPIO_PIN2;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;	    
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ; //不上拉不下拉
	GPIO_Init(RHEOSTAT_ADC_GPIO_PORT2, &GPIO_InitStructure);
	
		// 配置 IO
	GPIO_InitStructure.GPIO_Pin = RHEOSTAT_ADC_GPIO_PIN3;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;	    
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ; //不上拉不下拉
	GPIO_Init(RHEOSTAT_ADC_GPIO_PORT3, &GPIO_InitStructure);
	
		// 配置 IO
	GPIO_InitStructure.GPIO_Pin = RHEOSTAT_ADC_GPIO_PIN4;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;	    
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ; //不上拉不下拉
	GPIO_Init(RHEOSTAT_ADC_GPIO_PORT4, &GPIO_InitStructure);

	// 配置 IO
	GPIO_InitStructure.GPIO_Pin = RHEOSTAT_ADC2_GPIO_PIN1;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;	    
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ; //不上拉不下拉
	GPIO_Init(RHEOSTAT_ADC2_GPIO_PORT1, &GPIO_InitStructure);
	
		// 配置 IO
	GPIO_InitStructure.GPIO_Pin = RHEOSTAT_ADC2_GPIO_PIN2;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;	    
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ; //不上拉不下拉
	GPIO_Init(RHEOSTAT_ADC2_GPIO_PORT2, &GPIO_InitStructure);
}

1.3DMA配置(adc.c)

定义需要用到的变量:

     __IO uint16_t ADC_ConvertedValue[4];
	 float ADC_ConvertedValueLocal[6]; 
	 vu16 AD_Value[20][4];   //AD采样值
     vu16 After_filter[4];   //AD滤波后
 static void Rheostat_ADC_Mode_Config(void)
{
  DMA_InitTypeDef DMA_InitStructure
	
  // ------------------DMA Init 结构体参数 初始化--------------------------
  // ADC1使用DMA2,数据流0,通道0,这个是手册固定死的
  // 开启DMA时钟
    RCC_AHB1PeriphClockCmd(RHEOSTAT_ADC_DMA_CLK, ENABLE); 
	// 外设基址为:ADC 数据寄存器地址,
	DMA_InitStructure.DMA_PeripheralBaseAddr = RHEOSTAT_ADC_CDR_ADDR;	
  // 存储器地址,实际上就是一个内部SRAM的变量	
	DMA_InitStructure.DMA_Memory0BaseAddr = (vu32)AD_Value;  
  // 数据传输方向为外设到存储器	
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;	
	/********************************************************
	缓冲区大小为,指一次传输的数据量,下边这个80是4*20等于80,不是要进行平均值滤波嘛,
	规则通道每个通道采集20个值,80采集完进中断。
	Q:为什么规则采样和注入采样一个6个通道,而这里只有4*20 = 80个数据,而不是120个呢?
	A:我个人理解:DMA存储的是规则通道的数据,注入组的数据有专门的数据寄存器进行存储。
	*********************************************************/
	DMA_InitStructure.DMA_BufferSize = 80;	
	// 外设寄存器只有一个,地址不用递增
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
  // 存储器地址固定
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; 
  // // 外设数据大小为半字,即两个字节 
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; 
  //	存储器数据大小也为半字,跟外设数据大小相同
	DMA_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_HalfWord;	
	// 循环传输模式,循环模式开启,buffer写满后,自动回到初始地址开始传输
	DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
  // DMA 传输通道优先级为高,当使用一个DMA通道时,优先级设置不影响
	DMA_InitStructure.DMA_Priority = DMA_Priority_High;
  // 禁止DMA FIFO	,使用直连模式
  DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;  
  // FIFO 大小,FIFO模式禁止时,这个不用配置	
  DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
  DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
  DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;  
	// 选择 DMA 通道,通道存在于流中
  DMA_InitStructure.DMA_Channel = RHEOSTAT_ADC_DMA_CHANNEL; 
  //初始化DMA流,流相当于一个大的管道,管道里面有很多通道
	DMA_Init(RHEOSTAT_ADC_DMA_STREAM, &DMA_InitStructure);
		//开启DMA中断
	DMA_ITConfig(RHEOSTAT_ADC_DMA_STREAM,DMA_IT_TC,ENABLE);  
	// 使能DMA流
  DMA_Cmd(RHEOSTAT_ADC_DMA_STREAM, ENABLE);

1.3.1DMA中断配置(adc.c)

	NVIC_InitTypeDef   NVIC_InitStructure;
//-----------------------------中断初始化--------------------------
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); 
  NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream0_IRQn;//外部中断   +
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x01;//抢占优先级1
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;//子优先级1
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能外部中断通道
  NVIC_Init(&NVIC_InitStructure);//配置

1.4ADC配置(adc.c)

//	先定义这两个ADC结构体
	ADC_InitTypeDef ADC_InitStructure;
    ADC_CommonInitTypeDef ADC_CommonInitStructure;
// 开启ADC时钟
	RCC_APB2PeriphClockCmd(RHEOSTAT_ADC1_CLK , ENABLE);
    RCC_APB2PeriphClockCmd(RHEOSTAT_ADC2_CLK , ENABLE);
	
  // -------------------ADC Common 结构体 参数 初始化------------------------
	// ADC双通道规则-注入模式
  ADC_CommonInitStructure.ADC_Mode = ADC_DualMode_RegSimult_InjecSimult;
  // 时钟为fpclk x分频	
  ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div4;
  // 禁止DMA直接访问模式	
  ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_1;
  // 采样时间间隔	
  ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;  
  ADC_CommonInit(&ADC_CommonInitStructure);
	
  // -------------------ADC Init 结构体 参数 初始化--------------------------
	ADC_StructInit(&ADC_InitStructure);
  // ADC 分辨率
  ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
  // 扫描模式,多通道采集才需要	
  ADC_InitStructure.ADC_ScanConvMode = ENABLE; 
  // 连续转换	
  ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; 
  //禁止外部边沿触发
  ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
  //外部触发通道,本例子使用软件触发,此值随便赋值即可
  ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1;
  //数据右对齐	
  ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
  /*********************************************************
  转换通道2个,配置要求:ADC1和ADC2分别两路规则采样,软件触发。
  这里我的理解是:
  这里的通道数是主ADC(ADC1)的规则转换通道数,因为选择的是双ADC规则-注入模式,
  所以从ADC(ADC2)的两个通道就不用往这里加上去。
  ************************************************************/
  ADC_InitStructure.ADC_NbrOfConversion = 2;

1.4.1ADC规则采样与注入采样配置(adc.c)

//---------------------------------------------------------------------------	
  ADC_Init(RHEOSTAT_ADC1, &ADC_InitStructure);	
   // 配置 ADC1 通道转换顺序,采样时间为3个时钟周期
  ADC_RegularChannelConfig(RHEOSTAT_ADC1, RHEOSTAT_ADC_CHANNEL1, 1, ADC_SampleTime_3Cycles); 
  ADC_RegularChannelConfig(RHEOSTAT_ADC1, RHEOSTAT_ADC_CHANNEL2, 2, ADC_SampleTime_3Cycles);   
  //---------------------------------------------------------------------------
	
	ADC_Init(RHEOSTAT_ADC2, &ADC_InitStructure);
  // 配置 ADC2 通道转换顺序,采样时间为3个时钟周期
  ADC_RegularChannelConfig(RHEOSTAT_ADC2, RHEOSTAT_ADC2_CHANNEL1, 1, ADC_SampleTime_3Cycles); 
  ADC_RegularChannelConfig(RHEOSTAT_ADC2, RHEOSTAT_ADC2_CHANNEL2, 2, ADC_SampleTime_3Cycles);     
  //---------------------------------------------------------------------------
	//配置注入采样的通道转换顺序,ADC1只使用一个注入通道
	ADC_InjectedSequencerLengthConfig(RHEOSTAT_ADC1,1);
	ADC_InjectedChannelConfig(RHEOSTAT_ADC1,RHEOSTAT_ADC_CHANNEL3,1,ADC_SampleTime_112Cycles);
	//配置注入采样的通道转换顺序,ADC2也只使用一个注入通道	
	ADC_InjectedSequencerLengthConfig(RHEOSTAT_ADC2,1);
	ADC_InjectedChannelConfig(RHEOSTAT_ADC2,RHEOSTAT_ADC_CHANNEL4,1,ADC_SampleTime_112Cycles);
	//配置注入采样的触发方式,TIM1-CH4的脉冲上升沿触发
	ADC_ExternalTrigInjectedConvConfig(RHEOSTAT_ADC1,ADC_ExternalTrigInjecConv_T1_CC4);
	ADC_ExternalTrigInjectedConvEdgeConfig(RHEOSTAT_ADC1,ADC_ExternalTrigInjecConvEdge_Rising);
	//这个照理说不用配置,因为我ADC的模式选的是双通道-规则-注入模式,但是我没有去掉这两行语句进行实验,
	ADC_ExternalTrigInjectedConvConfig(RHEOSTAT_ADC2,ADC_ExternalTrigInjecConv_T1_CC4);
	ADC_ExternalTrigInjectedConvEdgeConfig(RHEOSTAT_ADC2,ADC_ExternalTrigInjecConvEdge_Rising);

	// 使能DMA请求 after last transfer (multi-ADC mode)
	ADC_MultiModeDMARequestAfterLastTransferCmd(ENABLE);
//ADC_DMARequestAfterLastTransferCmd(RHEOSTAT_ADC1,ENABLE);
// 使能ADC DMA
  ADC_DMACmd(RHEOSTAT_ADC1, ENABLE);
	ADC_DMACmd(RHEOSTAT_ADC2, ENABLE);
	
  // 使能ADC
  ADC_Cmd(RHEOSTAT_ADC1, ENABLE);  
  ADC_Cmd(RHEOSTAT_ADC2, ENABLE);   
  
  //开始adc转换,软件触发
  ADC_SoftwareStartConv(RHEOSTAT_ADC1);
  ADC_SoftwareStartConv(RHEOSTAT_ADC2);
}

2.TIM配置(adc.c)

void ADC1_External_T1_CC4_Init(void)
{
	 TIM_TimeBaseInitTypeDef   TIM_TimeBaseStructure;
     TIM_OCInitTypeDef         TIM_OCInitStructure;
	
	 RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);

 /* Time Base configuration */
    TIM_TimeBaseStructInit(&TIM_TimeBaseStructure); 
    TIM_TimeBaseStructure.TIM_Period = 99;          
    TIM_TimeBaseStructure.TIM_Prescaler = 1679;       
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;    
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  
    TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
	 
	TIM_SelectOutputTrigger(TIM1,TIM_TRGOSource_Update); //选择TRGO触发源为计时器更新时间

    /* TIM1 channel1 configuration in PWM mode */
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; 
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;                
    TIM_OCInitStructure.TIM_Pulse =10; 
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;         
    TIM_OC4Init(TIM1, &TIM_OCInitStructure);

//    TIM_ITConfig(TIM1, TIM_IT_CC4, ENABLE);        // CCR4的中断,这个通过设置CCR4的pulse来控制产生中断相当于PWM-ON的位置
		
		TIM_CtrlPWMOutputs(TIM1, ENABLE);
    TIM_Cmd(TIM1, ENABLE);
	
}

//主函数初始化Rheostat_Init()就可以
void Rheostat_Init(void)
{
	Rheostat_ADC_GPIO_Config();
	Rheostat_ADC_Mode_Config();
	ADC1_External_T1_CC4_Init();
}

3.滤波函数(adc.c)

/*---------------------------------均值滤波-----------------------------*/
void FILTER(void)
{
	 int  sum = 0;
   u8 count,i;
	for(i=0; i<4; i++)
	{
	for(count=0; count<N; count++)
		{
		sum += AD_Value[count][i];
		}
		After_filter[i] = sum / N;
//		ADC_ConvertedValueLocal[i] =(float)(After_filter[i] *3.3/4096);
		sum = 0;
	}
}

4.中断服务函数(stm32f4xx_it.c)

extern __IO uint16_t ADC_ConvertedValue[4];              //开始全部初始化为0
extern	  float ADC_ConvertedValueLocal[7]; 
void DMA2_Stream0_IRQHandler(void)
{
	if(DMA_GetITStatus(DMA2_Stream0,DMA_IT_TCIF0) != RESET)
	{
  // 读取ADC的转换值
		  FILTER();
	}
	DMA_ClearITPendingBit(DMA2_Stream0,DMA_IT_TCIF0);
}

5.在while循环中运行VALUE_PRINTF函数(main.c)

static void Delay(__IO uint32_t nCount)	 //简单的延时函数
{
	for(; nCount != 0; nCount--);
}

void VALUE_PRINTF(void)
{
		__IO uint16_t ADC_InjectValue[2]={0};  //定义数组
		/******************存放注入转换数据*****************************/
		ADC_InjectValue[0] = ADC_GetInjectedConversionValue(RHEOSTAT_ADC1,ADC_InjectedChannel_1);
		ADC_InjectValue[1] = ADC_GetInjectedConversionValue(RHEOSTAT_ADC2,ADC_InjectedChannel_1);
       
   	    ADC_ConvertedValueLocal[0] =(float)(After_filter[0] *3.3/4096); 
	 	ADC_ConvertedValueLocal[1] =(float)(After_filter[1] *3.3/4096); 
	 	ADC_ConvertedValueLocal[2] =(float)(After_filter[2] *3.3/4096); 
	 	ADC_ConvertedValueLocal[3] =(float)(After_filter[3] *3.3/4096); 
	    ADC_ConvertedValueLocal[4] =(float)((uint16_t)ADC_InjectValue[0]*3.3/4096); 
        ADC_ConvertedValueLocal[5] =(float)((uint16_t)ADC_InjectValue[1]*3.3/4096); 

    printf("\r\n The current PB0 value = %f V \r\n",ADC_ConvertedValueLocal[0]); 
    printf("\r\n The current PA3 value = %f V \r\n",ADC_ConvertedValueLocal[1]);
    printf("\r\n The current PB1 value = %f V \r\n",ADC_ConvertedValueLocal[2]); 
    printf("\r\n The current PA4 value = %f V \r\n",ADC_ConvertedValueLocal[3]);  
    printf("\r\n The current PA6 value = %f V \r\n",ADC_ConvertedValueLocal[4]); 
    printf("\r\n The current PA1 value = %f V \r\n",ADC_ConvertedValueLocal[5]);  
	printf("\r\n\r\n");		
	
	 Delay(0xffffff);
}

如有错误,还望大家不吝赐教!
未经同意,请勿转载!

你可能感兴趣的:(Keil,STM32,stm32)