aboutsummaryrefslogtreecommitdiffstats
path: root/docs/source/pwm.rst
blob: 8d3a8d98c1ce5d4245d5827e27290f698c2eb0c3 (plain)
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
.. _pwm:

==============================
 Pulse-Width Modulation (PWM)
==============================

Pulse Width Modulation is a basic technique to create repeated square
waves (digital high/low voltage transitions) of user defined length
and duty cycle. It can be used as a way to encode an "analog" signal
on a single digital (high/low) line using the time between transitions
("pulse width") as the variable; this technique is commonly used to
send servo position and motor speed commands. Another use is to use to
the ratio of "high" and "low" time to approximate a voltage output;
this technique can be used to dim an LED or even (with careful
filtering) generate audio waveforms.

.. contents:: Contents
   :local:

Overview
--------

The Maple has a large number of 16-bit PWM outputs, each connected to
one of 4 timers.  Some configuration, such as the clock rate or
prescaling, must be common to the entire timer; see the :ref:`timer
documentation <timers>` for more information.

Note that unlike the Arduino, the Maple does not have PWM
functionality on pin D10; all other pins are :ref:`compatible
<compatibility>`.

The following table shows which :ref:`timer <timers>` generates which
PWM outputs. See the :ref:`pin mapping table <pin-mapping-mega-table>`
to track down exactly which timer *channel* corresponds to each pin.

.. _pwm-timer-table:

.. csv-table::
   :header: Timer, PWM Headers
   :delim: |

   Timer1 | D6,D7,D8
   Timer2 | D0,D1,D2,D3
   Timer3 | D11,D12,D27,D28
   Timer4 | D5,D9,D14,D24 

Background
----------

In its simplest form, the device is a single counter with two
variables.  The counter starts at zero, and the output starts at
"high".  The counter increments every clock cycle until it reaches the
first variable number, at which point the output goes "low".  The
counter continues incrementing until it reaches the second variable at
which point the output goes "high" again and the counter resets to
zero. The time spent with output high is called the **pulse duration**
or **duty**; the total time before repeat is the **period**.

This simple functionality could be approximated in software by setting
a GPIO high or low, but the beauty of PWM is that user code simply has
to configure the device and set the two variables and the device will
function on its own; no further microprocessor cycles will be
consumed, and a repeated high/low waveform will spew out.

The Maple has 16-bit PWM resolution, which means that the counter and
variables can be as large as 65535, as opposed to 255 with 8-bit
resolution. With a 72MHz clock rate, a PWM output could have maximum
period of about one millisecond; using a :ref:`prescaler
<pwm-prescaler>` (clock divider) in front of the counter can increase
this maximum period.  Setting the :ref:`period <pwm-overflow>` to
something other than the maximum value gives further control over the
total length of the waveform.  However, this effectively limits the
resolution with which the duty can be modified: the duty must be less
than or equal to the period.

Here are some commonly used PWM configurations (note that servos are
notoriously variable, especially the lower cost models):

+-------------+----------+-----------+---------+---------------+------+
|**Purpose**  |**Period**|**Duty**   |Prescaler|Period         |Duty  |
|             |(ms)      |(ms)       |         |               |      |
+=============+==========+===========+=========+===============+======+
|LED throb    |0.020     |0--0.020   |1 (none) |65535 (default)|0--767|
|             |          |           |         |               |      |
+-------------+----------+-----------+---------+---------------+------+
|Servo control|20        |1.25 (0°)  |21       |65535 (default)|4096  |
|             |          |           |         |               |      |
|             |          |1.50 (90°) |21       |65535 (default)|4915  |
|             |          |           |         |               |      |
|             |          |1.75 (180°)|21       |65535 (default)|5734  |
|             |          |           |         |               |      |
+-------------+----------+-----------+---------+---------------+------+

Function Reference
------------------

``pinMode(pin_num, PWM)``

    This command is usually called from `setup()`_ to tell the
    microcontroller that pin_num should be configured to PWM
    output. ``PWM`` implies regular driven OUTPUT; ``PWM_OPEN_DRAIN`` is
    also available (see the list of :ref:`GPIO modes <gpio-modes>` for
    more information).

.. _pwm-pwmwrite:

``pwmWrite(pin_num, value)``

    This command sets the PWM duty. User code is expected to determine
    and honor the maximum value (based on the configured period). As a
    convenience, ``analogWrite`` is an alias for ``pwmWrite`` to ease
    porting Arduino code, though period and duty will have to be
    recalibrated (see :ref:`compatibility <compatibility>`).

.. _pwm-overflow:

``Timer1.setOverflow(overflow)``

    This function sets the period ("reload" or "overflow") value for
    an entire PWM timer bank. The value is 16bit (0 to 65535) and
    determines the maximum value that can be written with
    :ref:`pwmWrite() <pwm-pwmwrite>` corresponding to 100% duty
    cycle. This also affects the PWM frequency: the higher reload is,
    the lower the PWM frequency will be.

    The PWM output pin starts HIGH, then the timer begins counting up
    from zero (with frequency equal to 72MHz/:ref:`prescaler
    <pwm-prescaler>`) until it hits the duty value, at which point it
    drops to LOW.  The timer then continues counting up until it hits
    the total period (set with this function), at which point the
    cycle starts again.

.. _pwm-prescaler:

``Timer[1,2,3,4].setPrescaleFactor(prescale)``

    Find the appropriate timer for a given PWM header using the table
    :ref:`above <pwm-timer-table>`, then set the prescaler.  A
    prescaler is a clock divider.  The timer will normally count with
    frequency equal to the STM32's normal clock (72MHz); this
    corresponds to setting ``prescale`` to 1 (which is the default).

    If a longer frequency is desired, use a larger ``prescale`` value.
    For instance, an 8MHz frequency can be achieved by setting
    ``prescale`` to 9, since 72MHz / 9 = 8MHz.

    This function is normally called once from, `setup()`_, but the
    timer can be reconfigured with a new prescaler at any time.

Recommended Reading
-------------------

* `Wikipedia Article on Pulse-width modulation
  <http://en.wikipedia.org/wiki/Pulse-width_modulation>`_
* `Arduino tutorial on PWM <http://www.arduino.cc/en/Tutorial/PWM>`_
* `Secrets of Arduino PWM
  <http://www.arcfn.com/2009/07/secrets-of-arduino-pwm.html>`_ by Ken
  Shirriff
* `So You Want To Use PWM, Eh? <http://www.arcfn.com/2009/07/secrets-of-arduino-pwm.html>`_ at Non-Lexical Vocables
* STMicro documentation for STM32F103RB microcontroller:

  * `All <http://www.st.com/mcu/devicedocs-STM32F103RB-110.html>`_
  * `Datasheet (pdf) <http://www.st.com/stonline/products/literature/ds/13587.pdf>`_
  * `Reference Manual (pdf) <http://www.st.com/stonline/products/literature/rm/13902.pdf>`_