当前位置: 首页 > news >正文

stm32—时钟、定时器和看门狗

1. 时钟

什么是时钟呢?

       一个可以产生周期性信号的设备

        什么是周期性信号?
                1        -----        -----        -----

                0                -----        -----        -----

        所以时钟信号就是周期性变化的信号

关于时钟我们有两个比较重要的概念需要理解:
        T:时钟周期,最小重复的信号单元的时间长度,基本单位为s(秒)

        F:时钟频率,1s内有多少个重复的信号单元(1s振动多少次),单位Hz

                ===> T * F == 1s

例子:

        F = 200Hz     意味着 1s 振动 200次

                ---> 每次振动的时间是 1 / 200 s == 5ms

2. 为什么需要时钟

时钟最主要的作用是用来同步信号用的

什么是同步呢?

        就比如我们的左腿和右腿实际上就需要进行同步,两腿需要协同工作,左腿迈一步然后右腿迈一步,依次重复,人就能正常前行。对应在我们的机器上也是一样的,机器的运行是由很多器件协同工作完成的,当一个器件完成分给它的工作时,理应通知别的器件它完成了,轮到你干活了,等你干完我再继续干。类似于左腿迈完步子了,要等右腿迈完左腿再迈。比如:在A和B进行数据的收发时,A发完数据后应该等待B去接收,等B接收完了之后A再继续发,这种就是同步通信


而我们的M4中大部分时序逻辑电路需要同步,那设备之间怎么实现同步?
        这就需要用到我们的时钟信号了

设备是怎么根据时钟信号怎么实现同步的呢?来看电路以及时序图:

 

如上电路图中假设A端和B端的时序变化如下:


理论上A&B端的电平变化应该如上图所示,输出端(A&B)的电平变换应该要与B端的一致,但是实际上却是下面这样:


为什么会有这样的现象呢?主要是因为A和B输入到&门时,输出需要经过一段的反应时间,虽然这个时间比较短。那么在B端电平变化后,不能立马去读取输出端的电平,应该要等待一段时间后再去读取,那么应该要等待多长时间?此时间段实际上是可以获取到的,我们只需要对电路进行如下改动:


电路修改后, C端的电平的变化如下:

很明显,C端的高电平时期就是&门处理数据的时间(记为de_t),这段时间是不正常的,正常情况下C端应该一直是低电平(当&门处理数据没有延时的时候),我们将C端时序中凸起的地方就称之为“毛刺”。“毛刺”时期是不正常的,我们应该要略过它,略过的意思是指当B的信号发生改变时我们不应该立马去读输出值,而是应该等待一段时间,等电路(如:&门)将数据处理完毕后,再去读取输出值此时才是准确的

怎么略过?此时就需要用到我们的时钟信号了

当我们在电路中加上REG后,在REG内部的触发器(假设触发器上升沿触发)的作用下,D端的电平变化如图:


通过上面的时序图可以得知,D此时输出的是一条干净的,没有“毛刺”的信号,那么通过上面这种现象我们可以总结出电路中处理“毛刺”的解决方案:

  • 输入信号只能在Clock低跳变(下降沿)时改变,在Clock为高电平时保持不变
  • T > 2 * de_t (周期必须大于2倍时延)

3. 时钟信号是怎么产生的呢?

在自然界中有一些物体天生就会产生摆动(振动)  ---> 石英晶体

如果想利用石英晶体规则地、周期性的产生方波信号,需要一些电路来保证:

        晶振电路:频率一般会比较小,如:12M、8M...

但是石英晶振难以满足现代计算机的高频需要,如:CPU它的频率会很高

那么我们就有“分频 / 倍频”电路:

        分频:把输入频率变小

        倍频:把输入频率变大

                比如:在M4中接触的比较多的有PLL:锁相环电路

4. STM32F4xx时钟树(时钟系统)

查看<STM32F4xx中文参考手册.pdf>第六章(复位和时钟控制)第二节(时钟)第107页可以得到M4的时钟树(见图<时钟树.png>)


时钟树中有几个关键名词:

        LSI:  Low Speed Internal      内部低速时钟 (32 kHz RC振荡器)

        LSE: Low Speed External     外部低速时钟 (32.768KHz)

        HSI:  High Speed Internal     内部高速时钟 (16 MHz RC振荡器)

        HSE: High Speed External    外部高速时钟 (8MHz)


内部时钟: 由内部集成的RC震荡电路产生
外部时钟: 由晶振产生

通过GEC-M4原理图可知:

        HSE_VALUE = 8M(外部高速时钟8M,取决于所接晶振大小)

从HSE出发沿着线路往右边走,会来到SW选择器,选择器一共有三路输入,分别为HSE/HSI/PLLCLK(锁相环时钟),选择这三者之一作为系统时钟来使用,我们的系统时钟最高可以达到168Mhz。很明显HSE/HSI提供不了168Mhz的频率,那么则由锁相环提供

而锁相环的输入是由HSI和HSE二选一之后进行M分频后得到的。在我们M4中我们选择的是HSE,也就是8M进行M分频后输入锁相环

即 SYSCLK = PLLCLK <--- 168M

= (HSE / M) * N / P

= (8Mhz / M ) * N / P

所以PLLCLK是由M和N以及P决定,实际上这三者的值可以在代码中指定:

        M --->  代码中用PLL_M表示 --->   8分频(根据HSE而来,目的将HSE分频为1M)

        N --->  代码中用PLL_N表示 --->   336倍频(336M)

        P --->  代码中用PLL_P表示 --->   2分频

选择器选择HSE/HSI/PLLCLK三者之一作为SYSCLK系统时钟后,接着就可以来到AHB总线,可以通过多个预分频器配置AHB频率、高速APB(APB2)和低速APB(APB1)频率

        AHB BUS = PLLCLK / (AHB Prescaler) // AHB Prescaler  AHB总线的预分频

        APBx BUS = AHB BUS CLK / (APBx Prescaler) // APBx Prescaler  APBx总线的预分频

        AHB总线时钟最大值为168M;

        低速APB(APB1)最大值为42M,高速APB(APB2)最大值为84M

来到APBx总线上后,我们的定时器就挂载在APBx总线上,那我们的定时器的时钟频率是多少呢?

通过时钟树我们可以得知定时器的时钟频率分为两种:

  • if (APBx presc == 1) 的意思是如果APB预分频值为1,那么定时器时钟频率等于APB的时钟频率;
  • else 的意思就是如果APB预分频器值不为1,那么定时器时钟频率等于两倍的APB的时钟频率

如果APB1 CLK = 42M = AHB BUS CLK / (APB1 Prescaler)

APB1 Prescaler = 168 / 42 = 4

则位于APB1总线上的定时器时钟频率为 42M*2 = 84M

如果APB2 CLK = 84M = AHB BUS CLK / (APB2 Prescaler)

APB2 Prescaler = 168 / 84 = 2

则位于APB2总线上的定时器时钟频率为 84M*2 = 168M

5. 修改固件库时钟相关代码

因为ST公司提供固件库的时候,不知道其他公司设计的板子会采用多少频率的HSE晶振,因此只提供了最大值配置

GECM4采用的是8M HSE,所以需要修改

        1)修改 HSE_VALUE 为 8M

                      stm32f4xx.h --> 144行

        2)修改 PLL 相关

                PLL_M   8             (371行)

                PLL_N    336        (不需要改)

                PLL_P   2             (不需要改)

                       system_stm32f4xx.c

上述修改需根据硬件电路的设计而来!!!

6. 定时器

timer:定时器就是用来定时的器件

在STM32上,一般来说,定时器由三部分组成:

        时基单元、输入捕获单元、输出比较单元

1. 时基单元:Time Bese Unit

        定时器的基本单元,所有定时器都具备的单元

        时基单元 = 计数器 + 重载计数值寄存器 + 定时器预分频器 组成

        时基单元工作原理:

                将计数器设置为一个值按照一定的时钟频率递减到0,或按照一定的时钟频率从0递增到某个值,当计数器溢出后,可以产生一个溢出事件/中断以此来达到定时的功能

组成时基单元的三个器件的作用:

1)定时器预分频器(TIMx_PSC)

        用来将定时器的总线时钟进行分频,提供一个合适的频率,给计数器去计数。分频系数介于1到65536之间,是一个16位的寄存器

2)重载计数值寄存器(TIMx_ARR)

        用来设定计数值,设为N值。如果自动重装载数值为0,则计数器停止

3)计数器(TIMx_CNT)

        按照预分频器得到的频率,从0递增到N,或者从N递减到0,并且可以在溢出后,产生定时器中断/事件

        溢出的含义为:

                如果为递增计数,当计数值达到N时产生溢出

                如果为递减计数,当计数值达到0时产生溢出

        比如:如果为递增计数,从0开始在一定的时钟频率下开始加1,一直加,加到N时,此时完成计数,就会溢出,产生定时器中断/事件

那么我们如何知道计数器溢出产生中断时,到底花费了多少时间呢?

        我们知道计数器每做一次运算(+1运算)是需要花费时间的,那么我们只需要将计数器每+1所花费的时间求出来t,那么产生中断的时间应该就为:

        (N+1) * t         // N+1指做了N+1次运算

                              // 从0递增到N或者从N递减到0是N次运算

                              // 此时通过重装载从N回到0或从0回到N又是一次运算

                             // 相当于秒钟计数从0加到59秒再加1又回到0

计数器每+1花费的时间到底是多少?首先我们要搞清楚定时器中断产生的流程:

(依据于<STM32F4xx中文参考手册>第17章<基本定时器>中图188<基本定时器框图>):

通过如上流程图可知计数器每+1所花费的时间是跟输入计数器的时钟频率有关系的

举个例子:

假设TIMEx是位于APB1总线上,那么Fin = 84M hz

为了方便计算一般情况下TIMEx_PSC设置为83

则计数器时钟频率为Fcnt = Fin / (TIMEx_PSC + 1) = 84M / 84 = 1M

此时计数器的时钟周期Tcnt = 1 / Fcnt = 1 / 1M = 1us

此时即意味着计数器每过1us加1

所以产生定时器中断的时间为Vt = (N + 1)* Tcnt = (N + 1)us

2. 输入捕获单元

可以对一个或多个输入信号进行处理

有些定时器不具备输入捕获单元

具体可以捕捉多少个输入信号需要看你的定时器有几个通道Channel

有什么用呢?

比如: 可以计算输入信号的频率

        输入信号经过"输入捕获阶段"(数字滤波,多路复用,预分频,去噪等等),到信号检测,当检测到需要的信号状态(上升沿变化/下降沿变化)变化时,就会把定时器时基单元中的TIMx_CNT计数器值,锁定到"输入捕获寄存器"中

这样就可以根据预先设定的定时器参数(时钟频率,N值等等),就可以计算出从开始捕获到锁定这个信号所花费的时间了t

那么进而我们就可以求出输入信号的频率 =  1/t

3. 输出比较单元

可以输出一个或多个信号

有些定时器没有输出比较单元

具体可以输出多少个信号,就需要看你的定时器有几个通道Channel

输出比较:

定时器可以向对应的GPIO引脚(复用功能)输出一个电平状态

并且可以根据"输出比较寄存器(TIMx_CCR)"中的值,来翻转输出的电平状态

比如:

TIMx_CCR > TIMx_CNT  向GPIO引脚输出一个低电平

TIMx_CCR <= TIMx_CNT 向GPIO引脚输出一个高电平

典型应用: 输出PWM波

注意:

        输入捕获和输出比较共用一个寄存器

        因此同一个定时器的输入捕获和输出比较功能不能同时使用

7. STM32F4xx定时器概述

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 代码随想录第38天|完全背包
  • mybatis常见面试问题
  • Cannot connect to the Docker daemon at unix:///var/run/docker.sock. 问题解决
  • Docker最佳实践进阶(一):Dockerfile介绍使用
  • 详解贪心算法
  • CANopen 控制多台设备的支持能力与定制方案评估
  • Cisco交换机SSH使用RSA公钥免密登录(IOS与Nexus,服务器以RHEL8为例)
  • Java线程池练习
  • Visual Studio Code安装与C/C++语言运行(下)
  • 1章4节:数据可视化, R 语言的静态绘图和 Shiny 的交互可视化演示(更新2024/08/14)
  • 数据结构---双向循环链表
  • elementplus 二次封装 select 自定义指令上拉加载更多 完美解决 多次接口调用 重新加载数据多次调用数据!!!
  • LeetCode-字母异位词分组
  • 用R语言进行数据类型的检查和基础转换
  • 如果将一个对象赋值给 ref,那么这个对象将通过 reactive() 转为具有深层次响应式的对象。这也意味着如果对象中包含了嵌套的 ref,它们将被深层地解
  • [译]Python中的类属性与实例属性的区别
  • 【399天】跃迁之路——程序员高效学习方法论探索系列(实验阶段156-2018.03.11)...
  • Docker容器管理
  • es6
  • Java程序员幽默爆笑锦集
  • java小心机(3)| 浅析finalize()
  • React组件设计模式(一)
  • TypeScript实现数据结构(一)栈,队列,链表
  • vue和cordova项目整合打包,并实现vue调用android的相机的demo
  • 等保2.0 | 几维安全发布等保检测、等保加固专版 加速企业等保合规
  • 类orAPI - 收藏集 - 掘金
  • 事件委托的小应用
  • 我从编程教室毕业
  • 我有几个粽子,和一个故事
  • 在GitHub多个账号上使用不同的SSH的配置方法
  • ​软考-高级-系统架构设计师教程(清华第2版)【第15章 面向服务架构设计理论与实践(P527~554)-思维导图】​
  • # windows 安装 mysql 显示 no packages found 解决方法
  • #Datawhale AI夏令营第4期#AIGC方向 文生图 Task2
  • #LLM入门|Prompt#1.8_聊天机器人_Chatbot
  • #每天一道面试题# 什么是MySQL的回表查询
  • (1)(1.13) SiK无线电高级配置(五)
  • (苍穹外卖)day03菜品管理
  • (二)原生js案例之数码时钟计时
  • (翻译)terry crowley: 写给程序员
  • (离散数学)逻辑连接词
  • (译)2019年前端性能优化清单 — 下篇
  • (转)socket Aio demo
  • (转)Unity3DUnity3D在android下调试
  • (转)利用ant在Mac 下自动化打包签名Android程序
  • (转)总结使用Unity 3D优化游戏运行性能的经验
  • .bat批处理(七):PC端从手机内复制文件到本地
  • .Net Attribute详解(上)-Attribute本质以及一个简单示例
  • .NET Windows:删除文件夹后立即判断,有可能依然存在
  • .net 使用ajax控件后如何调用前端脚本
  • .net开发时的诡异问题,button的onclick事件无效
  • .NET微信公众号开发-2.0创建自定义菜单
  • .NET学习全景图
  • .xml 下拉列表_RecyclerView嵌套recyclerview实现二级下拉列表,包含自定义IOS对话框...
  • //usr/lib/libgdal.so.20:对‘sqlite3_column_table_name’未定义的引用
  • @Autowired和@Resource的区别