ARM篇八--中断实验
一、前言
本篇使用华清远见的FS4412开发板,进行中断实验,知识包括CPU与硬件的交互方式、中断的原理、中断的实现,本篇通过中断信号的产生,中断信号的传递、中断程序的实现来对中断进行详细讲解。
二、准备工作
FS4412开发板,我用的是华清远见的FS4412开发板- ubuntu下linux操作系统(包含交叉编译工具)
serial-com8程序烧入执行工具- 开发板电路原理图、芯片手册
- 开发程序模板
开发板资料、交叉编译工具、程序烧入工具下载(包含教程)
开发板可选择到华清远见官网进行租赁。
工程模板文件下载
三、CPU与硬件的交互方式
轮询
CPU执行程序时不断地询问硬件是否需要其服务,若需要则给予其服务,若不需要一段时间后再次询问,周而复始中断
CPU执行程序时若硬件需要其服务,对应的硬件给CPU发送中断信号,CPU接收到中断信号后,将当前的程序暂停下来,转而去执行中断服务程序,执行完成后再返回到被打断的点继续执行DMA
硬件产生数据后,硬件控制器可将产生的数据直接写入到存储器中,整个过程无需CPU的参与
四、轮询方式实现按键实验
1、电路原图


2、按键的实现
在【Linux】ARM篇四–简单使用汇编点亮LED灯这篇文章中以讲述了GPIO寄存器,这里不在讲述,直接上代码,实验现象在文章最末。
/*
* @Author: 修成真
* @Date: 2022-04-22 18:55:45
* @LastEditTime: 2022-04-22 18:58:46
*/
#include "exynos_4412.h"
#define LED2_ON (GPX2.DAT |= (1 << 7)) //LED2亮
#define LED2_OFF GPX2.DAT &= (~(1 << 7)) //LED2灭
int main()
{
int i = 1;
/* 设置按键引脚为输入模式 */
GPX1.CON &= (~(0xF << 8));
/* 配置LED2的引脚为输出模式 */
GPX2.CON |= (0x1 << 28);
while(1)
{
/*不断检查按键是否按下,按键按下LED电平翻转*/
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

0:外设中断触发使能1:外设触发屏蔽
因此配置程序为:
/* 配置key2引脚为中断模式 */
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寄存器
功能:中断的总开关
0: 忽略外部所用中断1: 监控外部中断
2、ICDISER寄存器
功能:对应外设中断通道开关


0:关闭对应通道中断1:打开对应通道中断
一个寄存器只有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);
/* 选择对应CPU处理中断 CPU0 */
ICDIPTR.ICDIPTR14 = ICDIPTR.ICDIPTR14 & (~(0xFF << 16)) | (0x01 << 16);
/* 打开中断处理器与CPU之间的通道开关 */
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。

- 相应位
置1,清除挂起状态。
3、ICCEOIR寄存器
功能通知中断控制器CPU已经执行完中断

0~9位:写入中断ID。
因此,在interface.c里写中断执行函数:
void do_IRQ()
{
unsigned int interrupt_id = 0;
interrupt_id = CPU0.ICCIAR & 0x3FF; //获得中断id
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;
/* 取消挂起状态,相应位置1 */
EXT_INT41_PEND = (1 << 2);
/* 通知中断控制器CPU已经执行完中断 */
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; //获得中断id
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;
/* 取消挂起状态,相应位置1 */
EXT_INT41_PEND = (1 << 2);
/* 通知中断控制器CPU已经执行完中断 */
CPU0.ICCEOIR = CPU0.ICCEOIR & (~0x3FF) | 58;
break;
// ... ...
case 159:
break;
default:
break;
}
}
void key3_init()
{
//外设中断配置
/* 配置key2引脚为中断模式 */
GPX1.CON |= (0xF << 8);
/* 配置中断触发方式,下降沿触发 */
EXT_INT41_CON = EXT_INT41_CON & (~(0x7)) | (0x2 << 8);
/* 打开外设中断开关 */
EXT_INT41_MASK &= (~(1 << 2));
//中断处理器配置
/* 打开外设与中断处理器之间的通道开关 */
ICDDCR |= 1;
/* 打开对应外设通道中断开关 */
ICDISER.ICDISER1 |= (1 << 26);
/* 选择对应CPU处理中断 CPU0 */
ICDIPTR.ICDIPTR14 = ICDIPTR.ICDIPTR14 & (~(0xFF << 16)) | (0x01 << 16);
/* 打开中断处理器与CPU之间的通道开关 */
CPU0.ICCICR |= 1;
}
int main()
{
/* 配置LED2、LED3的引脚为输出模式 */
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、中断按键实验现象

本文介绍了在华清远见FS4412开发板上进行中断实验的过程,从CPU与硬件交互的三种方式(轮询、中断、DMA)到具体实现GPIO中断。通过配置GPIO寄存器、中断控制器,实现了按键中断服务程序,详细解析了中断控制器的各个寄存器功能,并给出了完整的中断服务代码和烧录步骤。


929

被折叠的 条评论
为什么被折叠?



