这节课我们来写一个定时器的中断服务程序
使用定时器来实现点灯计数
查考资料就是第10章PWM TIMER
我们先把这个结构图展示出来
这个图的结构很好
这里面肯定有一个clk(时钟),
1 、每来一个clk(时钟)这个TCNTn减去1
2、 当TCNTn == TCMPn时,可以产生中断,也可以让对应的SPWM引脚反转,(比如说原来是高电平,发生之后电平转换成低电平)
3、 TCNTn继续减1,当TCNTn == 0时,可以产生中断,pwm引脚再次反转 TCMPn 和 TCNTn的初始值来自TCMPBn,TCNTBn
4 、TCNTn == 0时,可自动加载初始
怎么使用定时器?
1、 设置时钟
2 、设置初值
3、 加载初始,启动Timer
4、 设置为自动加载
5 、中断相关
由于2440没有引出pwm引脚,所以pwm功能无法使用,也就无法做pwm相关实验,所谓pwm是指可调制脉冲
T1高脉冲和T2低脉冲它的时间T1, T2可调整,可以输出不同频率不同占控比的波形,在控制电机时特别有用
我们这个程序只做一个实验,当TCNTn这个计数器到0的时候,就产生中断,在这个中断服务程序里我们点灯
写代码
打开我们的main函数
int main(void)
{
led_init();
interrupt_init(); /* 初始化中断控制器 */
//我们初始化了中断源,同样的,我们初始化timer
key_eint_init(); /* 初始化按键, 设为中断源 */
//初始化定时器
timer_init();
我们需要实现定时器初始化函数
新建一个timer.c ,我们肯定需要操作一堆寄存器,添加头文件
#include "s3c2440_soc.h"
void timer_init(void)
设置TIMER0的时钟
设置TIMER0的初值
加载初值, 启动timer0
设置为自动加载并启动(值到0以后会自动加载)
设置中断,显然我们需要提供一个中断处理函数void timer_irq(void)在这里面我们需要点灯
打开芯片手册,我们想设置timer0的话
首先设置8-Bit Prescaler
设置5.1 MUX(选择一个时钟分频)
设置TCMPB0和TCNTB0
设置TCONn寄存器
看手册上写如何初始化timer
把初始值写到TCNTBn 和TCMPBn寄存器
设置手动更新位
设置启动位
往下看到时钟配置寄存器
有个计算公式
Timer clk = PCLK / {(预分频数)prescaler value+1} / {divider value(5.1MUX值)}
PCLK是50M
= 50000000/(99+1)/16
= 31250
也就是说我们得TCON是31250的话,从这个值一直减到0
Prescaler0等于99
TCFG0 = 99; /* Prescaler 0 = 99, 用于timer0,1 */
TCFG1 MUX多路复用器的意思,他有多路输入,我们可以通过MUX选择其中一路作为输出
根据上面mux的值,我们要把MUX0 设置成0011
只需要设置这4位即可,先清零
再或上 0011 就是3
TCFG1 &= ~0xf;
TCFG1 |= 3; /* MUX0 : 1/16 */
再来看看初始值控制寄存器
一秒钟点灯太慢了 ,就让0.5秒
TCNTB0 = 15625; /* 0.5s中断一次 */
这个寄存器是用来观察里面的计数值的,不需要设置
现在可以设置TCON来设置这个寄存器
现在需要设置Timer0
开始需要手工更新
TCON |= (1<<1); /* Update from TCNTB0 & TCMPB0 */
把这两个值放到TCNTB0 和 TCMPB0中
注意:这一位必须清楚才能写下一位
设置为自动加载并启动,先清掉手动更新位,再或上bit0 bit3
TCON &=~ (1<<1);
TCON |= (1<<0) | (1<<3); /* bit0: start, bit3: auto reload */
设置中断,显然我们需要提供一个中断处理函数void timer_irq(void)
在Timer里没有看到中断相关的控制器,我们需要回到中断章节去看看中断控制器,看看有没有定时器相关的中断
我们没有看到更加细致的Timer0寄存器
当TCNTn=TCMPn时,他不会产生中断,会发生脉冲的反转,只有当TCNTn等于0的时候才可以产生中断,我们之前以为这个定时器可以产生两种中断,那么肯定有寄存器中断或者禁止两种寄存器其中之一,那现在只有一种中断的话,就相对简单些
设置中断的话,我们只需要设置中断控制器
设置interrupu.c中断控制器
*初始化中断控制器 void interrupt_init(void)
INTMSK &= ~((1<<0) | (1<<2) | (1<<5));
*把定时器相应的位清零就可以了,哪一位呢?
INTPND的哪一位?
INT_TIMER0第10位即可
INTMSK &= ~(1<<10); /* enable timer0 int */
当定时器减到0的时候就会产生中断,就会进到start.s这里一路执行do_irq
do_irq:
/* 执行到这里之前:
* 1. lr_irq保存有被中断模式中的下一条即将执行的指令的地址
* 2. SPSR_irq保存有被中断模式的CPSR
* 3. CPSR中的M4-M0被设置为10010, 进入到irq模式
* 4. 跳到0x18的地方执行程序
*/
/* sp_irq未设置, 先设置它 */
ldr sp, =0x33d00000
/* 保存现场 */
/* 在irq异常处理函数中有可能会修改r0-r12, 所以先保存 */
/* lr-4是异常处理完后的返回地址, 也要保存 */
sub lr, lr, #4
stmdb sp!, {r0-r12, lr}
/* 处理irq异常 */
bl handle_irq_c
/* 恢复现场 */
ldmia sp!, {r0-r12, pc}^ /* ^会把spsr_irq的值恢复到cpsr里 */
让后进入irq处理函数中处理,处理这个irq
void handle_irq_c(void)
{
/* 分辨中断源 */
int bit = INTOFFSET;
/* 调用对应的处理函数 */
if(bit ==0 || bit == 2 || bit == 5)/*eint0,2,rint8_23*/
{
key_eint_irq(bit);/*处理中断,清中断源EINTPEND*/
}else if(bit == 10)//如果等于10的话说明发生的是定时器中断,这时候就调用我们得timer_irq
{
timer_irq();
}
/* 清中断 : 从源头开始清 */
SRCPND = (1< 回到timer.c文件中,在这个定时器处理函数中我们需要点灯 void timer_irq(void) { /* 点灯计数 循环点灯*/ static int cnt = 0; int tmp; cnt++; tmp = ~cnt; tmp &= 7; GPFDAT &= ~(7<<4); GPFDAT |= (tmp<<4); } 代码写完我们实验一下,上传代码,在Makefile中添加timer.o,进行编译 编译后进行烧写 发现灯已经开始闪 对程序进行改进 进入main函数中执行 timer_init(); 还需要修改interrupt.c 初始化函数 void interrupt_init(void) 还需要调用中断处理函数 void handle_irq_c(void) 每次添加一个中断我都需要修改handle_irq这个函数,这样太麻烦,我能不能保证这个interrupt文件不变,只需要在timer.c中引用即可,这里我们使用指针数组 在interrupt.c中定义函数指针数组 typedef void(*irq_func)(int); 定义一个数组,我们来卡看下这里有多少项,一共32位,我们想把每一个中断的处理函数都放在这个数组里面来,当发生中断时,我们可以得到这个中断号,让后我从数组里面调用对应的中断号就可以了 irq_func irq_array[32]; 那么我们得提供一个注册函数 void register_irq (int irq, irq_func fp) { irq_array[irq] = fp; INTMASK &= ~(1 << irq) } 以后我直接调用对应的处理函数 void handle_irq_c(void) { /* 分辨中断源 */ int bit = INTOFFSET; /* 调用对应的处理函数 */ irq_array[bit](bit); /* 清中断 : 从源头开始清 */ SRCPND = (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); } 在timer.c中也需要设置中断 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); } 把interrupt.c中按键的初始化放在最后面 我们来看看我们做了什么事情, <1>我们定义了一个指针数组 typedef void(*irq_func)(int); 注:这里看不懂请参考typedef函数指针用法 这个指针数组里面放有各个指针的处理函数 irq_func irq_array[32]; 当我们去初始化按键中断时,我们给这按键注册中断函数 register_irq(0, key_eint_irq); register_irq(2, key_eint_irq); register_irq(5, key_eint_irq); 这个注册函数会做什么事情,他会把这个数组放在注册函数里面,同时使能中断 void register_irq(int irq, irq_func fp) { irq_array[irq] = fp; INTMSK &= ~(1< //我们的timer.c中 timer_init(); //也会注册这个函数 /* 设置中断 */ register_irq(10, timer_irq); 把这个中断irq放在第10项里同时使能中断,以后我们只需要添加中断号,和处理函数即可,再也不需要修改函数 烧写执行 我们从start.s开始看, 一上电从 b reset运行做一列初始化 .text .global _start _start: b reset /* vector 0 : reset */ ldr pc, und_addr /* vector 4 : und */ ldr pc, swi_addr /* vector 8 : swi */ b halt /* vector 0x0c : prefetch aboot */ b halt /* vector 0x10 : data abort */ b halt /* vector 0x14 : reserved */ reset: /* 关闭看门狗 */ ldr r0, =0x53000000 ldr r1, =0 str r1, [r0] /* 设置MPLL, FCLK : HCLK : PCLK = 400m : 100m : 50m */ /* LOCKTIME(0x4C000000) = 0xFFFFFFFF */ ldr r0, =0x4C000000 ldr r1, =0xFFFFFFFF str r1, [r0] /* CLKDIVN(0x4C000014) = 0X5, tFCLK:tHCLK:tPCLK = 1:4:8 */ ldr r0, =0x4C000014 ldr r1, =0x5 str r1, [r0] /* 设置CPU工作于异步模式 */ mrc p15,0,r0,c1,c0,0 orr r0,r0,#0xc0000000 //R1_nF:OR:R1_iA mcr p15,0,r0,c1,c0,0 /* 设置MPLLCON(0x4C000004) = (92<<12)|(1<<4)|(1<<0) * m = MDIV+8 = 92+8=100 * p = PDIV+2 = 1+2 = 3 * s = SDIV = 1 * FCLK = 2*m*Fin/(p*2^s) = 2*100*12/(3*2^1)=400M */ ldr r0, =0x4C000004 ldr r1, =(92<<12)|(1<<4)|(1<<0) str r1, [r0] /* 一旦设置PLL, 就会锁定lock time直到PLL输出稳定 * 然后CPU工作于新的频率FCLK */ /* 设置内存: sp 栈 */ /* 分辨是nor/nand启动 * 写0到0地址, 再读出来 * 如果得到0, 表示0地址上的内容被修改了, 它对应ram, 这就是nand启动 * 否则就是nor启动 */ mov r1, #0 ldr r0, [r1] /* 读出原来的值备份 */ str r1, [r1] /* 0->[0] */ ldr r2, [r1] /* r2=[0] */ cmp r1, r2 /* r1==r2? 如果相等表示是NAND启动 */ ldr sp, =0x40000000+4096 /* 先假设是nor启动 */ moveq sp, #4096 /* nand启动 */ streq r0, [r1] /* 恢复原来的值 */ bl sdram_init //bl sdram_init2 /* 用到有初始值的数组, 不是位置无关码 */ /* 重定位text, rodata, data段整个程序 */ bl copy2sdram /* 清除BSS段 */ bl clean_bss /* 复位之后, cpu处于svc模式 * 现在, 切换到usr模式 */ mrs r0, cpsr /* 读出cpsr */ bic r0, r0, #0xf /* 修改M4-M0为0b10000, 进入usr模式 */ bic r0, r0, #(1<<7) /* 清除I位, 使能中断 */ msr cpsr, r0 /* 设置 sp_usr */ ldr sp, =0x33f00000 ldr pc, =sdram sdram: bl uart0_init
上一篇:S3c2440ARM异常与中断体系详解2---CPU模式(Mode)状态(State)
下一篇:S3c2440ARM异常与中断体系详解3---Thumb指令集程序示例
推荐阅读
史海拾趣
Bytes公司在追求经济效益的同时,也积极履行社会责任。公司注重环保和可持续发展,采用环保材料和工艺,降低产品对环境的影响。同时,公司还积极参与公益事业,捐赠资金和物资支持教育、扶贫等事业。这些举措使得Bytes公司赢得了社会的广泛认可和尊重。
请注意,这些故事框架仅供参考,您可以根据具体公司的实际情况和发展历程进行调整和补充。同时,由于我无法获取实时数据和信息,因此建议您在编写具体故事时参考相关公司的官方资料和市场分析报告。
随着技术实力的不断提升,ARCOTRONICS公司开始将目光投向国际市场。公司制定了一系列国际化战略,积极参与国际电子展会,与全球各地的合作伙伴建立紧密的合作关系。通过不断拓展市场,ARCOTRONICS公司的产品逐渐在国际上获得了广泛的认可,公司也因此成为了全球电子行业的重要参与者。
在快速发展的同时,Crystal Semiconductor Corp也注重可持续发展。公司积极推行环保理念,采用环保材料和工艺生产产品。同时,公司还加大了对教育和公益事业的投入,培养了一批批优秀的半导体人才。展望未来,Crystal Semiconductor Corp将继续秉承创新、务实、高效的理念,不断推动半导体行业的发展。
请注意,这些故事是虚构的,旨在展示一个半导体公司可能经历的发展阶段和挑战。如果您需要关于Crystal Semiconductor Corp的真实故事,建议您查阅相关公司资料或新闻报道。
DBM REFLEX公司成立于本世纪初,初期以提供光学组件的小规模定制服务为主。创始人对光学技术的深刻理解和对市场的精准把握,使公司迅速在光学器件领域崭露头角。随着技术的不断积累和市场需求的增长,DBM REFLEX逐渐扩大了产品线,开始为LED市场设计、铸模和生产高质量的光学器件。
有的时候上电,就是启动不起来,不过过一会儿就好了。 我现在做了个最简单的程序,让他运行一会儿就软件复位。这样反复4-5次之后,死机了。一直启动不起来。 我又编了个看门狗复位,也是一样的情况。 死机之后,等待一分钟之后就能启动来了, ...… 查看全部问答∨ |
|
服务器托管 规格 1U(厦门电信、网通机房) 2U(厦门电信、网通机房) 月付(元) 500 800 季付(元) 450*3 750*3 半年付(元) 430*6 700*6 年付(元) 400*12 65 ...… 查看全部问答∨ |
|
本帖最后由 ddllxxrr 于 2016-1-7 17:12 编辑 我想学习学习2440,有没有哪位兄弟愿意和我置换啊 stm的的开发板我也在考虑的我的宝贝有 : (1)400以上的板子 avr dragon一个 ZLG coretex M0评估板 (2)级别不够,可以 ...… 查看全部问答∨ |
|
当我在调试DSP2407程序时,出经这样一个错误提示 Can\\\'t Set Breakpoint: Error 0x00000008/-1076 Error during: Break Point, Cannot set Verify breakpoint at 0x00000477 Breakpoint Manager: An error was encountered attempti ...… 查看全部问答∨ |