一、前言
本篇使用华清远见的FS4412 开发板,进行中断实验 ,知识包括CPU与硬件的交互方式、中断的原理、中断的实现,本篇通过中断信号的产生 ,中断信号的传递 、中断程序的实现 来对中断进行详细讲解。
二、准备工作
FS4412开发板 ,我用的是华清远见的FS4412开发板- ubuntu下linux操作系统(包含交叉编译工具)
serial-com8 程序烧入执行工具- 开发板电路原理图、芯片手册
- 开发程序模板
开发板资料、交叉编译工具、程序烧入工具下载(包含教程) 开发板可选择到华清远见官网进行租赁。 工程模板文件下载
三、CPU与硬件的交互方式
轮询 CPU执行程序时不断地询问硬件是否需要其服务 ,若需要则给予其服务,若不需要一段时间后再次询问,周而复始中断 CPU执行程序时若硬件需要其服务,对应的硬件给CPU发送中断信号 ,CPU接收到中断信号后,将当前的程序暂停下来,转而去执行中断服务程序,执行完成后再返回到被打断的点继续执行DMA 硬件产生数据后,硬件控制器可将产生的数据直接写入到存储器中 ,整个过程无需CPU的参与
四、轮询方式实现按键实验
1、电路原图
2、按键的实现
在【Linux】ARM篇四–简单使用汇编点亮LED灯这篇文章中以讲述了GPIO寄存器,这里不在讲述,直接上代码,实验现象在文章最末。
#include "exynos_4412.h"
#define LED2_ON (GPX2.DAT |= (1 << 7))
#define LED2_OFF GPX2.DAT &= (~(1 << 7))
int main()
{
int i = 1;
GPX1.CON &= (~(0xF << 8));
GPX2.CON |= (0x1 << 28);
while(1)
{
if(!(GPX1.DAT & (1 << 2)))
{
if(i % 2 == 1)
LED2_ON;
else
LED2_OFF;
while(!(GPX1.DAT & (1 << 2)));
i++;
}
else
{
}
}
return 0;
}
五、RAM中断实现原理
由外设硬件产生中断信号,中断信号经过中断控制器传递给CPU处理。
Exynos4412 的外设硬件 产生的中断被视为FIQ或者IRQ的异常信号 Exynos4412 总共160个中断,包括16个软件生成中断(SGIs)、16个私有外围中断(PPIs)和 128个支持共享外围中断(spi) 。Exynos4412 有四个CPU处理器,下面中断实验统一将中断信号发送给CPU0
关于RAM的异常处理,在这篇文章中有详细讲解:【Linux】ARM篇二–寄存器组织与异常处理
六、GPIO中断
外设硬件中断的产生方式 可以通过控制相应寄存器产生,这里已GPIO外部引脚中断 为例,配置中断的产生。
1、GPX1CON
按键k3 是GPX1_2引脚 ,配置相应位,使引脚为中断模式。
2、EXT_INT41CON寄存器
功能:中断触发方式设置 40-GPX0, 41-GPX1, 42-GPX2, 43-GPX3 GPX1_2引脚对应的是EXT_INT41CON[2] ,设置该四位为下降沿触发 ,(根据按键原理图,引脚接高电平,按键按下时,变为引脚电平变为低电平,所以为下降沿)。
3、EXT_INT41_MASK寄存器
功能:中断触发开关 40-GPX0, 41-GPX1, 42-GPX2, 43-GPX3
因此配置程序 为:
GPX1.CON |= (0xF << 8);
EXT_INT41_CON = EXT_INT41_CON & (~(0x7)) | (0x2 << 8);
EXT_INT41_MASK &= (~(1 << 2));
七、中断控制器
外设产生的中断信号需要通过中断控制器,才能将信号发送给CPU。
有人会问,为啥不直接把信号传递给CPU?
主要是中断控制器有这些功能:
- 多个中断同时产生时,可对这些中断挂起
排队 ,然后按照优先级依次发送给CPU处理 - 可以为每一个中断分配一个
优先级 - 一个中断正在处理时若又产生其它中断,可将新的中断挂起,待CPU空闲时再发送
- 可以为每一个中断选择一个CPU处理
- 可以为每一个中断选择一个
中断类型(FIQ或IRQ) CPU接收到中断信号后并不能区分是哪个外设产生的,此时CPU可查询中断控制器 - 来获取当前的中断信号是由哪个硬件产生的,然后再进行对应的处理
可以打开或禁止每一个中断
可以在(4412手册完整版)SEC_Exynos 4412 SCP_Users Manual_Ver.0.10.00_Preliminary ,第749页,找到对应外设中断信号的ID
1、ICDDCR寄存器
功能:中断的总开关
2、ICDISER寄存器
功能:对应外设中断通道开关
一个寄存器只有32位,但是我们有160个中断,因此用了5个寄存器来分配相应的中断位。(根据中断ID号 寻找对应的位)
3、ICDIPTR寄存器
功能:选择哪个CPU处理中断
- 只有寄存器只有
0-7控制 ,每一位控制一个寄存器的选择,因此可支持8个CPU处理(8核),对应着CPU0-CPU8 - 位
置1 时,选择该位对应的寄存器处理中断,例如:值为0x3时,中断被发送到处理器0和处理器1。 - 一个寄存器可以控制4个外设中断的选择,有160中断,因此需要40个寄存器控制
对应表如下:
本实验中断控制器配置程序为:
ICDDCR |= 1;
ICDISER.ICDISER1 |= (1 << 26);
ICDIPTR.ICDIPTR14 = ICDIPTR.ICDIPTR14 & (~(0xFF << 16)) | (0x01 << 16);
CPU0.ICCICR |= 1;
八、中断的实现
ARMCPU对中断的处理跟ARM本身对异常的相应有关,详情看【Linux】ARM篇二–寄存器组织与异常处理RAM异常响应部分。 因此根据异常响应原理,我们在启动文件中,改变和添加相应的代码: 相应代码:
IRQ_handler:
SUB LR, LR, #4
STMFD sp!, {R0-R12, LR}
BL do_IRQ
LDMFD sp!, {R0-R12, PC}^
1、ICCIAR寄存器
功能:CPU向中断控制器中获得对应中断ID号 0~9位 存放着中断ID号。
2、EXT_INT41_PEND寄存器
功能:中断信号进入中断控制器后,会将相应的挂起并将信号发给CPU,挂起状态需要手动清除,否则会不断将信号发送给CPU。
3、ICCEOIR寄存器
功能通知中断控制器CPU已经执行完中断
因此,在interface.c里写中断执行函数:
void do_IRQ()
{
unsigned int interrupt_id = 0;
interrupt_id = CPU0.ICCIAR & 0x3FF;
switch(interrupt_id)
{
case 0:
break;
case 1:
break;
case 58:
flag++;
printf("%d\n", flag);
if(flag%2 == 1)
LED2_ON;
else
LED2_OFF;
EXT_INT41_PEND = (1 << 2);
CPU0.ICCEOIR = CPU0.ICCEOIR & (~0x3FF) | 58;
break;
case 159:
break;
default:
break;
}
}
九、完整代码与烧入
1、中断完整代码
#include "exynos_4412.h"
#define LED2_ON GPX2.DAT |= (1 << 7)
#define LED2_OFF GPX2.DAT &= (~(1 << 7))
#define LED3_ON GPX1.DAT |= (1 << 0)
#define LED3_OFF GPX1.DAT &= (~(1 << 0))
int flag = 0;
void delay(unsigned int i)
{
while(i--);
}
void do_IRQ()
{
unsigned int interrupt_id = 0;
interrupt_id = CPU0.ICCIAR & 0x3FF;
switch(interrupt_id)
{
case 0:
break;
case 1:
break;
case 58:
flag++;
printf("%d\n", flag);
if(flag%2 == 1)
LED2_ON;
else
LED2_OFF;
EXT_INT41_PEND = (1 << 2);
CPU0.ICCEOIR = CPU0.ICCEOIR & (~0x3FF) | 58;
break;
case 159:
break;
default:
break;
}
}
void key3_init()
{
GPX1.CON |= (0xF << 8);
EXT_INT41_CON = EXT_INT41_CON & (~(0x7)) | (0x2 << 8);
EXT_INT41_MASK &= (~(1 << 2));
ICDDCR |= 1;
ICDISER.ICDISER1 |= (1 << 26);
ICDIPTR.ICDIPTR14 = ICDIPTR.ICDIPTR14 & (~(0xFF << 16)) | (0x01 << 16);
CPU0.ICCICR |= 1;
}
int main()
{
GPX2.CON |= (0x1 << 28);
GPX1.CON |= (0x1 << 0);
key3_init();
while(1)
{
LED3_ON;
delay(1000000);
LED3_OFF;
delay(1000000);
}
return 0;
}
2、程序烧入步骤
1.输入make 生成.bin文件
2.将.bin文件 复制到共享文件夹 ① ② ③ 输入:
cp ARM_LED.bin /mnt/hgfs/cca
将文件复制到cc文件夹
3.打开serial-com8 ,连接开发板(COM2 接口),烧入程序 ①查看端口 ②打开serial-com8设置 ③接通开发板电源,迅速按enter 键,使其快速进入裸机模式。 ④烧入程序 输入:
loadb 40008000
选择可执行文件烧入 烧入成功:
⑤执行程序 输入:
go 40008000
十、实验现象
1、轮询按键实验现象
2、中断按键实验现象
|