kinetis L系列的 NVIC 与实现(一)
[前言]
作为 uController 的从业人员, 我们频繁使用中断, 那么我们尝试根据各种 samples 的支持下, 增加对 kinetis L serials (cortex-M0+) 的interrupt 的理解, 并用实际的 sample 的实现来强化我们对 L series 的 Interrupt 的认知.
[PPB 与 Interrupt Registers]
在 Kinetis L series 中, NVIC 位于 Private Peripheral Bus(PPB) memory map.
要提到的是, 从 0xE0000000 到 0xE00FFFFF 的 1M 系统地址空间, 都是 ARM core 留给 PPB 使用.
这里的 Relative Interrupt Registers, 应该包括: ISER, ICER, ISPR, ISCP, IP (我们这里类似 Sample Code 的头文件定义, 均省略上述 registers 的后缀 R 称呼, R 代表 Register). 遗憾的是, Interrupt Registers 的定义, 翻遍了 RM(Reference Manual) 也没有找到, 至于其他的更简略的 reference documents, 更不必提. 带着疑惑, 我们对 PPB memory map 相关的 NVIC registers 与 System control Block 的 registers 定义进行了广泛的查找, 最终 clue 在:
https://community.freescale.com/community/kinetis/content?query=0xE000ED04
答案是: 因为 NVIC 属于 ARM Core(即ARM IP), 故 Freescale 不提供任何文档支持,
这样, 在 http://infocenter.arm.com/help/index***p?topic=/com.arm.doc.ddi0337e/Cihcbadd.html 我们 download 到了:
Cortex-M0+ Technical Reference Manual.pdf, 里面提到了 ISER, ICER, ISPR, ISCP, IP 的 registers 定义, 应该在
ARMv6-M Architecture Reference Manual.pdf 中查找.
最终, 我们同样在 ARM infocenter, down 到了该篇 document(From: https://silver.arm.com/browse/AR585)
同样的, 从 sample code 里, 我们也提取到相应文档(Architecture RM)的下列 Interrupt Registers 的结构体定义:
[图例1: 从 sample code 中获得的 Interrupt relative registers 的定义]
[NMI 中断的实现]
敬爱的同行之诸君, 我们进入 Kinetis L series Interrupt 的实现之旅, 第一步或许我们应该举出 NMI 中断例子.
其深刻的内在原因是, NVIC(Nested Vectored Interrupt Controller), 首先是属于 Arm Cortex-M0+ Core 的组成 Module.
而 NVIC 本身, 如我们之前在文章, "Kinetis L系列的启动过程及其实现" (http://forum.eet-cn.com/BLOG_allen_zhan_359.HTM) 提及的, 通过异常向量表, 以及实际 datasheet 中的定义, 共有着 48 pcs 中断向量.
而 48pcs 中断 module 中, 又记有 16pcs core-interrupt (Vector numer向量编号为: [0, 15]).
以及, 48-16 = 32pcs non-core interrupt (Vector numer向量编号为: [16, 47]), 我们也注意到, 只有 non-core interrupt 才有着 IRQ number( [0, 31] ).
这也似乎是否提醒我们, 如果我们参与一款 arm cortex-m0 的设计, 可能我们必须保留 16pcs core-interrupt 设计, 但是因各个sub-modules 的不同或者增减, 或因具体功能不同, 我们可以设计不同数目的 IRQ, 甚至自行安排编写其在 non-core interrupt 中的 NO. 位序?
当然, 根据 ARM cortex-M0+ TRM 文档, 事实上最大的外部 interrupt 数目应该是 32pcs, 可以减少, 不能超过, 见到类似 ISER 的定义则原因自明:
对于 4-byte 的 word 长度的 register, 每个 bit 对应一个 interrupt 的使能与否, 当然我们判断出 non-core interrupt 即 IRQ 至多为 32pcs.
[图例2: ISER register 定义, From ARM info document]
[Core Interrupt]
回到 NMI sample, 我们阅读下面的图例3, 可知 NMI 就是 core-interrupt 的其中一个中断向量, 甚至它是在我们之前"Kinetis L系列的启动过程及其实现" 一文中, 就已经提到的, 在 system boot 之际, 最重要的异常向量表的 No.1(SP), No.2(PC) 之后, 表现为 No.3(NMI interrupt, Non-maskable Interrupt).
[图例3: core-interrupt]
[在 blinky sample 中实现 NMI Interrupt]
在 uController world 中, blinky sample 往往等同于高级言语第一课 "hello world".
不意外的, 我们从 blinky sample(从 IAR 的自带 sample 与 freescale 的网站上都可以下载, 或者开发板安装自带), 检查到了 sample 已经完整替我们建立了一个 interrupt 的完成机制, 我们注意到除 sp 与 pc, 剩余的全部中断, 都被对应了 default_isr 这个 ISR (中断服务例程).
[图例4: 在 blinky sample 的 vectors.h 中, 全部中断都指向同一个临时的 default_isr ]
通过完成 rebuild 生成 .bin, 我们明确见到这个 ISR:
[图例5: Linker 产生的 map file 与 .bin 文件的比较]
这实际意味着, 我们无需其他工作, NMI 中断 ISR 已经实现并完成.
由于 NMI_PIN 默认启用, 为 PTA4 复用. 在 KL25Z 开发板上, 我们测量存在高电平(与 RM 中提到内部弱上拉的说法一致). 短暂用 GND 触碰 FREEDOM KL25Z 开发板之 PTA4, 我们进入了 default_isr(已预置断点), 与预期一致.
应当说明的是, 在 default_isr 中, 我们 read 0xE000ED04, 也就是 ICSR(Interrupt Control State Register) 为: 0x80002002.
根据 ICSP 的定义分析(From: Architecture RM):
(a) bit31 表示: activate NMI exception, 与我们的触发 NMI 中断动作一致.
(b) 0x20xx 表示: vector number(0x02), 即此时最高优先级的 pending interrupt(也就是 NMI)
(c) 0x02 表示": vector number(0x02) 当前中断为 NMI(0x02).
因此, 不意外的, 我们在完成该 NMI ISR 后, 再次进入了 ISR 的调用, 此时 ICSR 为: 0x02(表当前中断为 NMI).
要提及到的是, NMI PIN(NMI_b) 有内部上拉, 我们猜测是一个弱上拉, 对 PIN 脚信号非常敏感, 经常的, 不用短路 GND, 仅仅镊子尖端碰到, NMI Interrtupt 即可触发.
使用下述代码, 我们直观见到了 NMI 的 low level 事件触发了 2pcs NMI's ISR 的的效果, 表现为 green led 进行了连续2组闪烁.
[图例6: 用 green led flinky 标识 NMI interrupt]
Tip1: 使用代码触发 NMI 中断
如果我们使用类似的代码:
#define SET_NMI_INTERRUPT() SCB_ICSR |= SCB_ICSR_NMIPENDSET_MASK
那么, SET_NMI_INTERRUPT() 理应当触发 NMI 中断, 进入 ISR (目前它是 default_isr). 我们没有测试这段代码. ;O 因我们有 FREEDOM 开发板可以用来真实低电平触发中断, 而无需上述代码替代测试.
Tip2: 复位是否会触发的中断服务例程 ISR?
当我们检查 ICSP 以及 IPSR, 从中获得当前的异常序号(Exception Number) 时, 根据 Exception Number 的定义, 请看图例7:
[图例7: Exception numbers 的定义, From: Architecture RM]
我们有个质朴的疑问, 如果 RESET 事件被命名为 Exception numbers 的 No.1, 那么在 RESET 过程发生后, 也就是 RESET 事件触发瞬间与 SYSTEM REBOOT 的过程, 是否会进入 ISR. 带着这个疑问, 我们反复测试 RESET button, 以及 power on 过程, 我们并回忆了我们的"Kinetis L系列的启动过程及其实现" 一文, 确认 reset 与 boot 过程, 在当前的工程中, 没有进入 ISR.
但是, 如图例8中, 文档提到了, ICSP 与 IPSR 会在 RESET 时产生一个瞬变的 0x01 的过程("甚至软件无法看到")...
-- 这个解释也有够奇怪: 如果 software 都无法 read 到这个 register 的瞬变 0x00 -> 0x01 -> 0x00 的过程, 那么为什么要保留... 说出疑惑与诸君知道.
不过这句话也恰好解答了, 为何 Reset 被编入 Exception number, 作为 No.1, 但是又不进入 ISR.
当然, 通过软件对 exception vector 的实现, 以及对 .bin 的认知, 反过来或者可以理解为, RESET 已经触发了 ISR... 但是这个 ISR 并没有指向 default_isr, 而是如我们前文中提到的 PC 指针, 标志着一次 RESET 引起 SYSTEM BOOT 过程的开始``` 罗列此刻思路, 与诸君共赏```
[图例8: 尽管 RESET 改变了 IPSR, 但是这个过程"瞬变而不被软件知道", From A_RM]
allen_zhan_752827529 2013-8-14 11:16
用户1173460 2010-11-19 20:32
roumao_411466022 2010-11-19 13:41
用户1012868 2010-6-27 19:35
用户1277994 2010-4-12 16:21