历史上的今天

今天是: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异常模示程序示例

推荐阅读

日前,Vicor 公司(NASDAQ 股票交易代號:VICR)宣布 Vicor 首席执行官 Patrizio Vinciarelli 荣获 2019 年 IEEE William E. Newell 电力电子大奖,Patrizio 获此殊荣主要因为:“他在为分布式电源系统应用开发高效、高功率密度的电源转换组件过程中所表现出的极具远见卓识的领导才能”。推动分布式电源系统效率、功率密度及灵活性发展的创...
2019年10月11日-14日香港秋季环球电子展(Global Sources Consumer Electronics)于香港亚洲国际博览馆隆重举行。作为环球资源旗舰展会,“环球资源电子产品展”将汇聚逾7800个展位的热门电子产品,云集来自中国、韩国及其他亚洲地区的参展商。已成为亚洲最大的消费电子贸易展览会。新联合众(北京)科技有限公司(简称:linxee领视)携自主研发的众多数据转...
本年度的iPhone 12受疫情影响,千呼万唤始出来,已经宣布在本月14日正式发布。其中比较受关注的热点是去年iPhone 11中首次推出的超宽带UWB技术,业界普遍猜测这次苹果会有更清晰的技术和产品路线,会在已有的基础上发扬光大,另一方面,安卓阵营对苹果推出的这项新技术并没有以往的好奇与跟随。那么,超宽带究竟是怎样的一种技术呢?按照苹果的设想,在...
今年,我国新能源汽车再次呈现出火热的增长态势,据公安部公布的数据显示,截至9月底,我国新能源汽车保有量已达678万辆,其中今年新注册的新能源汽车达187万辆,占新注册汽车总量的9.2%。并且,据多家机构数据显示,近几个月新能源汽车的市场渗透率已达到20%,提前达到了2023年的目标。而随着新能源汽车发展的提速,汽车智能网联程度也不断加深。9月25...

史海拾趣

问答坊 | AI 解惑

火灾自动报警系统的管理使用

火灾自动报警系统是由触发装置、火灾报警装置、火灾警报装置以及具有其它辅助功能装置组成的,它具有能在火灾初期,将燃烧产生的烟雾、热量、火焰等物理量,通过火灾探测器变成电信号,传输到火灾报警控制器,并同时显示出火灾发生的部位、时间等, ...…

查看全部问答∨

模电教程

本帖最后由 paulhyde 于 2014-9-15 09:29 编辑  …

查看全部问答∨

个人感觉keil好用

个人感觉keil好用…

查看全部问答∨

单片机选型求助(多路AD同时采集)

现在要做一个项目,需要用到单片机进行模拟信号的采集.但是需要在同一时间点进行同时采集,然后进行数据传输. 请问有没有合适的8位或者16位的单片机,能够对2路(以上)信道同时进行信号采集. 单片机采集后信号的输出可以是多路一起输出,也可以是单路 ...…

查看全部问答∨

keil 优化器 惹得祸?

事情是这样的,我使用keil编写代码,目的是对flash进行擦写读的操作。调试的时候是分阶段调试的。就是先调试擦,在调试写和读。现在各个操作可以分别独立运行了。但是在整合到一起的时候出了问题。问题是: 1、擦操作使用keil的优化器为0或者1。 ...…

查看全部问答∨

求助,wince5.0ppc sdk下 socket函数出错!!!

wince5.0ppc sdk下 socket函数出错!!! 具体错误是“此函数仅在win32模式下有效”。 s_Socket = socket(AF_IRDA, SOCK_STREAM, 0);         if (INVALID_SOCKET == s_Socket)         {   &nb ...…

查看全部问答∨

用Energia写一个同样功能的USB<->I2C工具

用CCS写的复杂版在这里https://bbs.eeworld.com.cn/thread-374278-1-6.html 最近有在玩Arduino,就觉得这样的编程思想很不错,完全无需知道底层代码和寄存器的实现方式,只要用封装好的函数就可以实现相应的功能,开发者只要专注在逻辑和应用上就可 ...…

查看全部问答∨

CCS v5

最近在学习使用CCS v5 。按照教程来的。。。但是一直没有搞懂    build  和  debug  的区别     不是一点也不懂    只是一提到这两个   我就有一种云山雾罩的 ...…

查看全部问答∨

用opencl做图像sobel的问题

找到一个例程,写了4了kernel:v1到v4 v1是每个workitem处理一个像素点的sobel。到v2,v3是一个workitem处理16个像素点,v4是一个workitem处理16*16个像素点,为什么速度越来越快?每个workitem处理的数据越多应该时间越久。。。 …

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

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

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

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

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