1
完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
扫一扫,分享给好友
|
|
相关推荐
1个回答
|
|
1.通用定时器基本介绍
开发平台: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.1基本介绍
我们可以通过输入捕获的来测量高电平脉宽时间,首先捕获到高电平,记录下改时间,然后切换为捕获低电平,得到时间
//定时器输入捕获初始化 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); } |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1780 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1621 浏览 1 评论
1081 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
728 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1679 浏览 2 评论
1938浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
731浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
570浏览 3评论
596浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
556浏览 3评论
小黑屋| 手机版| Archiver| 德赢Vwin官网 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-23 20:11 , Processed in 0.905526 second(s), Total 78, Slave 62 queries .
Powered by 德赢Vwin官网 网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
德赢Vwin官网 观察
版权所有 © 湖南华秋数字科技有限公司
德赢Vwin官网 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号