1 |
60 |
zero_gravi |
<<<
|
2 |
|
|
:sectnums:
|
3 |
|
|
==== Pulse-Width Modulation Controller (PWM)
|
4 |
|
|
|
5 |
|
|
[cols="<3,<3,<4"]
|
6 |
|
|
[frame="topbot",grid="none"]
|
7 |
|
|
|=======================
|
8 |
|
|
| Hardware source file(s): | neorv32_pwm.vhd |
|
9 |
|
|
| Software driver file(s): | neorv32_pwm.c |
|
10 |
|
|
| | neorv32_pwm.h |
|
11 |
|
|
| Top entity port: | `pwm_o` | up to 60 PWM output channels (1-bit per channel)
|
12 |
|
|
| Configuration generics: | _IO_PWM_NUM_CH_ | number of PWM channels to implement (0..60)
|
13 |
|
|
| CPU interrupts: | none |
|
14 |
|
|
|=======================
|
15 |
|
|
|
16 |
|
|
The PWM controller implements a pulse-width modulation controller with up to 60 independent channels and 8-
|
17 |
|
|
bit resolution per channel. The actual number of implemented channels is defined by the _IO_PWM_NUM_CH_ generic.
|
18 |
|
|
Setting this generic to zero will completely remove the PWM controller from the design.
|
19 |
|
|
|
20 |
|
|
The PWM controller is based on an 8-bit base counter with a programmable threshold comparators for each channel
|
21 |
|
|
that defines the actual duty cycle. The controller can be used to drive fancy RGB-LEDs with 24-
|
22 |
|
|
bit true color, to dim LCD back-lights or even for "analog" control. An external integrator (RC low-pass filter)
|
23 |
|
|
can be used to smooth the generated "analog" signals.
|
24 |
|
|
|
25 |
|
|
**Theory of Operation**
|
26 |
|
|
|
27 |
64 |
zero_gravi |
The PWM controller is activated by setting the _PWM_CTRL_EN_ bit in the module's control register `CTRL`. When this
|
28 |
60 |
zero_gravi |
bit is cleared, the unit is reset and all PWM output channels are set to zero.
|
29 |
|
|
The 8-bit duty cycle for each channel, which represents the channel's "intensity", is defined via an 8-bit value. The module
|
30 |
64 |
zero_gravi |
provides up to 15 duty cycle registers `DUTY[0]` to `DUTY[14]` (depending on the number of implemented channels).
|
31 |
60 |
zero_gravi |
Each register contains the duty cycle configuration for 4 consecutive channels. For example, the duty cycle of channel 0
|
32 |
64 |
zero_gravi |
is defined via bits 7:0 in `DUTY[0]`. The duty cycle of channel 2 is defined via bits 15:0 in `DUTY[0]`.
|
33 |
|
|
Channel 4's duty cycle is defined via bits 7:0 in `DUTY[1]` and so on.
|
34 |
60 |
zero_gravi |
|
35 |
|
|
[NOTE]
|
36 |
|
|
Regardless of the configuration of _IO_PWM_NUM_CH_ all module registers can be accessed without raising an exception.
|
37 |
|
|
Software can discover the number of available channels by writing 0xff to all duty cycle configuration bytes and
|
38 |
|
|
reading those values back. The duty-cycle of channels that were not implemented always reads as zero.
|
39 |
|
|
|
40 |
|
|
Based on the configured duty cycle the according intensity of the channel can be computed by the following formula:
|
41 |
|
|
|
42 |
64 |
zero_gravi |
_**Intensity~x~**_ = `DUTY[y](i*8+7 downto i*8)` / (2^8^)
|
43 |
60 |
zero_gravi |
|
44 |
|
|
The base frequency of the generated PWM signals is defined by the PWM core clock. This clock is derived
|
45 |
64 |
zero_gravi |
from the main processor clock and divided by a prescaler via the 3-bit PWM_CTRL_PRSCx in the unit's control
|
46 |
66 |
zero_gravi |
register. The following pre-scalers are available:
|
47 |
60 |
zero_gravi |
|
48 |
|
|
.PWM prescaler configuration
|
49 |
|
|
[cols="<4,^1,^1,^1,^1,^1,^1,^1,^1"]
|
50 |
|
|
[options="header",grid="rows"]
|
51 |
|
|
|=======================
|
52 |
64 |
zero_gravi |
| **`PWM_CTRL_PRSCx`** | `0b000` | `0b001` | `0b010` | `0b011` | `0b100` | `0b101` | `0b110` | `0b111`
|
53 |
60 |
zero_gravi |
| Resulting `clock_prescaler` | 2 | 4 | 8 | 64 | 128 | 1024 | 2048 | 4096
|
54 |
|
|
|=======================
|
55 |
|
|
|
56 |
|
|
The resulting PWM base frequency is defined by:
|
57 |
|
|
|
58 |
|
|
_**f~PWM~**_ = _f~main~[Hz]_ / (2^8^ * `clock_prescaler`)
|
59 |
|
|
|
60 |
|
|
<<<
|
61 |
64 |
zero_gravi |
.PWM register map (`struct neorv32_pwm_t`)
|
62 |
60 |
zero_gravi |
[cols="<4,<4,<6,^2,<8"]
|
63 |
|
|
[options="header",grid="all"]
|
64 |
|
|
|=======================
|
65 |
|
|
| Address | Name [C] | Bit(s), Name [C] | R/W | Function
|
66 |
64 |
zero_gravi |
.4+<| `0xfffffe80` .4+<| `NEORV32_PWM.CTRL` <|`0` _PWM_CTRL_EN_ ^| r/w | PWM enable
|
67 |
|
|
<|`1` _PWM_CTRL_PRSC0_ ^| r/w .3+<| 3-bit clock prescaler select
|
68 |
|
|
<|`2` _PWM_CTRL_PRSC1_ ^| r/w
|
69 |
|
|
<|`3` _PWM_CTRL_PRSC2_ ^| r/w
|
70 |
|
|
.4+<| `0xfffffe84` .4+<| `NEORV32_PWM.DUTY[0]` <|`7:0` ^| r/w <| 8-bit duty cycle for channel 0
|
71 |
|
|
<|`15:8` ^| r/w <| 8-bit duty cycle for channel 1
|
72 |
|
|
<|`23:16` ^| r/w <| 8-bit duty cycle for channel 2
|
73 |
|
|
<|`31:24` ^| r/w <| 8-bit duty cycle for channel 3
|
74 |
|
|
| ... | ... | ... | r/w | ...
|
75 |
|
|
.4+<| `0xfffffebc` .4+<| `NEORV32_PWM.DUTY[14]` <|`7:0` ^| r/w <| 8-bit duty cycle for channel 56
|
76 |
|
|
<|`15:8` ^| r/w <| 8-bit duty cycle for channel 57
|
77 |
|
|
<|`23:16` ^| r/w <| 8-bit duty cycle for channel 58
|
78 |
|
|
<|`31:24` ^| r/w <| 8-bit duty cycle for channel 59
|
79 |
60 |
zero_gravi |
|=======================
|