当前课程知识点:ARM微控制器与嵌入式系统 > 第五章 ARM微控制器的各种外设 > 5.3.1 ARM微控制器外设:IO的中断编程(上) > 5.3.1 ARM微控制器外设:IO的中断编程(上)
各位同学大家好
我是清华大学工程物理系的曾鸣老师
欢迎大家回到我们
ARM微控制器与嵌入式系统的MOOC课堂
那么在这个单元里呢
我们将真真正正地第一次
来开始做一个中断的编程
那么一说中断可能很多同学心里就慌了
因为我们实际上在第四章
给大家讲了第一类最简单的外设IO
那么在第五章的第二个单元里给大家讲了
外设的通讯
大家刚刚摸到了点门道
说这个外设就是老师把原理讲清楚
我们理解它的电路怎么实现
然后再掌握一些寄存器
然后在main函数里把它一句话一句话写出来
程序就开发出来了
但是中断是什么呢
大家突然觉得这个词老师讲过
但是好像有点模糊
没有关系
我们在讲中断的时候就给大家说过
中断是一个难点
在之前的基础知识讲的时候
给大家更多的是梳理了一下
建立了一个初步的印象
而对它的真正的掌握
本来就应该与外设结合实际开发编程来掌握
什么事情一旦做一遍
你就会发现它简单了一百倍
那么从中断的基本概念开始梳理
在这个单元和下个单元的课里
我们用最最简单的一类外设
来进行编程
实现一个中断的程序开发
首先我们回顾一下中断是什么
那么我们首先脑袋会浮现出
当时老师讲中断这张图
说中断是以轮询相对应的一种工作模式
轮询我们实际上在做第四章
或者第一个实验的时候已经体会过
我们要检查一个键按下了没有
我们delay一段时间
去查一下寄存器这个键按了没有
这种模式就是轮询
而中断是什么
中断从本质上我们回忆它是一个
需要CPU立刻处理的内部或者外部的事件
也就是说一个事情
我们设置好条件CPU应该响应它
当它发生的时候它通知CPU必须去响应
这就成为中断
英文就是Interrupt
所以在内部可能是我们设置好的
一个定时的时间到了
可能是一次AD转化结束了
对于外部可能就是我们接下来
可能要做的一个按键按下了 这么一个动作
或者是外部来了一个通讯的数据收到了
这些都可以是中断触发的条件
而中断的工作模式
就是在这样一些事情发生的时候
它打断CPU原有程序的执行
跳转而去执行一段约定好了的与之相应的处理
而在执行完之后
又回到程序正常的流程继续往下执行
我不知道大家回忆起来没有
那么在这个过程当中从正常的程序
切换到约定好的服务程序
需要使用到堆栈来保存我们CPU的
一个局部的寄存器环境
我们称为上下文寄存器context对吧
那么当中断这个子程序执行完毕的时候
我们要把CPU内部的这些寄存器都还原
让主程序好像什么事情都没有发生过一样
继续往下执行
而大家在之前我讲中断
给大家一直强调的一个问题是
中断的这个程序执行的时间什么时候会发生
在编写程序的时候其实并不是特别明确的
因为这段程序的调用
是由中断也就是硬件来触发的
当我们设置好的条件满足的时候
它就自动的被CPU加以调用
那么说了半天这个概念以后
我们再接下来要考虑怎么样真正编程
实现这样一个流程
那么在这个单元里我会带着大家一点点的做
那么我们从哪里来切入
大家会觉得难度会比较低呢
从我们第一次实验的一个任务说起
第一次实验我们最后第五个任务是这样一个任务
说按上我们板子上的某一个键
来切换我们两个LED灯的闪烁模式
比如说本来是两个灯同亮同灭的
然后变成两个灯交替亮灭对吧
那么这样一种模式我们在实验五里头
老师是给了一个例程的
那么我们就以它为例来看看
能不能把中断给用进去
这样内容大家也熟悉
做起来大家心里有底
我是能实现的
我能不能换个方式来实现
那么当时我们给出的例程是这样的
这么一个程序非常非常简单
在这里我就不再过多的解释了
还是那几个步骤对吧
时钟打开 配置成IO用
设置这个IO模块
让它这个设置好引脚的方向
来控制灯的亮和灭
大家注意的是这个main函数里头
第一句话是delay 延时
然后接着有一个if语句
把后面执行内容都给括起来了
那么这个if语句实际上读了GPIOA的PDIR寄存器
也就是检查PORTA上的哪一个引脚的值
有没有变零或者变一
也就是键有没有按下
那么在这个键被按下的时候
我们在这个if的语句里
是切换了两个灯的亮灭关系
比如说是一亮一灭
或者变成两个都亮
然后在这个if语句之外
最后两句话是使所有的灯翻转周期地进行闪烁
所以大家要具备写这个代码
和读这个代码的能力
那么这段程序我们如果用自己大脑
来逻辑上运行一下我们会发现它是不停地延时
除掉这个if的部分
每次延时了以后
是GPIOA进行翻转
让这个灯进行交替闪烁
或者是同亮同灭的闪烁
而if语句呢
仅当键按下来的时候满足
然后每次按键会切换一下灯闪烁的模式
这是背后程序的编程思想
那么这个程序
为了引入中断我要问大家一个问题
它有什么毛病
那么大家可能会想几秒钟
或者你把这个视频停下来
大家把这个MOOC课停下来
自己想一想
最好是自己有个答案
那么我在这里给大家说
它的其中一个问题
如果大家用脑袋
去运行这个程序的流程的时候你会发现
我们每次delay
比方说花掉半秒钟或者一秒钟
实际上都是到程序执行完delay
执行if这条语句的这个瞬间
才会去检查我们的引脚有没有被按下
那么换言之每半秒钟的delay之后
也就是执行这条语句的那几十个几百个纳秒
会去查这个按键有没有被按下
那如果说
我们手按这个键的时间
在delay函数的那个半秒里我已经按下去
又松开了呢
那是不是等到delay函数执行完
来检查这个按键有没有按下的时候
我们这一次按键信息就已经丢掉了
那有的同学说
那我把这个delay改短一点
老师不是说人按一下键总得个百毫秒
几十毫秒嘛
那我让这个delay函数变成
十毫秒 五毫秒的delay
这样我查这个按键查的很频繁
能不能解决这个问题
能
这就是典型的轮询的方式
你轮询的频率能不丢你要的事件
但是另一个问题来了
你把delay改短了
是不是底下你会发现这个灯就闪的变快了
闪到眼睛都看不出来了
所以呢你还得再进一步的改
进这个for循环delay若干次才闪一次灯
所以当我们在一个for循环里头
要通过delay来做若干件不同的事情的时候
我给大家一点提示
你的delay的那个时间
是不是得是你所有这些时间所需要的
那个最小单元或者说是他们的最大公约数啊
所以这就是一种圆形的时间片的这种机制
大家以后慢慢的会从这个过渡到学习操作系统
那么在这个单元里我们因为存在这个问题
我们实际上这个程序跑起来是不顺畅的
会丢人的按键的
我们能不能用中断的方式来做呢
这就是这个单元我带着大家学习和掌握的内容
那么我们看看我们的电路图我们会发现
在我们现在这个电路板上
所有的八个引脚都接到了PORTA上
分别是PORTA1一直到十几的八个IO
用来接这八个按键
那么非常幸运的是
我们在Cortex M0的KL25
我们用的这个系列的MCU微控制器上
PORTA和PORTD都是具有中断功能的
也就是每个引脚都可以触发中断通知CPU
那我们现在就来尝试编程
让这个按键工作在中断方式下
从思想上来讲就是键设置好能够响应这个中断
于是无论任何时候 这个键被按下
就自动通知CPU 键被按了
然后同时再进行相应的处理
我们切换到这样一种编程思想
尝试在这个单元里
学会把程序实现出来
那么它有几个步骤呢
比原来多一个步骤
四个步骤
其实原来讲中断的时候给大家讲过
第一个步骤配置好中断的这个开关
把中断打开
第二个步骤呢
配置好对应的中断源
然后第三个步骤呢
把这个中断的服务子程写好
最后一个步骤
把这个中断服务子程填进中断向量表
确定它是跟我们设置的中断源一一对应的
而让CPU在中断发生的时候
知道该执行这个函数
那么在这个单元里我只讲步骤一
在下个单元
我会给大家讲后面三个步骤编程实现
如何把中断打开
一句话就是在我们的微控制器里头
对于一个中断要发生
除了标志位置位这个中断事情发生之外
我们需要打开一个总开关
Global的enable
还要打开一个子开关
也就是我们这个中断源对应的Dedicated的enable
那么总开关在哪
在讲基本概念的时候给大家讲过
我们现在再来回顾一下 其实就是一句话
那么在我们CPU内部的寄存器
注意CPU内部的寄存器跟R0到R15
这些数据寄存器在一起的
CPU的状态寄存器里头
是不是有一个寄存器是PRIMASK
中断和优先级的这个掩码寄存器
它里头的最后一个比特PM这个比特
为0为1是控制这个微控制器是否响应中断
那么它会是一个总开关
而对它进行操作
这是CPU内部的寄存器
大家一定概念清楚
它不是映射在地址上的
它是CPU内部的
直接通过两条指令分别对它可以置0和置1
相应的打开或者关闭中断的总开关
那么这两条指令
一条是CPSIE i 一条是CPSID i
D对应Disable
分别是打开全局中断开关和关闭全局中断开关
好 一条汇编指令呢
我们就搞定了这个中断的总开关
那么这个中断源对应的子开关怎么打开呢
大家不要急
我来一点点的带着大家把它弄清楚
如果我们回忆一下之前讲过的内容
或者翻我们KL25这个芯片手册的
第53页去看一看
回忆一下我们中断向量表
我们CPU除了总开关能够响应
各种各样的中断源
当然我前面举过的例子
定时 按键
那么一共有多少个中断源可以响应呢
如果我们回头看一看这个中断的
这个地址分配表或者向量表
我们会发现从序号0到序号47
一共有48个vector也就是向量可以响应
那么在这48个里头并不是全部都是
我们所说的外部中断
那么在这个CPU设计的时候
由谁设计
由ARM公司设计的时候呢
定义了前16个也就是0到15
被预留给了CPU的内部作为异常处理
包括我们学过的复位
包括我们学过的系统时钟
非法指令的复位等等这些响应
称为异常处理
占用了前16个
那么我们所说的外部中断
48减16
还剩下32个呢
从vector这个向量表的编号上
就是从第16号到47号
但是从中断号上来讲
就是从第0号到第31号一共32个
是我们的外设中断
那么他们这些外设中断每一个都在芯片
最终设计成我们买到芯片型号的时候
必须由芯片的最终设计厂商
来指定给哪个外设用
比如有的给AD用
有的给定时器用
有的给我们现在用的按键来用
那么这些信息呢
是在我们的这个KL25芯片手册上可以查到的
那么归而简之就是我们向量表里一共有48个项
那么这个向量表的前16项
是由ARM公司设计的
那么指定了内部CPU的一些异常处理
后面的32项是由像NXP
或者是Freescale这样的
芯片的实际设计厂商设计的
指定给了不同的外设使用
我们看芯片手册能够看到它们分别是什么功能
如果我们想设置这每一个单独功能的
中断的子开关
回到我们的正题
我们需要对一个叫做NVIC这样的
模块的寄存器进行编程
那么稍微有点特殊的是
NVIC这个模块它的英文全称呢
是Nested Vector Interrupt Controller Module
这样一个缩写
那么换言之就是可嵌套的外部的
中断向量的控制模块
NVIC模块不是我们之前所学的
由Freescale或者NXP
这个生产厂商所设计的外设
它是各家各户的ARM统一的标准的
由ARM公司设计的一个外设模块
所以如果我们回到
地址向量表的时候我们会发现
这样一个模块
它的地址并不在我们之前学过的
IO啊 串行通讯那个地址段
而是在我们整个地址表的最底端
也就是以前所讲的Private Peripherals
这个地址段里头
E000开头的这样一个地址段
是NVIC这样一个ARM公司定义的
Private外设的地址所在
那么除了地址放的地方不一样
编程使用跟前面讲的都一样
就是找到每个寄存器的功能
找到对应的地址对它进行编程就可以了
那么相应的NVIC这个模块
这个寄存器的定义我们在两个地方都可以找到
一个呢是看我们NXP Freescale提供的
芯片手册KL25芯片手册的138页这个位置
另外呢
就是看我们的ARM的芯片手册的第57页
它也会详细的讲这些NVIC模块里的定义
那么我们仔细打开看一看就会发现
NVIC这个模块跟以前学过的一样
跟IO跟串口一样包含了若干个寄存器
那么这些寄存器的起始地址是E000E100这个地址段
那么我们进入这个模块
看看这样一个模块里头究竟有几个寄存器呢
我们会发现跟IO模块
跟这个UART串行通讯模块一样 不多
一共只有5个寄存器可以使用
而在这门课的范畴里头
我们其实只要大致知道三个寄存器
而在编程范畴里头我们其实只用到了其中一个
那么这是哪几个寄存器我们展开看一看
这五个寄存器当中的有用的
或者我们会涉及到寄存器的第一个
是NVIC_ISER寄存器
其次呢是NVIC_ICER寄存器
这两个寄存器一个是对0到31
那32个外部中断源一一对应的
置位打开中断子开关
一个呢是一一对应的置位关闭中断子开关
那么这五个寄存器的最后一个寄存器
还有一个我们可能以后会用到的是NVIC_IPR
是设置中断的优先级用的
那么在这个单元里头
我们学会最简单的一个编程
就是打开中断子开关 非常简单
那么NVIC_ISER这个寄存器是一个32位的寄存器
那么它的地址
我们从这个寄存器定义上就直接能看到
是E000开头的这样一个地址
那么它的内容是0到31位
32位的寄存器
换言之你可以给它设置32个0或者1
那么这32个0或者1
每一个就对应刚才说的外部中断
从0号到31号一个中断源的子开关
所以我们要在中断向量表里
把哪一个功能的中断子开关打开
我们就在这个寄存器里
把它对应的那个比特至1就可以了
而如果回头我们想把这样一个中断子开关关闭
注意我们不是把这个寄存器的这个位清零
而是把它隔壁的那个寄存器
NVIC_ICER寄存器的对应位写一个1
这样就关闭了这样一个中断源的子开关
那么优先级的设置呢
大家在芯片手册里
自己还可以进一步去进行看和学习
所以回过头来我们想一想
中断的总开关用一句汇编语言就能打开
中断子开关就要看我们要用的是哪一个中断源
我们要用的是哪个中断源
大家不要跟着我讲讲讲
忘了我们的目的
我们回忆电路图我们有八个按键
这八个按键都接到了PORTA的
8个不同的IO引脚上
我们希望PORTA是能够当中断源使用的
那么我们就应该把PORTA或者GPIOA
所对应的中断子开关打开
我们查看中断向量表
我们会发现中断向量表的第30号
也就是0到31的倒数第二项
是对应的PORTA的中断源
那么它的中断子开关也就是我们的
NVIC_ISER这个寄存器的比特30
也就从最高位数起来的第二位
那大家这个操作怎么写呢
很简单
要打开这个中断子开关
我们只用写NVIC_ISER寄存器
或等于0X400...00这么一个串对吧
这么一或是不是这个第二位就置1了
回头如果我们要关闭
这样一个中断源的子开关
也很简单
就是NVIC_ICER寄存器
或等于0X400...0这么一个串
把这个关中断的寄存器的对应位置写一个1
就关中断了
那么这些信息如我们所说
在手册上大家都可以找到
希望大家一定认真的对应
把老师刚才讲的内容加以理解
如果真正理解了
你会发现讲了半天
我们的第一个步骤实现起来非常简单
在我们原来main函数的开时钟
配置给引脚的IO用
这些都不变的情况下
只多了这么两句话
一句话是ASM汇编语言括起来的汇编指令
什么呀
开中断总开关
一句话是NVIC_ISER寄存器
或等于0X400...00把我们要用的
PORTA这样一个中断源的子开关
在NVIC模块里找到对应的比特给打开
这样我们就把我们中断的开关配置好了
换言之对于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 嵌入式系统的实例