当前课程知识点:ARM微控制器与嵌入式系统 > 第五章 ARM微控制器的各种外设 > 5.4.1 ARM微控制器外设:定时器的原理 > 5.4.1 ARM微控制器外设:定时器的原理
各位同学大家好
我是清华大学工程物理系的曾鸣老师
欢迎大家继续回到我们MOOC课堂
进行每周的ARM微控制器与嵌入式系统之旅
我们的课程在这一周进入了一个新的单元
那么我们在这个单元里要学习的
是一类全新的外设
它非常非常的有用
就是定时器
定时器在计算机系统里
在很多很多不同类型的功能里都将要使用
那么从我们这门课的范畴
我们为什么要引入这个知识的学习呢
我们从我们之前已经反复做过的一个实验
回顾再讲起
我们对它来进行不断的完善和改进
就是上个单元给大家讲过的
这样一个点灯的实验
我们通过一个按键
来控制这两个小灯的闪烁模式
从两个灯一起闪
变成交替闪
那么在最早最早
我们看到的程序是这么一个完全的
一个轮询方式的一个main函数
我们delay一段时间在for循环里
去检查这个键有没有被按下
然后在这个delay的时间里
去切换这个按键灯闪烁的模式
以及每次delay完了这个灯会闪烁一下
那么当时说这个程序
首先它会有一个问题就是
每当延迟的时间结束的时候
才检查按键的值
实际上对按键实际上是不灵敏的
受这个delay时间的限制
当然它可以在轮询的模式下进一步的改进
而在上一个单元里
我们教大家做了第一类的中断实验
学会了说主程序正常运行的流程当中
我们可以设置好一个外部的硬件的条件
可以打断主程序的运行
去执行一个中断服务程序
然后借助堆栈保护这个现场
然后在中断服务程序执行完后
又回到主程序加以执行
于是我们学习了IO这样一类中断的
最简单的一类中断编程
得到的程序呢就像这个屏幕上所示
程序被拆成了两个部分
被打断的主程序是一个在for循环里
不停的延时和让灯闪烁的一个正常流程
而来打断这个主程序的呢是我们
由我们配置好的某一个按键所触发的中断
每当中断发生
在中断的服务程序里会改变灯的一亮一灭
或者两个同亮的模式而返回主程序
让这个灯继续周期闪烁
得到了这么一个程序
那么这个程序有没有问题呢
其实是还有一些问题的
大家会发现我们的main函数里头
仍然有一个delay
仍然有一个这个灯的闪烁是靠delay来做的
那么换言之我们的CPU
它这个点灯的时间强烈依赖于这个delay函数
比如说我们的灯点的这个周期准不准
取决于delay函数准不准
我们其实并不知道它到底有多准
其次我们在这个主程序里
所有的时间都在运行这个delay
如果我们不做其他的事情
这个CPU的所有的运行所有的功耗都是浪费的
因为它在做for循环的空循环来浪费时间
同时浪费电
而如果我们在main函数里
在这个for循环里还做点别的事
那么我们其实实质延迟的时间是delay
加上这些操作的时间
于是我们灯就可能根据任务的轻重程度
闪的忽快忽慢
不那么准确
那么这样一系列的问题
其实体现的是我们嵌入式开发里头
经常碰到的一个难题
如何进行准确的定时
如何让多个任务有条不悖的能够加以运行
那么这样一个挑战下我们在这个单元
就会引入计时器或者叫定时器这样一种学习
这既是我们学习的一类新的外设
也是我们将要进行编程开发的
第二种中断程序的编写
那我们来看看什么是定时器在这个单元
下一个单元我会教大家
如何对它加以编程和实现
总体上来讲定时器也就是一种计数器
它是所有的微控制器各家各户的微控制器
非常通用一定都有的一个功能
大量的程序的实现都需要定时器
因为我们做程序的时候
我们经常会需要进行时间的测量和控制
比如一件事情持续多长时间
一个信号脉宽是多少
以至于我们隔多长时间才能做一个操作
一个生产流程在控制的时候要延时等待多久
才能做下一个步骤
这些东西都需要依赖于各种各样的定时器
所以所有的微控制器都会提供不止一个定时器
以及各种各样的丰富的功能来给我们使用
本质上来讲定时器为什么要跟计数器一起讲
因为它就是一种计数器
定时就是对一个固定的时间间隔
进行累加计数的功能
大家想一想这件事
时间间隔是一秒
我们数了多少个一秒就是多少秒对吧
所以我们总会依赖于一个时钟信号
来完成计数和定时
那么这个时钟信号
可以是我们的CPU的系统时钟
也可以为了更高的精度
乃至别的原因
是一个由外部给予我们微控制器的时钟
其次呢 我们定时的总计时长度
就取决于一个计数器
而这个计数器我们日常现在能用到的
往往是8位、16位乃至32位的
原因呢 就是因为8位、16位、32位
正好是整字节数
方便我们进行寄存器的配置和读写
那换言之如果是8位的计数器
我们就可以从0到255进行256周期的计时
如果是16位我们就可以从0到65535
进行16比特的计时
这也就是我们总的计数长度
那么定时这样一个基本的功能
其实它有非常广泛的应用
它除了能够计量一个时间之外
像我们在这个单元里头
它会产生一个定时的中断
来知道多长时间去做一件什么事
其实在嵌入式系统和微控制器里头
它的用途远大于单单的去做一个计时
比如说基于一个特定的脉冲周期信号和计数器
我们可以产生固定脉宽的脉冲信号加以输出
我们可以对输入的信号对它的周期
对它的频率
对它的占空比进行测量
而如果我们将输入的脉冲信号
当做被测量 用计数器
我们可以对外部的脉冲进行计数
而对单位时间的脉冲进行计数
我们又可以进行频率的测量
比如在智能车里头
我们会通过霍尔元件或者光电传感器
将我们测速轮或者轮子的转速
变成脉冲信号给微控制器
我们对它进行一定时间长度内的计数
就知道了轮子的转速乃至车的车速对不对
这些功能都是可以通过类似于定时器的功能
加以实现的
在这门课程里
有一些功能我们在后面的章节会给大家再讲
那么我们说因为有了定时器
所以我们可以更加准确的
去测量一个时间间隔
或者更加准确的
去设定一个时间的周期和延时
如果在依助中断的这样一种工作模式
我们可以相对于主程序比较独立的
去定时去执行一个操作和任务
这就是对定时器进行中断编程的一个基本思想
在下面我们会展开进行讲解
那么我们如果我们拿到了一个定时器
最low最low的定时器我们会发现
我们可能工作在一个free running的模式
也就free running counter自由运行这样一种模式
那这个计数器总是从0一直到它的最大值
然后再溢出得到0
再到最大值 这样周而复始的运行
那么比方说我们输入了一个8兆的时钟
我们用一个8比特的计数器
对它进行这种free running方式的计时
那么得到的信号的时钟周期是多少呢
其实我们非常简单可以算一算
也就是8比特每256个周期会溢出一次
那么8兆的时钟
它的时钟间隔是多少
这两个一乘再一除
倒过来我们就知道了总的周期时间和它的倒数
能得到它的频率
有兴趣的同学可以算一下
那么如果我们拿到了一个指定位宽的计数器
每次都要靠溢出归0才完成一次计时
这就比较笨了对吧
所以我们日常使用的绝大多数的外设模块
都是我们称为Modulated counter
也就是说一个调制的计数器
那么在这样里头
我们会给这个计数器设一个预设值
那么这个预设值每次递减到0
就完成了一个计时周期
然后再归为这个预设值
于是我们就会在一个时钟基础上
数任意多个数完成我们所要的周期
相对而言我们就会比较准确的
得到我们任意想要的时间间隔
比如说我就要一个微秒或者要一个毫秒
而这个定时的误差取决于两件事
从精度上来讲是取决于
我们这个计数器的时钟频率
它每个step有多宽
这是最小单位
另外呢 取决于我们这个时钟源是否稳定
当然这是一个更深入的话题
它对温度
对环境各种因素的稳定性
但不管怎么样
我们可以在程序上正冲着一个目标
去灵活的加以编程
如果我们希望学习timer或者定时器
我们当然要找一个我们微控制器上有的模块来上手
那么我们实际上在一个微控制器里
有很多很多外设模块
我这里列出来了
那么在这个课里头
我用我们第四章系统时钟这个单元
也就是SysTick来给大家做一个例子
而实际上在这个微控制器的芯片手册里头
我们实际上有大量的时钟模块
比如第32章周期中断定时器
比如第34章我们的实时钟模块
这是一个实时钟
年月日的这个实时钟
比如第31章
我们其实本身是timer
同时可以当脉冲PWM调制模块
这都是形形色色功能丰富强大的定时器
而作为这个章节的入门我给大家讲
最最简单的一类SysTick
麻雀虽小 五腑俱全
方便入门的同学来加以理解
那么SysTick这个模块
虽然在我们芯片手册里可以看到
我要给大家首先说的是
它不是由芯片的生产厂商NXP
或者是Freescale设计的一个外设
它跟我们之前讲到的中断控制器一样
是各家各户的ARMCortexM0这个内核
都有的一个由ARM公司设计的
内部的基本外设
所以它是ARM内核的一部分
是所有家的ARM都有
那么这个定时器非常的简单
它就是一个简单的24比特位宽的
带调制的计数定时器
那么它的时钟源呢
可以是我们的系统时钟源
也可以是我们通过编程
指定的系统时钟源的分频
那么这个SysTick模块可以产生中断
而且最大的一个好处是
它是一个最最简单的定时器单元
只用三到四个寄存器的编程
我们就可以把它用起来
非常适合大家初学和理解这个概念
顾名思义这样一个模块
叫做SysTick也就是系统节拍
也就是当我们把一个ARM内核
工作在有操作系统
或者没有操作系统跑一个复杂任务的时候
这个模块主要用来提供系统程序运行的
那个主时钟
比如说操作系统的时钟节拍
那么对于我们自己写的程序
我们当然可以随意的去使用它
那么我们看看它有哪些寄存器
Sys Tick模块如我们所说
是由ARM公司所设置的
一个ARM内核的功能单元模块
所以它的寄存器映射到了
我们整个地址表的最下端
Private Peripherals这个地址段
也就是E00开头的这个地址
如果我们到ARM的芯片手册上
去看这个寄存器所在的位置
我们会发现
它在ARM的Devices Generic User Guide第100页
而对应的内容我们在KL25的芯片手册的
第113页也是可以看到的
所以这个寄存器我们会明显的看到
它在E010开始的这样一个地址段
也就是E000E010开始的地址段
如果我们到芯片手册里
把这个模块的寄存器打开看
我们会发现一共只有四个寄存器非常的简单
那么这四个寄存器的功能
我们逐一给大家展开讲解
第一个寄存器呢就是SYST_CSR寄存器
也就是大家熟悉的Control and status register
它既完成对这个模块的控制
也可以获得这个模块的状态
那么在这个寄存器里最低位的三个比特
是主要的开关状态
最下面一个比特enable
是控制了我们整个这个Sys Tick时钟模块
是否打开
比特1
第二个比特呢
是 是否打开我们这个模块产生中断
所以它是中断的子开关
那么第三个比特呢
是我们的clock source的选择
那么在这个里头
如果我们详细看芯片手册会发现
这一位置1使用是我们的微控制器的core clock
这一位如果置0
在ARM芯片手册的设计当中
是可以使用别的时钟
具体到我们的型号使用的是
Core clock除以16以后的时钟
那么在这门课
我们一般将它至1
直接使用系统Core clock
那么再往上除掉几个reserve的比特
有用的另外一个比特是第16个比特
它的名字顾名思义叫count flag
所以它是一个标志位
是我们将Sys Tick这个时钟
工作在中断模式时候的中断标志位
那么换言之一旦我们定的时间到达
这一位会置1
来触发我们的CPU产生中断
而对它的清零
在我们芯片手册里写的非常清楚
也就是说
我们在上一次进行读写之后
会导致这一位清零
所以我们每一次对这个寄存器进行读取
就会使这一个标志位清零
从而使下一个中断可以发生
那么回到我们的Core clock
Core clock我们前面讲过
我们微控制器里头时钟树的概念
有一个非常清楚的概念是
从上电以后默认的时钟树
加上片内的振荡器和锁频环 锁相环
最后的系统的时钟是20.97兆
这也就是我们刚才所说Sys Tick时钟模块
所使用的计数时钟
那么我们知道我们时钟是什么
把它打开
把它的这个中断也允许 也有标志位以后
剩下的能够用到的两个寄存器就非常简单了
一个寄存器呢就是我们的
reload value register
这个寄存器一共32位宽
只有24位的值是可以使用的
这个值是我们刚才所说的
对这个计数器进行调制的初始计数值
也就是我们在一个固定的时钟下
数到多少归零一次
就决定了我们的周期是多少
所以对这个寄存器赋的值
决定了在那个时钟源的频率下
我们究竟计时的时钟周期是多少
还有一个寄存器呢是当前的
current value register
也就是我们当前这个计数器 数数 数到多少了
因为计数器是24比特
所以这个寄存器可读的也就只有24个比特
它记录了当前计数器一共值存到了多少
那么对于一个这样的当前值的寄存器
我们对它做任何写操作
都会把这个值给清零
同时也会帮助我们把前面的状态
和控制寄存器里头中断那个标志位清零
所以根据芯片手册上来讲呢
我们配置一个Sys Tick模块
完成初始化的正确步骤它给出了三句话
第一句话呢
是对这个reload value寄存器编辑值配置
我们究竟计数器数到多少产生一次中断
也就是定这个时钟周期
第二呢 我们是通过一个写操作
把Current Value寄存器的值给清零
这个操作同时会导致我们的标志位
也就清除了
第三个步骤是操作内第一个寄存器
也就是控制和状态寄存器
让它被打开
被启用中断
并且工作在我们选定的时钟源下
这就是对于Sys Tick这么一个
在KL25或者ARMcortexM0所有的CPU里头
都有的最最简单的时钟模块操作
所要用到的最简单的三个寄存器
所以有了这个寄存器
加上对于这个定时器
大家讲过的基本概念
大家是不是隐隐约约已经觉得
我只要写几句话就能把它用起来呢
那我们在下一个单元再来给大家讲
如何真正的编程
乃至改进我们刚才讲到的
这个点灯这个程序
-1.1 课程概览
--1.1 课程概览
-1.2 如何学好嵌入式系统
-2.1 计算机的基本概念、发展历史
-2.2 从晶体管到CPU
-2.3 概念CPU、微控制器MCU和嵌入式系统
-2.4 八卦计算机史
-2.5 不同领域、不同系列的嵌入式系统
-2.6 ARM历史与MKL25Z128 MCU
--2.6 ARM历史与MKL25Z128 MCU【习题】
-3.1 CPU的基本结构和运行机制
-3.2.1 堆栈的概念
--3.2.1 堆栈的概念【习题】
-3.2.2 堆栈的概念-头脑体操
-3.3.1 ARM的体系结构
--3.3.1 ARM的体系结构【习题】
-3.3.2 ARM的体系结构-头脑体操
-3.4.1 中断的概念和机制
-3.4.2 中断子程的概念和编程
-3.5 复位、时钟、存储器和总线
--3.5 复位、时钟、存储器和总线【习题】
-3.6 小结:MCU的总体结构和程序运行机制
--3.6 小结:MCU的总体结构和程序运行机制【习题】
-4.1 第一种外设:IO
-4.2 IO外设的编程实操-点亮LED
-4.3 IO外设的进阶知识
-4.4 嵌入式开发的基本概念与工具链
-4.5 嵌入式开发的进阶知识
-4.6 嵌入式开发中的C语言(上)
--4.6 嵌入式开发中的C语言(上)【习题】
-4.7 嵌入式开发中的C语言(下)
--4.7 嵌入式开发中的C语言(下)【习题】
-E0.1 实验零 开发板的初步认识与工具链的安装
-E0.2 实验零 体验一个例程的编译与下载
-E0.3 实验零 编写第一个程序:点亮核心板LED
-E1 实验一 点灯秘籍
-5.1 ARM微控制器外设学习概述
-5.2.1 ARM微控制器外设:通讯
-5.2.2 ARM微控制器外设:异步串行通讯UART的原理(上)
--5.2.2 ARM微控制器外设:异步串行通讯UART的原理(上)
--5.2.2 ARM微控制器外设:异步串行通讯UART的原理(上)【习题】
-5.2.3 ARM微控制器外设:异步串行通讯UART的原理(下)
--5.2.3 ARM微控制器的外设:异步串行通讯UART的原理(下)
--5.2.3 ARM微控制器外设:异步串行通讯UART的原理(下)【习题】
-5.2.4 ARM微控制器外设:RS-232串口与USB虚拟串口
--5.2.4 ARM微控制器外设:RS-232串口与USB虚拟串口
-5.2.5 ARM微控制器外设:UART的寄存器编程(上)
--5.2.5 ARM微控制器外设:UART的寄存器编程(上)
-5.2.6 ARM微控制器外设:UART的寄存器编程(下)
--5.2.6 ARM微控制器外设:UART的寄存器编程(下)
--5.2.6 ARM微控制器外设:UART的寄存器编程(下)【习题】
-E2 实验二 UART编程实操
-5.3.1 ARM微控制器外设:IO的中断编程(上)
-5.3.2 ARM微控制器外设:IO的中断编程(下)
-5.4.1 ARM微控制器外设:定时器的原理
-5.4.2 ARM微控制器外设:定时器的编程
--5.4.2 ARM微控制器外设:定时器的编程【习题】
-E3 实验三 定时器中断编程实操
-5.5.1 ARM微控制器外设:PWM的原理
-5.5.2 ARM微控制器外设:PWM寄存器与编程
-5.5.3 ARM微控制器外设:PWM编程实例—电子音乐
--5.5.3 ARM微控制器外设:PWM编程实例—电子音乐
-E4 实验四 数码管显示编程实操
-5.6.1 ARM微控制器外设:SPI通讯简介
--5.6.1 ARM微控制器外设:SPI通讯简介【习题】
-5.6.2 ARM微控制器外设:SPI寄存器与编程
-5.6.3 ARM微控制器外设:SPI编程实例—OLED显示屏驱动
--5.6.3 ARM微控制器外设:SPI编程实例—OLED显示屏驱动
-5.7.1 ARM微控制器外设:I2C通讯简介
-5.7.2 ARM微控制器外设:I2C的通讯协议
-5.7.3 ARM微控制器外设:I2C寄存器与编程
--5.7.3 ARM微控制器外设:I2C寄存器与编程【习题】
-5.7.4 ARM微控制器外设:I2C编程实例—加速度传感器
--5.7.4 ARM微控制器外设:I2C编程实例—加速度传感器
-5.8.1 ARM微控制器外设:ADC简介
-5.8.2 ARM微控制器外设:ADC基础
-5.8.3 ARM微控制器外设:ADC寄存器与编程
-E5 实验五 ADC编程实操
-E6 挑战实验
--E6 挑战实验
-6.1 嵌入式系统的接口与设计
-6.2 嵌入式系统的实例