ILD

PWM Timer
作者:Herbert Yuan 邮箱:yuanjp89@163.com
发布时间:2017-11-5 站点:Inside Linux Development

本文来自s3c2440手册第10章,PWM Timer.


1 Overview

有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时。


2 PWM Timer operation

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模式的定时器不再产生定时器中断。


3 PWM Timer control register

Timer configuration register 0 (TCFG0)

定时器输入时钟频率 = PCLK / (1 + prescalar value) / (divider value}

{prescalar value} = 0-255

{divider value} = 2, 4, 8, 16


RegisterAddressR/WDescReset value
TCFG00x51000000R/WConfigures the two 8-bit prescalers0x0

[31:24] 保留

[23:16] Dead zone length,8位,决定死亡区长度,1个单位是Timer 0的定时长度。

[15:8]  prescaler 1

[7:0] prescaler 0


Timer configuration register 1 (TCFG1)

RegisterAddressR/WDescReset value
TCFG10x51000004R/W5-MUX & DMA mode selection register0x0

[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)

RegisterAddressR/WDescReset value
TCON0x51000008R/WTimer Control register0x0

[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)

RegisterAddressR/WDescReset value
TCNTB00x5100000CR/WTimer 0 count buffer register0x0
TCMPB00x51000010R/WTimer 0 compare buffer register1 0
TCNTO00x51000014RTimer 0 count observation register0

其它几个定时器的依次编号。地址连续。均为16位寄存器。


TCNTB4/TCNTO4

4没有PWN输出引脚,也即不需要PWN,因此没有TCMPB寄存器。开始地址接上面最后一个地址。

RegisterAddressR/WDescReset value
TCNTB40x5100003CR/WTimer 0 count buffer register0x0
TCNTO40x51000040RTimer 0 count observation register0

均为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;
}

Copyright © linuxdev.cc 2017-2024. Some Rights Reserved.