PWM — Pulse width modulation

The PWM module enables the generation of pulse width modulated signals on GPIO. The
module implements an up or up-and-down counter with four PWM channels that drive assigned
GPIOs.

Three PWM modules can provide up to 12 PWM channels with individual frequency control in groups
of up to four channels. Furthermore, a built-in decoder and EasyDMA capabilities make it possible
to manipulate the PWM duty cycles without CPU intervention. Arbitrary duty-cycle sequences are
read from Data RAM and can be chained to implement ping-pong buffering or repeated into complex
loops.

Listed here are the main features of one PWM module:

Fixed PWM base frequency with programmable clock divider

Up to four PWM channels with individual polarity and duty-cycle values

Edge or center-aligned pulses across PWM channels

Multiple duty-cycle arrays (sequences) defined in Data RAM

Autonomous and glitch-free update of duty cycle values directly from memory through
EasyDMA

Change of polarity, duty-cycle, and base frequency possibly on every PWM period

Data RAM sequences can be repeated or connected into loops

Figure 1.
PWM Module

Wave counter

The wave counter is responsible for generating the pulses at a duty-cycle that depends
on the compare values, and at a frequency that depends on COUNTERTOP.

There is one common 15-bit counter with four compare channels. Thus, all four channels will
share the same period (PWM frequency), but can have individual duty-cycle and polarity. The
polarity is set by the value read from RAM (see Figure 4), while the MODE
register controls if the counter counts up, or up and down. The timer top value is controlled by
the COUNTERTOP register. This register value in conjunction with the selected PRESCALER of the
PWM_CLK will result in a given PWM period. A COUNTERTOP value smaller than the compare setting
will result in a state where no PWM edges are generated. Respectively, OUT[n] is held high, given
that the polarity is set to FallingEdge. All the compare registers are internal and can only be
configured through the decoder presented later.

COUNTERTOP can be safely written at any time. It will get sampled following a START task. If
DECODER.LOAD is anything else than WaveForm, it will also get sampled following a STARTSEQ[n]
task, and when loading a new value from RAM during a sequence playback. If DECODER.LOAD=WaveForm,
the register value is ignored, and taken from RAM instead (see Decoder with EasyDMA below).

Figure 2 shows the counter operating in up
(MODE=PWM_MODE_Up) mode with three PWM channels with the same frequency but different duty cycle.
The counter is automatically reset to zero when COUNTERTOP is reached and OUT[n] will invert.
OUT[n] is held low if the compare value is 0 and held high respectively if set to COUNTERTOP
given that the polarity is set to FallingEdge. Running in up counter mode will result in pulse
widths that are edge-aligned. See the code example below:

In up counting mode, the following formula can be used to compute PWM period and step size:

PWM period: TPWM(Up)=
TPWM_CLK* COUNTERTOP

Step width/Resolution: Tsteps=
TPWM_CLK

Figure 3 shows the counter
operating in up and down mode with (MODE=PWM_MODE_UpAndDown) two PWM channels with the same
frequency but different duty cycle and output polarity. The counter starts decrementing to zero
when COUNTERTOP is reached and will invert the OUT[n] when compare value is hit for the second
time. This results in a set of pulses that are center- aligned.

In up-and-down counting modes, the following formula can be used to compute PWM period and step
size: TPWM(Up And Down) =
TPWM_CLK * 2 * COUNTERTOP

Step width/Resolution: Tsteps =
TPWM_CLK * 2

Decoder with EasyDMA

The decoder uses EasyDMA to take PWM parameters stored in Data RAM by ways of EasyDMA
and updates the internal compare registers of the wave counter based on the mode of operation.

The mentioned PWM parameters are organized into a sequence containing at least one half word
(16 bit). Its most significant bit[15] denotes the polarity of the OUT[n] while bit[14:0] is the
15-bit compare value. See below for further details of these RAM defined registers.

Bit number

31

30

29

28

27

26

25

24

23

22

21

20

19

18

17

16

15

14

13

12

11

10

9

8

7

6

5

4

3

2

1

0

Id

B

A

A

A

A

A

A

A

A

A

A

A

A

A

A

A

Reset 0x00000000

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

Id

RW

Field

Value Id

Value

Description

A

RW

COMPARE

Duty cycle setting - value loaded to internal compare register

B

RW

POLARITY

Edge polarity of GPIO.

RisingEdge

0

First edge within the PWM period is rising

FallingEdge

1

First edge within the PWM period is falling

The DECODER register controls how the RAM content is interpreted and loaded to the internal
compare registers. The LOAD field can be used to control if the RAM values are loaded to all
compare channels - or alternatively to update a group or all channels with individual values.
Figure 4 illustrates how the parameters
stored in RAM are organized and routed to the various compare channels in the different
modes.

A special mode of operation is available when DECODER.LOAD is set to WaveForm. In this mode, up
to three PWM channels can be enabled - OUT[0] to OUT[2]. In RAM, four values are loaded at a
time: the first, second and third location are used to load the values, and the fourth RAM
location is used to load the COUNTERTOP register. This way one can have up to three PWM channels
with a frequency base that changes on a per PWM period basis. This mode of operation is useful
for arbitrary wave form generation in applications such as LED lighting.

The register SEQ[n].REFRESH=N (one per sequence n=0 or 1) will instruct a new RAM stored pulse
width value on every (N+1)th PWM period. Setting the register to zero will result in a
new duty cycle update every PWM period as long as the minimum PWM period is observed.

Note that registers SEQ[n].REFRESH and SEQ[n].ENDDELAY are ignored when DECODER.MODE=NextStep . The next value is loaded upon receiving every NEXTSTEP task.

Figure 4. Decoder memory access modes

SEQ[n].PTR is the pointer used to fetch COMPARE values from RAM. If the SEQ[n].PTR is not
pointing to the Data RAM region, an EasyDMA transfer may result in a HardFault or RAM corruption.
See Memory for more information about the
different memory regions.

After the SEQ[n].PTR is set to the desired RAM location, the SEQ[n].CNT register must be set to the
number of 16-bit half words in the sequence. It is important to observe that the Grouped and
Single modes require one half word per group or one half word per channel respectively, and thus
increases RAM size occupation. If PWM generation was not running yet at that point, sending the
SEQSTART[n] task will load the first value from RAM, then start the PWM generation. A SEQSTARTED[n]
event is generated as soon as the EasyDMA has read the first PWM parameter from RAM and the wave
counter has started executing it. When LOOP.CNT=0, sequence n=0 or 1 is played back once.
After the last value in the sequence has been loaded and started executing, a SEQEND[n] event is
generated. The PWM generation will then continue with the last loaded value. See Figure 5 for an example of such simple
playback.

To completely stop the PWM generation and force the associated pins to a defined state, a STOP
task can be fired at any time. A STOPPED event is generated when the PWM generation has stopped
at the end of currently running PWM period, and the pins go into their idle state as defined in
GPIO->OUT. PWM generation can then only be restarted through a SEQSTART[n] task.
SEQSTART[n] will resume PWM generation after having loaded the first value from the RAM buffer defined
in the SEQ[n].PTR register.

The table below provides indication of when specific registers get sampled by the hardware. Care
should be taken when updating these registers to avoid values to be applied earlier than expected.

Table 1. When to safely update PWM registers

Register

Taken into account by hardware

Recommended (safe) update

SEQ[n].PTR

When sending the SEQSTART[n] task

After having received the SEQSTARTED[n] event

SEQ[n].CNT

When sending the SEQSTART[n] task

After having received the SEQSTARTED[n] event

SEQ[0].ENDDELAY

When sending the SEQSTART[0] task

Every time a new value from sequence [0] has been loaded from RAM and gets
applied to the Wave Counter (indicated by the PWMPERIODEND event)

Before starting sequence [0] through a SEQSTART[0] task

When no more value from sequence [0] gets loaded from RAM (indicated by the
SEQEND[0] event)

At any time during sequence [1] (which starts when the SEQSTARTED[1] event
is fired)

SEQ[1].ENDDELAY

When sending the SEQSTART[1] task

Every time a new value from sequence [1] has been loaded from RAM and gets
applied to the Wave Counter (indicated by the PWMPERIODEND event)

Before starting sequence [1] through a SEQSTART[1] task

When no more value from sequence [1] gets loaded from RAM (indicated by the
SEQEND[1] event)

At any time during sequence [0] (which starts when the SEQSTARTED[0] event
is fired)

SEQ[0].REFRESH

When sending the SEQSTART[0] task

Every time a new value from sequence [0] has been loaded from RAM and gets
applied to the Wave Counter (indicated by the PWMPERIODEND event)

Before starting sequence [0] through a SEQSTART[0] task

At any time during sequence [1] (which starts when the SEQSTARTED[1] event
is fired)

SEQ[1].REFRESH

When sending the SEQSTART[1] task

Every time a new value from sequence [1] has been loaded from RAM and gets
applied to the Wave Counter (indicated by the PWMPERIODEND event)

Before starting sequence [1] through a SEQSTART[1] task

At any time during sequence [0] (which starts when the SEQSTARTED[0] event
is fired)

COUNTERTOP

In DECODER.LOAD=WaveForm: this register is ignored.

In all other LOAD modes: at the end of current PWM period (indicated by the
PWMPERIODEND event)

Before starting PWM generation through a SEQSTART[n] task

After a STOP task has been issued, and the STOPPED event has been received.

MODE

Immediately

Before starting PWM generation through a SEQSTART[n] task

After a STOP task has been issued, and the STOPPED event has been received.

DECODER

Immediately

Before starting PWM generation through a SEQSTART[n] task

After a STOP task has been issued, and the STOPPED event has been received.

PRESCALER

Immediately

Before starting PWM generation through a SEQSTART[n] task

After a STOP task has been issued, and the STOPPED event has been received.

LOOP

Immediately

Before starting PWM generation through a SEQSTART[n] task

After a STOP task has been issued, and the STOPPED event has been received.

PSEL.OUT[n]

Immediately

Before enabling the PWM instance through the ENABLE register

Important: SEQ[n].REFRESH and SEQ[n].ENDDELAY are ignored at the end of a complex
sequence, indicated by a LOOPSDONE event. The reason for this is that the last value loaded from
RAM is maintained until further action from software (restarting a new sequence, or stopping PWM
generation).

Figure 5 depicts the source code used
for configuration and timing details in a sequence where only sequence 0 is used and only run
once with a new PWM duty cycle for each period.

A more complex example is shown in Figure 6, where LOOP.CNT>0 . In this case, an automated playback takes place, consisting
of SEQ[0], delay 0, SEQ[1], delay 1, then again SEQ[0], etc. The user can choose to start a
complex playback with SEQ[0] or SEQ[1] through sending the SEQSTART[0] or SEQSTART[1] task.

The complex playback always ends with delay 1.

The two sequences 0 and 1 are defined with address of values tables in Data RAM (pointed by
SEQ[n].PTR) and respective buffer size (SEQ[n].CNT). The rate at which a new value is loaded is
defined individually for each sequence by SEQ[n].REFRESH . The chaining of sequence 1 following
sequence 0 is implicit, the LOOP.CNT register allows the chaining of sequence 1 to sequence 0
for a determined number of times. In other words, it allows to repeat a complex sequence a number
of times in a fully automated way.

In the example below, sequence 0 is defined with SEQ[0].REFRESH set to one - that means that a
new PWM duty cycle is pushed every second PWM period. This complex sequence is started with the
SEQSTART[0] task, so SEQ[0] is played first. Since SEQ[0].ENDDELAY=1 there will be one PWM period
delay between last period on sequence 0 and the first period on sequence 1. Since SEQ[1].ENDDELAY=0
there is no delay 1, so SEQ[0] would be started immediately after the end of SEQ[1]. However, as
LOOP.CNT is one, the playback stops after having played only once SEQ[1], and both SEQEND[1] and
LOOPSDONE are generated (their order is not guaranteed in this case).

The decoder can also be configured to asynchronously load a new PWM duty cycle. If the
DECODER.MODE register is set to NextStep - then the NEXTSTEP task will cause an update of the
internal compare registers on the next PWM period.

The figures below provide an overview of each part of an arbitrary sequence, in various modes
(LOOP.CNT=0 and LOOP.CNT>0). In particular are represented:

Initial and final duty cycle on the PWM output(s)

Chaining of SEQ[0] and SEQ[1] if LOOP.CNT>0

Influence of registers on the sequence

Events fired during a sequence

DMA activity (loading of next value and applying it to the output(s))

Note that the single-shot example applies also to SEQ[1], only SEQ[0] is represented for
simplicity.

Figure 7. Single shot (LOOP.CNT=0)

Figure 8. Complex sequence (LOOP.CNT>0) starting with SEQ[0]

Figure 9. Complex sequence (LOOP.CNT>0) starting with SEQ[1]

Note that if a sequence is in use in a simple or complex sequence, it must have a length of SEQ[n].CNT > 0 .

Limitations

The previous compare value will be repeated if the PWM period is selected to be shorter
than the time it takes for the EasyDMA to fetch from RAM and update the internal compare
registers.

This is to ensure a glitch-free operation even if very short PWM periods are chosen.

Pin configuration

The OUT[n] (n=0..3) signals associated to each channel of the PWM module are mapped to
physical pins according to the configuration specified in the respective PSEL.OUT[n] registers. If
a PSEL.OUT[n].CONNECT is set to Disconnected, the associated PWM module signal will not be
connected to any physical pins.

The PSEL.OUT[n] registers and their configurations are only used as long as the PWM module is
enabled and PWM generation is active (wave counter started), and retained only as long as the
device is in System ON mode, see POWER
chapter for more information about power modes.

To ensure correct behaviour in the PWM module, the pins used by the PWM module must be
configured in the GPIO peripheral as described in Table 2 before enabling the PWM module. The pins' idle state is defined by the OUT
registers in the GPIO module. This is to ensure that the pins used by the PWM module are driven
correctly, if PWM generation is stopped through a STOP task, the PWM module itself is temporarily
disabled, or the device temporarily enters System OFF. This configuration must be retained in the
GPIO for the selected IOs as long as the PWM module is supposed to be connected to an external
PWM circuit.

Only one peripheral can be assigned to drive a particular GPIO pin at a time. Failing to do so
may result in unpredictable behaviour.

Stops PWM pulse generation on all channels at the end of current PWM period, and stops sequence playback

TASKS_SEQSTART[0]

0x008

Loads the first PWM value on all enabled channels from sequence 0, and starts playing that sequence at the rate defined in SEQ[0]REFRESH and/or DECODER.MODE. Causes PWM generation to start it was not running.

TASKS_SEQSTART[1]

0x00C

Loads the first PWM value on all enabled channels from sequence 1, and starts playing that sequence at the rate defined in SEQ[1]REFRESH and/or DECODER.MODE. Causes PWM generation to start it was not running.

TASKS_NEXTSTEP

0x010

Steps by one value in the current sequence on all enabled channels if DECODER.MODE=NextStep. Does not cause PWM generation to start it was not running.

EVENTS_STOPPED

0x104

Response to STOP task, emitted when PWM pulses are no longer generated

EVENTS_SEQSTARTED[0]

0x108

First PWM period started on sequence 0

EVENTS_SEQSTARTED[1]

0x10C

First PWM period started on sequence 1

EVENTS_SEQEND[0]

0x110

Emitted at end of every sequence 0, when last value from RAM has been applied to wave counter

EVENTS_SEQEND[1]

0x114

Emitted at end of every sequence 1, when last value from RAM has been applied to wave counter

EVENTS_PWMPERIODEND

0x118

Emitted at the end of each PWM period

EVENTS_LOOPSDONE

0x11C

Concatenated sequences have been played the amount of times defined in LOOP.CNT