历史上的今天

今天是:2024年09月03日(星期二)

正在发生

2021年09月03日 | 11.S3C2440 中断实验(五)定时器中断实验

发布者:DelightfulGaze 来源: eefocus关键字:S3C2440  中断实验  定时器中断 手机看文章 扫描二维码
随时随地手机看文章

定时器中断实验

        S3C2440提供了3种时钟:FCLK用于CPU核;HCLK用于AHB总线上的设备,比如存储器控制器中断控制器、LCD控制器、DMA和USB主机模块,同时也可以在特殊情况下用于CPU核;而PCLK则用于APB总线上的设备,比如看门狗、IIS、IIC、PWM定时器、ADC、UART、GPIO、RTC和SPI等。


        总的来说,AHB总线主要用于高性能模块,APB总线主要用于低带宽的周边外设之间的连接。


        S3C2440定时器的总时钟源为PCLK,首先通过两个8位的预分频器降低频率,定时器0、1共用第一个预分频器,定时器2、3、4共用第二个预分频器;之后预分频器的输出将被第二级的五路选择分频器处理,之后供定时器各自选择。


原理介绍

时钟频率选择

        S3C2440 SoC内部含有5个16位的定时器,分别是timer0、timer1、timer2、timer3、timer4。前四个定时器同时还具有PWM(Pulse Width Modulation)功能,而因为第5个定时器timer4没有外部输出引脚,所以不具备PWM功能。同时需要说明,由于timer0定时器还具有一个死区发生器,所以它可以被用来驱动大电流设备。

在这里插入图片描述

        由上图可知,timer0和timer1共用一个8位的预分频器以降低频率;timer2、timer3和timer4共用另外一个预分频器。从预分频器出来之后,会分别进入第二级的5路选择分频器。5路选择分频器可以选择2分频、4分频、8分频、16分频或者直接使用外部时钟TCLK0/TCLK1。每个定时器都可以从第二级分出来的5种频率中选择各自使用的时钟频率。

        对于预分频器,我们可以通过TCFG0寄存器来进行设置。而对于第二级时钟频率选择,则是通过TCFG1寄存器来选择的。


定时器内部控制逻辑

在这里插入图片描述
在这里插入图片描述

        当时钟分频设置好之后,我们开始研究一下定时器本身的设置。

        首先,我们需要设置初始计数缓存寄存器TCNTBn和初始比较值寄存器TCMPBn,用以分别表示定时器n的初始计数值和初始比较值。

        第二,我们需要设置TCON寄存器使得计数器和比较器的值被加载进内部寄存器TCMPn和TCNTn。

        第三,当启动定时器之后,每来一个时钟周期,TCNTn的值就递减1,同时我们可以通过TCNTOn这个观察寄存器来对当前数值进行观察。

        第四,当TCNTn等于TCMPn时,定时器n的输出管脚TOUTn就会电平反转。

        第五,当TCNTn的值到达0时,TOUTn的输出管脚电平会再次反转;同时如果使能了中断的话,并会触发定时器中断。

        第六,如果通过TCON寄存器设置了自动加载计数值的话,当TCNTn递减到0时,TCMPBn和TCNTBn寄存器的值会被自动装入内部寄存器,从而开始新一轮的定时周期。如果没有设置为自动加载的话,定时器就是一次性的。

        通过上述分析我们可知,我们可以设置TCMPBn和TCNTBn的值,来调整TOUTn管脚输出信号的占空比,从而输出PWM方波,因而这几个可以输出PWM的定时器又叫做PWM定时器。所谓PWM,就是可调制脉冲(Pulse Width Modulation)。


定时器设置

TCFG0寄存器

在这里插入图片描述

我们可以看到,TCFG0[7:0]和TCFG0[15:8]分别控制Prescaler0和Prescaler1这两个一级预分频器,其值域为闭区间[0, 255],分频后的输出值为PCLK / (prescaler value + 1)。由于我们之前设置的PCLK频率为50MHz,所以我们将prescaler value设置为99,方便后续计算,即:


TCFG0 = (99 << 0) | (99 << 8);


TCFG1寄存器

在这里插入图片描述

如上图所示,经过一级预分频器后的时钟接下来会被二级五路选择分频器所分频。具体的五路选择则由TCFG1寄存器来配置。这里我们随便选择1/4分频,即:


TCFG1 = (1 << 16) | (1 << 12) | (1 << 8) | (1 << 4) | (1 << 0);


由于这5个定时器操作都是类似的,下面为简便起见,我们只是用定时器0来进行实验。

由于一级分频为1/100,二级分频为1/4,所以定时器0的时钟频率为50MHz/400 = ‭125000‬ Hz。所以我们下面的计数器就要设置为该值,从而1秒产生一次IRQ中断。


哈哈,希望是美好的,现实是残酷的。


我们通过下一张图片可知计数器TCNTB0只能使用16位,所以它可设置的最大计数值为65535,比我们希望设置的值12500小了一个数量级。所以如果我们想1秒产生一次中断,那么就要修改分频系数,我们二级分频系数采用1/8,这样计数值就变为62500,符合要求。


TCFG1 = (2 << 16) | (2 << 12) | (2 << 8) | (2 << 4) | (2 << 0);


TCNTB0和TCMPB0寄存器

在这里插入图片描述

通过上图可知,我们只用到了TCNTB0和TCMPB0 这两个寄存器的低16位。TCNTB0寄存器中保存的是定时器0的计数初始值,TCMPB0寄存器中保存的是定时器0的比较值。

这里需要强调的一点是,由于定时器4没有输出引脚,即没有PWM功能,所以对应定时器4来说,它没有TCMPB4和TCMP4寄存器。

在定时器工作时,我们可以通过TCNTO0这个观察寄存器,对当前定时器内部TCNT0寄存器中的值进行观察。

我们将计数值设置为62500,比较值设置为其一般,即‭31250‬。


TCNTB0 = 62500;

TCMPB0 = 31250;


TCON寄存器

在这里插入图片描述

这里需要强调几点内容:


第一次启动定时器前,一定要手动将TCNTBn和TCMPBn寄存器的值加载到定时器内部寄存器TCNTn和TCMPn中。那么怎么手动加载呢?以定时器0来举例,就是将TCON寄存器的第1位置1即可。之后在后面启动定时器的时候,还需要将这一位清零。

还要设置当本次计数完毕后,是否自动加载计数值,从而开始下一个计数周期。

还要设置定时器TOUTn管脚的输出电平是否反转。

我们设置定时器0为自动加载,从而周期性的产生中断。代码如下所示:


//停止定时器0

TCON &= (~(0x1F << 0));

//将计数器缓冲和比较器缓冲中的值加载到定时器0内

TCON |= (1 << 1);

TCON &= (~(1 << 1));

//启动自动加载,并启动定时器0

TCON = (1 << 3) | (1 << 0);


这样定时器0就开始工作了,每个1秒产生一次IRQ中断。


中断控制器设置

通过查看中断源我们发现,定时器0对应的INT_TIMER0中断源没有子中断源, 所以我们只需设置INTMSK寄存器即可。

在这里插入图片描述

通过上图可知,INT_TIMER0对应的硬件中断号是10。


INTMSK &= (~(1 << 10));


定时器0的中断处理函数

我们在中断处理函数中只打印一句话,表明定时器中断发生了。


void timer0_irq_handler(void){

        printf("nrtimer0 irq is handled.nr");

}


void irqExpHandler(void) {

//现在处于SVC模式

//我们先获取硬件中断号

unsigned int interruptNum = INTOFFSET;

int subInterruptNum = 0;

printf("nrIRQ Interrupt Number: %dnr", interruptNum);

//按键 按下后为低电平

unsigned int isKeyUp = 0;

int ledNum = -1;

switch(interruptNum){

case 0:

//EINT0中断 s2按键

isKeyUp = (GPFDAT & 0x1);

ledNum = LED1;

break;

case 2:

//EINT2 s3按键

isKeyUp = (GPFDAT & (0x1 << 2));

ledNum = LED2;

break;

case 5:

//EINT8_23 s4按键

isKeyUp = (GPGDAT & (0x1 << 3));

ledNum = LED4;

break;

case 10:

timer0_irq_handler();

break;

case 28:

subInterruptNum = SUBSRCPND & 0x7;

if(SUBSRCPND & 0x1) {

//INT_RXD0

uart0_handle_irq(UART0_RX_IRQ);

}

if(SUBSRCPND & 0x2) {

//INT_RXD0

uart0_handle_irq(UART0_TX_IRQ);

}

if(SUBSRCPND & 0x4) {

//INT_RXD0

uart0_handle_irq(UART0_ERR_IRQ);

}

break;

default:

break;

}

if(ledNum > 0) {

if(isKeyUp) {

//熄灭LED1

led_off(ledNum);

printf("Key is Up.LED%d is turn off.nr", ledNum);

}else {

led_on(ledNum);

printf("Key is Down.LED%d is turn on.nr", ledNum);

}

}

//处理完毕,从源头开始清理中断标志

//注意是写1清零

if(interruptNum == 5) {

EINTPEND = (0x1 << 11);

}

SUBSRCPND = subInterruptNum;

SRCPND = (0x1 << interruptNum);

INTPND = (0x1 << interruptNum);


printf("IRQ Interrupt is Handlednr");

}


char gCh = 'A';

int main(void) {

leds_init();

keys_init_irq();

printf("%snr", "IRQ Test.");

dump_norFlash(80);

timer_init();

timer_start();


while(1) {

printf("%c(0x%02x) ", gCh, gCh);

gCh++;

wait(888888);

}

return 0;

}


实验结果

我们可以看到,每个1秒钟产生一次定时器0 IRQ中断,如下图所示:

在这里插入图片描述

实验相关源文件

timer.c


//timer.c

#include "s3c2440_soc.h"

#include "timer.h"

#include "myprintf.h"


void timer0_irq_handler(void){

printf("nrtimer0 irq is handled.nr");

}


void timer_init(void) {

TCFG0 = (99 << 0) | (99 << 8);

TCFG1 = (2 << 16) | (2 << 12) | (2 << 8) | (2 << 4) | (2 << 0);

TCNTB0 = 62500;

TCMPB0 = 31250;

INTMSK &= (~(1 << 10));

}



void timer_start(void) {

//停止定时器0

TCON &= (~(0x1F << 0));

//将计数器缓冲和比较器缓冲中的值加载到定时器0内

TCON |= (1 << 1);

TCON &= (~(1 << 1));

//启动自动加载,并启动定时器0

TCON = (1 << 3) | (1 << 0);

}


intException.c


//intException.c

#include "myprintf.h"

#include "s3c2440_soc.h"

#include "led.h"

#include "uart.h"

#include "timer.h"


void undExpHandler(unsigned int* undAddr) {

printf("Undefined Instruction(0x%08x) at address(0x%08x) is found!nr",

*undAddr, undAddr);

}


void swiExpHandler(unsigned int swiNum) {

printf("Software Exception is Checked with Number %d(0x%x)nr", swiNum, swiNum);

}


void abortPrefetchExpHandler(unsigned int* abtPrefetchAddr) {

printf("Fetch instruction from address(0x%08x) failed.nr", abtPrefetchAddr);

}


void abortDataExpHandler(unsigned int* abtDataAddr) {

printf("Fetch data failed at address(0x%08x).nr", 

abtDataAddr);

}


void irqExpHandler(void) {

//现在处于SVC模式

//我们先获取硬件中断号

unsigned int interruptNum = INTOFFSET;

int subInterruptNum = 0;

printf("nrIRQ Interrupt Number: %dnr", interruptNum);

//按键 按下后为低电平

unsigned int isKeyUp = 0;

int ledNum = -1;

switch(interruptNum){

case 0:

//EINT0中断 s2按键

isKeyUp = (GPFDAT & 0x1);

ledNum = LED1;

break;

case 2:

//EINT2 s3按键

isKeyUp = (GPFDAT & (0x1 << 2));

ledNum = LED2;

break;

case 5:

//EINT8_23 s4按键

isKeyUp = (GPGDAT & (0x1 << 3));

ledNum = LED4;

break;

case 10:

timer0_irq_handler();

break;

case 28:

subInterruptNum = SUBSRCPND & 0x7;

if(SUBSRCPND & 0x1) {

//INT_RXD0

uart0_handle_irq(UART0_RX_IRQ);

}

if(SUBSRCPND & 0x2) {

//INT_RXD0

uart0_handle_irq(UART0_TX_IRQ);

}

if(SUBSRCPND & 0x4) {

//INT_RXD0

uart0_handle_irq(UART0_ERR_IRQ);

}

break;

default:

break;

}

if(ledNum > 0) {

if(isKeyUp) {

//熄灭LED1

led_off(ledNum);

printf("Key is Up.LED%d is turn off.nr", ledNum);

}else {

led_on(ledNum);

printf("Key is Down.LED%d is turn on.nr", ledNum);

}

}

//处理完毕,从源头开始清理中断标志

//注意是写1清零

if(interruptNum == 5) {

EINTPEND = (0x1 << 11);

}

SUBSRCPND = subInterruptNum;

SRCPND = (0x1 << interruptNum);

INTPND = (0x1 << interruptNum);


printf("IRQ Interrupt is Handlednr");

}


void fiqExpHandler(void) {

printf("FIQ is Detected!nr");

unsigned int isKeyUp = 0;

int offset = -1;

int hardwareIntNum = -1;

printf("EINTPEND(0x%08x)nr", EINTPEND);

printf("SRCPND(0x%08x)nr", SRCPND);

if(EINTPEND & (0x1 << 11)) {

//S4按下或松开 EINT11

isKeyUp = (GPGDAT & (0x1 << 3));

if(isKeyUp) {

//熄灭LED4

led_off(LED4);

printf("Key is Up.LED4 is turn off.nr");

}else {

led_on(LED4);

printf("Key is Down.LED4 is turn on.nr");

}

offset = 11;

//EINT11对应的硬件中断号是5

hardwareIntNum = 5;

}else if(EINTPEND & (0x1 << 19)) {

//S5按下或松开 EINT19

isKeyUp = (GPGDAT & (0x1 << 11));

if(isKeyUp) {

//熄灭LED4

led_off(LED1);

led_off(LED2);

led_off(LED4);

printf("Key is Up.LED1 LED2 LED4 is turn off.nr");

}else {

led_on(LED1);

led_on(LED2);

led_on(LED4);

printf("Key is Down.LED1 LED2 LED4 is turn on.nr");

}

offset = 19;

//EINT19对应的硬件中断号是5

hardwareIntNum = 5;

}else {

printf("Unknown FIQ Interrupt!nr");

}

//从源头开始清理中断标志

//注意是写1清零

if(offset >= 0) {

EINTPEND = (1 << offset);

SRCPND = (1 << hardwareIntNum);

}

printf("FIQ is Handled.nr");

}




void keys_init_irq(void) {

//按键S2对应的GPIO是GPF0

//S3对应的GPIO是GPF2

//S4对应的GPIO是GPG3

//初始化GPF0 GPF2为中断功能,对应EINT2 EINT0

GPFCON &= (~(0x33));

GPFCON |= (0x2 << 4) | (0x2 << 0);

//初始化GPG3为中断功能,对应EINT11

GPGCON &= (~(0x3 << 6));

GPGCON |= (0x2 << 6);

//按键S5对应EINT19, GPG11

GPGCON &= (~(0x3 << 22));

GPGCON |= (0x2 << 22);

//设置GPIO中断触发方式为双边沿触发

EXTINT0 |= (0x7 | (0x7 << 8));

EXTINT1 |= (0x7 << 12);

//设置EINT19的触发方式为双边沿触发

EXTINT2 |= (0x7 << 12); 

//打开GPIO中断屏蔽开关

//对于EINT0~EINT3来说,GPIO控制器这里是始终没有屏蔽中断的。

//我们只需打开EINT11中断屏蔽即可。

EINTMASK &= (~(0x1 << 11));

//打开EINT9中断屏蔽开关

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

//设置EINT19对应的中断方式为FIQ

//当我们将中断方式设置为FIQ时,中断发生时不会去设置INTPND INTOFFSET

INTMOD |= (1 << 5);

//打开中断控制器对应的中断开关

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

}


main.c


//main.c

#include "myprintf.h"

#include "nand.h"

#include "nor.h"

#include "uart.h"

[1] [2]
关键字:S3C2440  中断实验  定时器中断 引用地址:11.S3C2440 中断实验(五)定时器中断实验

上一篇:【S3C2440】第14课、异常与中断之学习笔记
下一篇:11.S3C2440 中断实验(一)und和swi实验

推荐阅读

近年来,随着我国智慧物流业的快速发展,供应链端的自动化和信息化程度亦在不断提高,自动化立体仓库在很多行业得到了广泛应用,市场空间不断增大。据相关资料统计,近十年来我国自动化物流仓储系统市场规模保持平均约20%的增速,到2020年我国自动化立体库的市场规模将达到325亿元;而2016年,该项数据仅为149亿元。自动化立体仓库对仓库的存储量和仓储效...
为应对中美贸易纠纷、日韩矛盾等变量,三星电子副会长李在镕虽启动紧急经营体制,但韩国大法院驳回二审结果后,恐影响李在镕的企业活动。目前全球半导体、智能手机市场气氛低迷,加上中美贸易纠纷、日本对韩加强出口限制,三星待解决的危机堆积如山,未来得降低李在镕官司问题,并努力维持企业正常活动。动摇李在镕 恐拖累韩国经济过去李在镕接受朴槿惠案...
和P40系列类似,华为Mate 40系列今年可能推出三款,即Mate 40、Mate 40 Pro和Mate 40 Pro+。  有数码大V在微博爆料称,Mate 40 Pro+的曲率比Mate 40 Pro更大。  当然,结合P40 Pro+的打造思路,Mate 40 Pro+在拍照元器件规格、存储规格、机身材质等方面或亦有不同之处。  至于Mate 40和Mate 40 Pro,外形均采用左上角的胶...
目前小芯片异构集成与系统级封装技术成为众多封测厂商(OSAT)追逐的热点。8月中旬,长电科技技术市场总监刘明亮(Michael Liu)接受了知名半导体技术分析机构semiengineering的采访,谈到了对先进封装技术未来的展望。刘明亮指出,有关先进封装技术的未来发展,除了成本、性能以及超越摩尔定律的突破点从前端到后端封装的技术演变之外,对市场应用的实际...

史海拾趣

问答坊 | AI 解惑

单片机的等精度测量

提出了基于P89LPC932单片机的等精度测量的信号处理方法,该方法有效地消除转速对扭矩检测的影响,提高了测量精度. …

查看全部问答∨

HDDMP3做的硬盘MP3资料

现在上传一个HDDMP3做的硬盘MP3资料,希望对大家有用。。。。…

查看全部问答∨

LPCXpresso——LPC1114

昨天刚收到板子,做工很好,开始以为有光盘什么的,打开发现没有,板子分两部分,左边是居于ARM9的 LPC-Link,这两部分是可以分开的,预留了十针的JTAG可以用于调试其他ARM7,ARM9板,速度应该比我哪个100块的快吧。右边是LPC1114,我找排针焊接上 ...…

查看全部问答∨

请问类似MP3的液晶显示屏型号有哪些?我想买一个~十分感谢~~~

请问类似MP3的液晶显示屏型号有哪些?我只要能显示2行字就可以了,呵呵 我想买一个~十分感谢~~~…

查看全部问答∨

问下串口中有什么原因会导致while(TI==0)这里出不来?

控制打印机时老是把要打印的东西打印一般然后就死机了, 因为背光还是设定的时间内不按键会关闭,有按键就亮,说明程序还是运行正常 所以现在怀疑是while(TI==0)这里没出来,想问下大家会有什么因素导致串口无法成功发送数据? …

查看全部问答∨

紧急求助!(USB问题)

那位大侠有 USB Mass Storage Class ATA Command Block 这个文档的给我发一份吧,急用! 多谢! 邮箱 playboybob@126.com…

查看全部问答∨

求助:用VS.C#不能调试CE中的程序?

VS中生成的测试工程,结果下载到WINCE中,调试运行是出现如下错误: \'t4.exe\' (Managed): Loaded \'mscorlib.dll\', No symbols loaded. \'t4.exe\' (Managed): Loaded \'d:\\test\\t4\\t4\\bin\\debug\\t4.exe\', Symbols loaded. \'t4.exe\' ...…

查看全部问答∨

便携数码产品,视听产品开发 问题,望赐教!!!

本人学生,想毕业后从事便携数码产品、视听产品开发(比如MP3 、 MP4 、 PMP 、数码相框)。但是对这个行业不是很熟悉,网上这方面的资料又好少好少,现在根本就不知道该怎么进行学习。比如说,该怎么入门,该学点什么,开发难点是什么,开发流程 ...…

查看全部问答∨

为什么说在中断服务程序中不能有i/o操作,不能有获取信号量的操作呢?

为什么说在中断服务程序中不能有i/o操作,不能有获取信号量的操作呢? 事实上我做过实验,在中断服务程序中狂打印的话,系统会立即死机或复位。 这个问题一直没怎么想明白,请高手解释一下。谢谢了! …

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

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

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

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

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