s3c2440裸机-异常中断5-irq定时器中断

发布者:huanli最新更新时间:2024-07-05 来源: elecfans关键字:异常中断 手机看文章 扫描二维码
随时随地手机看文章

之前讲过s3c2440时钟体系,看了时钟体系再来看定时器中断会更好的结合运用所学知识点。

S3c2440共有2种定时器:

1.Watchdog看门狗定时器
2.PWM脉冲可调制定时器

下面详细介绍2种定时器的原理,来了解定时器是如何产生定时器中断的。

1. Watchdog看门狗定时器

1)Watchdog看门狗定时器原理

Watchdog定时器的原理很简单,寄存器很少,框图如下:



1.定时器,定时器那肯定是需要用到时钟的,从框图中可以看到Watchdog定时器采用的时钟源是PCLK,从s3c2440时钟体系中也可以体现出来,接的是APB总线。

2.然后到达一个8bit的分频器,可以通过配置WTCON[15:8]来设置分频器的预设值。

3.再设置WTCON[4:3]来设置除数因子来进一步分频。 所以最终的Watchdog定时器的时钟周期t_watchdog = 1/[ PCLK / (Prescaler value + 1) / Division_factor ]

4.到达WTCNT:看门狗递减寄存器。WTCNT里的数据就开始在输入时钟频率下递减。WTCNT的值由WTDAT寄存器提供。

5.WTDAT:WTDAT寄存器用于指定计数器的初始值,也就是它的超时时间,系统上电之后硬件自动的将0x8000的初始值载入到WTCNT里,在发生了第一次超时操作时,WTDAT的值才会载入到WTCNT寄存器。

当WTCNT的值减到0时,就会触发看门狗定时器中断,进而产生复位。中断框图中可以看到可以设置WTCON[2]来设置是否产生中断信号,可以设置WTCON[0]来设置是否产生复位信号。

WTCON寄存器如下图:



WTCNT、WTDAT寄存器如下图:



2)看门狗定时器中断编程实现

在之前的章节中,我们在start.s启动代码中首先做的就是关闭看门狗,把WTCON[5]=0,也就是把Watchdog timer给disable。那么Watchdog Timer就不再工作了,这样做是为了防止在启动代码进行硬件初始化的时候出现超时,发出复位信号又去重启硬件,这样就陷入了不断重启过程中。因为s3c2440芯片默认WTCON[5]是1,也就是Watchdog Timer默认是处于使能状态。

我们之前s3c2440时钟体系中配置了PCLK=50M Hz, 那么让WTDAT取默认值0x8000,那么根据公式算出从开机到触发复位重启的时间t=WTDAT*( 1/[ PCLK / (Prescaler value + 1) / Division_factor ])。

根据WTCON寄存器配置Prescaler value=255,配置Division_factor=128,这样最终定时器分得的频率更低,那么减数器递减的更慢,也就代表从开机到触发复位重启的时间T=0x8000 * (1/[50*10^6/(255+1)/128]) = 21474836.48us = 21s。

之前的start.s中把看门狗已经关闭了,那么我们在跳转到main函数中调用wtd_timer_init函数实现如下:

void wtd_timer_init(void)
{
    WTCON |= (1<<0) | (1<<5);//使能定时器,开启reset复位
    WTCON |= (3<<3) | (255<<8);
}

我们查看测试结果:

xxx.png

果然初始化wtd_timer_init后,过21s后板子重启了,说明我们watchdog定时器功能已经OK了。

现在修改代码如下:

void wtd_timer_init2(void)
{
    WTCON |= (1<<0) | (1<<2);//使能定时器,开启watchdog定时器中断
    WTCON |= (3<<3) | (255<<8);

    WTDAT = 0x4000;
}

我们看到我们现在定时器的初值被修改成了0x4000, 相对于默认值少了一半,那么触发wtd_timer中断的时间应该减半,也就是约等于10s。

那么需要写一个wtd_timer的中断服务程序,同样需要先在do_irq中去保护现场、调用handle_irq_c、恢复现场,可以参考上一节irq之外部中断。查看INTOFFSET寄存器:


得知: handle_irq_c代码修改如下:


void handle_irq_c(void)

{

    /* 分辨中断源 */

    int bit = INTOFFSET;


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

    if (bit == 0 || bit == 2 || bit == 5)  /* eint0,2,bit==5还需细分eint8_23 */

    {

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

    }

else if(bit == 9) { 这里还需区分子中断源 }


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

    SRCPND = (1<    INTPND = (1<}

查看芯片手册查找“INT_WDT_AC97”如下图:



从上图可以看到SRCPND和SUBSRCPND的映射关系。 SUBSRCPND寄存器如下图:



我们可以读取SUBSRCPND来区分到底是哪一个子中断源产生了中断,当SUBSRCPND中哪一位被置1,表示对应的中断源发生了中断。

前面做完wtd_timer_init,还要进行中断控制器的初始化,查看INTMSK寄存器如下图:



查看INTSUBMSK寄存器如下图:


在interrupt_init中添加:

INTMSK &= ~(1<<9);
INTSUBMSK &= ~(1<<14);

修改handle_irq_c:

...
else if (bit == 9)
{
    if (SUBSRCPND & 1<<14)
    {
        printf('watchdog timer interrupt occured.n');
    }
}
...

测试结果如下:

2. PWM脉冲宽度调制定时器

PWM(Pulse Width Modulation),字面上是脉冲可调制的意思,就是可以调节占空比。

s3c2440有5个定时器,其中定时器0、1、2和3具有脉宽调制(PWM)功能。定时器4是一个无输出引脚 的内部定时器。

先认识下s3c2440的pwm timer的框架:



从框架图中得知:

1.时钟源为PCLK

2.pclk经过8 bit的预分频系数(Prescaler),和4 bit的时钟除数因子(clock divider),进行分频

3.经过MUX选择器选择用哪个定时器(5选1)

4.设置TCMPB0和TCNTB0和TCONn寄存器

1)pwm定时器原理

pwm定时器的逻辑控制单元结构如下:



1 TCMPBn和TCNTBn寄存器中的值分别加载到TCMPn和TCNTn寄存器
2 每来一个clk(时钟)这个TCNTn减去1
3 当TCNTn == TCMPn时,可以产生中断,pwm输出引脚反转
4 TCNTn继续减1,当TCNTn == 0时,又产生一次中断,pwm引脚再次反转
5 重复1-4过程

从上述过程我们得知可以设置TCNTBn寄存器来设置加载初值,设置后TCNTn中的值就会按照时钟周期递减。 从上述过程我们得知可以设置TCMPBn寄存器来设置占空比,从而控制高低电平持续时间的比例。

2) pwm定时器编程实现

要开始一个PWM定时器功能的步骤如下:(假设使用的是timer0)

① 初始化pwm定时器

先进行初始化工作,定义一个pwm_timer_init() 1)设置时钟:


分别设置定时器0的预分频器值(prescaler)和时钟分频值(clock divider),从而控制TCNT0减数器的频率。

根据公式:


pwm Timer clk = PCLK / {(预分频数)prescaler value+1} / {divider value(5.1MUX值)}

PCLK是50M,设置prescaler value=99, divider value=16,所以pwm Timer clk= 50000000/(99+1)/16 = 31250 Hz


TCFG0 = 99; 

TCFG1 &= ~0xf;

TCFG1 |= 3;

2)设置初值:


/* 设置比较缓存寄存器TCMPB0和计数缓存寄存器TCNTB0的初始值*/

TCNTB0 = 31250 << 1;  /* 2s中断一次 */

TCMPB0 = 31250 >> 1;  /* 设置占空比*/

3)开启定时器0的手动更新TCNTB0&TCMPB0功能(设置TCON的第1位):



TCON |= (1<<1); //开始需要手工更新,这样才能将TCNTB0&TCMPB0同步到TCNT0&TCMP0

4)开启定时器0的自动加载:

TCON &= ~(1<<1); //开启自动加载要先清除手动更新
TCON |= (1<<3);

5)启动定时器0(设置TCON的第0位);

TCON |= (1<<0);

② 初始化中断控制器



interrupt_init(){
    ...
    INTMSK &= ~(1<<10);  /* enable timer0 int */        
    ...
}

做完这些初始化工作,就可以产生定时器中断了,同样我们需要在handle_irq_c函数中区分中断源: 我们还可以通过查看TCNTO0寄存器来查看当前TCNT的值。



void handle_irq_c(void)

{

    /* 分辨中断源 */

    int bit = INTOFFSET;


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

    if (bit == 0 || bit == 2 || bit == 5)  /* eint0,2,bit==5还需细分eint8_23 */

    {

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

    }


    else if(bit == 9) //INT_WDT_AC97

    {

        ...

    }


    else if(bit == 10) //timer0

    {

        printf('timer0 interrupt occured.n');

        print_hex(TCNTO0);

    }


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

    SRCPND = (1<    INTPND = (1<}


测试结果如下: xxx.ong

irq的优化改进:

我们对比irq外部中断、irq定时器中断发现每增加一个中断源,又要去修改中断控制器的初始化interrupt_init()和handle_irq_c(),要在handle_irq_c()中去添加分支去执行不同的中断服务。

那么我们现在不去改变interrupt文件,在timer.c、key_eint.c中去注册自己的中断服务程序即可,这里我们使用函数指针数组,建立一个中断号和中断服务程序的映射关系。这样就可以根据中断号来执行对应的中断服务程序,即在handle_irq_c()中去回调不同类型的中断源注册下来的函数即可。

/* 定义函数指针数组 */
#define IRQ_NUM 32
typedef void(*irq_func)(int);
irq_func irq_array[IRQ_NUM];

然后实现一个register_irq(...)如下:

void register_irq (int irq, irq_func fp)
{
    irq_array[irq] = fp;
    INTMASK &= ~(1 << irq)
}

handle_irq_c()修改实现如下:

void handle_irq_c(void)
{
    /* 分辨中断源 */
    int bit = INTOFFSET;

    irq_array[bit](bit); //根据中断号回调不同的中断处理函数

    /* 清中断 */
    SRCPND = (1<

这样子我们的irq中断就被统一管理了起来,只要在其他各中断模块初始化的时候调用register_irq(...)注册即可


关键字:异常中断 引用地址:s3c2440裸机-异常中断5-irq定时器中断

上一篇:s3c2440裸机-spi编程-1-spi协议
下一篇:s3c2440裸机-异常中断4-irq外部中断

推荐阅读最新更新时间:2024-11-06 19:55

十七、S3C2440裸机—IIC 接口
17.1 IIC接口介绍 17.1.1 IIC 总线的概念     I2C总线是由 Philips 公司开发的一种简单、双向二线制同步串行总线。它只需要两根线即可在连接于总线上的器件之间传送信息。   主器件用于启动总线传送数据,并产生时钟以开放传送的器件,此时任何被寻址的器件均被认为是从器件.在总线上主和从、发和收的关系不是恒定的,而取决于此时数据传送方向。如果主机要发送数据给从器件,则主机首先寻址从器件,然后主动发送数据至从器件,最后由主机终止数据传送;如果主机要接收从器件的数据,首先由主器件寻址从器件.然后主机接收从器件发送的数据,最后由主机终止接收过程。在这种情况下.主机负责产生定时时钟和终止数据传送。    I2C总线特
[单片机]
十七、<font color='red'>S3C2440</font><font color='red'>裸机</font>—IIC 接口
s3c2440裸机-nandflash编程(二. nand控制器和nand访问时序)
一.Steppingstone 我们知道nand没有独立地址线,cpu无法直接访问nand上的指令,所以nand不能片上执行。那么为何程序还能支持nand启动的呢? 为了支持NAND启动,S3C2440A配备了一个称为“ Steppingstone”的内部SRAM缓冲区,容量为4K。 开机时,Nandflash中的前4K数据将被加载到Steppingstone中,而引导代码将被加载到SRAM中将被执行,如下图所示: 我们知道s3c2440支持2种boot方式,nand或者nor,那么需要配置OM引脚来设置引导方式,如下图: 内存控制器的地址映射表如下: 我们得知OM1接地,OM0接了一个开关SW2,那么我们的OM
[单片机]
<font color='red'>s3c2440</font><font color='red'>裸机</font>-nandflash编程(二. nand控制器和nand访问时序)
ARM中异常中断问题分析总结
一、ARM中异常中断的类型: 异常中断名称含义复位(Reset) 当处理器复位引脚有效时,系统产生复位异常中断,程序跳转到复位异常中断处理程序处执行。复位异常中断通常用在下面几种情况: 1、系统加电时 2、系统复位时 3、跳转到复位中断向量处执行,称为软复位 未定义的指令当ARM处理器或者是系统中协处理器认为当前指令未定义时,产生未定义指令异常中断。可以通过该异常中断机制仿真浮点向量运算。 软件中断 (softwareinterruptSWI) 这是一个由用户定义的中断指令。可以用于用户模式下程序调用特权操作指令。在实时操作系统(RTOS)中可以通过该机制实现系统功能调用 指令预取中止
[单片机]
ARM中异常中断问题详情分析总结
一、ARM中异常中断的类型: 异常中断名称 含义 复位(Reset) 当处理器复位引脚有效时,系统产生复位异常中断,程序跳转到复位异常中断处理程序处执行。复位异常中断通常用在下面几种情况: 1、系统加电时 2、系统复位时 3、跳转到复位中断向量处执行,称为软复位 未定义的指令 当ARM处理器或者是系统中协处理器认为当前指令未定义时,产生未定义指令异常中断。可以通过该异常中断机制仿真浮点向量运算。 软件中断 (software interrupt SWI) 这是一个由用户定义的中断指令。可以用于用户模式下程序调用特权操作指令。在实时操作系统(RTOS)中可以通过该机制实现系统功能调用 指令预取中止 (Prefec
[单片机]
Cortex-M3 中异常中断返回 EXC_RETURN
在进入异常服务程序后,硬件自动更新LR的值为特殊的EXC_RETURN。 当程序从异常服务程序返回,把这个EXC_RETURN值送往PC时,就会启动处理器的异常中断返回序列。 因为LR的值EXC_RETURN是由硬件自动设置的,所以只要没有特殊需求,就不要改动它。 RETURN的高28位全为1,只有bit 的值有特殊含义。位段如下: 合法的EXC_RETURN值共有3个,如下: 如果主程序在线程模式下运行,并且在使用MSP时被中断,则在服务程序中LR=0xFFFFFFF9(主程序被打断前LR已被自动入栈)。 如果主程序在线程模式下运行,并且在使用PSP时被中断,则在服务程序中LR=0xFFFFFFFD(主程序被打断前LR已
[单片机]
S3C2440裸机------异常与中断__und异常模示程序示例
1.异常向量表 我们先看一下芯片手册里面的异常向量表, 2.代码流程 我们在重定位的第10个程序的基础上进行修改。 我们要在start.S的前面增加跳转到相应异常向量表的代码,并且要增加代码,当发生未定义指令异常时候跳转到该代码块进行保存现场、处理未定义异常以及恢复现场的工作,然后在下面故意写一条不能被识别的指令。 2.1增加异常向量表代码 首先在start.S的前面增加相应的跳转指令,当发生异常时会根据这里的跳转指令跳转到相应的地方,代码如下。 _start: b reset /* vector 0 : reset */ b do_und /* vector 4 : und */ 2.2设置栈 进入异
[单片机]
<font color='red'>S3C2440</font><font color='red'>裸机</font>------异常与<font color='red'>中断</font>__und异常模示程序示例
s3c2440裸机-代码重定位-4-清bss的优化和位置无关码
1.代码重定位的改进 用ldr、str代替ldrb, strb加快代码重定位的速度。 前面重定位时,我们使用的是ldrb命令从的Nor Flash读取1字节数据,再用strb命令将1字节数据写到SDRAM里面。 我们2440开发板的Nor Flash是16位,SDRAM是32位。 假设现在需要复制16byte数据。 不同的读写指令 cpu读取nor的次数 cpu写入sdram的次数 ldrb、strb 16 16 ldr、str 8 4 可以看出我们更换读写指令后读写次数变少了,提升了cpu的访问效率。 修改后的start.s代码如下图所示,这里我只简单的列出了重定位的实现: ... cpy: ldr r4, str
[单片机]
小广播
设计资源 培训 开发板 精华推荐

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

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

换一换 更多 相关热搜器件
随便看看

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

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