从51到ARM裸机开发实验(009)LPC2138 中断实验

一、场景设计

        中断的概念在《从51到ARM裸机开发实验(007) AT89C51 中断实验》中已经介绍过,LPC2138的Keil工程创建在《从51到ARM裸机开发实验(005)LPC2138 GPIO实验》中已经介绍过。本次使用LPC2138来实现一个这样的场景:四个LED依次亮灭,时间间隔最小0.1秒,最大1秒,要求精确延时。使用两个按键分别控制间隔时间的增减,每按一次增或减0.1秒。精确延时用定时器中断实现,按键响应使用外部中断实现。(Protues仿真的时间和现实时间差距较大,可以注意Protues左下方的仿真时间或者接示波器观察曲线变化时间间隔)

二、LPC2138时钟系统

        从芯片手册可以看到,PCLK时钟(记住这个时钟,它是定时器发挥作用的前置条件)是由CCLK时钟经过分频而来,分频多少由寄存器VPBDIV决定。PCLK可以等CCLK或1/2CCLK或1/4CCLK。那么CCLK又是怎么来的呢?它是晶振或外部时钟源经PLL锁相环配置而来。

        关于寄存器更详细的解释见文末附件中的芯片数据手册。
本实验中的采用的晶振频率为10MHz,将系统配置为 Fosc = 10MHz , CCLK= 60MHz 。根据芯片手册的描述计算如下:
M=cclk/Fosc = 60MHz/10MHz = 6 。因此, M-1 = 5 写入 PLLCFG4:0 。
P 值可由 P=Fcco/(cclk*2) 得出, Fcco 必须在 156MHz~320MHz 内。假设 Fcco 取最低频率 156MHz ,则P=156MHz/(2*60MHz) = 1.3 。 Fcco 取最高频率可得出 P=2.67 。因此,同时满足 Fcco 最低和最高频率要求的 P 值只能为 2 。所以, PLLCFG=6:5=1 。

接下来配置VPBDIV,本实验中将PCLK配置为CCLK的1/4,即15MHz

三、LPC2138中断系统

        本实验中使用EINT0和EINT1两个中断。

EINT0使用P0.16 配置PINSEL1寄存器1:0位为01
EINT0使用P0.14 配置PINSEL0寄存器29:28位为10

        通过以下两个寄存器配置中断的触发方式:

重点内容:LPC2138不支持中断嵌套。LPC2138为ARM7TDMI内核。是一种ARM处理器架构,具体来说是ARMv4T微架构。在这种处理器中,如果当前处于一个高优先级中断处理程序中,而此时又发生了一个同级别或更低优先级的中断,那么处理器将不会立即进入这个新的中断处理程序,即不支持中断嵌套。这意味着如果一个中断服务程序(ISR)正在执行,并且在该ISR完成之前又发生了一个中断,这第二个中断将被忽略,直至当前的ISR执行完毕。
问题解决方法:① 优化中断处理程序:确保每个中断处理程序尽可能短小精悍,以减少中断处理时间,从而减少对中断嵌套的需求。② 使用轮询方式:如果确实需要在一个中断处理程序执行期间处理另一个中断,可以在ISR中设置一个标志,然后在ISR返回前将控制权交还给处理器,然后在主循环中或者一个低优先级中断中处理这个标志。③ 优先级重新配置:在系统设计中,可以为某些中断设置高的优先级,以确保在处理低优先级中断时不会被高优先级中断打断。④ 使用专用的嵌套向量中断控制器(NVIC):如果硬件和ARM核支持中断嵌套,可以配置NVIC来允许中断嵌套。
在实际应用中,通常会根据实际需求和系统资源,选择最合适的解决方案。如果系统对中断响应有严格的实时要求,且对中断处理时间有严格控制,那么可能需要重新评估系统设计,避免不必要的中断嵌套,或采取措施减少每个中断的处理时间。

LPC2138的中断实现方式:

①、通过VICVectAddrX指定某中断(此时还不知道是哪个中断)发生时的执行函数。

②、通过VICVectCntlX控制寄存器,启用编号为X的中断,并将某VIC通道的中断(指定哪个VIC通道号就是哪个中断)和X中断进行关联。

③、通过VICIntEnable配置启用某中断(VICIntEnable中的位和X存在对应关系)。

四、仿真电路

        注意仿真时间线和现实时间并不一致,比如现实中可能过去三五秒了,仿真时间才过去1秒。仿真时间线注意仿真软件左下角即可。比如本实验中最长延时LED 1秒切换一次,是指Protues的仿真时间线过去1秒才会切换,与现实时间无关。

五、程序设计

1、驱动程序

interrupt.h

#ifndef _INTERRUPT_H_
#define _INTERRUPT_H_
#include "lpc2138.h"void set_interrupt_callback(int interrupt_num, void* isr_callback);
void init_timer0_isr();
void init_external_isr();
#endif

lpc2138.h 里面主要是寄存器到内存地址的映射配置,也可以不使用此文件,自己根据芯片手册配置寄存器。 

interrupt.c

#include "interrupt.h"
#include "delay.h"void (*callback0)(); // 声明一个指向同样参数、返回值的函数指针类型
void (*callback1)(); // 声明一个指向同样参数、返回值的函数指针类型
void (*callback2)(); // 声明一个指向同样参数、返回值的函数指针类型//设置中断回调函数
void set_interrupt_callback(int interrupt_num, void* isr_callback){if(interrupt_num == 0){callback0 = isr_callback;}else if(interrupt_num == 1){callback1 = isr_callback;}else if(interrupt_num == 2){callback2 = isr_callback;}
}//定时器0中断事件处理
void timer0_isr(void) __irq {//清除中断T0IR = (1<<0);//中断应答VICVectAddr = 0;callback2();
}//cclk = 60MHz
void initPLL() {// 设置 PLL0CFG 寄存器,选择合适的 M 和 P 值PLLCFG |= ((1 << 5) |(1 << 2) | (1 << 0));// 启动 PLLPLLCON |= 0x01;// 等待 PLL 锁定while (!(PLLSTAT & (1 << 10)));// 选择 PLL 为系统时钟源PLLCON |= (1 << 1);PLLFEED = 0xAA;PLLFEED = 0x55;// 等待 PLL 切换完成while (!(PLLSTAT & (1 << 9)));
}//pclk = 15MHz
void initVPBdivider() {// 设置 VPBDIV 寄存器,选择 VPB 时钟分频比VPBDIV &= ~(0x03);
}//定时器中断初始化
void init_timer0_isr(){initPLL();initVPBdivider();// 定时器模式:每上升一次PCLK边T0CTCR = 0x00;// 预刻度寄存器:15 MHz PCLK, 15000-1得到毫秒T0PR = 14999;// 匹配寄存器:计时100毫秒即0.1秒T0MR0 = 100;// 每经过T0PR+1个PCLK周期,T0TC值增加1T0TC = 0;// 在MR0上中断和复位(T0TC值 = T0MR0值时触发中断和复位)T0MCR = (1<<0) | (1<<1);// 定时器0 ISR地址VICVectAddr3 = (unsigned long)timer0_isr;// 启用定时器0中断,使用槽位4VICVectCntl3 = (1<<5) | 4;// 在VIC中使能定时器0中断VICIntEnable = (1<<4);// 启动计时器0T0TCR = 0x01;
}// 定义外部中断处理函数0
void external_interrupt_handler0(void) __irq {delayms(10);EXTINT = 1 << 0; // 清除外部中断0的中断标志位VICVectAddr = 0;callback0();
}// 定义外部中断处理函数1
void external_interrupt_handler1(void) __irq {delayms(10);EXTINT = 1 << 1; // 清除外部中断1的中断标志位VICVectAddr = 0;callback1();
}//按键中断初始化
void init_external_isr(){// 配置外部中断引脚// 配置P0.14为ENIT1PINSEL0 |= (1 << 29);		//第29位配置为1PINSEL0 &= ~(0x01<<28); //第28位配置为0// 配置P0.16为ENIT0PINSEL1 |= (1 << 0);		//第0位配置为1PINSEL1 &= ~(0x01<<1);  //第1位配置为0IODIR0 &= ~(1<<16);// 配置外部中断触发方式EXTMODE |= (1 << 0) | (1 << 1); // 设置外部中断0和1为边沿触发模式EXTPOLAR |= (1 << 0) | (1 << 1); // 设置外部中断0和1为上升沿触发// 启用外部中断中断VICVectAddr0 = (unsigned)external_interrupt_handler0; // 设置中断处理函数0VICVectCntl0 |= (1 << 5) | 0x0E; 											// 设置为外部中断0并启用,EINT0中断编号为14VICIntEnable |= (1 << 14); 														// 启用外部中断0VICVectAddr1 = (unsigned)external_interrupt_handler1; // 设置中断处理函数1VICVectCntl1 |= (1 << 5) | 0x0F; 											// 设置为外部中断1并启用,EINT1中断编号为15VICIntEnable |= (1 << 15); 														// 启用外部中断1
}

led.h

#ifndef _LED_H_
#define _LED_H_#define PINSEL0 (*(volatile unsigned long *)0xE002C000)#define IO0PIN (*(volatile unsigned long *)0xE0028000)#define IO0DIR  (*(volatile unsigned long *)0xE0028008)void led_init();void led_on(unsigned char site);void led_off(unsigned char site);char get_led_status(unsigned char site);void led_operate(unsigned char site,unsigned char on_off);
#endif

led.c

#include "led.h"void led_init(){PINSEL0 = PINSEL0 & 0xffffff00;IO0DIR = IO0DIR | 0x0f;
}void led_on(unsigned char site){led_init();switch(site){case 0: IO0PIN &= ~(0x01);break;case 1:IO0PIN &= ~(0x01<<1);break;case 2: IO0PIN &= ~(0x01<<2);break;case 3: IO0PIN &= ~(0x01<<3);break;default:break;}
}void led_off(unsigned char site){led_init();switch(site){case 0: IO0PIN |= (0x01); 	 break;case 1:IO0PIN |= (0x01<<1); break;case 2: IO0PIN |= (0x01<<2); break;case 3: IO0PIN |= (0x01<<3); break;default:break;}
}char get_led_status(unsigned char site){switch(site){case 0: return (IO0PIN >> 0) & (0x01);case 1:return (IO0PIN >> 1) & (0x01);case 2: return (IO0PIN >> 2) & (0x01);case 3: return (IO0PIN >> 3) & (0x01);default:return -1;}
}
//on_off 0:on 1:off
void led_operate(unsigned char site,unsigned char on_off){if(on_off == 0){led_on(site);}else if(on_off == 1){led_off(site);}
}

delay.h

#ifndef _DELAY_H_
#define _DELAY_H_void delayms(unsigned int xms);
#endif

delay.c

#include "delay.h"void delayms(unsigned int xms){unsigned int i,j;for(i=xms;i>0;i--){for(j=2500;j>0;j--);}
}

2、应用程序

application.h

#ifndef _APPLICATION_H_
#define _APPLICATION_H_void timer0_callback();void external0_callback();void external1_callback();
#endif

application.c

#include "application.h"
#include "interrupt.h"#define uchar unsigned char
#define uint unsigned int
#define MAX_CYCLE 10
#define MIN_CYCLE 1uchar num = 0;
uchar time_cycle = 1;int main(void){led_operate(0,0);led_operate(1,1);led_operate(2,1);led_operate(3,1);init_timer0_isr();init_external_isr();set_interrupt_callback(0,external0_callback);set_interrupt_callback(1,external1_callback);set_interrupt_callback(2,timer0_callback);while(1);
}//外部中断0回调函数
void external0_callback(){if(time_cycle > MIN_CYCLE){time_cycle = time_cycle - 1;}
}//外部中断1回调函数
void external1_callback(){if(time_cycle < MAX_CYCLE){time_cycle = time_cycle + 1;}
}//定时器0中断回调函数
void timer0_callback(){num++;if(num>=time_cycle){num=0;if(get_led_status(0) == 0){led_operate(0,1);led_operate(1,0);}else if(get_led_status(1) == 0){led_operate(1,1);led_operate(2,0);}else if(get_led_status(2) == 0){led_operate(2,1);led_operate(3,0);}else if(get_led_status(3) == 0){led_operate(3,1);led_operate(0,0);}}
}

六、资料下载

源码&仿真电路&芯片手册下载地址:https://download.csdn.net/download/qq_54140018/89142182

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/818587.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

测试人必看,小程序常见问题

小程序是一种轻盈的存在&#xff0c;用户无需为了使用它而下载和安装。它依附于微信这个强大的平台&#xff0c;只需轻轻一扫或一搜&#xff0c;它便跃然屏上&#xff0c;随时服务。小程序为我们带来更多前所未有的惊喜和便利&#xff0c;以下分享关于小程序相关的热门问题。 …

链表基础3——单链表的逆置

链表的定义 #include <stdio.h> #include <stdlib.h> typedef struct Node { int data; struct Node* next; } Node; Node* createNode(int data) { Node* newNode (Node*)malloc(sizeof(Node)); if (!newNode) { return NULL; } newNode->data …

逆向IDA中Dword,数据提取

我们可以看见数据是这样的&#xff0c;第一个是1cc 但是我们shifte就是 这个因为他的数据太大了&#xff0c;导致高位跑后面去了 这个时候&#xff0c;我们右键——convert——dword 这样就可以提取到争取的数据了 比如第一个数据 0x1cc a0xcc b0x1 print(hex((b<<8…

HarmonyOS开发实例:【事件的订阅和发布】

介绍 本示例主要展示了公共事件相关的功能&#xff0c;实现了一个检测用户部分行为的应用。具体而言实现了如下几点功能&#xff1a; 1.通过订阅系统公共事件&#xff0c;实现对用户操作行为&#xff08;亮灭屏、锁屏和解锁屏幕、断联网&#xff09;的监测&#xff1b; 2.通…

vmware安装ubuntu-18.04系统

一、软件下载 百度网盘&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1fK2kygRdSux1Sr1sOKOtJQ 提取码&#xff1a;twsb 二、安装ubuntu系统 1、把ubuntu-18.04的压缩包下载下来&#xff0c;并且解压 2、打开vmware软件&#xff0c;点击文件-打开 3、选择我们刚刚解…

6. Django 深入模板

6. 深入模板 6.1 Django模板引擎 Django内置的模板引擎包含模板上下文(亦可称为模板变量), 标签和过滤器, 各个功能说明如下: ● 模板上下文是以变量的形式写入模板文件里面, 变量值由视图函数或视图类传递所得. ● 标签是对模板上下文进行控制输出, 比如模板上下文的判断和循…

项目7-音乐播放器1+BCrypt加密

1.创建项目 1.1 引入依赖 1.2 yml相关配置 application.yml spring:profiles:active: prod mybatis:mapper-locations: classpath:mapper/**Mapper.xmlconfiguration:map-underscore-to-camel-case: true #配置驼峰⾃动转换log-impl: org.apache.ibatis.logging.stdout.StdO…

openGauss学习笔记-261 openGauss性能调优-使用Plan Hint进行调优-将部分Error降级为Warning的Hint

文章目录 openGauss学习笔记-261 openGauss性能调优-使用Plan Hint进行调优-将部分Error降级为Warning的Hint261.1 功能描述261.2 语法格式261.3 示例261.3.1 忽略非空约束261.3.2 忽略唯一约束261.3.3 忽略分区表无法匹配到合法分区261.3.4 更新/插入值向目标列类型转换失败 o…

【算法】数组元素循环右移k位,并要求只用一个元素大小的附加存储,元素移动或交换次数为O(n)

两种写法思路&#xff1a; 思路一&#xff1a;三次倒置 前言&#xff1a;C/C函数 reverse 是 左闭右开区间的&#xff0c;作用是将指定范围数组元素全部倒置&#xff0c;数组从 0 开始&#xff0c;这里主要讲解思路&#xff0c;就直接用 函数 reverse 简化过程 这个方法 实现 …

vue3第十八节(diff算法)

引言&#xff1a; 上一节说了key的用途&#xff0c;而这个key属性&#xff0c;在vue的vnode 中至关重要&#xff0c;直接影响了虚拟DOM的更新机制&#xff1b; 什么场景中会用到diff算法 如&#xff1a;修改响应式属性需要重新渲染页面&#xff0c;会重新执行render渲染函数返…

为了执行SQL语句,MySQL的架构是怎样设计的

1. 把MySQL当个黑盒子一样执行SQL语句 上一讲我们已经说到&#xff0c;我们的系统采用数据库连接池的方式去并发访问数据库&#xff0c;然后数据库自己其实也会维护一个连 接池&#xff0c;其中管理了各种系统跟这台数据库服务器建立的所有连接 我们先看下图回顾一下 当我们的…

数据可视化-ECharts Html项目实战(12)

在之前的文章中&#xff0c;我们深入学习ECharts特殊图表中的矩形树图以及Echarts中高级功能的多图表联动。想了解的朋友可以查看这篇文章。同时&#xff0c;希望我的文章能帮助到你&#xff0c;如果觉得我的文章写的不错&#xff0c;请留下你宝贵的点赞&#xff0c;谢谢。 数…

Directory Monitor:全方位监控文件系统变动的专业利器

目录 一、软件介绍 二、软件功能 三、软件特点 四、安装说明 五、使用说明 一、软件介绍 Directory Monitor是一款强大易用的实时文件系统监视工具&#xff0c;它由Michael Humpa开发&#xff0c;专为满足用户监控特定目录下文件和子目录变化的需求。无论是为了保障系统安…

python与设计模式之工厂模式的那些事儿

一、工厂模式 工厂模式实现了按需创建的最佳模式&#xff0c;其目的是为了隐藏创建类的细节与过程&#xff0c;通过一个统一的接口来创建所需的对象。 话说没了皇位争夺权的皇三接到了一个外征的工作&#xff0c;始皇给了5个亿的经费让皇三组建一个军队。打权总是要进行武器采…

【Java开发指南 | 第二篇】标识符、Java关键字及注释

读者可订阅专栏&#xff1a;Java开发指南 |【CSDN秋说】 文章目录 标识符Java关键字Java注释 标识符 Java 所有的组成部分都需要名字。类名、变量名以及方法名都被称为标识符。 所有的标识符都应该以字母&#xff08;A-Z 或者 a-z&#xff09;,美元符&#xff08;$&#xff0…

CentOS 7安装、卸载MySQL数据库

说明&#xff1a;本文介绍如何在CentOS 7操作系统下使用yum方式安装MySQL数据库&#xff0c;及卸载&#xff1b; 安装 Step1&#xff1a;卸载mariadb 敲下面的命令&#xff0c;查看系统mariadb软件包 rpm -qa|grep mariadb跳出mariadb软件包信息后&#xff0c;敲下面的命令…

【Qt】:事件的处理

系统相关 一.鼠标事件二.键盘事件三.定时器 事件是应用程序内部或者外部产生的事情或者动作的统称。在Qt中使用一个对象来表示一个事件。所有的Qt事件均继承于抽象类QEvent。事件是由系统或者Qt平台本身在个同的的刻友出的。当用广投下鼠标、敲下键盘&#xff0c;或者是窗口需要…

第四百六十二回

文章目录 1. 概念介绍2. 实现方法3. 示例代码4. 内容总结 我们在上一章回中介绍了"关于MediaQuery的优化"相关的内容&#xff0c;本章回中将介绍readMore这个三方包.闲话休提&#xff0c;让我们一起Talk Flutter吧。 1. 概念介绍 我们在本章回中介绍的readMore是一个…

【团体程序设计天梯赛 往年关键真题 25分题合集 详细分析完整AC代码】(L2-001 - L2-024)搞懂了赛场上拿下就稳了

L2-001 紧急救援 最短路路径打印 样例 输入1 4 5 0 3 20 30 40 10 0 1 1 1 3 2 0 3 3 0 2 2 2 3 2输出1 2 60 0 1 3分析 用一遍dijkstra算法。设立 n u m [ i ] num[i] num[i]和 w [ i ] w[i] w[i]表示从出发点到i结点拥有的路的条数&#xff0c;以及能够找到的救援队的数目…

Websocket (帧格式, 握手过程, Spring 中使用 WebScoket 协议)

什么是 WebSocket 客户端 A 和客户端 B 的消息传播需要借助服务器的中转 (原因是内网不能给另一个局域网的内网直接联通, 需要借助服务器的外网做 “中介”) (NAT 地址转换) Http 协议 不支持实时通讯 (或者说不支持服务器主动推送数据给客户端) TCP 本身是具有服务器推送数据这…