中级会员
- 积分
- 254
- 金钱
- 254
- 注册时间
- 2020-3-22
- 在线时间
- 35 小时
|
本帖最后由 YOKI 于 2020-4-25 01:51 编辑
/********************************************/
时间 :2020/04/22/
作者 :YOKI
导师 :正点原子 左忠凯
硬件 :正点原子 ALPHA I.MX LINUX 开发板
写在前面的话:中断函数涉及到 ARM Cortex-A7内核 构架,建议学习本视频前回到第6章去复习一下构架的有关知识
/*******************************************/
GPIO 中断设置
KEY0和 GPIO1_I018 硬件连接
GPIO中断由 GPIOx_ICR1/2 寄存器(32位)控制 以2个bit 控制1个 IO
以 GPIO1_IO18 为例: 对应(ICR18)为 GPIO2_ICR1 的bit4-bit5控制
触发方式:
00 : 低电平
01 : 高电平
10 : 上升沿
11 : 下降沿
GPIOx_IMR (GPIOx中断使能寄存器(32位)) 每1位对应1个 GPIOx IO口
GPIOx_IMR bit18 置1 时使能 GPIO1_I018中断
GPIOx_EDGE_SEL(边沿检测寄存器(32位))也可设置触发方式,且会覆盖GPIOx_ICR1/2寄存器设置
当 GPIO1_EDGE_SEL 的 bit8 置1 时 可检测 GPIO1_IO18 的边沿变化情况上升延下降延都会触发中断
GPIOx_ISR (GPIOx中断标记寄存器(32位))
当 GPIO1_IO18 的中断触发时 GPIOx_ISR bit18 会被标记为 1
软件读取 GPIOx_ISR的值 可以获取当前有哪些 I/O 口触发了中断,中断执行完成后可由用户用软件清除中断标志位
清除(对GPIOx_ISR寄存器的bit 写1 清零)
GIC (通用中断控制) 设置
1、使能相应的 中断ID: GPIO1_IO18 对应 中断ID 99(32+67)
2、优先级 (本节未设置)
在 bsp_gpio.c 中添加 gpio中断设置相关的函数
/***************** GPIOx中断使能**************************/
/* 参数1:GPIOx GPIO口名称 如:GPIO1、GPIO5 */
/* 参数2:pin 引脚号 如: 18 、1 */
/* 例如:GPIO1_IO18 中断使能: */
/* GPIOx_interrupt_enable(GPIO1,18,1); */
/*******************************************************/
void GPIOx_interrupt_enable(GPIO_Type *GPIOx, uint8_t pin)
{
GPIOx->IMR |= (1<<pin); // GPIOx_IMR 对应位 置1 使能
}
/***************** GPIOx中断禁止**************************/
/* 参数1:GPIOx GPIO口名称 如:GPIO1、GPIO5 */
/* 参数2:pin 引脚号 如: 18 、1 */
/* GPIOx_interrupt_disable(GPIO1,18,1); */
/*******************************************************/
void GPIOx_interrupt_disable(GPIO_Type *GPIOx, uint8_t pin)
{
GPIOx->IMR &= ~(1<<pin); // GPIOx_IMR 对应位 置0 禁止
}
/***************** GPIOx中断 标志位清除函数*****************/
/* 参数1:GPIOx GPIO口名称 如:GPIO1、GPIO5 */
/* 参数2:pin 引脚号 如: 18 、1 */
/* GPIOx_clearintflag(GPIO1,18); */
/*******************************************************/
void GPIOx_clearintflag(GPIO_Type *GPIOx, uint8_t pin)
{
GPIOx->ISR |= (1 << pin); // GPIOx_ISR 对应位 写1 清除
}
/***************** GPIOx中断初始化(触发方式设置)*************/
/* 参数1:GPIOx GPIO口名称 如:GPIO1、GPIO5 */
/* 参数2:pin 引脚号 如: 18 、1 */
/* 参数3:MODE 触发模式 */
/* 如: Low High Rising_EDGE Falling_EDGE Edge */
/* 低电平 高电平 上升沿 下降沿 跳变沿 */
/* 例如:设置GPIO1_IO18为低电平触发模式: */
/* GPIOx_interrupt_activeMODE(GPIO1,18,Low); */
/*********************************************************/
void GPIOx_interrupt_activeMODE(GPIO_Type *GPIOx, uint8_t pin, uint8_t MODE)
{
(GPIOx->EDGE_SEL) &= ~(1<<pin); // 先将GPIO_EDGE_SEL清零 使 GPIO_ICR 寄存器有效
if( pin >= 0 && pin < 16 )
{ (GPIOx->ICR1) &= ~(0x3<<(pin*2)); // 先将GPIO_ICR1 对应位清零
switch (MODE)
{
case Low : (GPIOx->ICR1) |= (MODE<<(pin*2)); break; // 低电平触发 写 00
case High : (GPIOx->ICR1) |= (MODE<<(pin*2)); break; // 高电平触发 写 01
case Rising_EDGE : (GPIOx->ICR1) |= (MODE<<(pin*2)); break; // 低电平触发 写 00
case Falling_EDGE : (GPIOx->ICR1) |= (MODE<<(pin*2)); break; // 高电平触发 写 01
case Edge : (GPIOx->EDGE_SEL) |= (1<<pin); break; // 跳变沿触发
default: break;
}
}
if( pin >= 16 && pin < 32 ) // pin值大于15
{ pin -=16; // 因为 ICR2 也是32位 2个bit 决定一个IO口 因此要把 pin-16 作为一个操作数
(GPIOx->ICR2) &= ~(0x3<<(pin*2)); // 先将GPIO_ICR2 对应位清零
switch (MODE)
{
case Low : (GPIOx->ICR2) |= (MODE<<(pin*2)); break; // 低电平触发 写 00
case High : (GPIOx->ICR2) |= (MODE<<(pin*2)); break; // 高电平触发 写 01
case Rising_EDGE : (GPIOx->ICR2) |= (MODE<<(pin*2)); break; // 低电平触发 写 00
case Falling_EDGE : (GPIOx->ICR2) |= (MODE<<(pin*2)); break; // 高电平触发 写 01
case Edge : (GPIOx->EDGE_SEL) |= (1<<(pin+16)); break; // 跳变沿触发
default: break;
}
}
}
这个函数实际上就是GPIOx中断初始化函数,视频中关于 跳变沿触发时有一个错误,
在引脚号为16-31时,因为 ICR2 也是32位 2个bit 决定一个IO口 因此要把 pin-16 作为一个操作数。
但是 EDGE_SEL 也是32位寄存器,却是由1个bit决定一个IO口 因此要把 先前剪掉的pin-16再加回来即:
case Edge : (GPIOx->EDGE_SEL) |= (1<<(pin+16)); break; // 跳变沿触发
在 bsp_exi.c 中编写 具体的外部中断服务函数
/* 初始化外部中断 GPIO1_IO18 */
void exti_init()
{
GPIOx_init(GPIO1,18,input); // 引脚复用为 GPIO1_IO18功能,且为输入模式
GPIOx_interrupt_activeMODE(GPIO1,18,Low); // 将 GPIO1_IO18 中断触发模式设置为低电平触发
GIC_EnableIRQ(GPIO1_Combined_16_31_IRQn); //GICC 中断ID 99(32+67)使能
Systerm_register_IRQ_Handler(GPIO1_Combined_16_31_IRQn, GPIO1_IO18_IRQ_handler, NULL); //注册 GPIO1_IO18_IRQ_handler 函数
GPIOx_interrupt_enable(GPIO1,18);
}
在初始化外部中断函数中主要做了一下工作:
1、引脚复用
2、中断触发方式设置(GPIOx中断初始化)
3、中断控制器 使能中断号
4、注册中断服务函数
5、使能GPIO中断
/* 中断响应函数 */
void GPIO1_IO18_IRQ_handler(unsigned int gicc_IAR, void *User_param)
{
int interrupt_over = 0;
interrupt_over = led_beep (200,5);
// interrupt_over = gpio1_io18_beep();
if( interrupt_over == 1)
{
GPIOx_clearintflag(GPIO1,18); // GPIO1_IO18 中断 标志位清除函数
GIC_DeactivateIRQ(GPIO1_Combined_16_31_IRQn); //向 GICC_EOIR 写入发送中断的中断号来释放中断
}
}
这个中断响应函数是一个具体的处理函数
和视频中有差别的是我采取了 调用效果函数+查询返回值 的方式
当效果函数执行完成时 返回完成标志, 中断响应函数查询返回值,确认中断响应结束后清除中断标志位并释放中断(自我感觉这样正规一点)
GIC_DeactivateIRQ(uint32_t value)是 core_ca7.h 中的函数,作用是向 GICC_EOIR(中断结束标志寄存器) 写入数据
写入参数为 中断ID
/********************** 中断效果函数 ***********************/
/* 参数1:cycle LED闪烁周期 */
/* 参数2:num LED闪烁次数 */
/* led_beep (100,10) */
/* 闪烁10次,亮灭时间各100ms */
/*********************************************************/
int led_beep (volatile unsigned int cycle, int num)
{
for(; num>0 ; num-- )
{
led_ON();
delay(cycle);
led_OFF();
delay(cycle);
}
beep_ON();
delay(150);
beep_OFF();
return (1);
}
这个就是按键按下的效果,大家自由发挥了
/**********************************************************************************************/
个人总结: 中断系统 精讲注释
到这里整个中断系统就结束拉(漏掉了优先级部分)回顾一下整个过程:
1、首先回顾了 根据第六章 ARM Cortex-A7内核 构架 的内容了解了9种运行模式,
重点: 通用中断控制器 GIC 和用于控制GIC的命令 CP15协处理器命令
2、复位中断函数 Reset_Handler 编写:中断向量表和中断向量偏移设置
3、IRQ 外部中断服务函数 IRQ_Interrupt_Handler 编写: 现场保护+调用C语言中断服务函数+现场恢复(压栈 出栈)
4、通用中断服务函数 system_IRQ_handler (unsigned int gicc_IAR) 编写
重点: 中断处理函数表、C语言环境下使用CP15协处理器、中断ID和中断函数注册
5、gpio相关中断设置:中断触发方式,GPIO中断使能,GPIO中断清除
6、具体中断响应(效果)函数编写
/**********************************************************************************************/
最近学习和更新的速度明显放缓了,一方面是因为复工后学习的时间变短了,还有就是被自己前面的一个错误困扰了好几天,
左萌主视频里关于 清BSS 段的坑虽然我提前避开了(机智如我)但是却犯了一个低级错误前一篇中在进入中断保护现场时我
把寄存器 r12 误写成了 r2,导致中断执行后一直回不到正确的程序点,查找了好几天 >_<!
IRQ_Interrupt_Handler:
push {lr} @ 保存 lr 地址 压栈 (存放IRQ_Interrupt_Handler 子程序返回地址) 压栈(栈号:0)
push {r0-r3,r2} (错误点) @ 保存 r0-r3, r12 寄存器 (保护现场) 压栈(栈号:1)
......
pop {r0-r3, r12} @ 出栈(栈号:1) 恢复中断执行前 r0-r3,r12 的值 (恢复现场) 出栈(栈号:1)
前面那篇帖子我就暂时不更正了,给自己留个教训!
我的代码虽然和视频中略有差别,但基本原理都是一致的,大家可以互相参考交流。
/**********************************************************************************************/
以上注释和代码都是在学习 《正点原子 linux 第二期 裸机开发视频 P30-P36 第15讲 中断系统 》 过程中
跟着 左萌主 学习并加入了一点点自己的思考写成的,作为一个小菜鸡,我大着胆子发出来跟大家一起记录学习
如果有帮助请大家自行参考、下载,如果 转载 请注明出处,并在论坛和我联系。如果有错误请大神指正!左萌主赛高!
作者 :YOKI
导师 :正点原子 左忠凯 (请允许我叫你一声导师吧)
硬件 :正点原子 ALPHA I.MX LINUX 开发板
/*********************************************************************************************/
|
|