STM32理论 —— 定时器、时钟

这篇具有很好参考价值的文章主要介绍了STM32理论 —— 定时器、时钟。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1. 定时器


以F103系列为例;

1.1 分类与简介

stm32定时器时钟,STM32,stm32,单片机,嵌入式硬件,定时器,时钟树

1.1.1 分类与主要功能特点

  • STM32F103系列单片机一共有11个定时器::
    2个高级定时器:TIM1、TIM8;挂载在APB2时钟总线上;
    4个通用定时器:TIM2~TIM5;挂载在APB1时钟总线上;
    2个基本定时器:TIM6、TIM7;
    2个看门狗定时器
    1个系统嘀嗒定时器

  • 主要功能特点

    • 位于低速的APB1时钟总线上;
    • 16位向上、向下、向上向下(中心对齐)计数模式,自动重装载寄存器(TIMx_ARR);
    • 16位可编程(可实时修改)预分频器寄存器(TIMx_PSC),计时器时钟频率的分频系数为0~65535之间的任意值;
    • 4个独立通道:输入捕获比较(测量输入信号的脉冲长度)、输出比较、PWM信号生成(边缘或中间对齐模式)、单脉冲模式输出;具体对应的IO查看手册中的“定时器复用功能重映射”;

其中预分频系数自动重载值就是我们要设置的参数,参数设置完后,打开定时器,定时器就开始计数工作;


1.1.2 三种常用的定时器简介

stm32定时器时钟,STM32,stm32,单片机,嵌入式硬件,定时器,时钟树

  • 三种定时器的主要功能:
    • 基本定时器:顾名思义,它只有最基本的定时功能,包含一个16位自动装载计数器,由可编程预分频器驱动,主要用驱动DAC,无对应的外部IO;
    • 通用定时器:在基本定时器基础上,还具有测量输入信号的脉冲长度( 输入捕获) 或者产生输出波形( 输出比较和PWM),每个定时器对应有4个外部IO;
    • 高级定时器:具有基本,通用定时器的所有的功能外,还具有控制交直流电动机所有的功能,比如它可以输出6路互补带死区的信号、紧急刹车功能、PWM电机控制等等,每个定时器对应有8个外部IO;
  • 高级定时器的互补输出:即高级定时器除了输出通道本身,另外还带有一个互补输出的通道(反相输出),比如下面的代码与输出波形:
    stm32定时器时钟,STM32,stm32,单片机,嵌入式硬件,定时器,时钟树
TIM_CCxCmd(TIM1, TIM_Channel_1, TIM_CCx_Enable);//W上管
TIM_CCxNCmd(TIM1,TIM_Channel_1, TIM_CCxN_Enable);//W下管

1.1.3 三种计数模式

  • 向上计数模式:从0开始,向上计到TIMx_ARR预设值,产生溢出事件,并返回重新计时;
  • 向下计数模式:从TIMx_ARR预设值开始,向下计到0,产生溢出事件,并返回重新计时;
  • 向上/向下计数模式:从0开始向上计数,计到TIMx_ARR产生溢出事件,然后向下计数,计数到0以后,又产生溢出,然后再从0开始向上计数,以此循环(又叫中央对齐计数模式);
    stm32定时器时钟,STM32,stm32,单片机,嵌入式硬件,定时器,时钟树

其中TIMx_ARR为自动重装载值

1.1.4 定时器计数原理

单片机的定时器独立于CPU,目的是分担CPU的计时功能,释放CPU性能;当定时器被开启后,内部的计数器会以预先设定的计数器时钟频率开始计数;

如:设定计时器时钟频率为1MHz,那么定时器被开启后,内部的计数器就每隔1μs(1/1M)进行加1计数;

定时器的计数过程是离散的,为了简化,可把计数过程近似成下图线性直线;
stm32定时器时钟,STM32,stm32,单片机,嵌入式硬件,定时器,时钟树
(以向上计数模式为例)当定时器计数到了目标数值(溢出时间)后,会产生溢出事件,然后返回到0,重新进行计数,如此循环(就像时钟走到12会回到0,然后小时数会加1一样);当然这个“目标数值”不是无限大的(就像一天的时间最多是24小时一样),其大小取决于定时器的存储空间位数,如16位定时器(有16位的空间去存储该“目标数值”)每次能计数的最大值就是2^16 - 1=65536-1=65535
stm32定时器时钟,STM32,stm32,单片机,嵌入式硬件,定时器,时钟树

1.1.5 如何快速定位定时器对应IO 引脚

  1. 打开对应芯片的官方datasheet;
  2. 找到Pinouts and pin descriptions一章;
  3. 在该章内找到xxxxxxx pin definitions的一个表格;
  4. 搜索TIMx,比如搜索高级定时器TIM1的通道1对应的IO 引脚,则搜索TIM1_CH1,注意,由于STM32 端口重映射功能,TIM1_CH1对应的IO 引脚可能不止一个;

stm32定时器时钟,STM32,stm32,单片机,嵌入式硬件,定时器,时钟树

1.2 时钟来源

  1. 内部时钟(CK_INT):如下图,系统时钟SYSCLK通过预分频产生定时器时钟TIMXCLK,因为APB1产生的时钟最大为36MHz,而APB1一般不为1,所以TIMXCLK的频率一般为72MHz;
  2. 外部时钟模式1:外部输入脚(TIx);
  3. 外部时钟模式2:外部触发输入(ETR);
  4. 内部触发输入(ITRx):使用一个定时器作为另一个定时器的预分频器;
    stm32定时器时钟,STM32,stm32,单片机,嵌入式硬件,定时器,时钟树

1.3 通用定时器简介

  • 通用定时器的工作过程:如下公式所示,内部时钟发生器产生的时钟信号,它又叫工作频率,再经过预分频器PSC分频后,得到计数器时钟频率(一般是72MHz),而计数器时钟频率根据自动重载值最终得到定时器的定时频率(即定时器计数的频率);
  • 如下图中的“实际单元”是通用定时器的主要部分,16位的计数器寄存器(TIMx_CNT)自动重装载寄存器(TIMx_ARR)预分频器寄存器(TIMx_PSC)
    stm32定时器时钟,STM32,stm32,单片机,嵌入式硬件,定时器,时钟树
    stm32定时器时钟,STM32,stm32,单片机,嵌入式硬件,定时器,时钟树

1.4 计数溢出时间公式

  • 定时器溢出时间就是定时频率(定时器计数频率)的倒数,它取决于以下两个参数:
    • 时钟频率(即以下公式中的(PSC+1)/Tclk,一般单片机的时钟频率Tclk为72MHz);
    • 自动装载值ARR

时钟频率,见3.2 时钟来源中的内部时钟(CK_INT)讲解;

stm32定时器时钟,STM32,stm32,单片机,嵌入式硬件,定时器,时钟树

  • 当定时器计数溢出时,产生一次更新事件,如果使能相应的定时器中断,就相当于在每次定时器溢出时,进入一次定时器中断服务函数;

1.4 定时器中断的原理

以定时器的向上计数模式为例说明定时器中断的原理,如下图:

  1. APB1产生的时钟:CK_INT;
  2. 定时器使能:CNT_EN,使定时器开始计数;
  3. 定时器时钟:CK_CNT,因为规定时钟分频系数为1,则CK_CNT=CK_INT,否则CK_CNT=CK_INT*2;
  4. 计数到装载值(图中是 36),计数器溢出,更新溢出事件;
  5. 如果已使能了中断,则在产生溢出事件时执行中断服务函数,相应的中断标志位更新;
  6. 中断服务函数执行完毕,开始新的一轮计数;
    stm32定时器时钟,STM32,stm32,单片机,嵌入式硬件,定时器,时钟树

1.5 输入捕获

见通用定时器的工作过程原理图的输入捕获部分,下以输入捕获通道 1为例:

  1. 检测TIMx_CHx 上的边沿信号;
  2. 边沿信号发生跳变(如设置了捕获一次上升沿跳变),将当前定时器的值TIMx_CNT 存放到对应捕获/比较寄存器TIMx_CCRx中,完成一次捕获;
  3. 同理进行下一次输入捕获,通过计算两次定时器值的差值,得到目标结果;
    stm32定时器时钟,STM32,stm32,单片机,嵌入式硬件,定时器,时钟树

1.6 核心代码

1.6.1 通用定时器初始化

/**********************************************************************************************************
*	函 数 名: Timer3_Init
*	功能说明: 定时器3初始化,定时器3是个通用定时器
*	形    参:
						arr:自动重装载值
						psc:预分配系数
*	返 回 值: 无
**********************************************************************************************************/
void Timer3_Init(u16 arr,u16 psc)
{
	//定义结构体
  TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	NVIC_InitTypeDef NVIC_InitStructure;

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //时钟使能

	TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值	
	TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值 
	TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
	TIM_TimeBaseStructure.TIM_RepetitionCounter = 0x0;
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //向上计数模式
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //初始化定时器的时间基数单位
 	//假设预分配系数为7199、计时周期为999,那么定时器的计数周期为:((1+TIM_Prescaler )/72M)*(1+TIM_Period )=((1+7199)/72M)*(1+999)=1ms,即每1ms进入一次定时器中断
	
	//定时器中断配置
	TIM_ITConfig
	(  //使能定时器3的Update中断、Trigger中断
		TIM3, 
		TIM_IT_Update  |  //TIM 中断源
		TIM_IT_Trigger,   //TIM 触发中断源 
		ENABLE  //使能
	);
	NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;  //TIM3中断
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;  //先占优先级0级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;  //从优先级3级
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
	NVIC_Init(&NVIC_InitStructure);  //初始化外设NVIC寄存器

	TIM_Cmd(TIM3, ENABLE);  //使能定时器,定时器开始工作,假如arr为7199,psc为999,则每1ms进入一次中断		
	TIM_Cmd(TIM3, DISABLE); // 失能定时器				 
}
/**********************************************************************************************************
*	函 数 名: TIM3_IRQHandler
*	功能说明: 定时器3中断服务函数,定时器每次计数溢出都会进入该函数
*	形    参:无
*	返 回 值: 无
**********************************************************************************************************/
void TIM3_IRQHandler(void)   
{
	if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) //检查定时器是否发生中断
		{	
			CPU_LED = CPU_LED^1;//LED指示灯亮/灭一次
			if(status_flag == 1) //相应标志位清零
			{
				status_flag = 0;
				rec3_id = 0;
			}
			countTime++;  // 时间计时,比如设置了每1ms进入一次中断,那么就是每1ms,该变量加1,实现精确计时
			TIM_ClearITPendingBit(TIM3, TIM_IT_Update);  //清除定时器的中断待处理位
		}
}

1.6.2 高级定时器初始化

/**********************************************************************************************************
*	函 数 名: Timer1_Init
*	功能说明: 定时器1初始化,定时器1为高级定时器
*	形    参:
						arr:自动重装载值
						psc:预分配系数
*	返 回 值: 无
* 修改日期:2020-9-9
**********************************************************************************************************/
void Timer1_Init(u16 arr,u16 psc)
{
	//结构体定义
  TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	NVIC_InitTypeDef NVIC_InitStructure;

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE); //时钟使能

	TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
	TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
	TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割,TDTS = Tck_tim
	TIM_TimeBaseStructure.TIM_RepetitionCounter = 0x0;
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //向上计数模式
	TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); //初始化定时器的时间基数单位
 	//假设预分配系数为7199、计时周期为9999,那么定时器的计数周期为:((1+TIM_Prescaler )/72M)*(1+TIM_Period )=((1+7199)/72M)*(1+9999)=1秒
	
	TIM_ClearFlag(TIM1, TIM_FLAG_Update);//清除更新标记
	TIM_ITConfig
	(  //使能定时器1的Update中断、Trigger中断
		TIM1, 
		TIM_IT_Update  |  //TIM 中断源
		TIM_IT_Trigger,   //TIM 触发中断源 
		DISABLE  //使能
	);
	NVIC_InitStructure.NVIC_IRQChannel = TIM1_UP_IRQn;  //TIM1中断
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;  //先占优先级0级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;  //从优先级3级
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
	NVIC_Init(&NVIC_InitStructure);  //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器

	TIM_Cmd(TIM1, ENABLE);  //使能定时器
	TIM_ClearITPendingBit(TIM1, TIM_IT_Update);      //清除TIMx的中断待处理位:TIM 中断源
	//TIM_ITConfig(TIM1,TIM_IT_Update,DISABLE);        //关闭 定时器中断		 
}
/**********************************************************************************************************
*	函 数 名: TIM1_UP_IRQHandler
*	功能说明: 定时器1中断服务函数,在定时器1发生更新事件时(计数发生上溢出或下溢出时),运行该函数
*	形    参:无
*	返 回 值: 无
**********************************************************************************************************/
void TIM1_UP_IRQHandler(void)   //TIM1中断
{
	if (TIM_GetITStatus(TIM1, TIM_IT_Update) != RESET) //检查指定的TIM中断发生与否:TIM 中断源 
	{
		Pulse_Count++; //电机脉冲计数加1
//		Uart1_Printf("%d\r\n*_*",Pulse_Count);
		if(Pulse_Count == motor_step)//脉冲计数达到目标值
		{
			TIM_SetCompare1(TIM1,0); //设置PWM信号占空比为0,即PWM信号输出恒定的低电平,让电机停下
			TIM_ITConfig(TIM1,TIM_IT_Update | TIM_IT_Trigger, DISABLE);  //关闭定时器1中断,即定时器仍然计数,但不产生中断
			//TIM_Cmd(TIM1, DISABLE);  //关闭定时器1,即完全关闭定时器计数功能
		}
		TIM_ClearITPendingBit(TIM1, TIM_IT_Update);  //清除TIMx的中断待处理位:TIM 中断源
	}
}

1.7 定时器应用 - PWM

  • PWM(Pulse Width Modulation):脉冲宽度调制,即占空比可调的脉冲波形;一种利用微处理器的数字输出来对模拟电路进行控制的一种技术;PWM信号仍然是数字信号 ,因为在给定的任何时刻,满幅值的直流供电要么完全有(ON),要么完全无(OFF);

stm32定时器时钟,STM32,stm32,单片机,嵌入式硬件,定时器,时钟树

占空比:即高电平在一个完整周期中占据的比例,如下图中PWM信号的占空比为T1/T


  • 一般通过STM32控制板,有两种方式能产生PWM

    1. 利用普通IO口 输出PWM;(不建议使用,占用太多MCU 资源,精度低,输出误差增大等)
    2. 利用高级定时器的输出PWM
  • 配置输出PWM 信号的一般步骤

    1. 查看芯片datasheet,找到高级定时器对应的IO 引脚
      stm32定时器时钟,STM32,stm32,单片机,嵌入式硬件,定时器,时钟树
    2. 参考下面代码进行配置;

1.7.1 定时器产生PWM信号的原理

(以向上计数模式为例)如图由定时器的计数原理知:

  1. 计数器计数到自动重装载值 (TIMx_ARR) 时会返回到0并产生溢出事件,那么自动重装载值就决定了PWM信号的完整周期,自动重装载值越大,PWM信号的周期越长;
  2. 捕获/比较寄存器(CCRx),当计数值等于CCRx时,输出的IO电平反转,难么CRRx就决定了高电平所占据的时间周期,也即决定了PWM信号的占空比;显然,CRRx的值不能超过TIMx_ARR;
  3. IO逻辑就是TIMx_ARR和CCRx共同输出的PWM信号;
  4. 预分频系数PSC:如下图,预分频寄存器PSC两端有输入时钟信号CK_PSC和输出时钟信号CK_CNT,前者是时钟源的输出,后者用于驱动计数器CNT计数,那么通过设置预分频系数PSC,就可以得到不同的CK_CNT,实现0~65535的分频(16位预分频器寄存器(TIMx_PSC)),同时它也决定了PWM信号的频率上限;
    stm32定时器时钟,STM32,stm32,单片机,嵌入式硬件,定时器,时钟树

stm32定时器时钟,STM32,stm32,单片机,嵌入式硬件,定时器,时钟树

1.7.2 PWM信号频率计算公式

其实就是定时器计数频率的公式,即计数溢出时间的倒数,这里使用的STM32 芯片主频为72MHz,则计算公式如下:
stm32定时器时钟,STM32,stm32,单片机,嵌入式硬件,定时器,时钟树

其中72000000= 72M为APB1时钟的频率,在APB1预分频系数不为1的情况下,APB1时钟的频率为72MHz;

如:需求一个频率为10KHz,占空比为80%的PWM信号,可把参数设置为:

  • PSC=0
  • ARR=(72M/10K)-1=7200-1=7199
  • CRRx=(ARR+1)*0.8=5760

1.7.3 PWM 初始化代码

下面配置STM32F103VET6 PB8 上的TIM4_CH3(通用定时器4的通道3) 输出PWM信号;

  1. 根据给定PWM 频率与占空比,计算参数:
int CalculateFrequencyPWM(uint32_t freq,uint32_t duty)
{
	uint8_t  i;
//	uint16_t ck;
	double   dt=0.0,dd=0.0,ck;
	//PortInfom  *pb=&InOutMessage;
	
	dd=72000000; // 该芯片的主频72MHz
	dt=(double)freq;
	i=0;				  
	ck=0.0;
	do 
	{
		i++;		
		ck=dd/dt;
		ck=ck/(double)i;	  
	}while(ck>60000.0);
	if(i>=1)
	{i=i-1;}
	dt=ck*((double)duty)/100;
	
	TIM4InitilToMotorPWM(i,ck,dt);
	
	return 0;		
}

  1. 根据给定的参数,初始化PWM
void TIM4InitilToMotorPWM(unsigned char divfreq, unsigned int CouVal, unsigned int ButyCycle)
{
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	TIM_OCInitTypeDef        TIM_OCInitStructure;
	GPIO_InitTypeDef         GPIO_InitStructure;	  
	NVIC_InitTypeDef         NVIC_InitStructure;	//定义数据结构体	 
	  	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);   	  //使能TIM4的时钟	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);

	TIM_DeInit(TIM4);	                      //复位时钟TIM2,恢复到初始状态
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 ;				  
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;			  //配置为复用
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
//	GPIO_PinRemapConfig(GPIO_Remap_TIM4 , DISABLE);			  //禁止映射

	 
	/*-------------------------------------------------------------------
	TIM3CLK=72MHz  预分频系数Prescaler=2 经过分频 定时器时钟为24MHz
	根据公式 通道输出占空比=TIM4_CCR2/(TIM_Period+1),可以得到TIM_Pulse的计数值	 
	捕获/比较寄存器2 TIM4_CCR2= CCR1_Val 	 
	 T= fCK/TIM_Prescaler-1/TIM_Period   
	-------------------------------------------------------------------*/
	TIM_TimeBaseStructure.TIM_Prescaler = divfreq;		       //预分频器TIM4_PSC=3 计数器的时钟频率CK_CNT等于fCK_PSC/(PSC[15:0]+1)。
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;	  //计数器向上计数模式 TIM4_CR1[4]=0
	TIM_TimeBaseStructure.TIM_Period = CouVal;			//自动重装载寄存器TIM4_APR  确定频率为1KHz 
	TIM_TimeBaseStructure.TIM_ClockDivision = 0x0;				  //时钟分频因子 TIM4_CR1[9:8]=00
	TIM_TimeBaseStructure.TIM_RepetitionCounter = 0x0;
	TIM_TimeBaseInit(TIM4,&TIM_TimeBaseStructure);				  //写TIM4各寄存器参数
	TIM_ClearFlag(TIM4,TIM_IT_Update);			   //清标示位
	TIM_ITConfig(TIM4,TIM_IT_Update,ENABLE);        //更新中断		
	
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; 			 //PWM模式2 TIM4_CCMR1[14:12]=111 在向上计数时,一旦TIMx_CNT<TIMx_CCR1时通道1为无效电平,否则为有效电平																 //
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; 	//输入/捕获2输出允许  OC2信号输出到对应的输出引脚PD13
	TIM_OCInitStructure.TIM_Pulse = ButyCycle; 					//确定占空比,这个值决定了有效电平的时间
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; 	    //输出极性   TIM4_CCER[5]=1;
//	TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Disable;
		
	TIM_OC3Init(TIM4,&TIM_OCInitStructure); 
	TIM_OC3PreloadConfig(TIM4, TIM_OCPreload_Enable);	

	
   // NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0); //将中断矢量放到Flash的0地址
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4); //设置优先级配置的模式,    
    NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn; //使能中断
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;  //先占优先级
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;	 //从优先级
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;      //IRQ通道被使能
    NVIC_Init(&NVIC_InitStructure);	   //将结构体丢到配置函数,即写入到对应寄存器中
	
	/* TIM4使能 */
	TIM_Cmd(TIM4,ENABLE);
	TIM_CtrlPWMOutputs(TIM4, ENABLE); 

 
}

void TIM4_IRQHandler(void)
{
	if(TIM_GetITStatus(TIM4,TIM_IT_Update)==SET) //
	{
		
	  TIM_ClearFlag(TIM4,TIM_FLAG_Update);            // 清除标志
	}
}

stm32定时器时钟,STM32,stm32,单片机,嵌入式硬件,定时器,时钟树

  1. 应用
CalculateFrequencyPWM(1425,50);// 1425Hz、50%占空比

stm32定时器时钟,STM32,stm32,单片机,嵌入式硬件,定时器,时钟树

1.7.3.1 关于TIM_SetComparex函数

从芯片资料可以找到如下图表33-1 一样的定时器通道引脚配置图表,以PA0为例,将PA0设为PWM 信号输出端口,见图表33-1知,PA0位于定时器通道CH1上,那么设置它的占空比的函数就是TIM_SetCompare1(). 以此类推,位于定时器通道CH2上的端口,设置它的占空比的函数就是TIM_SetCompare2().

  • 占空比计算公式:由1.7.2 节中知,【CRRx=(ARR+1)*占空比】,而这里的compare 值就是公式中的CRRx,故占空比公式就是 Duty_Cycle= compare/(arr+1)

比如在PWM初始化时,自动重装载值arr设为99,则设定50%的占空比就是TIM_SetCompare1(TIMx,50);

TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; ,中参数若配置成 TIM_OCPolarity_High ,则输出的占空比取反,如配置输出占空比为10%,配置成 TIM_OCPolarity_High 则输出占空比为90%

  • 函数原型
void TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare1) 
//其中:
//TIMx:定时器
//Compare1:与TIMx比较的数,即TIMx的一个完整周期的时间减去这个Compare1,使得TIMx的周期从这个Compare1后的时间内输出值取反;

stm32定时器时钟,STM32,stm32,单片机,嵌入式硬件,定时器,时钟树

stm32定时器时钟,STM32,stm32,单片机,嵌入式硬件,定时器,时钟树

1.7.3.2 关于TIM_OCMode参数

该参数用于选择脉冲宽度调制的模式,假设高电平为有效电平:

  1. TIM_OCMode_PWM1:在向上计数时,当TIMx_CNT<TIMx_CCR1时通道输出高电平,否则为低电平;在向下计数时,当TIMx_CNT>TIMx_CCR1时,通道为低电平,否则为高电平。
  2. TIM_OCMode_PWM2:在向上计数时,当TIMx_CNT<TIMx_CCR1时通道输出低电平,否则为高电平;在向下计数时,当TIMx_CNT>TIMx_CCR1时,通道为高电平,否则为低电平。

1.7.4 PWM信号控制步进电机

该部分定时器中断服务函数见3.6.2 高级定时器初始化中的中断服务函数;

函数索引:
void PSC_Updata(u16 MotorSpeed);
void motorSpeedControl(u16 motorSpeed,u16 moveStep,u8 motorDir);
u8 motorDirSet(int moveStep);
void motorGo(u16 speed);
u8 GetPluseValue(char *string);
void motorMovePluse(u8 motorDir,int motorPluse);
u8 motorGoHome(void);
/**********************************************************************************************************
*	函 数 名: PSC_Updata
*	功能说明: 以电机速度更新对应TIM1的PSC值(定时器预分频系数),等效于设置电机速度,MotorSpeed越大,PSC越小,定时器计数频率越大
*	形    参:MotorSpeed:电机速度,单位:r/min
*	返 回 值: 无
**********************************************************************************************************/
void PSC_Updata(u16 MotorSpeed)
{
	u16 psc = 0,freq = 0;
	freq = (MotorSpeed*16000) /60;//电机运动频率
	psc = (720000/freq) - 1;
	TIM_PrescalerConfig(TIM1,psc,TIM_PSCReloadMode_Update);//更新定时器1预分频系数PSC值
	return;
}
/**********************************************************************************************************
*	函 数 名: motorSpeedControl函数
*	功能说明: 电机输出控制函数,控制电机加减速
*	形    参:
					spped:电机速度,r/min
					moveStep:电机运动步数
					Direction:电机运动方向
*	返 回 值: 无
**********************************************************************************************************/

void motorSpeedControl(u16 spped,u16 moveStep,u8 Direction)
{
	u16 speed = 0;//实时速度
	u16 maxSpeed = spped;//设定最大速度
	u8 i=0;	
	u8 RunStep = 0;//梯形运动过程记录
	gMotor_add_step = 0;	//电机加速脉冲计数
	gMotor_sub_step = 0;	//电机减速脉冲计数

	motorBrakeRelease();	//松开刹车
	pulseCount = 0;
	PSC_Updata(5);   //更新定时器1预分频PSC值,电机加速阶段
	TIM_SetCompare1(TIM1,50);
	TIM_ITConfig(TIM1,TIM_IT_Update|TIM_IT_Trigger,ENABLE);//使能定时器1中断

	while(pulseCount < moveStep)
	{
	//超出物理限制位置
	if((motorPosSensor == 0)&&(Direction == 1))
	{
		Uart1_Printf("Motor triager POS Limit.\r\n*_*");	
		TIM_SetCompare1(TIM1,0);
		TIM_ITConfig(TIM1,TIM_IT_Update | TIM_IT_Trigger, DISABLE);
		motorBrake;	
		return;
	}
	if((motorNegSensor == 0)&&(Direction == 0))
	{
		Uart1_Printf("Motor triager NEG Limit.\r\n*_*");	
		TIM_SetCompare1(TIM1,0);
		TIM_ITConfig(TIM1,TIM_IT_Update | TIM_IT_Trigger, DISABLE);
		motorBrake;	
		return;
	}

	/* 开始加、匀、减速度运动 */
		if(speed < maxSpeed - 5)             	 //加速阶段
		{
			i+=2;
			delay_ms(400/(maxSpeed-5));
			if(speed >= maxSpeed) speed = maxSpeed;
			else speed += i;
			PSC_Updata(speed+2);//定时器
		}
		if((speed >= maxSpeed-5)&&(RunStep == 0)	)//匀速阶段
		{
			RunStep = 1;			
			speed = maxSpeed;
			gMotor_sub_step = gMotor_add_step = pulseCount;  
			i = 0;
		}
		if(pulseCount >= moveStep - gMotor_sub_step) //减速阶段
		{
			if(speed > 5)
			{
				i+=2;
				delay_ms(400/(maxSpeed-5));//在要求时间内跑到最小速度
				if(speed <= 5)speed=5;
				else	speed -= i;
				PSC_Updata(speed);
			}
		}
	}
	TIM_SetCompare1(TIM1,0);
	TIM_ITConfig(TIM1,TIM_IT_Update | TIM_IT_Trigger, DISABLE);
	motorBrake;
}
/**********************************************************************************************************
*	函 数 名: motorDirSet
*	功能说明: 控制电机运动方向
*	形    参:moveStep:电机运动步数
*	返 回 值: 0-电机正向运动,1-电机反向运动
**********************************************************************************************************/
u8 motorDirSet(int moveStep)
{
	u8 dirFlag = 0;//电机运动方向标志位
	//设置电机运动方向
	if(moveStep > 0){SetMotorDirPos;}
	else
	{
		SetMotorDirNeg;
		moveStep = -moveStep;
		dirFlag = 1; //更新标志位
	}
	motorStep = moveStep;
	//motorSpeedControl(motorSpeed,moveStep);
	//更新电机运动脉冲
	if(dirFlag == 0)
		{motorNewStep = motorNewStep + pulseCount;}
	else	
		{motorNewStep = motorNewStep - pulseCount;}
		
	if(pulseCount >= moveStep)
		return 0;
	else
		return 1;
}
/**********************************************************************************************************
*	函 数 名: motorGo
*	功能说明: 电机以目标速度启动
*	形    参:speed:电机速度值
*	返 回 值: 无
**********************************************************************************************************/
void motorGo(u16 speed)
{
		PSC_Updata(speed);//更新定时器1预分频PSC值,电机加速阶段
		TIM_SetCompare1(TIM1,50);//设置PWM占空比
		TIM_ITConfig(TIM1,TIM_IT_Update | TIM_IT_Trigger, ENABLE);    //使能定时器1中断
		motorBrakeRelease;//松开电机刹车
}
/**********************************************************************************************************
*	函 数 名: GetPluseValue函数
*	功能说明: 从串口获取电机运动脉冲数
*	形    参:string:串口输入的字符串
*	返 回 值:0-电机反向运动,1-电机正向运动
**********************************************************************************************************/
u8 GetPluseValue(char *string)
{
	u8 motorDir;
	char *ptr = &string[16];
	g_MovePluse = atoi(ptr);
	motorNewStep += g_MovePluse;
	if(*ptr == '-')
	{
		g_MovePluse = -g_MovePluse;//把数值转换为正数
		motorDir = 0;
	}
	else
	{
		motorDir = 1;	
	}
	return motorDir;
}
/**********************************************************************************************************
*	函 数 名: motorMovePluse
*	功能说明: 电机走指定脉冲
*	形    参:
					motorDir:1为正向,0为反向 
					MotorPluse:电机脉冲
*	返 回 值: 无
**********************************************************************************************************/
void motorMovePluse(u8 motorDir,int motorPluse)
{
	// motorDir只能是0或者1
	if(motorDir > 1) return;

//	motorSpeedControl(30,motorPluse);
//	if(motorPluse > 200)
//	motorGo(30);
//	else 
//	motorGo(10);	
	if(motorDir == 1) //电机正向运动
	{
		SetMotorDirPos; //设置电机正向运动
		pulseCount = 0; //脉冲计数清零
		motorSpeedControl(30,motorPluse,motorDir);//电机运动
//		while((pulseCount <= motorPluse)&&(motorPosSensor == 1));
//		TIM_SetCompare1(TIM1,0);
//		TIM_ITConfig(TIM1,TIM_IT_Update | TIM_IT_Trigger, DISABLE);    //使能定时器1中断
//		motorBrake;
//		Uart1_Printf("Motor MovePluse: +%5d\r\n*_*",motorPluse);
//		Uart1_Printf("Motor now TotalPluse: %5d\r\n*_*",motorNewStep);	
		return;
	}
	else
	{ //电机反向运动
		SetMotorDirNeg;
		pulseCount = 0;
		motorSpeedControl(30,motorPluse,motorDir);
//		while((pulseCount <= motorPluse)&&(motorNegSensor == 1));
//		TIM_SetCompare1(TIM1,0);
//		TIM_ITConfig(TIM1,TIM_IT_Update | TIM_IT_Trigger, DISABLE);    //使能定时器1中断
//		motorBrake;	
//		Uart1_Printf("Motor MovePluse: -%5d\r\n*_*",motorPluse);		
//		Uart1_Printf("Motor now TotalPluse: %5d\r\n*_*",motorNewStep);			
		return;
	}
}

/**********************************************************************************************************
*	函 数 名: motorGoHome
*	功能说明: 电机回原点
*	形    参:无
*	返 回 值:0-回原点成功
**********************************************************************************************************/

u8 motorGoHome(void)
{
		u8 motorPotionFlag = 0; //电机位置标志位,当motorPotionFlag为2时表示电机处于原点位置
	/* 电机不在正限位位置,先向上找到正限位 */
		if(motorPosSensor != 0)
		{
//			Uart1_Printf("Run step1.d\r\n*_*");
			motorBrakeRelease; //松开电机刹车
			SetMotorDirPos; //设置电机运动方向为正向
			pulseCount = 0; //脉冲计数清零
			motorStep = 50000;//预设一个较大的电机运动步数,电机会在该步数内走到传感器,否则就是传感器出问题
			motorGo(30);//电机以速度30开始运动
			//先找到上极限
			while((pulseCount <= motorStep)&&(motorPosSensor == 1));//等待电机走到正限位
			
			//电机向下找原点
			SetMotorDirNeg;
			motorGo(20);
			pulseCount = 0;	
			motorStep = 20000;
			while((pulseCount <= motorStep)&&(motorOriSensor == 1));	//等待电机走到原点位
//			Uart1_Printf("Step 1 get Motor Ori Sta = %d\r\n*_*",motorOriSensor);
			motorPotionFlag = 2;
		}
		/* 电机已在正限位,电机向下找原点 */
		else  
		{
//			Uart1_Printf("Run step2.d\r\n*_*");
			SetMotorDirNeg;
			motorGo(20);
			pulseCount = 0;
			motorStep = 20000;
			while((pulseCount <= motorStep)&&(motorOriSensor == 1));
//			{Uart1_Printf("Step 2 get Motor Ori Sta = %d\r\n*_*",motorOriSensor);}				
			motorPotionFlag = 2;
		}
			TIM_SetCompare1(TIM1,0);//设置电机占空比为0,电机停止运动
			TIM_ITConfig(TIM1,TIM_IT_Update | TIM_IT_Trigger, DISABLE);    //失能定时器1中断
			motorBrake;	//电机刹车	
		/* 电机到原点后再慢速往下离开原点,再慢速返回 */
		if(motorPotionFlag == 2)
		{
//			Uart1_Printf("Run step3.d\r\n*_*");
			SetMotorDirNeg;
			motorGo(5);
			pulseCount = 0;
			motorStep = 2000;
			while(pulseCount <= motorStep&&(motorOriSensor == 0));	//等待电机离开原点位
//			Uart1_Printf("Finish step3.d\r\n*_*");	
			//电机慢速返回原点
			SetMotorDirPos;
			motorGo(5);
			pulseCount = 0;
			motorStep = 2000;
			while(pulseCount <= motorStep&&(motorOriSensor == 1));	//等待电机离开原点位
			//让电机停止运动
			TIM_SetCompare1(TIM1,0);
			TIM_ITConfig(TIM1,TIM_IT_Update | TIM_IT_Trigger, DISABLE);    ///失能定时器1中断
			motorBrake;
			motorPotionFlag=0;//电机回原点成功
		}
		gMotorPosSta = 0;
		return 0;
}
1.7.4.1 关于函数TIM_SetCompare1对电机运动的控制

交流步进电机内部有两组线圈(A+/A-,B+/B-),电机的运动需要轮番对两组线圈通电,每次通电,电机就运动一定的角度,这种交流电信号必须是50%占空比的方波信号,不然电机运动会发生紊乱,所以需要电机正常运动,就需要把PWM信号的占空比设为50%,TIM_SetCompare1(TIM1,50);,反之要电机停止运动,就把PWM信号的占空比设为0;而电机的运动速度则通过PWM信号的频率决定,频率越高,电机转速越快;

  • 函数原型:
/**
  * @brief  Sets the TIMx Capture Compare1 Register value,设置TIMx Capture Compare1寄存器值
  * @param  TIMx: where x can be 1 to 17 except 6 and 7 to select the TIM peripheral.TIMx: x可以是1到17,除了6和7,以选择TIM外围设备。
  * @param  Compare1: specifies the Capture Compare1 register new value.Compare1:指定捕获的Compare1寄存器新值。
  * @retval None
  */
void TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare1)
{
  /* Check the parameters */
  assert_param(IS_TIM_LIST8_PERIPH(TIMx));
  /* Set the Capture Compare1 Register value */
  TIMx->CCR1 = Compare1;
}

1.7.5 PWM 信号控制LED

函数索引:
void ledUpdataFrequence(u16 frequence);
void ledPwmControl(u8 ledNubmer,u8 status,u8 showOption);
/**********************************************************************************************************
*	函 数 名: ledUpdataFrequence
*	功能说明: 更新led PWM信号 PSC预分频系数
*	形    参:frequence:PWM信号频率,frequence越大,PSC越小,定时器计数频率越大
*	返 回 值: 无
* 修改日期:2020-5-7
**********************************************************************************************************/
void ledUpdataFrequence(u16 frequence)
{
	u16 psc = 0;       
	psc = (720000/frequence) - 1;
	TIM_PrescalerConfig(TIM1,psc,TIM_PSCReloadMode_Update);//更新定时器1预分频系数PSC值
	return;
}
/**********************************************************************************************************
*	函 数 名: ledPwmControl
*	功能说明: 以PWM信号控制LED灯
*	形    参:
					ledNubmer:led编号,0: ALS1, 1: ALS2, 2: ALS3  
					status:led状态
          showOption:是否回显结果
*	返 回 值: 无
* 修改日期:2020-5-7
**********************************************************************************************************/
void ledPwmControl(u8 ledNubmer,u8 status,u8 showOption)
{
	ledUpdataFrequence(ALS_freq);//更新led PWM信号频率
	if(ledNubmer ==1)
	{
		if(status == ON)
		{
			TIM_SetCompare2(TIM1,100-ALS1_duty);//设置定时器2 PWM信号占空比
		}
		else
		{
			TIM_SetCompare2(TIM1,100);
		}
	}
	else if(ledNubmer == 2)
	{
		if(status == 1)
		{
			TIM_SetCompare4(TIM1,100-ALS2_duty);
		}
		else
		{
			TIM_SetCompare4(TIM1,100);
		}
	}
	if(showOption == 1)
	{
		Uart1_Printf("Led set pass\r\n*_*");
	}
}

1.7.6 PWM信号控制LED闪烁

// 设置PWM的频率于占空比
int CalculateFrequencyPWM(uint32_t freq,uint32_t duty)
{															  
	uint8_t  i;
//	uint16_t ck;
	double   dt=0.0,dd=0.0,ck;
	//PortInfom  *pb=&InOutMessage;
	
	dd=72000000;	
	if(state.hawking_f==1)
	{dt=((double)freq);}//1mm=360脉冲 (1mm=360Hz)	
	else
	{dt=(double)freq;}
	i=0;				  //步进电机一圈为 10mm(3600脉冲 Hz)
	ck=0.0;
	do 
	{
		i++;		//取出分频系数
		ck=dd/dt;
		ck=ck/(double)i;	  
	}while(ck>60000.0);
	if(i>=1)
	{i=i-1;}
	dt=ck*((double)duty)/100;

	TIM4InitilToMotorPWM(i,ck,dt);

	return 0;		
}

void TIM4InitilToMotorPWM(unsigned char divfreq, unsigned int CouVal, unsigned int ButyCycle)
{
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	TIM_OCInitTypeDef        TIM_OCInitStructure;
	GPIO_InitTypeDef         GPIO_InitStructure;	  
	NVIC_InitTypeDef         NVIC_InitStructure;	//定义数据结构体	 
	  	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);   	  //使能TIM4的时钟	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);

	TIM_DeInit(TIM4);	                      //复位时钟TIM2,恢复到初始状态
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 ;				  
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;			  //配置为复用
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
//	GPIO_PinRemapConfig(GPIO_Remap_TIM4 , DISABLE);			  //禁止映射

	 
	/*-------------------------------------------------------------------
	TIM3CLK=72MHz  预分频系数Prescaler=2 经过分频 定时器时钟为24MHz
	根据公式 通道输出占空比=TIM4_CCR2/(TIM_Period+1),可以得到TIM_Pulse的计数值	 
	捕获/比较寄存器2 TIM4_CCR2= CCR1_Val 	 
	 T= fCK/TIM_Prescaler-1/TIM_Period   
	-------------------------------------------------------------------*/
	TIM_TimeBaseStructure.TIM_Prescaler = divfreq;		       //预分频器TIM4_PSC=3 计数器的时钟频率CK_CNT等于fCK_PSC/(PSC[15:0]+1)。
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;	  //计数器向上计数模式 TIM4_CR1[4]=0
	TIM_TimeBaseStructure.TIM_Period = CouVal;			//自动重装载寄存器TIM4_APR  确定频率为1KHz 
	TIM_TimeBaseStructure.TIM_ClockDivision = 0x0;				  //时钟分频因子 TIM4_CR1[9:8]=00
	TIM_TimeBaseStructure.TIM_RepetitionCounter = 0x0;
	TIM_TimeBaseInit(TIM4,&TIM_TimeBaseStructure);				  //写TIM4各寄存器参数
	TIM_ClearFlag(TIM4,TIM_IT_Update);			   //清标示位
	TIM_ITConfig(TIM4,TIM_IT_Update,ENABLE);        //更新中断		
	
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; 			 //PWM模式2 TIM4_CCMR1[14:12]=111 在向上计数时,一旦TIMx_CNT<TIMx_CCR1时通道1为无效电平,否则为有效电平																 //
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; 	//输入/捕获2输出允许  OC2信号输出到对应的输出引脚PD13
	TIM_OCInitStructure.TIM_Pulse = ButyCycle; 					//确定占空比,这个值决定了有效电平的时间。
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; 	    //输出极性   TIM4_CCER[5]=1;
//	TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Disable;
		
	TIM_OC3Init(TIM4,&TIM_OCInitStructure); 
	TIM_OC3PreloadConfig(TIM4, TIM_OCPreload_Enable);	

	
   // NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0); //将中断矢量放到Flash的0地址
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4); //设置优先级配置的模式,    
    NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn; //使能中断
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;  //先占优先级
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;	 //从优先级
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;      //IRQ通道被使能
    NVIC_Init(&NVIC_InitStructure);	   //将结构体丢到配置函数,即写入到对应寄存器中
	
	/* TIM4禁止使能 */
	TIM_Cmd(TIM4,ENABLE);
	TIM_CtrlPWMOutputs(TIM4, ENABLE); 

 
}

2. 时钟


时钟系统是CPU的脉搏,决定cpu速率,是CPU正常工作的时间基准。STM32使用任何外设都需要先启动时钟才能使用,但并不是所有的外设都需要用到系统时钟那么高的频率,如果都用高速时钟,必造成能耗浪费,且同一个电路,时钟越快不但功耗越大,同时抗电磁干扰能力也越弱,所以较为复杂的MCU都是采用多时钟源的方法来解决这些问题。对不同模块的时钟增加开启和关闭功能,可以降低单片机的功耗。

默认状态下,单片机把所有的外设时钟配置为失能(disable),用到哪个外设,就打开对应外设的时钟即可。

2.1 STM32F1系列时钟系统框图

stm32定时器时钟,STM32,stm32,单片机,嵌入式硬件,定时器,时钟树
系统时钟SYSCLK的左边 是设置系统时钟选择哪个作为时钟源。系统时钟SYSCLK 的右边,是系统时钟通过AHB预分频器,给对应的外设设置对应的时钟频率 。

从左到右可以简单理解为 :各个时钟源—>系统时钟来源的设置—>各个外设时钟的设置。

2.2 时钟源

即图中系统时钟SYSCLK的左边 部分,STM32 有4个独立时钟源:HSI、HSE、LSI、LSE:

  1. HSI高速内部时钟:RC振荡器,频率为8MHz,精度不高。
  2. HSE高速外部时钟:可接石英/陶瓷谐振器,或者接外部时钟源,频率范围为4MHz~16MHz。
  3. LSI低速内部时钟:RC振荡器,频率为40kHz,提供低功耗时钟。一般作为IWDGCLK(独立看门狗)时钟源和RTC时钟源而独立使用。
  4. LSE低速外部时钟:接频率为32.768kHz的石英晶体。

除LSI外,其他三个时钟源都可以过分频者倍频作为系统时钟来使用。

PLL锁相环倍频输出:其时钟输入源可选择为HSI/2、HSE或者HSE/2。倍频可选择为2~16倍,但是其输出频率最大不得超过72MHz。Keil编写程序时默认的时钟为72Mhz,是因为外部晶振(HSE)提供的8MHz(与电路板上的晶振的相关)通过PLLXTPRE分频器后,进入PLLSRC选择开关,进而通过PLLMUL锁相环进行倍频(x9)后,为系统提供72MHz的系统时钟(SYSCLK)。然后AHB预分频器对时钟信号进行分频,为低速外设提供时钟。但也可以是内部RC振荡器(HSI) 为8MHz/2 =4MHz进入PLLSRC选择开关*,通过PLLMUL锁相环进行倍频(x18)后为72MHz。

2.3 系统时钟SYSCLK

从4.2节知,系统时钟SYSCLK来源于三个时钟源:
①、HSI振荡器时钟
②、HSE振荡器时钟
③、LSE振荡器时钟

系统时钟SYSCLK最大为72MHz.

stm32定时器时钟,STM32,stm32,单片机,嵌入式硬件,定时器,时钟树

2.4 USB 时钟

STM32中有一个全速功能的USB模块,其串行接口引擎需要一个频率为48MHz的时钟源。该时钟源只能从PLL输出端获取。其分频系数可选择为1.5分频或者1分频,即当需要使用USB模块时,PLL必须使能,且时钟频率只能配置为48MHz或72MHz。
stm32定时器时钟,STM32,stm32,单片机,嵌入式硬件,定时器,时钟树

2.5 时钟输出

STM32可选择一个时钟信号输出到MCO引脚(GPIO)上供外部使用。
stm32定时器时钟,STM32,stm32,单片机,嵌入式硬件,定时器,时钟树

2.6 系统时钟通过AHB分频器给外设提供时钟

从4.1节的时钟系统框图中右边知,外设时钟从左到右依次为:系统时钟—>AHB分频器—>各个外设分频倍频器 —> 外设时钟的设置。AHB分频器可选择1、2、4、8、16、64、128、256、512分频。其中AHB分频器输出的时钟送给5大模块使用:

  1. 内核总线:送给AHB总线、内核、内存和DMA使用的HCLK时钟
  2. Tick定时器:通过8分频后送给Cortex的系统定时器时钟。
  3. I2S总线:直接送给Cortex的空闲运行时钟FCLK
  4. APB1外设:送给APB1分频器。APB1分频器可选择1、2、4、8、16分频,其输出一路供APB1外设使用(PCLK1,最大频率36MHz),另一路送给通用定时器使用。该倍频器可选择1或者2倍频,时钟输出供定时器2-7使用。
  5. APB2外设:送给APB2分频器。APB2分频器可选择1、2、4、8、16分频,其输出一路供APB2外设使用(PCLK2,最大频率72MHz),另一路送给高级定时器。该倍频器可选择1或者2倍频,时钟输出供定时器1和定时器8使用。另外,APB2分频器还有一路输出供ADC分频器使用,分频后送给ADC模块使用。ADC分频器可选择为2、4、6、8分频。

2.6.1 APB1和APB2对应的外设(F1系列)

  • APB1上面连接的是低速外设,包括电源接口、备份接口、CAN、USB、I2C1、I2C2、USART2、USART3、UART4、UART5、SPI2、SP3等;
  • APB2上面连接的是高速外设,包括UART1、SPI1、Timer1、ADC1、ADC2、ADC3、所有的普通I/O口(PA-PE)、第二功能I/O(AFIO)口等。

stm32定时器时钟,STM32,stm32,单片机,嵌入式硬件,定时器,时钟树

2.7 RCC相关寄存器(F1系列)

//RCC 寄存器结构,RCC_TypeDeff,在文件“stm32f10x.h”中定义如下:
//1059行->1081行:  
typedef struct  
{  
vu32 CR;                  //HSI,HSE,CSS,PLL等的使能  
vu32 CFGR;              //PLL等的时钟源选择以及分频系数设定 
vu32 CIR;                // 清除/使能 时钟就绪中断 
vu32 APB2RSTR;      //APB2线上外设复位寄存器 
vu32 APB1RSTR;      //APB1线上外设复位寄存器 
vu32 AHBENR;         //DMA,SDIO等时钟使能 
vu32 APB2ENR;       //APB2线上外设时钟使能 
vu32 APB1ENR;      //APB1线上外设时钟使能 
vu32 BDCR;           //备份域控制寄存器 
vu32 CSR;             
} RCC_TypeDef; 

2.8 RCC初始化

以HSE(高速外部时钟)为例(实际应用中使用HSE最多),使用HSE时钟,程序设置时钟参数流程:文章来源地址https://www.toymoban.com/news/detail-717962.html

  1. 将RCC寄存器重新设置为默认值 RCC_DeInit;
  2. 打开外部高速时钟晶振HSE RCC_HSEConfig(RCC_HSE_ON);
  3. 等待外部高速时钟晶振工作 HSEStartUpStatus = RCC_WaitForHSEStartUp();
  4. 设置AHB时钟 RCC_HCLKConfig;
  5. 设置高速AHB时钟 RCC_PCLK2Config;
  6. 设置低速速AHB时钟 RCC_PCLK1Config;
  7. 设置PLL RCC_PLLConfig;
  8. 打开PLL RCC_PLLCmd(ENABLE);
  9. 等待PLL工作 while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
  10. 设置系统时钟 RCC_SYSCLKConfig;
  11. 判断是否PLL是系统时钟 while(RCC_GetSYSCLKSource() != 0x08)
  12. 打开要使用的外设时钟 RCC_APB2PeriphClockCmd()/RCC_APB1PeriphClockCmd()
//使用外部8MHz晶振
//系统时钟72MHz,APH 72MHz,APB2 72MHz,APB1 32MHz,USB 48MHz TIMCLK=72M
void RCC_Configuration(void)
{
	//----------使用外部RC晶振-----------
	RCC_DeInit();			//初始化为缺省值
	RCC_HSEConfig(RCC_HSE_ON);	//使能外部的高速时钟 
	while(RCC_GetFlagStatus(RCC_FLAG_HSERDY) == RESET);	//等待外部高速时钟使能就绪
	
	FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);	//Enable Prefetch Buffer
	FLASH_SetLatency(FLASH_Latency_2);		//Flash 2 wait state
	
	RCC_HCLKConfig(RCC_SYSCLK_Div1);		//HCLK = SYSCLK
	RCC_PCLK2Config(RCC_HCLK_Div1);			//PCLK2 =  HCLK
	RCC_PCLK1Config(RCC_HCLK_Div2);			//PCLK1 = HCLK/2
	RCC_PLLConfig(RCC_PLLSource_HSE_Div1,RCC_PLLMul_9);	//PLLCLK = 8MHZ * 9 =72MHZ
	RCC_PLLCmd(ENABLE);			//Enable PLLCLK
 
	while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);	//Wait till PLLCLK is ready
    RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);	//Select PLL as system clock
	while(RCC_GetSYSCLKSource()!=0x08);		//Wait till PLL is used as system clock source
	
	//---------打开相应外设时钟--------------------
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);	//使能APB2外设的GPIOA的时钟		 
}

到了这里,关于STM32理论 —— 定时器、时钟的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • STM32自学☞定时器外部时钟案例

    本案例主要是通过外部时钟实现对射式红外传感器的计次,在oled显示屏上显示CNT的次数 #include \\\"stm32f10x.h\\\" #include \\\"stm32f10x_tim.h\\\" #include \\\"timer_interrupt.h\\\" #include \\\"stdint.h\\\" //初始化函数 void Timer_Init(void) {  /*开启时钟*/  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //开启TIM2的时钟  RCC_A

    2024年02月22日
    浏览(46)
  • STM32 学习笔记(六)定时器中断:内部时钟模式,外部时钟模式

    定时器是功能最强大,内容最复杂的32结构。 之前51用过的功能,定时产生中断。 输出比较,常用于产生 PWM 波形,驱动电机等。 输入捕获,测量方波频率。 编码器,读取正交编码器的波形。 最大定时时间:72M/65536/65536=中断频率,中断频率取倒数是最大定时时间。 定时器可

    2024年02月08日
    浏览(58)
  • 江科大stm32视频学习笔记——TIM定时中断&定时器外部时钟

    目录 一、TIM(Timer)定时器简介  1.1 定时器类型 摘要 1.1.1 基本定时器 1.1.2 通用定时器 1.1.3 高级定时器  1.2 定时中断基本结构 1.2.1 结构框图 1.2.2 时序图 二、定时器定时中断定时器外部时钟 2.1 内部时钟闹钟代码 2.1.1 Timer.c 2.1.2 Buzzer.c加入间隔发声函数 2.1.3 main.c 2.1.4 实验视频

    2024年01月23日
    浏览(64)
  • STM32F4基础:时钟系统、中断及定时器

            时钟系统是CPU的脉搏, 由于STM32 本身非常复杂,外设非常的多,并不是所有外设都需要系统时钟这么高的频率,比如看门狗以及 RTC 只需要几十 k 的时钟即可,因此STM32F4 的时钟系统比较复杂 ,不像简单的 51 单片机一个系统时钟就可以解决一切。         如下图所

    2023年04月09日
    浏览(41)
  • STM32 第20讲 通用定时器(简介/框图/时钟源)

    对于STM32F407有10个通用定时器,TIM2 ~ TIM5 和 TIM9 ~ TIM14。 主要特性: 16位递增、递减、中心对齐计数器(计数值:0~65535) 16位预分频器(分频系数:1~65536) 可用于触发DAC、ADC 在更新事件、触发事件、输入捕获、输出比较时,会产生中断/DMA请求 4个独立通道,可用于:输入捕

    2024年02月09日
    浏览(39)
  • STM32F103学习笔记(5.1)——定时器时钟

    STM32F103RCT6上总共有8个定时器,其中TIM1和TIM8是高级定时器,它们挂载在APB2高速总线上。而TIM2、TIM3、TIM4、TIM5是通用定时器,TIM6、TIM7是基本定时器,它们都挂载在APB1上,基本定时器的功能最少,高级定时器功能最多。所以以学习通用定时器为主。 需要参考手册第二章存储器

    2024年02月15日
    浏览(42)
  • 【单片机】STM32单片机的各个定时器的定时中断程序,标准库,STM32F103

    高级定时器和普通定时器的区别(https://zhuanlan.zhihu.com/p/557896041): TIM1是高级定时器,使用的时钟总线是RCC_APB2Periph_TIM1,和普通定时器不一样。 timer.c timer.h 调用 timer.c timer.h 调用 timer.c timer.h 调用 timer.c timer.h 调用 timer.c timer.h 调用

    2024年02月07日
    浏览(58)
  • STM32学习笔记(四)丨TIM定时器及其应用(定时中断、内外时钟源选择)

    ​  本次课程采用单片机型号为STM32F103C8T6。 ​  课程链接:江科大自化协 STM32入门教程   往期笔记链接:   STM32学习笔记(一)丨建立工程丨GPIO 通用输入输出   STM32学习笔记(二)丨STM32程序调试丨OLED的使用   STM32学习笔记(三)丨中断系统丨EXTI外部中断

    2023年04月13日
    浏览(63)
  • 【单片机】STM32单片机的各个定时器的定时中断程序,标准库

    高级定时器和普通定时器的区别(https://zhuanlan.zhihu.com/p/557896041): TIM1是高级定时器,使用的时钟总线是RCC_APB2Periph_TIM1,和普通定时器不一样。 timer.c timer.h 调用 timer.c timer.h 调用 timer.c timer.h 调用 timer.c timer.h 调用 timer.c timer.h 调用

    2024年02月11日
    浏览(53)
  • STM32 F103C8T6学习笔记4:时钟树、滴答计时器、定时器定时中断

    今日理解一下STM32F103 C8T6的时钟与时钟系统、滴答计时器、定时器计时中断的配置,文章提供原理,代码,测试工程下载。 目录 时钟树与时钟系统: 滴答计时器: 定时器计时中断: 测试结果: 测试工程下载: 该系统介绍在 STM32F10x-中文参考手册 P56页开始 微控制器的时钟系

    2024年02月13日
    浏览(47)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包