本文来自s3c2440手册第10章,PWM Timer.
有5个16位定时器,Timer 0,1,2,3有PWM输出功能。Timer 4是内部定时器,没有输出引脚。Timer 0有一个dead-zone generator。
Timer 0,1共享一个8位预分频器,Timer 2,3,4共享另一个预分频器。每个定时器有自己的clock divider,产生5种不同的divided signals (1/2, 1/4, 1/8, 1/16, and TCLK)。每个定时器的timer block接收它自己的来自clock divider时钟信号。clock divider接收预分频器的信号。预分频器分频PCLK,预分频器可编程,通过TCFG0和TCFG1。
定时器计数缓存寄存器(TCNTBn)有一个初始化值,定时器开始时加载到down-counter。定时器比较缓存寄存器(TCMPBn)有一个初始值,加载到compare register。
每个定时器有自己的16位down counter,由定时器时钟驱动,当down counter减为0时,产生定时器中断,同时TCNBn自动装载进down counter,开始下一次定时。然而,如果定时器停止,例如在定时器运行过程中清除TCONn的对应位,TCNTBn不会装载进counter。
TCMPBn用来实现PWM。定时器控制逻辑改变输出电平,当down-counter的值等于compare register时。
prescaler & divider
二级分频,1级为8为预分频(1-256),二级为4位divider,1/2, 1/4, 1/8, 1/16 4种。
Auto reload & double buffering
外部寄存器TCNTBn和内部寄存器TCNTn,修改TCNTB不会改变TCNT,TCNT可设置为在达到0时自动加载。
Timer Initialization using manual update bit and inverter bit
步骤如下:
1 设置TCNTBn和TCMPBn的初始值。
2 设置manual update bit, 推荐设置inverter on/off 位。
3 设置start bit开始定时器,然后清除manual update bit
Ouput level control
TOUTn可以翻转,通过inverter on/off bit in TCON。
Dead zone generator
这是功率设备的特殊要求,配置成死区输出时,TOUT1脚输出nTOUT0_DZ。
DMA request mode
用来产生DMA请求,DMA模式的定时器不再产生定时器中断。
Timer configuration register 0 (TCFG0)
定时器输入时钟频率 = PCLK / (1 + prescalar value) / (divider value}
{prescalar value} = 0-255
{divider value} = 2, 4, 8, 16
Register | Address | R/W | Desc | Reset value |
TCFG0 | 0x51000000 | R/W | Configures the two 8-bit prescalers | 0x0 |
[31:24] 保留
[23:16] Dead zone length,8位,决定死亡区长度,1个单位是Timer 0的定时长度。
[15:8] prescaler 1
[7:0] prescaler 0
Timer configuration register 1 (TCFG1)
Register | Address | R/W | Desc | Reset value |
TCFG1 | 0x51000004 | R/W | 5-MUX & DMA mode selection register | 0x0 |
[31:24] reserved
[23:20] Select DMA request channel
0000 = No DMA, 0001 = Timer 0, 0010 = Timer 1, 0010 = Timer2
0100 = Timer 3, 0101 = Timer 4, 0110 = Reserved.
[19:16] MUX 4
select MUX Input for PWM Timer4
0000 = 1/2 0001 = 1/4 0010 = 1/8 0011 = 1/16 01xx = External TCLK1
[15:12] MUX 3
[11:8] MUX 2
[7:4] MUX 1
[3:0] MUX 0
Timer control register (TCON)
Register | Address | R/W | Desc | Reset value |
TCON | 0x51000008 | R/W | Timer Control register | 0x0 |
[22] Timer 4 auto reload on/off
0 = one-shot 1 = interval mode (auto reload)
[21] Timer 4 manual update, (需要再下一次写时清除)
0 = No operation 1 = Update TCNTB4
[20] Determine start/stop for Timer 4
0 = stop 1 = start for timer 4
[19] Timer 3 auto reload on/off
0 = one-shot 1 = interval mode (auto reload)
[18] Timer 3 output inverter on/off
0 = off, 1 = on
[17] Timer 3 manual update
[16] Timer 3 start / top
[15:12] Timer2
[11:8] Timer 1
[7:5] reserved
[4] dead zone enable
[3:0] Timer 0
TCNTBn/TCMPBn/TCNTOn (n:0-3)
Register | Address | R/W | Desc | Reset value |
TCNTB0 | 0x5100000C | R/W | Timer 0 count buffer register | 0x0 |
TCMPB0 | 0x51000010 | R/W | Timer 0 compare buffer register1 | 0 |
TCNTO0 | 0x51000014 | R | Timer 0 count observation register | 0 |
其它几个定时器的依次编号。地址连续。均为16位寄存器。
TCNTB4/TCNTO4
4没有PWN输出引脚,也即不需要PWN,因此没有TCMPB寄存器。开始地址接上面最后一个地址。
Register | Address | R/W | Desc | Reset value |
TCNTB4 | 0x5100003C | R/W | Timer 0 count buffer register | 0x0 |
TCNTO4 | 0x51000040 | R | Timer 0 count observation register | 0 |
均为16位寄存器。
使用Timer 4来产生中断,在中断中实现跑马的效果。
首先,取消Timer 4的中断屏蔽:
1 2 3 4 | void init_interrupt_controller() { intcont->INTMSK = ~ ((1<<14)); // timer 4 id: 14 } |
初始化Timer 4的参数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | struct S3C2440_PWM_TIMER { uint32_t TCFG0; uint32_t TCFG1; uint32_t TCON; struct { uint32_t TCNTBn; uint32_t TCMPBn; uint32_t TCNTOn; } TIMER0_3[4]; struct { uint32_t TCNTB4; uint32_t TCNTO4; } TIMER4; }; volatile struct S3C2440_PWM_TIMER * timer = ( volatile struct S3C2440_PWM_TIMER *)0x51000000; void pwm_timer_init() { #define HZ 10 timer->TCFG0 = 255 << 8; // Prescaler 1 [15:8] timer->TCFG1 = 0x3<<16; // MUX 4 [19: 16] 0011 = 1/16 timer->TIMER4.TCNTB4 = 67000000 / 255/ 16 / HZ; } void pwm_timer_start() { // first set manual update and auto reload bit timer->TCON = (1 << 22) | (1 << 21); // now start timer and clear manual update bit timer->TCON = (1 << 22) | (1 << 20); } |
整个初始化:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | void init() { disable_watchdog(); clk_init(); init_gpio(); init_interrupt_controller(); pwm_timer_init(); asm volatile ( "msr cpsr_c, #0xd2\n\t" // enter IRQ mode "ldr sp, =3072\n\t" // set IRQ mode stack "msr cpsr_c, #0x53" ); // re-enter svc mode and enable IRQ and FIQ pwm_timer_start(); } |
中断处理函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | int led = 4; void irq_handler() { unsigned long off = intcont->INTOFFSET; GPFDAT = ~(1 << led); led ++; if (led >= 7) led = 4; if (off == 5) { EINTPND = 1 << 11; } intcont->SRCPND = 1 << off; intcont->INTPND = 1 << off; } |