使用stm32制作双脉冲发生器

使用stm32制作双脉冲发生器

在工作中由于需要对电机驱动电路进行双脉冲测试,以检测电机驱动电路的性能,之前用过555定时器做过一板,但结果不是很理想,后面不甘心就利用业余时间重新做一板双脉冲测试板,不得不说软件的灵活性还是要远远大于硬件的,PCB见下图一,没有使用的I/O口均引出备用。使用stm32制作双脉冲发生器软件主要实现以下功能:
1、每按一次增大按钮,脉冲2以延长10us;
2、每按一次减小按钮,脉冲2以减小10us;
3、按输出按钮,输出两个脉冲;
4、根据AD采样值,定时器TIM2对应的PA0、PA1、PA2、PA3共四个通道输出固定频率,不同占空比的PWM波。
由于网上的参考代码较多,使用库函数编写,程序较为简单,主要驱动代码有:

#include "pwm.h"       //PWM驱动
#include "adc.h"       //ADC驱动
#include "key.h"       //按键驱动
#include "oled.h"      //OLED屏驱动
#include "bmp.h"       //图像驱动
#include "exti.h"      //外部中断驱动程序

PWM驱动程序如下:

/**********************
函数名称:P_OUT_Init

功   能:脉冲PA、PB输出初始化

参   数:无

 返回值:无

作   者:lkjx
*********************/	
void P_OUT_Init()
{

 GPIO_InitTypeDef  GPIO_InitStructure;
 	
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);	 //使能PBD端口时钟
	
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5|GPIO_Pin_6;				 //PB5  PB6 端口配置
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		       //推挽输出
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		       //IO口速度为50MHz
 GPIO_Init(GPIOB, &GPIO_InitStructure);					            //根据设定参数初始化GPIOB
 GPIO_ResetBits(GPIOB,GPIO_Pin_5|GPIO_Pin_6);						    //PB5  PB6 输出低
	}

/**********************
函数名称:TIM2_PWM_Init

功   能:定时器2通道1、2、3、4的PWM模块输出初始化

参   数:arr--------自动重装值
         psc--------时钟预分频数

 返回值:无

作   者:lkjx
*********************/
void TIM2_PWM_Init(u16 arr,u16 psc)
{  
	 GPIO_InitTypeDef GPIO_InitStructure;
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	TIM_OCInitTypeDef  TIM_OCInitStructure;

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);     // 
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE);   //使能GPIO外设时钟使能
	                                                                     	
  //设置该引脚为复用输出功能,输出TIM2 CH1\CH2\CH3\CH4\的GPIO_A0、GPIO_PA1、GPIO_PA2、GPIO_PA3的PWM脉冲波形
	GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_0 |GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 ; //TIM_CH1  TIM_CH2  TIM_CH3  TIM_CH4
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	//初始化TIM2
	TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值	 20K
	TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值  不分频
	TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
	
	//初始化TIM2  Channel1的PWM输出模式
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
	//TIM_OCInitStructure.TIM_Pulse = 0; //设置待装入捕获比较寄存器的脉冲值
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高
	TIM_OC1Init(TIM2, &TIM_OCInitStructure);  //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
	
	//初始化TIM2  Channel2的PWM输出模式
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
	//TIM_OCInitStructure.TIM_Pulse = 0; //设置待装入捕获比较寄存器的脉冲值
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高
	TIM_OC2Init(TIM2, &TIM_OCInitStructure);  //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
	
	//初始化TIM2  Channel3的PWM输出模式
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
	//TIM_OCInitStructure.TIM_Pulse = 0; //设置待装入捕获比较寄存器的脉冲值
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高
	TIM_OC3Init(TIM2, &TIM_OCInitStructure);  //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
	
	//初始化TIM2  Channel4的PWM输出模式
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
	//TIM_OCInitStructure.TIM_Pulse = 0; //设置待装入捕获比较寄存器的脉冲值
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高
	TIM_OC4Init(TIM2, &TIM_OCInitStructure);  //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
	

  //  TIM_CtrlPWMOutputs(TIM2,ENABLE);	//MOE 主输出使能	
	TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Enable);  //TIM2 CH1预装载使能	
	TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Enable);  //TIM2 CH2预装载使能	
  TIM_OC3PreloadConfig(TIM2, TIM_OCPreload_Enable);  //TIM2 CH3预装载使能	
  TIM_OC4PreloadConfig(TIM2, TIM_OCPreload_Enable);  //TIM2 CH4预装载使能	
	
//	TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Enable);  //CH1预装载使能	 
//	TIM_ARRPreloadConfig(TIM2, ENABLE); //使能TIMx在ARR上的预装载寄存器
   TIM_Cmd(TIM2, ENABLE);  //使能TIM2
  
}

ADC驱动程序如下:

/**********************
函数名称:Adc_Init

功   能:ADC1模块初始化

参   数:无

 返回值:无

作   者:lkjx
*********************/
void  Adc_Init(void)
{ 	
	ADC_InitTypeDef ADC_InitStructure; 
	GPIO_InitTypeDef GPIO_InitStructure;

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_ADC1	, ENABLE );	  //使能ADC1通道时钟
 
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);   //设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M

	//PB0 作为模拟通道输入引脚                         
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;		//模拟输入引脚
	GPIO_Init(GPIOB, &GPIO_InitStructure);	

	ADC_DeInit(ADC1);  //复位ADC1,将外设 ADC1 的全部寄存器重设为缺省值

	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;	//ADC工作模式:ADC1和ADC2工作在独立模式
	ADC_InitStructure.ADC_ScanConvMode = DISABLE;	//模数转换工作在单通道模式
	ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;	//模数转换工作在单次转换模式
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;	//转换由软件而不是外部触发启动
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;	//ADC数据右对齐
	ADC_InitStructure.ADC_NbrOfChannel = 1;	//顺序进行规则转换的ADC通道的数目
	ADC_Init(ADC1, &ADC_InitStructure);	//根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器   

  
	ADC_Cmd(ADC1, ENABLE);	//使能指定的ADC1
	
	ADC_ResetCalibration(ADC1);	//使能复位校准  
	 
	while(ADC_GetResetCalibrationStatus(ADC1));	//等待复位校准结束
	
	ADC_StartCalibration(ADC1);	 //开启AD校准
 
	while(ADC_GetCalibrationStatus(ADC1));	 //等待校准结束
 
//	ADC_SoftwareStartConvCmd(ADC1, ENABLE);		//使能指定的ADC1的软件转换启动功能

}				  



/**********************
函数名称:Get_Adc

功   能:获得ADC采样值

参   数:ch------采样通道

 返回值:ADC_GetConversionValue(ADC1),ADC采样转换值

作   者:lkjx
*********************/
u16 Get_Adc(u8 ch)   
{
  	//设置指定ADC的规则组通道,一个序列,采样时间
	ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_239Cycles5 );	//ADC1,ADC通道,采样时间为239.5周期	  			    
  
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);		//使能指定的ADC1的软件转换启动功能	
	 
	while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));//等待转换结束

	return ADC_GetConversionValue(ADC1);	//返回最近一次ADC1规则组的转换结果
}



/**********************
函数名称:Get_Adc_Average

功   能:对ADC进行多次采样,求取平均值

参   数:ch--------采样通道
        times------采样次数

 返回值:temp_val/times,采样平均值

作   者:lkjx
*********************/
u16 Get_Adc_Average(u8 ch,u8 times)
{
	u32 temp_val=0;
	u8 t;
	for(t=0;t<times;t++)
	{
		temp_val+=Get_Adc(ch);
		delay_ms(5);
	}
	return temp_val/times;
} 	 


按键驱动程序如下:

/**********************
函数名称:KEY_Scan

功   能:按键处理函数,对按键进行扫描
         此函数有响应优先级,KEY_ADD>KEY_DEC>KEY_OUT!!

参   数:mode------0,不支持连续按;
         mode------1,支持连续按;

 返回值:0,没有任何按键按下
         KEY_ADD,KEY_ADD_PRES按下
         KEY_DEC,KEY_DEC_PRES按下
         KEY_OUT,KEY_OUT_PRES按下 

作   者:lkjx
*********************/	
u8 KEY_Scan(u8 mode)
{	 
	static u8 key_up=1;//按键按松开标志
	if(mode)key_up=1;  //支持连按		  
	if(key_up&&(KEY_ADD==0||KEY_DEC==0||KEY_OUT==0))
	{
		delay_ms(10);//去抖动 
		key_up=0;
		if(KEY_ADD==0)return KEY_ADD_PRES;
		else if(KEY_DEC==0)return KEY_DEC_PRES;
		else if(KEY_OUT==0)return KEY_OUT_PRES; 
	}
	else if(KEY_ADD==1&&KEY_DEC==1&&KEY_OUT==1)
		key_up=1; 	     
	return 0;// 无按键按下
}

exti外部中断驱动程序:

/**********************
函数名称:EXTIX_Init

功   能:外部中断初始化

参   数:无

 返回值:无

作   者:lkjx
*********************/

void EXTIX_Init(void)
{
 
 	  EXTI_InitTypeDef EXTI_InitStructure;
 	  NVIC_InitTypeDef NVIC_InitStructure;

  	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//外部中断,需要使能AFIO时钟

	  KEY_Init();//初始化按键对应io模式

    //GPIOA.15 中断线以及中断初始化配置
  	GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource15);

  	EXTI_InitStructure.EXTI_Line=EXTI_Line15;
  	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;	
  	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;//下降沿触发
  	EXTI_InitStructure.EXTI_LineCmd = ENABLE;
  	EXTI_Init(&EXTI_InitStructure);	 	//根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器

    //GPIOB.3	  中断线以及中断初始化配置
  	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource3);

  	EXTI_InitStructure.EXTI_Line=EXTI_Line3;
  	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;	
  	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
  	EXTI_InitStructure.EXTI_LineCmd = ENABLE;
  	EXTI_Init(&EXTI_InitStructure);	  	//根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器

    //GPIOB.4  中断线以及中断初始化配置
  	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource4);

   	EXTI_InitStructure.EXTI_Line=EXTI_Line4;
  	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;	
  	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
  	EXTI_InitStructure.EXTI_LineCmd = ENABLE;
  	EXTI_Init(&EXTI_InitStructure);		//根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器


 
  	NVIC_InitStructure.NVIC_IRQChannel = EXTI3_IRQn;			//使能按键所在的外部中断通道
  	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;	//抢占优先级2 
  	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01;					//子优先级1
  	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;								//使能外部中断通道
  	NVIC_Init(&NVIC_InitStructure);  	  //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
		
		NVIC_InitStructure.NVIC_IRQChannel = EXTI4_IRQn;			//使能按键所在的外部中断通道
  	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;	//抢占优先级2, 
  	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;					//子优先级1
  	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;								//使能外部中断通道
  	NVIC_Init(&NVIC_InitStructure); 
 
 
   	NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;			//使能按键所在的外部中断通道
  	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;	//抢占优先级2, 
  	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x00;					//子优先级1  0x00的优先级最高
  	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;								//使能外部中断通道
  	NVIC_Init(&NVIC_InitStructure); 
 
}

 /**********************
函数名称:EXTI3_IRQHandler

功   能:外部中断处理程序

参   数:无

 返回值:无

作   者:lkjx
*********************/

void EXTI3_IRQHandler(void)
{
  delay_ms(5);    //消抖
	if(KEY_DEC==0)   //由1改为0
	{	  
	   m=m-10;
		if(m<0) m=0;
	} 		
	EXTI_ClearITPendingBit(EXTI_Line3);  //清除EXTI3线路挂起位
}


 /**********************
函数名称:EXTI4_IRQHandler

功   能:外部中断处理程序

参   数:无

 返回值:无

作   者:lkjx
*********************/
 void EXTI4_IRQHandler(void)
{			
	delay_ms(5);   //消抖			
				  PA_M_OUT=0;			
        PB_M_OUT=0;
        delay_us(100);
			  PA_M_OUT=1;			
        PB_M_OUT=1;
        delay_us(30);	  //第1次脉冲时间		
        PA_DQ_OUT=0;			
        PB_DQ_OUT=0;
        delay_us(100);  //第1次脉冲与第2次脉冲之间的间隔时间				
				PA_M_OUT=1;			
        PB_M_OUT=1;
			  delay_us(m);	  //第2次脉冲时间		
	      PA_DQ_OUT=0;			
        PB_DQ_OUT=0;	
 	 EXTI_ClearITPendingBit(EXTI_Line4);    //清除LINE4上的中断标志位  
}




 /**********************
函数名称:EXTI15_10_IRQHandler

功   能:外部中断处理程序

参   数:无

 返回值:无

作   者:lkjx
*********************/

void EXTI15_10_IRQHandler(void)
{
	
  delay_ms(5);    //消抖		
  if(KEY_ADD==0)	
		{
		m=m+10;
	}
	 EXTI_ClearITPendingBit(EXTI_Line15);  //清除LINE15线路挂起位
}

以上为代码的驱动程序,其中OLED驱动代码太长,这里就不列举了,上述代码主要来自于网络,主要实现功能如下图2。
使用stm32制作双脉冲发生器
后续想将该OLED屏做成菜单栏形式,不仅可以设置脉冲1,也能够设置脉冲2,但最近一直忙于学习TI的电机矢量控制相关程序,以后有时间再进行升级。

上一篇:stm32 定时器使用


下一篇:STM32学习笔记(5)——定时器中断