Skip to content

Commit

Permalink
updated helicopter motor page
Browse files Browse the repository at this point in the history
  • Loading branch information
Hunter Adams committed Nov 13, 2024
1 parent 11e2e14 commit f7b9ff1
Show file tree
Hide file tree
Showing 8 changed files with 32 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,8 @@
"\n",
"By the end of the lab session in week one of the lab you must have:\n",
"\n",
"- The mechanical assembly finished and be able to control motor speed open loop, from the PWM output. [Here is some demo code](https://github.com/vha3/Hunter-Adams-RP2040-Demos/tree/master/PWM/PWM_Demo) to get you started.\n",
"- **You will build your motor control circuit on a solderboard, not on the breadboard!**\n",
"- The mechanical assembly finished and be able to control motor speed open loop, from the PWM output. [Here is some demo code](https://github.com/vha3/Hunter-Adams-RP2040-Demos/tree/master/PWM/PWM_Demo) to get you started. Remember that you'll need to add one line which inverts the PWM output! See the [motor circuit page](https://vanhunteradams.com/Pico/Helicopter/MotorCircuit.html).\n",
"- **You will build your motor control circuit on a solderboard, not on the breadboard!** See [Bruce's guide](https://people.ece.cornell.edu/land/courses/ece4760/Analog/motor_controller/index_motor.html), also linked on the [motor circuit page](https://vanhunteradams.com/Pico/Helicopter/MotorCircuit.html).\n",
"- *Finishing a checkpoint does NOT mean you can leave lab early!*\n",
"\n",
"#### Week two checkpoint\n",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
"### Overview of circuit\n",
"The motor circuit must protect the microcontroller from the large $L \\frac{dI}{dt}$ voltage spikes that come off of a PWM-driven DC motor. The 4N35 optoisolator completely isolates the MCU from the motor. The 1N4001 snubber diode provides a path to ground for reverse-polarity spikes coming off the motor, and the capacitor in parallel with the 1N4001 provides a path to ground for higher frequency noise. Some of the components in this circuit require some experimentation/trial and error. The resistor attached to the base of the 4N35 should be set for best falltime, probably ~1MOhm. The capacitor in parallel with the motor should be ceramic (electrolytics are too slow) and should start with a value ~0.1uF. If there is too much spike noise on the analog input, this value can be increased. Note that we're driving the gate of the power mosfet at 12V, and the motor at 5-6V.\n",
"\n",
"Note that this circuit has the benefit of putting the BJT into high-gain switch mode, but inverts the PWM signal from the microcontroller. Fortunately, a bit in the PWM control register for the RP2040 allows for us to invert the output.\n",
"\n",
"<figure>\n",
" <img align=\"center\" width=\"700\" height=\"500\" src=\"motorcircuit.png\" alt='missing' />\n",
" <center><figcaption></figcaption></center>\n",
Expand Down Expand Up @@ -76,7 +78,7 @@
"source": [
"### Generating PWM\n",
"\n",
"The PWM chapter in the [RP2040 datasheet](https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf) is short, read it!! The RP2040's PWM block is organized into \"slices\", each of which is connected to two GPIO ports. All 30 GPIO ports can be driven by the PWM block, but only 16 can be driven independently. Each PWM slice is equipped with a 16-bit counter, 8.4 fractional clock divider, two independent output channels, dual slope and trailing edge modulation, interrupt request and DMA request on counter wrap, and advanceable/retardable phase. Furthermore, each can be used in *input mode* to measure the duty cycle and frequency of an input signal.\n",
"The PWM chapter in the [RP2040 datasheet](https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf) is short, read it!! The RP2040's PWM block is organized into \"slices\", each of which is connected to two GPIO ports. All 30 GPIO ports can be driven by the PWM block, but only 16 can be driven independently. Each PWM slice is equipped with a 16-bit counter, 8.4 fractional clock divider, two independent output channels, dual slope and trailing edge modulation, interrupt request and DMA request on counter wrap, and advanceable/retardable phase. Furthermore, each can be used in *input mode* to measure the duty cycle and frequency of an input signal. Each PWM channel can also be configured such that its output is inverted, which is handy for our application!\n",
"\n",
"The following code shows how to configure a PWM channel to throw an interrupt each time it wraps. We first map a particular GPIO (in this case, GPIO 5) to the PWM block. We use an SDK function to obtain the PWM slice associated with that GPIO (we could alternatively have looked this up in the datasheet). Then, we clear the interrupt associated with that GPIO slice, enable it, configure it such that it enters the ISR called `on_pwm_wrap()` each time the PWM wraps, end then enable the interrupt with the `PWM_IRQ_WRAP` interrupt flag.\n",
"\n",
Expand Down Expand Up @@ -105,6 +107,12 @@
"pwm_set_wrap(slice_num, WRAPVAL) ;\n",
"pwm_set_clkdiv(slice_num, CLKDIV) ;\n",
"\n",
"// Invert?\n",
"// First argument is slice number.\n",
"// If second argument is true, channel A is inverted.\n",
"// If third argument is true, channel B is inverted.\n",
"pwm_set_output_polarity (slice_num, 0, 1) \n",
"\n",
"// This sets duty cycle\n",
"pwm_set_chan_level(slice_num, PWM_CHAN_B, 3125);\n",
"\n",
Expand Down
4 changes: 2 additions & 2 deletions Pico/Helicopter/Helicopter.html
Original file line number Diff line number Diff line change
Expand Up @@ -13185,8 +13185,8 @@ <h2 id="Program-organization">Program organization<a class="anchor-link" href="#
<h2 id="Weekly-checkpoints-and-lab-report">Weekly checkpoints and lab report<a class="anchor-link" href="#Weekly-checkpoints-and-lab-report">&#182;</a></h2><p>Note that these checkpoints are <strong>cumulative</strong>. In week 2, for example, you must have also completed all of the requirements from week 1.</p>
<h4 id="Week-one-checkpoint">Week one checkpoint<a class="anchor-link" href="#Week-one-checkpoint">&#182;</a></h4><p>By the end of the lab session in week one of the lab you must have:</p>
<ul>
<li>The mechanical assembly finished and be able to control motor speed open loop, from the PWM output. <a href="https://github.com/vha3/Hunter-Adams-RP2040-Demos/tree/master/PWM/PWM_Demo">Here is some demo code</a> to get you started.</li>
<li><strong>You will build your motor control circuit on a solderboard, not on the breadboard!</strong></li>
<li>The mechanical assembly finished and be able to control motor speed open loop, from the PWM output. <a href="https://github.com/vha3/Hunter-Adams-RP2040-Demos/tree/master/PWM/PWM_Demo">Here is some demo code</a> to get you started. Remember that you'll need to add one line which inverts the PWM output! See the <a href="https://vanhunteradams.com/Pico/Helicopter/MotorCircuit.html">motor circuit page</a>.</li>
<li><strong>You will build your motor control circuit on a solderboard, not on the breadboard!</strong> See <a href="https://people.ece.cornell.edu/land/courses/ece4760/Analog/motor_controller/index_motor.html">Bruce's guide</a>, also linked on the <a href="https://vanhunteradams.com/Pico/Helicopter/MotorCircuit.html">motor circuit page</a>.</li>
<li><em>Finishing a checkpoint does NOT mean you can leave lab early!</em></li>
</ul>
<h4 id="Week-two-checkpoint">Week two checkpoint<a class="anchor-link" href="#Week-two-checkpoint">&#182;</a></h4><p>By the end of the lab session in week two of the lab you must have:</p>
Expand Down
4 changes: 2 additions & 2 deletions Pico/Helicopter/Helicopter.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,8 @@
"\n",
"By the end of the lab session in week one of the lab you must have:\n",
"\n",
"- The mechanical assembly finished and be able to control motor speed open loop, from the PWM output. [Here is some demo code](https://github.com/vha3/Hunter-Adams-RP2040-Demos/tree/master/PWM/PWM_Demo) to get you started.\n",
"- **You will build your motor control circuit on a solderboard, not on the breadboard!**\n",
"- The mechanical assembly finished and be able to control motor speed open loop, from the PWM output. [Here is some demo code](https://github.com/vha3/Hunter-Adams-RP2040-Demos/tree/master/PWM/PWM_Demo) to get you started. Remember that you'll need to add one line which inverts the PWM output! See the [motor circuit page](https://vanhunteradams.com/Pico/Helicopter/MotorCircuit.html).\n",
"- **You will build your motor control circuit on a solderboard, not on the breadboard!** See [Bruce's guide](https://people.ece.cornell.edu/land/courses/ece4760/Analog/motor_controller/index_motor.html), also linked on the [motor circuit page](https://vanhunteradams.com/Pico/Helicopter/MotorCircuit.html).\n",
"- *Finishing a checkpoint does NOT mean you can leave lab early!*\n",
"\n",
"#### Week two checkpoint\n",
Expand Down
9 changes: 8 additions & 1 deletion Pico/Helicopter/MotorCircuit.html
Original file line number Diff line number Diff line change
Expand Up @@ -13093,6 +13093,7 @@ <h1 id="DC-motor-circuit-and-PWM-on-RP2040">DC motor circuit and PWM on RP2040<a
</div><div class="inner_cell">
<div class="text_cell_render border-box-sizing rendered_html">
<h3 id="Overview-of-circuit">Overview of circuit<a class="anchor-link" href="#Overview-of-circuit">&#182;</a></h3><p>The motor circuit must protect the microcontroller from the large $L \frac{dI}{dt}$ voltage spikes that come off of a PWM-driven DC motor. The 4N35 optoisolator completely isolates the MCU from the motor. The 1N4001 snubber diode provides a path to ground for reverse-polarity spikes coming off the motor, and the capacitor in parallel with the 1N4001 provides a path to ground for higher frequency noise. Some of the components in this circuit require some experimentation/trial and error. The resistor attached to the base of the 4N35 should be set for best falltime, probably ~1MOhm. The capacitor in parallel with the motor should be ceramic (electrolytics are too slow) and should start with a value ~0.1uF. If there is too much spike noise on the analog input, this value can be increased. Note that we're driving the gate of the power mosfet at 12V, and the motor at 5-6V.</p>
<p>Note that this circuit has the benefit of putting the BJT into high-gain switch mode, but inverts the PWM signal from the microcontroller. Fortunately, a bit in the PWM control register for the RP2040 allows for us to invert the output.</p>
<figure>
<img align="center" width="700" height="500" src="motorcircuit.png" alt='missing' />
<center><figcaption></figcaption></center>
Expand Down Expand Up @@ -13139,7 +13140,7 @@ <h3 id="Building-and-debugging-the-circuit">Building and debugging the circuit<a
<div class="cell border-box-sizing text_cell rendered"><div class="prompt input_prompt">
</div><div class="inner_cell">
<div class="text_cell_render border-box-sizing rendered_html">
<h3 id="Generating-PWM">Generating PWM<a class="anchor-link" href="#Generating-PWM">&#182;</a></h3><p>The PWM chapter in the <a href="https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf">RP2040 datasheet</a> is short, read it!! The RP2040's PWM block is organized into "slices", each of which is connected to two GPIO ports. All 30 GPIO ports can be driven by the PWM block, but only 16 can be driven independently. Each PWM slice is equipped with a 16-bit counter, 8.4 fractional clock divider, two independent output channels, dual slope and trailing edge modulation, interrupt request and DMA request on counter wrap, and advanceable/retardable phase. Furthermore, each can be used in <em>input mode</em> to measure the duty cycle and frequency of an input signal.</p>
<h3 id="Generating-PWM">Generating PWM<a class="anchor-link" href="#Generating-PWM">&#182;</a></h3><p>The PWM chapter in the <a href="https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf">RP2040 datasheet</a> is short, read it!! The RP2040's PWM block is organized into "slices", each of which is connected to two GPIO ports. All 30 GPIO ports can be driven by the PWM block, but only 16 can be driven independently. Each PWM slice is equipped with a 16-bit counter, 8.4 fractional clock divider, two independent output channels, dual slope and trailing edge modulation, interrupt request and DMA request on counter wrap, and advanceable/retardable phase. Furthermore, each can be used in <em>input mode</em> to measure the duty cycle and frequency of an input signal. Each PWM channel can also be configured such that its output is inverted, which is handy for our application!</p>
<p>The following code shows how to configure a PWM channel to throw an interrupt each time it wraps. We first map a particular GPIO (in this case, GPIO 5) to the PWM block. We use an SDK function to obtain the PWM slice associated with that GPIO (we could alternatively have looked this up in the datasheet). Then, we clear the interrupt associated with that GPIO slice, enable it, configure it such that it enters the ISR called <code>on_pwm_wrap()</code> each time the PWM wraps, end then enable the interrupt with the <code>PWM_IRQ_WRAP</code> interrupt flag.</p>
<p>Finally, we configure the wrapvalue and clock divider for the PWM channel, set the level (i.e. duty cycle) and start the channel.</p>
<p>Depending what you're trying to do with the PWM channel, you might omit some of these configurations.</p>
Expand All @@ -13163,6 +13164,12 @@ <h3 id="Generating-PWM">Generating PWM<a class="anchor-link" href="#Generating-P
<span class="n">pwm_set_wrap</span><span class="p">(</span><span class="n">slice_num</span><span class="p">,</span> <span class="n">WRAPVAL</span><span class="p">)</span> <span class="p">;</span>
<span class="n">pwm_set_clkdiv</span><span class="p">(</span><span class="n">slice_num</span><span class="p">,</span> <span class="n">CLKDIV</span><span class="p">)</span> <span class="p">;</span>

<span class="c1">// Invert?</span>
<span class="c1">// First argument is slice number.</span>
<span class="c1">// If second argument is true, channel A is inverted.</span>
<span class="c1">// If third argument is true, channel B is inverted.</span>
<span class="n">pwm_set_output_polarity</span> <span class="p">(</span><span class="n">slice_num</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>

<span class="c1">// This sets duty cycle</span>
<span class="n">pwm_set_chan_level</span><span class="p">(</span><span class="n">slice_num</span><span class="p">,</span> <span class="n">PWM_CHAN_B</span><span class="p">,</span> <span class="mi">3125</span><span class="p">);</span>

Expand Down
10 changes: 9 additions & 1 deletion Pico/Helicopter/MotorCircuit.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
"### Overview of circuit\n",
"The motor circuit must protect the microcontroller from the large $L \\frac{dI}{dt}$ voltage spikes that come off of a PWM-driven DC motor. The 4N35 optoisolator completely isolates the MCU from the motor. The 1N4001 snubber diode provides a path to ground for reverse-polarity spikes coming off the motor, and the capacitor in parallel with the 1N4001 provides a path to ground for higher frequency noise. Some of the components in this circuit require some experimentation/trial and error. The resistor attached to the base of the 4N35 should be set for best falltime, probably ~1MOhm. The capacitor in parallel with the motor should be ceramic (electrolytics are too slow) and should start with a value ~0.1uF. If there is too much spike noise on the analog input, this value can be increased. Note that we're driving the gate of the power mosfet at 12V, and the motor at 5-6V.\n",
"\n",
"Note that this circuit has the benefit of putting the BJT into high-gain switch mode, but inverts the PWM signal from the microcontroller. Fortunately, a bit in the PWM control register for the RP2040 allows for us to invert the output.\n",
"\n",
"<figure>\n",
" <img align=\"center\" width=\"700\" height=\"500\" src=\"motorcircuit.png\" alt='missing' />\n",
" <center><figcaption></figcaption></center>\n",
Expand Down Expand Up @@ -76,7 +78,7 @@
"source": [
"### Generating PWM\n",
"\n",
"The PWM chapter in the [RP2040 datasheet](https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf) is short, read it!! The RP2040's PWM block is organized into \"slices\", each of which is connected to two GPIO ports. All 30 GPIO ports can be driven by the PWM block, but only 16 can be driven independently. Each PWM slice is equipped with a 16-bit counter, 8.4 fractional clock divider, two independent output channels, dual slope and trailing edge modulation, interrupt request and DMA request on counter wrap, and advanceable/retardable phase. Furthermore, each can be used in *input mode* to measure the duty cycle and frequency of an input signal.\n",
"The PWM chapter in the [RP2040 datasheet](https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf) is short, read it!! The RP2040's PWM block is organized into \"slices\", each of which is connected to two GPIO ports. All 30 GPIO ports can be driven by the PWM block, but only 16 can be driven independently. Each PWM slice is equipped with a 16-bit counter, 8.4 fractional clock divider, two independent output channels, dual slope and trailing edge modulation, interrupt request and DMA request on counter wrap, and advanceable/retardable phase. Furthermore, each can be used in *input mode* to measure the duty cycle and frequency of an input signal. Each PWM channel can also be configured such that its output is inverted, which is handy for our application!\n",
"\n",
"The following code shows how to configure a PWM channel to throw an interrupt each time it wraps. We first map a particular GPIO (in this case, GPIO 5) to the PWM block. We use an SDK function to obtain the PWM slice associated with that GPIO (we could alternatively have looked this up in the datasheet). Then, we clear the interrupt associated with that GPIO slice, enable it, configure it such that it enters the ISR called `on_pwm_wrap()` each time the PWM wraps, end then enable the interrupt with the `PWM_IRQ_WRAP` interrupt flag.\n",
"\n",
Expand Down Expand Up @@ -105,6 +107,12 @@
"pwm_set_wrap(slice_num, WRAPVAL) ;\n",
"pwm_set_clkdiv(slice_num, CLKDIV) ;\n",
"\n",
"// Invert?\n",
"// First argument is slice number.\n",
"// If second argument is true, channel A is inverted.\n",
"// If third argument is true, channel B is inverted.\n",
"pwm_set_output_polarity (slice_num, 0, 1) \n",
"\n",
"// This sets duty cycle\n",
"pwm_set_chan_level(slice_num, PWM_CHAN_B, 3125);\n",
"\n",
Expand Down
Binary file modified Pico/Helicopter/motorcircuit.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified Pico/Helicopter/motorcircuit_start.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit f7b9ff1

Please sign in to comment.