2017-04-20 15:45:25 csdn_xfactor 阅读数 185
  • 单片机控制第一个外设-LED灯-第1季第6部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第1季第6个课程,主要讲解LED的工作原理和开发板原理图、实践编程等,通过学习目的是让大家学会给单片机编程控制LED灯,并且为进一步学习其他外设打好基础。

    4006 人正在学习 去看看 朱有鹏
意法半导体开发的stm32系列单片机功能强大,其高级定时器TIM1和TIM8功能强大,配置较繁琐,本文介绍使用标准库进行配置的过程。

##标准库例程##

int main(void)
{
  /*!< At this stage the microcontroller clock setting is already configured, 
       this is done through SystemInit() function which is called from startup
       files (startup_stm32f40_41xxx.s/startup_stm32f427_437xx.s/startup_stm32f429_439xx.s)
       before to branch to application main. 
       To reconfigure the default setting of SystemInit() function, refer to
       system_stm32f4xx.c file
     */     
       
  /* TIM1 Configuration */
  TIM_Config();

  /* ---------------------------------------------------------------------------
  TIM1 Configuration to:

  1/ Generate 3 complementary PWM signals with 3 different duty cycles:
  
    TIM1 input clock (TIM1CLK) is set to 2 * APB2 clock (PCLK2), since APB2 
    prescaler is different from 1.   
    TIM1CLK = 2 * PCLK2  
    PCLK2 = HCLK / 2 
    => TIM1CLK = 2 * (HCLK / 2) = HCLK = SystemCoreClock
  
    TIM1CLK is fixed to SystemCoreClock, the TIM1 Prescaler is equal to 0 so the 
    TIM1 counter clock used is SystemCoreClock (168MHz).

    The objective is to generate PWM signal at 17.57 KHz:
    - TIM1_Period = (SystemCoreClock / 17570) - 1

    The Three Duty cycles are computed as the following description: 

    The channel 1 duty cycle is set to 50% so channel 1N is set to 50%.
    The channel 2 duty cycle is set to 25% so channel 2N is set to 75%.
    The channel 3 duty cycle is set to 12.5% so channel 3N is set to 87.5%.
    
    The Timer pulse is calculated as follows:
      - ChannelxPulse = DutyCycle * (TIM1_Period - 1) / 100

  2/ Insert a dead time equal to (11/SystemCoreClock) ns

  3/ Configure the break feature, active at High level, and using the automatic 
     output enable feature

  4/ Use the Locking parameters level1. 
  
  Note: 
    SystemCoreClock variable holds HCLK frequency and is defined in system_stm32f4xx.c file.
    Each time the core clock (HCLK) changes, user had to call SystemCoreClockUpdate()
    function to update SystemCoreClock variable value. Otherwise, any configuration
    based on this variable will be incorrect. 
  --------------------------------------------------------------------------- */

  /* Compute the value to be set in ARR register to generate signal frequency at 17.57 Khz */
  TimerPeriod = (SystemCoreClock / 17570) - 1;

  /* Compute CCR1 value to generate a duty cycle at 50% for channel 1 */
  Channel1Pulse = (uint16_t) (((uint32_t) 5 * (TimerPeriod - 1)) / 10);

  /* Compute CCR2 value to generate a duty cycle at 25%  for channel 2 */
  Channel2Pulse = (uint16_t) (((uint32_t) 25 * (TimerPeriod - 1)) / 100);

  /* Compute CCR3 value to generate a duty cycle at 12.5%  for channel 3 */
  Channel3Pulse = (uint16_t) (((uint32_t) 125 * (TimerPeriod - 1)) / 1000);

  /* Time Base configuration */
  TIM_TimeBaseStructure.TIM_Prescaler = 0;
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
  TIM_TimeBaseStructure.TIM_Period = TimerPeriod;
  TIM_TimeBaseStructure.TIM_ClockDivision = 0;
  TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;

  TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);

  /* Channel 1, 2 and 3 Configuration in PWM mode */
  TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
  TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;
  TIM_OCInitStructure.TIM_Pulse = Channel1Pulse;
  TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;
  TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_Low;
  TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;
  TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCIdleState_Reset;

  TIM_OC1Init(TIM1, &TIM_OCInitStructure);

  TIM_OCInitStructure.TIM_Pulse = Channel2Pulse;
  TIM_OC2Init(TIM1, &TIM_OCInitStructure);

  TIM_OCInitStructure.TIM_Pulse = Channel3Pulse;
  TIM_OC3Init(TIM1, &TIM_OCInitStructure);

  /* Automatic Output enable, Break, dead time and lock configuration*/
  TIM_BDTRInitStructure.TIM_OSSRState = TIM_OSSRState_Enable;
  TIM_BDTRInitStructure.TIM_OSSIState = TIM_OSSIState_Enable;
  TIM_BDTRInitStructure.TIM_LOCKLevel = TIM_LOCKLevel_1;
  TIM_BDTRInitStructure.TIM_DeadTime = 11;
  TIM_BDTRInitStructure.TIM_Break = TIM_Break_Enable;
  TIM_BDTRInitStructure.TIM_BreakPolarity = TIM_BreakPolarity_High;
  TIM_BDTRInitStructure.TIM_AutomaticOutput = TIM_AutomaticOutput_Enable;

  TIM_BDTRConfig(TIM1, &TIM_BDTRInitStructure);

  /* TIM1 counter enable */
  TIM_Cmd(TIM1, ENABLE);

  /* Main Output Enable */
  TIM_CtrlPWMOutputs(TIM1, ENABLE);

  while (1)
  {
  }
}

##标准库对应寄存器详解##

2018-08-12 09:45:57 private_void_main 阅读数 23579
  • 单片机控制第一个外设-LED灯-第1季第6部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第1季第6个课程,主要讲解LED的工作原理和开发板原理图、实践编程等,通过学习目的是让大家学会给单片机编程控制LED灯,并且为进一步学习其他外设打好基础。

    4006 人正在学习 去看看 朱有鹏

1.通用定时器基本介绍

  • 通用定时器包括TIM2、TIM3、TIM4和TIM5
  • STM32通用定时器是一个通过可编程预分频器驱动的16位自动装载计数器构成。
  • 每个定时器都是完全独立的,没有互相共享任何资源。它们可以一起同步操作。
  • 定时器可以进行定时器基本定时输出4路PWM输入捕获
  • 本文详细介绍这三个功能并且利用定时器3并且示例代码使用

2.开发环境

开发平台:keil5
单片机:STM32F103ZET6


#3.基本定时功能

## 3.1定时器时钟来源分析
STM32部分时钟树:
定时器部分时钟树
  3.1.1 首先我们我们的系统时钟(SYSCLK 72MHz) 经过AHB分频器给APB1外设,但是APB1外设最大的只能到36Mhz,所以必须要系统时钟的二分频。下面又规定了如果APB1预分频系数为1则频率不变,否则频率X2至定时器27**,**所以定时器27的时钟频率为还是72MHz

  3.1.2 分配给我们定时器的时钟是72MHz,我们可以根据自己的需求再设置定时器的分频,设置它的定时值

/*
	* 初始化定时器的时候指定我们分频系数psc,这里是将我们的系统时钟(72MHz)进行分频
	* 然后指定重装载值arr,这个重装载值的意思就是当 我们的定时器的计数值 达到这个arr时,定时器就会重新装载其他值.
		例如当我们设置定时器为向上计数时,定时器计数的值等于arr之后就会被清0重新计数
	* 定时器计数的值被重装载一次被就是一个更新(Update)
	* 计算Update时间公式
	Tout = ((arr+1)*(psc+1))/Tclk
	公式推导详解:
		Tclk是定时器时钟源,在这里就是72Mhz 
		我们将分配的时钟进行分频,指定分频值为psc,就将我们的Tclk分了psc+1,我们定时器的最终频率就是Tclk/(psc+1) MHz
		这里的频率的意思就是1s中记 Tclk/(psc+1)M个数 (1M=10的6次方) ,每记一个数的时间为(psc+1)/Tclk ,很好理解频率的倒数是周期,这里每一个数的周期就是(psc+1)/Tclk 秒
		然后我们从0记到arr 就是 (arr+1)*(psc+1)/Tclk
	举例:比如我们设置arr=7199,psc=9999
	我们将72MHz (1M等于10的6次方) 分成了(9999+1)等于 7200Hz
	就是一秒钟记录9000数,每记录一个数就是1/7200秒
	我们这里记录9000个数进入定时器更新(7199+1)*(1/7200)=1s,也就是1s进入一次更新Update
*/
//简单进行定时器初始化,设置 预装载值 和 分频系数
void MY_TIM3_Init(u16 arr,u16 psc){
	
	//初始化结构体
	TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
	
	//1.分配时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
	
	//2.初始化定时器相关配置
	TIM_TimeBaseStructure.TIM_Period = arr;
	TIM_TimeBaseStructure.TIM_Prescaler = psc;
	
	/*在这里说一下这个TIM_ClockDivision 是设置与进行输入捕获相关的分频
		设置的这个值不会影响定时器的时钟频率,我们一般设置为TIM_CKD_DIV1,也就是不分频*/
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数
	TIM_TimeBaseInit(TIM3,&TIM_TimeBaseStructure);
	
	//3.打开定时器
	TIM_Cmd(TIM3,ENABLE);
}

/****************** 主函数 ********************/
//在主函数中我们可以调用初始化
int main(){
	//定时器初始化
	MY_TIM3_Init(7199,9999);
	while(1){
		
		//检测更新标志位
		if(TIM_GetFlagStatus(TIM3,TIM_IT_Update)){
			//清除标志位
			TIM_ClearFlag(TIM3,TIM_IT_Update);
			//....(每隔一秒执行任务)
		}
		
	}
}

#4.定时器输出PWM # 4.1基本介绍   **4.1.1** **PWM**是脉冲宽度调制,我们是通过改变**脉冲的宽度**来达到改变**输出电压**的效果,本质上就是调节**占空比**实现的,STM32除了**基本定时器(TIM6,TIM7)不能输出PWM**以外,其它的定时器都具有输出PWM,其中**高级定时器(TIM1和TIM8)**还能输出**7**路PWM,**基本定时器(TIM2,TIM3,TIM4,TIM5)**也可以输出**4**路PWM > 输出**PWM**是很有用的,比如我们可以通过**控制电机**来玩小车,或者通过输出PWM改变LED的亮度,制造**呼吸灯**等等

  4.1.2 我们通用定时器能输出PWM的IO口是固定的,虽然我们可以通过重映射可以改变引脚,具体是哪一些IO口我们要通过查阅STM32的参考手册

这里涉及到一个重映射的概念,重映射就是管脚的外设功能映射到另一个管脚,但是不是可以随便映射的,具体对应关系参考手册上的管脚说明。这样优点是可以优化电路设计;扩展功能,减少外设芯片资源

/**
	定时器3,可产生四路的PWM输出,四个通道分别对应的引脚情况如下
	TIM3_CH1,TIM3_CH2,TIM3_CH3,TIM3_CH4
	没有重映像的对应情况:
	PA6,PA7,PB0,PB1
	部分重映像:
	PB4,PB5,PB0,PB1
	完全重映像:
	PC6,PC7,PC8,PC9	

	当我们的IO口不仅仅是做普通的输入输出使用的时候,作为别的外设(AD,串口,定时器等)的特定功能引脚,就需要开启外设.
	这里我们还需要开启APB2外设上的复用时钟AFIO,同时IO口采用的是复用输出!

	我们这里是没有使用重映射功能.
*/
// 宏定义
//判断当前是处于哪一种模式,以便于我们初始化IO口
#define NO_REAMP   0
#define PART_REAMP 1
#define FULL_REAMP 2

// ---> 这里是需要制定的参数

//指定这里的 当前的模式,我们给她默认指定是 没有重映射
#define CURRENT_MODE NO_REAMP 

//*************根据当前模式初始化IO口 函数
void MY_TIM3_GPIO_Init(void){
	
	GPIO_InitTypeDef 	GPIO_InitStructure;
	
	//1.开启AFIO时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
	
	//2. 根据当前的重映像的模式 配置时钟 和 初始化相关引脚
	switch(CURRENT_MODE){
		
		//2.1 如果没有重映射
		case NO_REAMP:{
			
			// 时钟分配
			RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB,ENABLE);
			// 初始化IO口
			GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
			GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
			GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;
			GPIO_Init(GPIOA,&GPIO_InitStructure);
			GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1;
			GPIO_Init(GPIOB,&GPIO_InitStructure);
			
			break;
		}
		//2.2 部分重映射
		case PART_REAMP:{
			
			// 时钟分配
			RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
			// 初始化IO口
			GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
			GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
			GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_4|GPIO_Pin_5;
			GPIO_Init(GPIOB,&GPIO_InitStructure);
			
			break;
		}
		//2.3 全映射
		case FULL_REAMP:{
			
			// 时钟分配
			RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
			// 初始化IO口
			GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
			GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
			GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_9;
			GPIO_Init(GPIOB,&GPIO_InitStructure);
			
			break;
		}
		default:break;
	}	
}

//***************** 定时器PWM输出初始化函数
void MY_TIM3_PWM_Init(u16 arr,u16 psc){
	
	//初始化结构体
	TIM_OCInitTypeDef TIM_OCInitstrcuture;
	
	//1.初始化定时器 和 相关的IO口
	MY_TIM3_Init(arr,psc); 
	MY_TIM3_GPIO_Init();
	
	//2.初始化PWM的模式
	
	/**
	选择PWM模式:
		PWM1模式:
			向上计数时,当我们 当前的 计数值 小于我们的设置阈值为有效电平,否则为无效电平,向下计数时与向上计数时相反
		PWM2模式:
			与PWM1模式向上向下计数时完全相反
	*/
	TIM_OCInitstrcuture.TIM_OCMode = TIM_OCMode_PWM1;
	TIM_OCInitstrcuture.TIM_OutputState = TIM_OutputState_Enable;
	TIM_OCInitstrcuture.TIM_OCPolarity = TIM_OCPolarity_High;   //输出电平为高,也就是有效电平为高
	TIM_OC1Init(TIM3,&TIM_OCInitstrcuture);						//这里是设置利用通道1输出
	
	//这里只初始化通道1,我们可以根据自己需求初始化其它通道
	
//	TIM_OC2Init(TIM3,&TIM_OCInitstrcuture);
//	TIM_OC3Init(TIM3,&TIM_OCInitstrcuture);
//	TIM_OC4Init(TIM3,&TIM_OCInitstrcuture);

	TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能预装载寄存器
}

//*********************主函数调用
int main(){
	
	//因为我们单片机引脚输出电压3.3V左右,我们设置预装载值为330
	MY_TIM3_PWM_Init(330,0);
	
	//我们初始化的时候选择的是PWM1模式,当计数值小于我们的设定值100时为有效电平,这里是高电平
	//所以对于的1通道(PA6)电压是大概就是 3.3 * (100/330) = 1V 左右,我们可以用万用表测量
	TIM_SetCompare1(TIM3,100);
	
	while(1);
}

#5.定时器输入捕获
## 5.1基本介绍

  • 上面介绍了定时器的四路通道可以输出PWM,同样的我们也可以捕获该定时器这四路通道上的边沿状态(上升沿,下降沿)

  • 由此可见基本定时器也不能进行输入捕获,没有思路通道

我们可以通过输入捕获的来测量高电平脉宽时间,首先捕获到高电平,记录下改时间,然后切换为捕获低电平,得到时间
## 5.2开发步骤
###   输入捕获 (捕获边沿信号,上升沿和下降沿)
   首先我们需要以一定的频率检测电平的跳变,然后对部分跳变(也就是部分输入的波形)进行过滤
      ------ 这就是定时器里面的滤波器的任务

  1. 指定输入滤波器时钟频率,首先是系统时钟分给定时器72Mhz,我们首先初始化定时器的时候指定了TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; 没有分频,输入给滤波器的时钟频率还是72MHz,TIM_ClockDivision也可以指定为2分频或者4分频
  2. 波形过滤(TIM_ICFilter),这里有一个指定过滤器的参数(参考芯片手册),例如我们设置参数为0101(二进制),采样频率(fsampling)为 滤波器频率/2 = 36Mhz,N=8.当检测到一个上升沿的时候,再以fsampling频率连续8次检测到高电平才确认是一个有效的上升沿,这样可以滤除那些高电平脉宽低于8个采样周期的脉冲信号,从而达到滤高频波的效果。
    这里写图片描述
  3. 配置输入分频(TIM_ICPrescaler),如果我们设置不分频,一个边沿(上升沿或者下降沿)就触发一次捕获,二分频就是两次边沿触发捕获,这里这个分频可以为1,2,4,8
//定时器输入捕获初始化
void MY_TIM3_Cap_Init(u16 arr,u16 psc){

	//初始化结构体
	TIM_ICInitTypeDef TIM_ICInitStructure;
	
	//1.初始化定时器 和 相关的IO口
	MY_TIM3_Init(arr,psc); 

	//这里的IO口根据自己需求改成输入,我这改成下拉输入,具体代码就不展现了
	MY_TIM3_GPIO_Init();
	
	//2.初始化定时器输入捕获
	TIM_ICInitStructure.TIM_Channel = TIM_Channel_1 ; // 设置输入捕获的通道
	
	//不使用过滤器,假设我们想使用,例如上述举例使用0101
	//我们就给TIM_ICFilter  = 0x05 ,(0000 0101),根据上表可以知道这个值范围(0x00~0x0F)
	TIM_ICInitStructure.TIM_ICFilter = 0x00;
	
	TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; //上升沿捕获
	TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;		//配置输入分频,这里不分频,1次检测到边沿信号就发生捕获

	/*
		这里说一下定时器通道可以进行交叉捕获,通道1捕获通道2引脚上的边沿信号,通道2捕获通道1引脚,通道3可以捕获通道4对应引脚,... 
		但是只能相邻一对可以相互捕获,例如通道2不能捕获通道3引脚边沿信号
		TIM_ICSelection_DirectTI 表示直接捕获,通道1对应通道1引脚,通道2对应通道2引脚
		TIM_ICSelection_IndirectTI 表示进行交叉捕获
	*/
	TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //映射捕获对应通道的引脚
	TIM_ICInit(TIM3,&TIM_ICInitStructure);													
	
}
//****************主函数
int main(){
	//初始化输入捕获
	MY_TIM3_Cap_Init(1000,0);
	
	while(1){
		//检测是否捕获到上升沿
		if(TIM_GetFlagStatus(TIM3,TIM_IT_CC1)){
			TIM_ClearFlag(TIM3,TIM_IT_CC1);
			//捕获到上升沿之后的任务...
			//一般测量高电平脉宽,我们可以先捕获上升沿再捕获下降沿
			//TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Falling); 修改为下降沿捕获
		}
		
	}
}

#6.定时器中断
 1.谈及到中断,我们就必须涉及到NVIC,具体关于NVIC请参考我的另外一篇,这里是直接使用,我们使能定时器3中断并且配置完抢占优先级和响应优先级之后,再在主函数中使能其更新中断和输入捕获中断

//使能更新中断和输入捕获通道1的中断
TIM_ITConfig(TIM3,TIM_IT_Update|TIM_IT_CC1,ENABLE);

 2.我们使用中断的一个主要目的就是能够及时处理信息,不用在主函数的while循环里面等待

//定时器3的中断处理函数
void TIM3_IRQHandler(void){
	
	//1.判断是什么中断
	
	// 1.1定时器更新中断
	if(TIM_GetITStatus(TIM3,TIM_IT_Update)){
		//...处理定时器更新之后任务
	}
	// 1.2如果是定时器 通道1的捕获中断
	else if( TIM_GetITStatus(TIM3,TIM_IT_CC1) ){
			//处理输入捕获之后的任务
			//TIM_OC1PolarityConfig(TIM3,TIM_ICPolarity_Falling);更改为下降沿捕获
	}
	
	//2.最后将中断标志位都清理掉
	TIM_ClearITPendingBit(TIM3,TIM_IT_Update|TIM_IT_CC1);
}
2018-06-03 20:10:27 WANGXIN342201 阅读数 391
  • 单片机控制第一个外设-LED灯-第1季第6部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第1季第6个课程,主要讲解LED的工作原理和开发板原理图、实践编程等,通过学习目的是让大家学会给单片机编程控制LED灯,并且为进一步学习其他外设打好基础。

    4006 人正在学习 去看看 朱有鹏
STM32F103系列的单片机一共有11个定时器,其中:
2个高级定时器
4个普通定时器
2个基本定时器
2个看门狗定时器
1个系统嘀嗒定时器

出去看门狗定时器和系统滴答定时器的八个定时器列表;

8个定时器分成3个组;
TIM1和TIM8是高级定时器
TIM2-TIM5是通用定时器
TIM6和TIM7是基本的定时器

这8个定时器都是16位的,它们的计数器的类型除了基本定时器TIM6和TIM7都支持向上,向下,向上/向下这3种计数模式

计数器三种计数模式
向上计数模式:从0开始,计到arr预设值,产生溢出事件,返回重新计时
向下计数模式:从arr预设值开始,计到0,产生溢出事件,返回重新计时
中央对齐模式:从0开始向上计数,计到arr产生溢出事件,然后向下计数,计数到1以后,又产生溢出,然后再从0开始向上计数。(此种技术方法也可叫向上/向下计数)

基本定时器(TIM6,TIM7)的主要功能:
只有最基本的定时功能,。基本定时器TIM6和TIM7各包含一个16位自动装载计数器,由各自的可编程预分频器驱动

通用定时器(TIM2~TIM5)的主要功能:
除了基本的定时器的功能外,还具有测量输入信号的脉冲长度( 输入捕获) 或者产生输出波形( 输出比较和PWM)

高级定时器(TIM1,TIM8)的主要功能:

高级定时器不但具有基本,通用定时器的所有的功能,还具有控制交直流电动机所有的功能,你比如它可以输出6路互补带死区的信号,刹车功能等等

通用定时器的时钟来源;
a:内部时钟(CK_INT)
b:外部时钟模式1:外部输入脚(TIx)
c:外部时钟模式2:外部触发输入(ETR)
d:内部触发输入(ITRx):使用一个定时器作为另一个定时器的预分频器

通用定时期内部时钟的产生:

从截图可以看到通用定时器(TIM2-7)的时钟不是直接来自APB1,而是通过APB1的预分频器以后才到达定时器模块。
当APB1的预分频器系数为1时,这个倍频器就不起作用了,定时器的时钟频率等于APB1的频率;
当APB1的预分频系数为其它数值(即预分频系数为2、4、8或16)时,这个倍频器起作用,定时器的时钟频率等于APB1时钟频率的两倍。

自动装在寄存器arr值的计算:
Tout= ((arr+1)*(psc+1))/Tclk;
Tclk:TIM3的输入时钟频率(单位为Mhz)。
Tout:TIM3溢出时间(单位为us)。

计时1S,输入时钟频率为72MHz,加入PSC预分频器的值为35999,那么:
((1+psc )/72M)*(1+arr )=((1+35999)/72M)*(1+arr)=1秒
则可计算得出自动窗装载寄存器arr=1999

通用定时器PWM工作原理
以PWM模式2,定时器3向上计数,有效电平是高电平,定时器3的第3个PWM通道为例:

定时器3的第3个PWM通道对应是PB0这引脚,三角顶点的值就是TIM3_ARR寄存器的值,上图这条红线的值就TIM3_CCR3
当定时器3的计数器(TIM3_CNT)刚开始计数的时候是小于捕获/比较寄存器(TIM3_CCR3)的值,
此时PB0输出低电平,随着计数器(TIM3_CNT)值慢慢的增加,
当计数器(TIM3_CNT)大于捕获/比较寄存器(TIM3_CCR3)的值时,这时PB0电平就会翻转,输出高电平,计数器(TIM3_CNT)的值继续增加,
当TIM3_CNT=TIM3_ARR的值时,TIM3_CNT重新回到0继续计数,PB0电平翻转,输出低电平,此时一个完整的PWM信号就诞生了。

PWM输出模式;
STM32的PWM输出有两种模式:
模式1和模式2,由TIMx_CCMRx寄存器中的OCxM位确定的(“110”为模式1,“111”为模式2)。区别如下:
110:PWM模式1,在向上计数时,一旦TIMx_CNT
在向下计数时,一旦TIMx_CNT>TIMx_CCR1时通道1为无效电平(OC1REF=0),否则为有效电平(OC1REF=1)。
111:PWM模式2-在向上计数时,一旦TIMx_CNTTIMx_CCR1时通道1为有效电平,否则为无效电平。
由以上可知:
模式1和模式2正好互补,互为相反,所以在运用起来差别也并不太大。而从计数模式上来看,PWM也和TIMx在作定时器时一样,也有向上计数模式、向下计数模式和中心对齐模式

PWM的输出管脚:
不同的TIMx输出的引脚是不同(此处设计管脚重映射
TIM3复用功能重映射:

注:重映射是为了PCB的设计方便。值得一提的是,其分为部分映射和全部映射

PWM输出频率的计算:
PWM输出的是一个方波信号,信号的频率是由TIMx的时钟频率和TIMx_ARR这个寄存器所决定的
输出信号的占空比则是由TIMx_CRRx寄存器确:
占空比=(TIMx_CRRx/TIMx_ARR)*100%
PWM频率的计算公式为:

其中
F就是PWM输出的频率,单位是:HZ;
ARR就是自动重装载寄存器(TIMx_ARR);
PSC 就是预分频器(TIMx_PSC);
72M就是系统的频率;

STM32 高级定时器PWM的输出

一路带死区时间的互补PWM的波形图



STM32F103VC这款单片机一共有2个高级定时器TIM1和TIM8
这2个高级定时器都可以同时产生3路互补带死区时间的PWM信号和一路单独的PWM信号,
具有刹车输入功能,在紧急的情况下这个刹车功能可以切断PWM信号的输出
还具有支持针对定位的增量(正交)编码器和霍尔传感器电路
高级控制定时器(TIM1 和TIM8) 由一个16位的自动装载计数器组成,它由一个可编程的预分频器驱动

它适合多种用途,包含测量输入信号的脉冲宽度( 输入捕获) ,或者产生输出波形(输出比较、PWM、嵌入死区时间的互补PWM等)。
使用定时器预分频器和RCC时钟控制预分频器,可以实现脉冲宽度和波形周期从几个微秒到几个毫秒的调节。
高级控制定时器(TIM1 和TIM8) 和通用定时器(TIMx) 是完全独立的,它们不共享任何资源

死区时间
H桥电路为避免由于关断延迟效应造成上下桥臂直通,有必要设置死区时间
死区时间可有效地避免延迟效应所造成的一个桥臂未完全关断,而另一桥臂又处于导通状态,避免直通炸开关管。
死区时间越大,电路的工作也就越可靠,但会带来输出波形的失真以及降低输出效率。
死区时间小,输出波形要好一些,但是会降低系统的可靠性,一般这个死区时间设置为us级

元器件死区时间是不可以改变的,它主要是取决于元器件的制作工艺和材料!

原则上死区时间当然越小越好。设置死区时间的目的,其实说白了就是为了电路的安全。最佳的设置方法是:在保证安全的前提下,设置的死区时间越小越好。以不炸功率管、输出不短路为目的。

STM32死区时间探究

设置寄存器:就是刹车和死区控制寄存器(TIMx_BDTR)

这个寄存器的第0—7位,这8个位就是用来设置死区时间的,使用如下:

以TIM1为例说明其频率是如何产生的。

定时器1适中产生路线:
系统时钟-> AHB预分频 -> APB2预分频 –> TIM1倍频器–> 产生TIM1的时钟系统
流程图看可以看出,要想知道TIM1的时钟,就的知道系统时钟,AHB预分频器的值,还有APB2预分频器的值,只要知道了这几个值,即可算出TIM1的时钟频率?
这些值从何来,在“SystemInit()”这个时钟的初始化函数中已经给我们答案了,在这个函数中设置的系统时钟是72MZ,AHB预分频器和APB2预分频器值都是设置为1,由此可算出:TIM1时钟频率:
72MHZ了,TDTS=1/72MHZ=13.89ns

Tdtg死区时间步进值,它的值是定时器的周期乘以相应的数字得到的

下面看看官方给的公式如何使用,如下:
DTG[7:5]=0xx=>DT=DTG[6:0]×Tdtg,Tdtg=TDTS
首先由
DTG[7:5]=0xx可以知道的是:DTG的第7位必须为0,剩余的0~6这7位可配置死区时间,假如TIM1的时钟为72M的话,那么由公式Tdtg=TDTS可计算出:TDTS=1/72MHZ=13.89ns。
有了这个值,然后通过公式DT=DTG[6:0]×Tdtg即可计算出DT的值。

如果DTG的第0~6位均为0的话,DT=0
如果
DTG的第0~6位均为1的话,DT=127*13.89ns=1764ns
如果TIM1的时钟为72M的话,
公式1可设置的死区时间0~1764ns,也就是说:
如果你的项目需要输出的PWM信号要求的死区时间是0——1764ns的时候你就可以用公式1

同样可计算出4个公式的死去区间,如下:
公式1:DT=0~1764ns
公式2:DT=1777.9ns~3528.88ns
公式3:DT=3555.84ns~7000.56ns
公式4:DT=7111.68ns~14001.12ns

如何设置死区时间:
假如我们设计了一个项目要求输出的PWM信号中加入一个3us的死区时间因为3us这个值在第二个公式决定的死区范围之内所以选择第二个公式。3000/(13.89*2)=108,
所以DTG[5:0]=108-64=44
所以DTG=127+44+32=203=0XCB,TIM1->BDTR|=0Xcb
这里为什么要在加上一个32那?在公式2中DTG的第5位是一个X,也就是说这一位可以设置为高电平,也可以设置为低电平,在这里我们将这一位设置为了高电平,所有要在加上一个32.如此而已!
2017-04-21 18:06:42 qq_31339221 阅读数 11795
  • 单片机控制第一个外设-LED灯-第1季第6部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第1季第6个课程,主要讲解LED的工作原理和开发板原理图、实践编程等,通过学习目的是让大家学会给单片机编程控制LED灯,并且为进一步学习其他外设打好基础。

    4006 人正在学习 去看看 朱有鹏

STM32F103系列的单片机一共有11个定时器,其中:
2个高级定时器
4个普通定时器
2个基本定时器
2个看门狗定时器

 

1个系统嘀嗒定时器

 

 

除去看门狗定时器系统滴答定时器的八个定时器列表;
 

 

8个定时器分成3个组;
TIM1和TIM8是高级定时器
TIM2-TIM5是
通用定时器
TIM6和TIM7是基本的定时器
这8个定时器都是16位的,它们的计数器的类型除了基本定时器TIM6和TIM7都支持向上,向下,向上/向下这3种计数模式

计数器三种计数模式
向上计数模式:从0开始,计到arr预设值,产生溢出事件,返回重新计时
向下计数模式:从arr预设值开始,计到0,产生溢出事件,返回重新计时
中央对齐模式:从0开始向上计数,计到arr产生溢出事件,然后向下计数,计数到1以后,又产生溢出,然后再从0开始向上计数。(此种技术方法也可叫向上/向下计数)

基本定时器(TIM6,TIM7)的主要功能:
只有最基本的定时功能,。基本定时器TIM6和TIM7各包含一个16位自动装载计数器,由各自的可编程预分频器驱动

通用定时器(TIM2~TIM5)的主要功能:
除了基本的定时器的功能外,还具有测量输入信号的脉冲长度( 输入捕获) 或者产生输出波形( 输出比较和PWM)

高级定时器(TIM1,TIM8)的主要功能:
高级定时器不但具有基本,通用定时器的所有的功能,还具有控制交直流电动机所有的功能,你比如它可以输出6路互补带死区的信号,刹车功能等等
 

通用定时器的时钟来源;
a:内部时钟(CK_INT)
b:外部时钟模式1:外部输入脚(TIx)
c:外部时钟模式2:外部触发输入(ETR)
d:内部触发输入(ITRx):使用一个定时器作为另一个定时器的预分频器

通用定时期内部时钟的产生:

从截图可以看到通用定时器(TIM2-7)的时钟不是直接来自APB1,而是通过APB1的预分频器以后才到达定时器模块。
当APB1的预分频器系数为1时,这个倍频器就不起作用了,定时器的时钟频率等于APB1的频率;
当APB1的预分频系数为其它数值(即预分频系数为2、4、8或16)时,这个倍频器起作用,定时器的时钟频率等于APB1时钟频率的两倍。

自动装在寄存器arr值的计算:
Tout= ((arr+1)*(psc+1))/Tclk;
Tclk:TIM3的输入时钟频率(单位为Mhz)。
Tout:TIM3溢出时间(单位为us)。

计时1S,输入时钟频率为72MHz,加入PSC预分频器的值为35999,那么:
((1+psc )/72M)*(1+arr )=((1+35999)/72M)*(1+arr)=1秒
则可计算得出自动窗装载寄存器arr=1999

通用定时器PWM工作原理
以PWM模式2,定时器3向上计数,有效电平是高电平,定时器3的第3个PWM通道为例:

定时器3的第3个PWM通道对应是PB0这引脚,三角顶点的值就是TIM3_ARR寄存器的值,上图这条红线的值就TIM3_CCR3
当定时器3的计数器(TIM3_CNT)刚开始计数的时候是小于捕获/比较寄存器(TIM3_CCR3)的值,
此时PB0输出低电平,随着计数器(TIM3_CNT)值慢慢的增加,
当计数器(TIM3_CNT)大于捕获/比较寄存器(TIM3_CCR3)的值时,这时PB0电平就会翻转,输出高电平,计数器(TIM3_CNT)的值继续增加,
当TIM3_CNT=TIM3_ARR的值时,TIM3_CNT重新回到0继续计数,PB0电平翻转,输出低电平,此时一个完整的PWM信号就诞生了。

PWM输出模式;
STM32的PWM输出有两种模式:
模式1和模式2,由TIMx_CCMRx寄存器中的OCxM位确定的(“110”为模式1,“111”为模式2)。区别如下:
110:PWM模式1,在向上计数时,一旦TIMx_CNT
在向下计数时,一旦TIMx_CNT>TIMx_CCR1时通道1为无效电平(OC1REF=0),否则为有效电平(OC1REF=1)。
111:PWM模式2-在向上计数时,一旦TIMx_CNTTIMx_CCR1时通道1为有效电平,否则为无效电平。
由以上可知:
模式1和模式2正好互补,互为相反,所以在运用起来差别也并不太大。而从计数模式上来看,PWM也和TIMx在作定时器时一样,也有向上计数模式、向下计数模式和中心对齐模式

PWM的输出管脚:
不同的TIMx输出的引脚是不同(此处设计管脚重映射
TIM3复用功能重映射:

注:重映射是为了PCB的设计方便。值得一提的是,其分为部分映射和全部映射

PWM输出频率的计算:
PWM输出的是一个方波信号,信号的频率是由TIMx的时钟频率和TIMx_ARR这个寄存器所决定的
输出信号的占空比则是由TIMx_CRRx寄存器确:
占空比=(TIMx_CRRx/TIMx_ARR)*100%
PWM频率的计算公式为:

其中
F就是PWM输出的频率,单位是:HZ;
ARR就是自动重装载寄存器(TIMx_ARR);
PSC 就是预分频器(TIMx_PSC);
72M就是系统的频率;

STM32 高级定时器PWM的输出

一路带死区时间的互补PWM的波形图


STM32F103VC这款单片机一共有2个高级定时器TIM1和TIM8
这2个高级定时器都可以同时产生3路互补带死区时间的PWM信号和一路单独的PWM信号,
具有刹车输入功能,在紧急的情况下这个刹车功能可以切断PWM信号的输出
还具有支持针对定位的增量(正交)编码器和霍尔传感器电路
高级控制定时器(TIM1 和TIM8) 由一个16位的自动装载计数器组成,它由一个可编程的预分频器驱动

它适合多种用途,包含测量输入信号的脉冲宽度( 输入捕获) ,或者产生输出波形(输出比较、PWM、嵌入死区时间的互补PWM等)。
使用定时器预分频器和RCC时钟控制预分频器,可以实现脉冲宽度和波形周期从几个微秒到几个毫秒的调节。
高级控制定时器(TIM1 和TIM8) 和通用定时器(TIMx) 是完全独立的,它们不共享任何资源

死区时间
H桥电路为避免由于关断延迟效应造成上下桥臂直通,有必要设置死区时间
死区时间可有效地避免延迟效应所造成的一个桥臂未完全关断,而另一桥臂又处于导通状态,避免直通炸开关管。
死区时间越大,电路的工作也就越可靠,但会带来输出波形的失真以及降低输出效率。
死区时间小,输出波形要好一些,但是会降低系统的可靠性,一般这个死区时间设置为us级

元器件死区时间是不可以改变的,它主要是取决于元器件的制作工艺和材料!

原则上死区时间当然越小越好。设置死区时间的目的,其实说白了就是为了电路的安全。最佳的设置方法是:在保证安全的前提下,设置的死区时间越小越好。以不炸功率管、输出不短路为目的。

STM32死区时间探究
设置寄存器:就是刹车和死区控制寄存器(TIMx_BDTR)

这个寄存器的第0—7位,这8个位就是用来设置死区时间的,使用如下:

以TIM1为例说明其频率是如何产生的。

定时器1适中产生路线:
系统时钟-> AHB预分频 -> APB2预分频 –> TIM1倍频器–> 产生TIM1的时钟系统
流程图看可以看出,要想知道TIM1的时钟,就的知道系统时钟,AHB预分频器的值,还有APB2预分频器的值,只要知道了这几个值,即可算出TIM1的时钟频率?
这些值从何来,在“SystemInit()”这个时钟的初始化函数中已经给我们答案了,在这个函数中设置的系统时钟是72MZ,AHB预分频器和APB2预分频器值都是设置为1,由此可算出:TIM1时钟频率:
72MHZ了,TDTS=1/72MHZ=13.89ns

Tdtg死区时间步进值,它的值是定时器的周期乘以相应的数字得到的

下面看看官方给的公式如何使用,如下:
DTG[7:5]=0xx=>DT=DTG[6:0]×Tdtg,Tdtg=TDTS

首先由
DTG[7:5]=0xx可以知道的是:DTG的第7位必须为0,剩余的0~6这7位可配置死区时间,假如TIM1的时钟为72M的话,那么由公式Tdtg=TDTS可计算出:TDTS=1/72MHZ=13.89ns。
有了这个值,然后通过公式DT=DTG[6:0]×Tdtg即可计算出DT的值。

如果DTG的第0~6位均为0的话,DT=0
如果
DTG的第0~6位均为1的话,DT=127*13.89ns=1764ns
如果TIM1的时钟为72M的话,
公式1可设置的死区时间0~1764ns,也就是说:
如果你的项目需要输出的PWM信号要求的死区时间是0——1764ns的时候你就可以用公式1


同样可计算出4个公式的死去区间,如下:
公式1:DT=0~1764ns
公式2:DT=1777.9ns~3528.88ns
公式3:DT=3555.84ns~7000.56ns
公式4:DT=7111.68ns~14001.12ns

死区时间的
设置

假如我们设计了一个项目要求输出的PWM信号中加入一个3us的死区时间因为3us这个值在第二个公式决定的死区范围之内所以选择第二个公式。3000/(13.89*2)=108,
所以DTG[5:0]=108-64=44
所以DTG=127+44+32=203=0XCB,TIM1->BDTR|=0Xcb
这里为什么要在加上一个32那?在公式2中DTG的第5位是一个X,也就是说这一位可以设置为高电平,也可以设置为低电平,在这里我们将这一位设置为了高电平,所有要在加上一个32.如此而已!

 

2012-05-07 00:40:37 dazhaozi 阅读数 5867
  • 单片机控制第一个外设-LED灯-第1季第6部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第1季第6个课程,主要讲解LED的工作原理和开发板原理图、实践编程等,通过学习目的是让大家学会给单片机编程控制LED灯,并且为进一步学习其他外设打好基础。

    4006 人正在学习 去看看 朱有鹏
/*****************************************************************
**    程序名:MC9S12G128 按键检测(中断方式)
**    参  数:无
**    功  能:当按下按键后LED关闭0.1秒  
**    注  意:LED位于G128 PD0口,按键位于G128 PP0口
**    作  者:赵露露
**    版  本:v1.0
**    时  间:2012.5.7   
******************************************************************/
#include <hidef.h>            /* common defines and macros */
#include "derivative.h"       /* derivative-specific definitions */


/*****************************************************************
**    函数名: delayms()
**    参  数:xms
**    功  能:在16MHz总线时钟下大约延时xms毫秒  
**    注  意:    
******************************************************************/
void delayms(uint xms)
{
  uint z,j;
	for(z=xms;z>0;z--)
	    for(j=1600;j>0;j--);
}
//中断函数
#pragma CODE_SEG __NEAR_SEG NON_BANKED//将中断函数置于非分页区内,地址空间超过了16位的寻址空间64K,但是它的中断向量地址只有16位,所以中断程序要执行,就得在它16位的寻址空间也即非分页区内,这就是为什么会将S12的中断函数置于非分页区的原因。
interrupt 56 void IRQ_ISR(void)       //56为PP口中断向量号,它是由文件"mc9s12g128.h"中定义的宏,可在这个文件中搜索"VectorNumber"查找各个中断的中断向量号
{ 
  DisableInterrupts;                  //关闭中断                  
  PIFP_PIFP0 = 1;                     //清除PP0的中断标志位
  PORTD_PD0 = 1;                      //关闭LED
  delayms(100);                       //延时以至于可以观察到效果
  EnableInterrupts;                   //打开中断
}
#pragma CODE_SEG DEFAULT              //将后续代码置于默认区域内

void main(void) 
{
  
  DDRD = 0xFF;                //设置PD(0-7)方向为输出
  PORTD = 0xFF;               //设置PD(0-7)输出高电平
  
  DDRP_DDRP0 = 0;             //设置PP0口方向为输入
  PERP_PERP0 = 1;             //设置PP0口拉设备启用
  PPSP_PPSP0 = 1;             //打开PP0口上拉
  PIEP_PIEP0 = 1;             //设置PP0口中断使能
                              
  EnableInterrupts;           //打开中断
 while(1)
  {
    PORTD_PD0 = 0;            //打开LED
  }            

}

注:G128可作为中断口的有PP,PJ,AD,其使用方法与其类似,程序中使用的寄存器介绍如下:




附:G128中断向量号


#define VectorNumber_Vsi                 63U
#define VectorNumber_Vportad             62U
#define VectorNumber_Vatdcompare         61U
#define VectorNumber_VReserved60         60U
#define VectorNumber_Vapi               59U
#define VectorNumber_Vlvi               58U
#define VectorNumber_VReserved57         57U
#define VectorNumber_Vportp             56U
#define VectorNumber_VReserved55        55U
#define VectorNumber_VReserved54        54U
#define VectorNumber_VReserved53        53U
#define VectorNumber_VReserved52        52U
#define VectorNumber_VReserved51        51U
#define VectorNumber_VReserved50        50U
#define VectorNumber_VReserved49        49U
#define VectorNumber_VReserved48        48U
#define VectorNumber_VReserved47        47U
#define VectorNumber_VReserved46        46U
#define VectorNumber_VReserved45        45U
#define VectorNumber_VReserved44        44U
#define VectorNumber_VReserved43        43U
#define VectorNumber_VReserved42        42U
#define VectorNumber_VReserved41        41U
#define VectorNumber_VReserved40        40U
#define VectorNumber_Vcantx             39U
#define VectorNumber_Vcanrx             38U
#define VectorNumber_Vcanerr             37U
#define VectorNumber_Vcanwkup           36U
#define VectorNumber_Vflash             35U
#define VectorNumber_Vflashfd           34U
#define VectorNumber_Vspi2               33U
#define VectorNumber_Vspi1              32U
#define VectorNumber_VReserved31         31U
#define VectorNumber_Vsci2               30U
#define VectorNumber_VReserved29         29U
#define VectorNumber_Vcpmuplllck         28U
#define VectorNumber_Vcpmuocsns         27U
#define VectorNumber_VReserved26         26U
#define VectorNumber_VReserved25        25U
#define VectorNumber_Vportj             24U
#define VectorNumber_VReserved23         23U
#define VectorNumber_Vatd               22U
#define VectorNumber_Vsci1               21U
#define VectorNumber_Vsci0               20U
#define VectorNumber_Vspi0               19U
#define VectorNumber_Vtimpaie           18U
#define VectorNumber_Vtimpaaovf         17U
#define VectorNumber_Vtimovf             16U
#define VectorNumber_Vtimch7             15U
#define VectorNumber_Vtimch6             14U
#define VectorNumber_Vtimch5             13U
#define VectorNumber_Vtimch4             12U
#define VectorNumber_Vtimch3             11U
#define VectorNumber_Vtimch2             10U
#define VectorNumber_Vtimch1             9U
#define VectorNumber_Vtimch0             8U
#define VectorNumber_Vrti               7U
#define VectorNumber_Virq               6U
#define VectorNumber_Vxirq               5U
#define VectorNumber_Vswi               4U
#define VectorNumber_Vtrap               3U
#define VectorNumber_Vcop               2U
#define VectorNumber_Vclkmon             1U
#define VectorNumber_Vreset             0U


新手上路,如有错误,欢迎指正。

没有更多推荐了,返回首页