定时器中断实验
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"
上一篇:【S3C2440】第14课、异常与中断之学习笔记
下一篇:11.S3C2440 中断实验(一)und和swi实验
推荐阅读
史海拾趣
除了电源产品,CUI Inc.还提供世界一流的配套板级元器件,包括互连、声音、运动控制和热产品。为了满足全球客户的需求,CUI积极扩展其全球化布局。通过与各地的合作伙伴建立紧密的合作关系,CUI成功地将其产品和服务推广到了全球范围内。这种全球化战略不仅提高了CUI的市场份额,还增强了其在国际市场上的影响力。
在竞争激烈的电子行业中,DACHANG公司始终坚持以品质为核心。公司不断引进先进的生产设备和技术,严格把控产品质量,确保每一件产品都能达到客户的期望。正是这种对品质的执着追求,让DACHANG公司的产品在市场上赢得了良好的口碑,公司也逐渐扩大了自己的市场份额。
HBControls的创立可以追溯到上世纪90年代初,当时电子工业正处于快速发展阶段。创始人李明(化名)凭借在电子行业多年的工作经验,敏锐地察觉到继电器市场的巨大潜力。然而,初创时期资金短缺、技术瓶颈和市场认可度低成为了摆在他面前的三座大山。李明带领团队夜以继日地研发产品,不断优化性能,同时积极寻找合作伙伴,逐步打开了市场。经过数年的不懈努力,HBControls终于在继电器领域站稳了脚跟。
进入21世纪后,随着电子技术的飞速发展,HBControls意识到只有不断创新才能保持竞争力。公司加大了研发投入,成立了专门的研发团队,专注于新型继电器产品的研发。经过多次试验和改进,HBControls成功推出了HD-4850系列高性能继电器,该系列产品以其高可靠性、长寿命和低功耗等特点迅速赢得了市场的青睐。这一创新不仅提升了公司的市场份额,也进一步巩固了HBControls在继电器领域的领先地位。
面对日益严峻的环境问题,Computer Conversions Corp积极响应环保号召,开始研发更加环保的计算机转换技术。公司不仅优化了生产工艺,减少了生产过程中的能源消耗和废弃物排放,还推出了一系列节能型转换设备,帮助客户在提升计算效率的同时,也降低了能源消耗。这一举措不仅赢得了客户的赞赏,也提升了公司的社会形象。
面对日益严峻的环境问题,Computer Conversions Corp积极响应环保号召,开始研发更加环保的计算机转换技术。公司不仅优化了生产工艺,减少了生产过程中的能源消耗和废弃物排放,还推出了一系列节能型转换设备,帮助客户在提升计算效率的同时,也降低了能源消耗。这一举措不仅赢得了客户的赞赏,也提升了公司的社会形象。
昨天刚收到板子,做工很好,开始以为有光盘什么的,打开发现没有,板子分两部分,左边是居于ARM9的 LPC-Link,这两部分是可以分开的,预留了十针的JTAG可以用于调试其他ARM7,ARM9板,速度应该比我哪个100块的快吧。右边是LPC1114,我找排针焊接上 ...… 查看全部问答∨ |
|
问下串口中有什么原因会导致while(TI==0)这里出不来? 控制打印机时老是把要打印的东西打印一般然后就死机了, 因为背光还是设定的时间内不按键会关闭,有按键就亮,说明程序还是运行正常 所以现在怀疑是while(TI==0)这里没出来,想问下大家会有什么因素导致串口无法成功发送数据? … 查看全部问答∨ |
|
那位大侠有 USB Mass Storage Class ATA Command Block 这个文档的给我发一份吧,急用! 多谢! 邮箱 playboybob@126.com… 查看全部问答∨ |
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操作,不能有获取信号量的操作呢? 事实上我做过实验,在中断服务程序中狂打印的话,系统会立即死机或复位。 这个问题一直没怎么想明白,请高手解释一下。谢谢了! … 查看全部问答∨ |