11.S3C2440 中断实验(五)定时器中断实验

最新更新时间:1970-01-01来源: 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  中断实验  定时器中断 编辑:什么鱼 引用地址:http://news.eeworld.com.cn/mcu/ic546834.html

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

推荐阅读

S3C2440裸机学习[2] - LCD驱动原理及代码分析[二]
下面看看2440test里面的lcd.c文件static void PutPixel(U32 x,U32 y,U16 c){    if(x<SCR_XSIZE && y<SCR_YSIZE)        LCD_BUFFER[(y)][(x)] = c;}很容易发现TFT LCD上显示单个像素的函数实际上很简洁看来似乎只需要LCD_BUFFER[(y)][(x)] = c这一句话下面就来分析下,是如何通过这一句话来实现在LCD上显示单个像素的先分析下LCD_Init()即LCD初始化函数rLCDCON1
发表于 2021-11-08
S3C2440裸机学习[2] - LCD驱动原理及代码分析[一]
1. LCD工作的硬件需求:要使一块LCD正常的显示文字或图像,不仅需要LCD驱动器,而且还需要相应的LCD控制器。在通常情况下,生产厂商把LCD驱动器会以COF/COG的 形式与LCD玻璃基板制作在一起,而LCD控制器则是由外部的电路来实现,现在很多的MCU内部都集成了LCD控制器,如S3C2410/2440等。通 过LCD控制器就可以产生LCD驱动器所需要的控制信号来控制STN/TFT屏了。 2. S3C2440内部LCD控制器结构图:我们根据数据手册来描述一下这个集成在S3C2440内部的LCD控制器:a:LCD控制器由REGBANK、LCDCDMA、TIMEGEN、VIDPRCS寄存器组成;b:REGBANK
发表于 2021-11-08
<font color='red'>S3C2440</font>裸机学习[2] - LCD驱动原理及代码分析[一]
基于s3c2440的简易bootloader实现
一、目的编写一个能够加载并启动OS内核的bootloader。 二、思路第一阶段:(1)arm920t的异常向量表有两种存放方式,一种是低端存放(从0x00000000处开始存放),另一种是高端存放(从0xfff000000处开始存放)。选择低端存放,建立异常向量表。(2)s3c2440的看门狗在上电启动时默认是开启的,所有要先把看门狗关了先。免得代码运行还没完成就强制复位。(3)屏蔽掉所有中断。(4)初始化时钟。(5)初始化内存。(6)清零bss段。(7)设置好各个模式下的栈空间。(8)重定位代码,使得代码的运行地址与链接地址相对应,之后就可以直接使用绝对地址。(9)使用绝对跳转指令跳转到第二阶段,用c语言来实现
发表于 2021-11-05
基于<font color='red'>s3c2440</font>的简易bootloader实现
一起学mini2440裸机开发(四)--S3C2440定时器学习
S3C2440定时器原理概述s3c2440有5个16位定时器,定时器0、1、2和3有脉冲宽度调制(PWM)功能,因此这4个定时器也被称为PWM定时器。定时器4是一个内部的定时器,没有外部输出引脚。定时器的时钟源是PCLK,定时器工作所需频率并不等于PCLK,还要进一步将PCLK通过内部的分频器分频才能得到。这里也可以看出外部设备所需的工作频率不一定等于它的时钟源。其中,定时器0、1公用一个分频器,另外3个定时器公用一个分频器。分频器输入信号经过第2级分频器进一步降低时钟频率,然后输出作为定时器工作的时钟。下图为由PCLK得到定时器工作时钟的框图:虽然S3C2440定时器有5个,但是它们的工作原理都是相同的,只需要理解一个定时器
发表于 2021-11-05
一起学mini2440裸机开发(四)--<font color='red'>S3C2440</font>定时器学习
mini2440也即S3C2440地址分配问题
写这篇文章完全是回答一个网友的问题,因为我之前也是对这里不理解,现在简单说一下。源引问题“mini2440的地址到底是怎么分配的呢?内部rom ram 外部的ram和rom分别占用的地址是多少啊,我从板子上看到有3个k4561632N芯片还有一个S29AL016J70TF102.但是看了相关的电路图也不知道他的地址是怎么分配的”你问的是mini2440的地址怎么分配。我只能再细节一下你的问题,mini2440处理器的地址怎么分配。mini2440的处理器是S3C2440,理论上,S3C2440处理器可以使用的物理地址空间可以达到4GB,其中前1GB的地址(也就是0x0000 0000--0x4000 0000)为外设地址空间
发表于 2021-11-04
mini2440也即<font color='red'>S3C2440</font>地址分配问题
S3C2440上MMC/SD卡驱动分析(二)
:2.6.30.4编译器:arm-linux-gcc-4.3.2上接:S3C2440上MMC/SD卡驱动实例开发讲解(一)6. s3cmci_ops SDI主机控制器操作接口函数功能分析:static struct mmc_host_ops s3cmci_ops = {    .request = s3cmci_request,//实现host的请求处理(即:命令和数据的发送和接收)    .set_ios = s3cmci_set_ios,//通过核心层传递过来的ios,配置host寄存器(使能时钟、总线带宽等)    .get_ro  = s3cmci_get_ro
发表于 2021-10-25

推荐帖子

操作问题:SDK如何添加?
在EVC软件中,有一个选择SDK的框,但是没有我要的SDK,随后我将需要的SDK安装完毕,但不知道怎么做,才能在这个里面选择出来。 我想这个问题应该是很初级的问题了,但我是初学者,所以弄不明白。 谢谢解答! 操作问题:SDK如何添加?
644012602 嵌入式系统
[以拆会友]电动自行车驱动器拆解
最近搞了一个电动自行车的驱动器,一直看着心痒痒想拆开看看里面到底是什么,看到论坛的活动第一个想到的还是拆! 先来一个没拆之前的照片: 铝壳,想必是为了更好的散热,而且出线的地方用胶封了,这个应该是为了做防水。想想最近网上暴雨中那些还能动电动车,防水杠杠的。 开始拆,出线的地方有5个螺丝: 侧面有5个螺丝,这个应该是用来固定MOSFET用的 用十字螺丝刀拆掉这10颗螺丝之后,从出线那一端往外抽,整个控制板就出来了: 麻雀虽小,五脏俱全。里面MOSFET用了有12个,MOS驱动没有采用专
bkn1860 以拆会友
微型红外追踪及无线遥控车设计
微型红外追踪及无线遥控车设计微型红外追踪及无线遥控车设计
亲善大使 单片机
求购Tektronix/泰克混合信号示波器DPO4104B-L
求购Tektronix/泰克混合信号示波器DPO4104B-L大家都知道Tektronix/泰克混合信号示波器DPO4104B-L1G带宽4个模拟通道5GS/s取样速率20M点记录长度,我正好需要一台,实验室应用 东莞市捷鑫仪器有限公司 罗先生:15920845969 **本公司专业经营各类二手进口仪器(销售、租赁、维修、回收等业务), 成色新,价格低,技术先进、质量可靠、性能稳定的优良产品。长期 承接销售、租赁、维修、回收二手高档仪器,包括Agilent、HP、An
luoping6 电源技术
求各位前辈指导
我本是从事网络技术与MB测试方面的,现要了解一些电子电路基础知识,需要详解点的,请各位前辈有什么好资料上传一些,小弟不胜感谢求各位前辈指导
viqptf 模拟电子
完整的硬件设计流程
需求分析→概要设计→详细设计→调试→测试→转产 需求分析:硬件设计的第一步,也是关键一步,在需求分析阶段,只有充分了解需求,才能有针对性的开展器件选型、方案规划等工作。 1)、整体性能要求:可初步进行CPU、存储器、主要器件选型 2)、功能要求:根据需求要求进一步针对主要芯片做进一步细分,筛选满足功能的所有器件。 3)、成本要求:在满足项目需求的前提下,尽
XYD漫漫 51单片机
小广播
何立民专栏 单片机及嵌入式宝典

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

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2022 EEWORLD.com.cn, Inc. All rights reserved