neorv32/docs/datasheet/soc_pwm.adoc

86 lines
4.2 KiB
Plaintext
Raw Permalink Normal View History

2024-02-24 08:25:27 +00:00
<<<
:sectnums:
==== Pulse-Width Modulation Controller (PWM)
[cols="<3,<3,<4"]
[frame="topbot",grid="none"]
|=======================
| Hardware source file(s): | neorv32_pwm.vhd |
| Software driver file(s): | neorv32_pwm.c |
| | neorv32_pwm.h |
| Top entity port: | `pwm_o` | PWM output channels (12-bit)
| Configuration generics: | `IO_PWM_NUM_CH` | number of PWM channels to implement (0..12)
| CPU interrupts: | none |
|=======================
**Overview**
**Overview**
The PWM module implements a pulse-width modulation controller with up to 12 independent channels providing
8-bit resolution per channel. The actual number of implemented channels is defined by the `IO_PWM_NUM_CH` generic.
Setting this generic to zero will completely remove the PWM controller from the design.
[NOTE]
The `pwm_o` has a static size of 12-bit. If less than 12 PWM channels are configured, only the LSB-aligned channel
bits are used while the remaining bits are hardwired to zero.
**Theory of Operation**
The PWM controller is activated by setting the `PWM_CTRL_EN` bit in the module's control register `CTRL`. When this
bit is cleared, the unit is reset and all PWM output channels are set to zero. The module
provides three duty cycle registers `DC[0]` to `DC[2]`. Each register contains the duty cycle configuration for four
consecutive channels. For example, the duty cycle of channel 0 is defined via bits 7:0 in `DC[0]`. The duty cycle of
channel 2 is defined via bits 15:0 in `DC[0]` and so on.
[NOTE]
Regardless of the configuration of `IO_PWM_NUM_CH` all module registers can be accessed without raising an exception.
Software can discover the number of available channels by writing 0xff to all duty cycle configuration bytes and
reading those values back. The duty-cycle of channels that were not implemented always reads as zero.
Based on the configured duty cycle the according intensity of the channel can be computed by the following formula:
_**Intensity~x~**_ = `DC[y](i*8+7 downto i*8)` / (2^8^)
The base frequency of the generated PWM signals is defined by the PWM core clock. This clock is derived
from the main processor clock and divided by a prescaler via the 3-bit `PWM_CTRL_PRSCx` in the unit's control
register.
.PWM prescaler configuration
[cols="<4,^1,^1,^1,^1,^1,^1,^1,^1"]
[options="header",grid="rows"]
|=======================
| **`PWM_CTRL_PRSCx`** | `0b000` | `0b001` | `0b010` | `0b011` | `0b100` | `0b101` | `0b110` | `0b111`
| Resulting `clock_prescaler` | 2 | 4 | 8 | 64 | 128 | 1024 | 2048 | 4096
|=======================
The resulting PWM carrier frequency is defined by:
_**f~PWM~**_ = _f~main~[Hz]_ / (2^8^ * `clock_prescaler`)
**Register Map**
.PWM register map (`struct neorv32_pwm_t`)
[cols="<4,<2,<6,^2,<8"]
[options="header",grid="all"]
|=======================
| Address | Name [C] | Bit(s), Name [C] | R/W | Function
.3+<| `0xfffff000` .3+<| `CTRL` <|`0` `PWM_CTRL_EN` ^| r/w <| PWM enable
<|`3:1` `PWM_CTRL_PRSC2 : PWM_CTRL_PRSC0` ^| r/w <| 3-bit clock prescaler select
<|`31:4` - ^| r/- <| _reserved_, read as zero
.4+<| `0xfffff004` .4+<| `DC[0]` <|`7:0` ^| r/w <| 8-bit duty cycle for channel 0
<|`15:8` ^| r/w <| 8-bit duty cycle for channel 1
<|`23:16` ^| r/w <| 8-bit duty cycle for channel 2
<|`31:24` ^| r/w <| 8-bit duty cycle for channel 3
.4+<| `0xfffff008` .4+<| `DC[1]` <|`7:0` ^| r/w <| 8-bit duty cycle for channel 4
<|`15:8` ^| r/w <| 8-bit duty cycle for channel 5
<|`23:16` ^| r/w <| 8-bit duty cycle for channel 6
<|`31:24` ^| r/w <| 8-bit duty cycle for channel 7
.4+<| `0xfffff00c` .4+<| `DC[2]` <|`7:0` ^| r/w <| 8-bit duty cycle for channel 8
<|`15:8` ^| r/w <| 8-bit duty cycle for channel 9
<|`23:16` ^| r/w <| 8-bit duty cycle for channel 10
<|`31:24` ^| r/w <| 8-bit duty cycle for channel 11
|=======================