OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 4388|回复: 1

<新手向>第二期 裸机开发 P30-P36 第15讲 中断系统 随堂笔记

[复制链接]

5

主题

17

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
254
金钱
254
注册时间
2020-3-22
在线时间
35 小时
发表于 2020-4-25 01:48:26 | 显示全部楼层 |阅读模式
本帖最后由 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 开发板
/*********************************************************************************************/

9_interrupt.rar

310.07 KB, 下载次数: 0

工程文件包

正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

2

主题

712

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
2178
金钱
2178
注册时间
2018-8-27
在线时间
258 小时
发表于 2020-4-25 13:05:52 | 显示全部楼层
楼主的学习状态很好啊!加油
森罗万象
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2024-11-25 09:32

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表