一、定时器大体结构
查看芯片手册,可以找到以下定时器结构框图
从做到右看,对该图进行分析:(不考虑)
Prescaler:定时器0和1共享一个8位分频器,而定时器2、3、4共享另一个8位分频器。分频器将输入的PCLK分频为:PCLK/(prescaler+1)。
Clock divider & MUX:每个定时器有一个时钟分频器,它产生5个不同的分频信号(1/2,1/4,1/8,1/16,和TCLK)。每个定时器块从时钟分频器接收自己的时钟信号,时钟分频器从相应的8位分频器接收时钟。8位分频器是可编程的,根据加载值对PCLK进行划分,存储在TCFG0和TCFG1寄存器中。此时定时器的时钟频率为:Timer input clock Frequency = PCLK / (prescaler+1) / divider value。
Control logic:计数缓冲区寄存器(TCNTBn)有一初始值,当计时器启用时,该初始值将加载到减计数器中。比较缓冲区寄存器(TCMPBn)有一初始值,它被加载到比较寄存器中,以便与减计数器的值进行比较。TCNTBn和TCMPBn的双缓冲特性使定时器在改变频率和占空比时产生稳定的输出。每个定时器有它自己的16位减计数器,由Timer input clock Frequency 进行计数。当减计数器达到0时,产生定时器中断请求,通知CPU定时器中断。当发生中断时,TCNTBn的值自动加载到下一个计数器中,继续下一个操作。其中可以通过在计时器运行模式期间通过清除计时器启用TCONn的位让计时器停止,TCNTBn的值将不会重新加载到计数器中。
TOUTn & Dead Zone Generator:S3C2440有五个16位定时器。定时器0、1、2、3有脉冲宽度调制(PWM)功能。定时器4只有一个没有输出引脚的内部定时器。定时器0有一个死区发生器。
*PS:
比较缓冲区寄存器(TCMPBn)用于PWM发生使用
计时器重新加载操作是自动发生的,减计数器达到0。
prescaler value = 0~255 ;divider value = 2, 4, 8, 16
关于死区的内容在之后的博文中讲到(挖坑)
二、定时器中断程序设计
I.初始化定时器中断
这个可以类比外部中断操作(因为同样为irq模式),所以重复寄存器的具体作用在上一篇博客S3C2440 开发板实战(4):外部中断中有提到。
在这里,我们设定终极目标是间隔0.5秒执行一次定时器中断函数。
Setp 1: 打开CPSR中 中断总开关
mrs r0, cpsr
bic r0, r0, #0xf //将模式设置为user模式
bic r0, r0, (1<<7) //外部中断IRQ打开
msr cpsr, r0 //user模式
ldr sp, =0x33f00000 //设置user的栈(没啥用只是和以后的中断地址做比较)
Step 2: INTERRUPT MASK (INTMSK) REGISTER
void init_EINT(void)
{
INTMSK &= ~(1<<10);
// 按键对应的中断: TIMER ->对应的位为:10
}
Step 3: 配置定时器中断参数
这里以PCLK=50Mhz为例,对于2440来说时钟的配置很简单,具体的时钟设置请看S3C2440 开发板实战(2):start.S初认识 + SDRAM配置 + 重定位的第二部分。
按照公式:Timer input clock Frequency = PCLK / (prescaler+1) / divider value。我们设置prescaler = 99;divider value = 16。可以计算得到定时器时钟的频率为31250hz。所以若要定时1s,计数器装在初值要为31250。按照这个参数配置,设置定时器寄存器如下:
1. TIMER CONFIGURATION REGISTER0 (TCFG0)
作用:配置两个8位 prescaler,配置死区长度
Do:将 prescaler0 置为99
2. TIMER CONFIGURATION REGISTER1 (TCFG1)
作用:5-MUX & DMA模式选择
Do: 将Timer对应的Clock divider配置为16分频
3. TIMER 0 COUNT BUFFER REGISTER(TCNTB0)
作用:设置减计数器的初值
Do: 由终极目标可以看出设定的初值为31250 / 2 =15625
4. TIMER CONTROL (TCON) REGISTER
作用: 更新减计数器的初值、设置为自动装载、启动、输出逆变器
Do: 先手动更新减计数器的初值,然后设置为自动装载并且启动
* 其中 manual update 这位需要在使用之后清0(下一次写入前清0)
综上可得。定时器初始化配置函数如下所示:
void init_timer(void)
{ //TCLK = PCLK/(99+1)/16 = 31250
TCFG0 = 0x63; //prescaler = 99
TCFG1 &= ~(0xf<<0);
TCFG1 |= (3<<0); //1/16 divider
TCNTB0 = 15625; // count 15625 (0.5s)
TCON |= (1<<1); // updata TCNTB0 to coutor
TCON &= ~(1<<1); // CLEAR
TCON |= ((1<<0) | (1<<3)); // AUTO RELOAD & START
}
II. 中断入口函数
和上一篇中相同,直接上代码!
do_irq:
/* 执行到这里之前:
* 1. lr_und保存有被中断模式中的下一条即将执行的指令的地址
* 2. SPSR_irq保存有被中断模式的CPSR
* 3. CPSR中的M4-M0被设置为10010, 进入到irq模式
* 4. 跳到0x18的地方执行程序
*/
// 1.保护现场
ldr sp, =0x33d00000
/* 在irq异常处理函数中有可能会修改r0-r12, 所以先保存 */
/* lr-4 是异常处理完后的返回地址, 也要保存 */
sub lr, lr, #4
stmdb sp!, {r0-r12, lr}
// 2.处理中断函数
bl Find_interrupt_source
/* 3.恢复现场 */
ldmia sp!, {r0-r12, pc}^ /* ^会把spsr的值恢复到cpsr里 */
III. 设置定时器中断函数
在上一篇中我们设计了一个外部中断的函数模板,所以在这里引用进行延展,所以定时器中断函数
void Find_interrupt_source(void)
{
puts("begin_interruptnr");
int bit = INTOFFSET;
if(bit == 10)
timer_interrupt_fun();
/* 清中断 : 从源头开始清 */
SRCPND = (1< void timer_interrupt_fun(void) { led_control_NOT(4); } void led_control_NOT(int val) { unsigned int val_DATF = GPFDAT; if(val_DATF & (1 << val)) GPFDAT &= ~(1 << val); else GPFDAT |= (1 << val); } 运行完就能看见有一个灯在闪烁了,这是我第一次手撸代码解完bug直接实现目标哈哈哈。虽然有了外部中断的程序上修改但是自己动手难道不是兴奋的事情?
上一篇:S3C2440 开发板实战(6):网络配置 + 设置NFS
下一篇:S3C2440 开发板实战(3):编译概念 + LED点亮闪烁
推荐阅读最新更新时间:2024-11-11 19:11
设计资源 培训 开发板 精华推荐
- 基于带有电流互感器和低电阻分流器的 AD7751 的防篡改瓦时电能表
- LT3825 演示板,具有同步整流功能的隔离式反激式转换器
- 亮——Fly
- LTM9012 演示板、14 位、125Msps 四路模块 ADC + 驱动器
- 支持蓝牙技术的高精度体温测量柔性 PCB 贴片参考设计
- USB isolator
- 基于ST1S41IPHR的、具有使能功能的4A、850kHz固定频率PWM同步降压演示板
- KEA128和MC10XS3425照明控制和电子开关
- LT1072CS8 升压型升压转换器的典型应用
- TWR-S08LL64、MC9S08LL 基于 MC9S08LL64 MCU 的 8 位段式 LCD 塔式系统模块