历史上的今天

今天是:2024年10月12日(星期六)

正在发生

2021年10月12日 | S3C2440裸机------异常与中断__定时器中断程序示例

发布者:平和宁静 来源: eefocus关键字:定时器中断 手机看文章 扫描二维码
随时随地手机看文章

1.定时器的工作原理

注意当TCNTn=TCMPn时不会产生中断。 

2.代码

2.1 timer.c

 

首先是根据上面的两个寄存器设置时钟;


/* 设置TIMER0的时钟 */

/* Timer clk = PCLK / {prescaler value+1} / {divider value} 

         = 50000000/(99+1)/16

         = 31250

*/

TCFG0 = 99;  /* Prescaler 0 = 99, 用于timer0,1 */

TCFG1 &= ~0xf;

TCFG1 |= 3;  /* MUX0 : 1/16 */

然后根据上图的寄存器设置初始值。


/* 设置TIMER0的初值 */

TCNTB0 = 15625;  /* 0.5s中断一次 */

然后根据上面的寄存器设置加载初值。


/* 加载初值, 启动timer0 */

TCON |= (1<<1);   /* Update from TCNTB0 & TCMPB0 */

然后还是根据上图的寄存器,设置为自动加载并启动。


/* 设置为自动加载并启动 */

TCON &= ~(1<<1);

TCON |= (1<<0) | (1<<3);  /* bit0: start, bit3: auto reload */

然后我们实现定时器中断处理函数,在里面循环点灯。


void timer_irq(void)

{

/* 点灯计数 */

static int cnt = 0;

int tmp;

 

cnt++;

 

tmp = ~cnt;

tmp &= 7;

GPFDAT &= ~(7<<4);

GPFDAT |= (tmp<<4);

}

完整的timer.c如下


 

#include "s3c2440_soc.h"

 

void timer_init(void)

{

/* 设置TIMER0的时钟 */

/* Timer clk = PCLK / {prescaler value+1} / {divider value} 

             = 50000000/(99+1)/16

             = 31250

*/

TCFG0 = 99;  /* Prescaler 0 = 99, 用于timer0,1 */

TCFG1 &= ~0xf;

TCFG1 |= 3;  /* MUX0 : 1/16 */

 

/* 设置TIMER0的初值 */

TCNTB0 = 15625;  /* 0.5s中断一次 */

 

/* 加载初值, 启动timer0 */

TCON |= (1<<1);   /* Update from TCNTB0 & TCMPB0 */

 

/* 设置为自动加载并启动 */

TCON &= ~(1<<1);

TCON |= (1<<0) | (1<<3);  /* bit0: start, bit3: auto reload */

 

/* 设置中断 */

}

 

void timer_irq(void)

{

/* 点灯计数 */

static int cnt = 0;

int tmp;

 

cnt++;

 

tmp = ~cnt;

tmp &= 7;

GPFDAT &= ~(7<<4);

GPFDAT |= (tmp<<4);

}

 

2.2 interrupt.c

然后还要设置中断控制器,开启定时器中断。


#include "s3c2440_soc.h"

 

 

/* SRCPND 用来显示哪个中断产生了, 需要清除对应位

 * bit0-eint0

 * bit2-eint2

 * bit5-eint8_23

 */

 

/* INTMSK 用来屏蔽中断, 1-masked

 * bit0-eint0

 * bit2-eint2

 * bit5-eint8_23

 */

 

/* INTPND 用来显示当前优先级最高的、正在发生的中断, 需要清除对应位

 * bit0-eint0

 * bit2-eint2

 * bit5-eint8_23

 */

 

/* INTOFFSET : 用来显示INTPND中哪一位被设置为1

 */

 

/* 初始化中断控制器 */

void interrupt_init(void)

{

INTMSK &= ~((1<<0) | (1<<2) | (1<<5));

INTMSK &= ~(1<<10);  /* enable timer0 int */

}

 

/* 初始化按键, 设为中断源 */

void key_eint_init(void)

{

/* 配置GPIO为中断引脚 */

GPFCON &= ~((3<<0) | (3<<4));

GPFCON |= ((2<<0) | (2<<4));   /* S2,S3被配置为中断引脚 */

 

GPGCON &= ~((3<<6) | (3<<22));

GPGCON |= ((2<<6) | (2<<22));   /* S4,S5被配置为中断引脚 */

 

/* 设置中断触发方式: 双边沿触发 */

EXTINT0 |= (7<<0) | (7<<8);     /* S2,S3 */

EXTINT1 |= (7<<12);             /* S4 */

EXTINT2 |= (7<<12);             /* S5 */

 

/* 设置EINTMASK使能eint11,19 */

EINTMASK &= ~((1<<11) | (1<<19));

}

 

/* 读EINTPEND分辨率哪个EINT产生(eint4~23)

 * 清除中断时, 写EINTPEND的相应位

 */

 

 

void key_eint_irq(int irq)

{

unsigned int val = EINTPEND;

unsigned int val1 = GPFDAT;

unsigned int val2 = GPGDAT;

if (irq == 0) /* eint0 : s2 控制 D12 */

{

if (val1 & (1<<0)) /* s2 --> gpf6 */

{

/* 松开 */

GPFDAT |= (1<<6);

}

else

{

/* 按下 */

GPFDAT &= ~(1<<6);

}

}

else if (irq == 2) /* eint2 : s3 控制 D11 */

{

if (val1 & (1<<2)) /* s3 --> gpf5 */

{

/* 松开 */

GPFDAT |= (1<<5);

}

else

{

/* 按下 */

GPFDAT &= ~(1<<5);

}

}

else if (irq == 5) /* eint8_23, eint11--s4 控制 D10, eint19---s5 控制所有LED */

{

if (val & (1<<11)) /* eint11 */

{

if (val2 & (1<<3)) /* s4 --> gpf4 */

{

/* 松开 */

GPFDAT |= (1<<4);

}

else

{

/* 按下 */

GPFDAT &= ~(1<<4);

}

}

else if (val & (1<<19)) /* eint19 */

{

if (val2 & (1<<11))

{

/* 松开 */

/* 熄灭所有LED */

GPFDAT |= ((1<<4) | (1<<5) | (1<<6));

}

else

{

/* 按下: 点亮所有LED */

GPFDAT &= ~((1<<4) | (1<<5) | (1<<6));

}

}

}

 

EINTPEND = val;

}

 

 

void handle_irq_c(void)

{

/* 分辨中断源 */

int bit = INTOFFSET;

 

/* 调用对应的处理函数 */

if (bit == 0 || bit == 2 || bit == 5)  /* eint0,2,eint8_23 */

{

key_eint_irq(bit); /* 处理中断, 清中断源EINTPEND */

}

else if (bit == 10)

{

timer_irq();

}

 

/* 清中断 : 从源头开始清 */

SRCPND = (1< INTPND = (1<

}

 

3. 代码改进

上面的代码我们可以看到,当我们添加了定时器中断之后,我们要在interrupt.c里面重新添加定时器中断相关的配置,那么如果我们以后再添加别的中断,那么还要再修改interrupt.c,太麻烦了,下面我们用函数指针数组改进代码。


我们首先定义一个数组,用来保存中断处理函数的指针。


typedef void(*irq_func)(int);

irq_func irq_array[32];

然后我们实现一个注册函数,在这个注册函数里面把中断处理函数存到数组里面,并且使能中断。


void register_irq(int irq, irq_func fp)

{

irq_array[irq] = fp;

 

INTMSK &= ~(1<}

3.1 优化后的interrupt.c

#include "s3c2440_soc.h"

 

typedef void(*irq_func)(int);

irq_func irq_array[32];

 

 

/* SRCPND 用来显示哪个中断产生了, 需要清除对应位

 * bit0-eint0

 * bit2-eint2

 * bit5-eint8_23

 */

 

/* INTMSK 用来屏蔽中断, 1-masked

 * bit0-eint0

 * bit2-eint2

 * bit5-eint8_23

 */

 

/* INTPND 用来显示当前优先级最高的、正在发生的中断, 需要清除对应位

 * bit0-eint0

 * bit2-eint2

 * bit5-eint8_23

 */

 

/* INTOFFSET : 用来显示INTPND中哪一位被设置为1

 */

 

/* 初始化中断控制器 */

void interrupt_init(void)

{

INTMSK &= ~((1<<0) | (1<<2) | (1<<5));

INTMSK &= ~(1<<10);  /* enable timer0 int */

}

 

/* 读EINTPEND分辨率哪个EINT产生(eint4~23)

 * 清除中断时, 写EINTPEND的相应位

 */

 

 

void key_eint_irq(int irq)

{

unsigned int val = EINTPEND;

unsigned int val1 = GPFDAT;

unsigned int val2 = GPGDAT;

if (irq == 0) /* eint0 : s2 控制 D12 */

{

if (val1 & (1<<0)) /* s2 --> gpf6 */

{

/* 松开 */

GPFDAT |= (1<<6);

}

else

{

/* 按下 */

GPFDAT &= ~(1<<6);

}

}

else if (irq == 2) /* eint2 : s3 控制 D11 */

{

if (val1 & (1<<2)) /* s3 --> gpf5 */

{

/* 松开 */

GPFDAT |= (1<<5);

}

else

{

/* 按下 */

GPFDAT &= ~(1<<5);

}

}

else if (irq == 5) /* eint8_23, eint11--s4 控制 D10, eint19---s5 控制所有LED */

{

if (val & (1<<11)) /* eint11 */

{

if (val2 & (1<<3)) /* s4 --> gpf4 */

{

/* 松开 */

GPFDAT |= (1<<4);

}

else

{

/* 按下 */

GPFDAT &= ~(1<<4);

}

}

else if (val & (1<<19)) /* eint19 */

{

if (val2 & (1<<11))

{

/* 松开 */

/* 熄灭所有LED */

GPFDAT |= ((1<<4) | (1<<5) | (1<<6));

}

else

{

/* 按下: 点亮所有LED */

GPFDAT &= ~((1<<4) | (1<<5) | (1<<6));

}

}

}

 

EINTPEND = val;

}

 

 

void handle_irq_c(void)

{

/* 分辨中断源 */

int bit = INTOFFSET;

 

/* 调用对应的处理函数 */

irq_array[bit](bit);

/* 清中断 : 从源头开始清 */

SRCPND = (1< INTPND = (1<

}

 

void register_irq(int irq, irq_func fp)

{

irq_array[irq] = fp;

 

INTMSK &= ~(1<}

 

 

/* 初始化按键, 设为中断源 */

void key_eint_init(void)

{

/* 配置GPIO为中断引脚 */

GPFCON &= ~((3<<0) | (3<<4));

GPFCON |= ((2<<0) | (2<<4));   /* S2,S3被配置为中断引脚 */

 

GPGCON &= ~((3<<6) | (3<<22));

GPGCON |= ((2<<6) | (2<<22));   /* S4,S5被配置为中断引脚 */

 

/* 设置中断触发方式: 双边沿触发 */

EXTINT0 |= (7<<0) | (7<<8);     /* S2,S3 */

EXTINT1 |= (7<<12);             /* S4 */

EXTINT2 |= (7<<12);             /* S5 */

 

/* 设置EINTMASK使能eint11,19 */

EINTMASK &= ~((1<<11) | (1<<19));

 

register_irq(0, key_eint_irq);

register_irq(2, key_eint_irq);

register_irq(5, key_eint_irq);

}

 

3.2 优化后的timer.c

 

#include "s3c2440_soc.h"

 

void timer_irq(void)

{

/* 点灯计数 */

static int cnt = 0;

int tmp;

 

cnt++;

 

tmp = ~cnt;

tmp &= 7;

GPFDAT &= ~(7<<4);

GPFDAT |= (tmp<<4);

}

 

void timer_init(void)

{

/* 设置TIMER0的时钟 */

/* Timer clk = PCLK / {prescaler value+1} / {divider value} 

             = 50000000/(99+1)/16

             = 31250

*/

TCFG0 = 99;  /* Prescaler 0 = 99, 用于timer0,1 */

TCFG1 &= ~0xf;

TCFG1 |= 3;  /* MUX0 : 1/16 */

 

/* 设置TIMER0的初值 */

TCNTB0 = 15625;  /* 0.5s中断一次 */

 

/* 加载初值, 启动timer0 */

TCON |= (1<<1);   /* Update from TCNTB0 & TCMPB0 */

 

/* 设置为自动加载并启动 */

TCON &= ~(1<<1);

TCON |= (1<<0) | (1<<3);  /* bit0: start, bit3: auto reload */

 

/* 设置中断 */

register_irq(10, timer_irq);

}

关键字:定时器中断 引用地址:S3C2440裸机------异常与中断__定时器中断程序示例

上一篇:S3C2440裸机------Nor Flash原理及硬件操作
下一篇:S3C2440裸机------异常与中断__swi异常模示程序示例

推荐阅读

摘要: 以前是用RVDS 的IDE来烧写调试ARM程序的,不过RVDS虽然是集成化的调试工具调试起来方便,但是有的时候只知其一,不知其二,只知道按部就班的来点击按钮,忽略了一些本质性的东西。而且RVDS还有一个不好的地方是它只能在windows平台下运行,不支持Linux OS。为了便于学习Linux,使用Openocd会是个不错的选择,可以学习gnu 汇编,Makefile编写,...
据说,未来5至10年内,汽车的变化将超过过去50年内的变化。虽然有些变化可能与电动汽车的增长有关,不过汽车业内最大的变化仍在自动驾驶技术和网联技术领域,将会彻底改变整个驾驶体验。而造成这些变化的背后驱动力就是改善道路安全、降低排放以及减轻驾驶压力。安全检测机构欧洲新车安全评估协会(Euro NCAP)正在推进零事故愿景( Vision Zero),而且...
iOS 14 的一项重大新功能是主屏幕小组件,可让用户一目了然地查看来自应用程序的信息。用户也可以将小组件固定在“主屏幕”上的各个位置并能调整大小,以实现许多不同的布局。许多第三方应用程序已经发布了小组件,现在有证据表明 Spotify 正在开发自己的官方小组件,该功能已经出现在 TestFlight 的新 Beta 中。当前有小型和中型两个尺寸的小组件...
限电措施持续对PCB产业带来影响,对于工厂聚集在苏州、昆山两大重点限电区域的厂商来说,在长短料问题尚未解决的前提下,其上下游管理能力将面临考验。据钜亨网报道,有PCB厂反映,上一波限电措施从9月26日持续至9月30日,进入10月后,也仍未全面恢复供电,各地仍要求工厂个别厂商采取降载用电措施,为供应链带来不确定因素。报道指出,目前PCB工厂交期为...

史海拾趣

问答坊 | AI 解惑

usb芯片设计原理图

usb芯片设计原理图…

查看全部问答∨

除法指令

老师,单片机中的除法指令中,进行除法运算的思路是:“用被除数减除数,如果够减,商加1。差再减除数,直到不够减为止。每减一次,商就加1。这样就求出了商。” 老师,对于这个思路,我有点不理解呢。 特别是我不理解“用被除数减除数”。 1.老 ...…

查看全部问答∨

开关电源设计

本帖最后由 paulhyde 于 2014-9-15 09:43 编辑 开关电源设计.pdf  …

查看全部问答∨

atmanavr 在哪里可以下载的

atmanavr 在哪里可以下载的 或者传我一分 gdyjdahai@163.com 谢谢!!!…

查看全部问答∨

IT交流学习(参考)

6月23日至25嵌入式手机游戏及应用开发零起点零基础基础免费课程 时间:6月23日至25日  每晚(19:30分-21:30分) 课程:嵌入式手机游戏及应用开发零起点零基础免费课程 每日预计用时2小时,中间休息15分钟(请直接与咨询师联系索取免 ...…

查看全部问答∨

GSM模块 短信接收问题

利用CMGR或者CMGL的AT命令读取短信时,经常收到内容之后,OK接收不完整。有时能够收到正常的,有时又无法收到,或只收到部分,导致对其没办法处理。因为我是在收到OK后再进行处理的,谁能够帮我看看是什么方面的原因,谢谢了? 注:我在程序中用过 ...…

查看全部问答∨

windML调试问题

请问各位大侠,我在调试应用程序时,使用单步跟踪的方式,为什么每次执行完uglOSTaskCreate();后,再单步跟,就跳出来汇编界面?即使我在主函数中设置了断点也跟不进去主函数,但我的科学试验的运行结果是正确的,请各位大侠不吝指教!具体入口函数 ...…

查看全部问答∨

哪个有现成的直流电流采样电路不???几十微安到几十毫安,,送单片机的AD里面的

哪个有现成的直流电流采样电路不???几十微安到几十毫安,,送单片机的AD里面的   哪个有现成的直流电流采样电路不???几十微安到几十毫安,,送单片机的AD里面的…

查看全部问答∨

(原创)洗牌的时刻(3)

打破通信巨头制定价格现状的基础设施和气候条件已经形成,洗牌的机会应当来自于我们十分熟悉的WIFI技术。大家一定要问,WIFI已经存在这么多年了,它有什么新鲜和特别之处。 事先要声明的是,这只是我的直觉,当然这个知觉也不一定对,不过有些证 ...…

查看全部问答∨
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved