├── .gitignore
├── LICENSE
├── README.md
├── firmware
└── src
│ ├── Button.cpp
│ ├── Button.h
│ ├── CMakeLists.txt
│ ├── CommonLogic.cpp
│ ├── CommonLogic.h
│ ├── ConfigOpts.h
│ ├── Debug.h
│ ├── Display.cpp
│ ├── Display.h
│ ├── DutyCycle.h
│ ├── Flash.cpp
│ ├── Flash.h
│ ├── Pico.cpp
│ ├── Pico.h
│ ├── Potentiometer.h
│ ├── PotentiometerLogic.cpp
│ ├── PotentiometerLogic.h
│ ├── Presets.cpp
│ ├── Presets.h
│ ├── Pwm.pio
│ ├── RotaryEncoder.h
│ ├── RotaryLogic.cpp
│ ├── RotaryLogic.h
│ ├── TwoButtonLogic.cpp
│ ├── TwoButtonLogic.h
│ ├── Uart.cpp
│ ├── Uart.h
│ ├── Utils.h
│ ├── config.h.in
│ ├── main.cpp
│ └── pico_sdk_import.cmake
├── img
├── ThrottleBlaster.svg
├── ThrottleBlaster_PCB_back.jpg
├── ThrottleBlaster_PCB_front.jpg
├── ThrottleBlaster_breadboard.jpg
├── ThrottleBlaster_pcb.jpg
├── button2_states.fig
├── button2_states.png
├── button_states.fig
├── button_states.png
├── diagram.png
├── rotary_states.fig
├── rotary_states.png
├── stpclk_pin_slot1.jpg
├── stpclk_pin_socket370.jpg
├── stpclk_pin_socket7.jpg
└── stpclk_pin_socketA.jpg
└── kicad
├── ThrottleBlaster.kicad_pcb
├── ThrottleBlaster.lib
├── ThrottleBlaster.pretty
└── raspberry_pi_pico.kicad_mod
├── ThrottleBlaster.pro
├── ThrottleBlaster.sch
├── fp-lib-table
└── sym-lib-table
/.gitignore:
--------------------------------------------------------------------------------
1 | build/*
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 2, June 1991
3 |
4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6 | Everyone is permitted to copy and distribute verbatim copies
7 | of this license document, but changing it is not allowed.
8 |
9 | Preamble
10 |
11 | The licenses for most software are designed to take away your
12 | freedom to share and change it. By contrast, the GNU General Public
13 | License is intended to guarantee your freedom to share and change free
14 | software--to make sure the software is free for all its users. This
15 | General Public License applies to most of the Free Software
16 | Foundation's software and to any other program whose authors commit to
17 | using it. (Some other Free Software Foundation software is covered by
18 | the GNU Lesser General Public License instead.) You can apply it to
19 | your programs, too.
20 |
21 | When we speak of free software, we are referring to freedom, not
22 | price. Our General Public Licenses are designed to make sure that you
23 | have the freedom to distribute copies of free software (and charge for
24 | this service if you wish), that you receive source code or can get it
25 | if you want it, that you can change the software or use pieces of it
26 | in new free programs; and that you know you can do these things.
27 |
28 | To protect your rights, we need to make restrictions that forbid
29 | anyone to deny you these rights or to ask you to surrender the rights.
30 | These restrictions translate to certain responsibilities for you if you
31 | distribute copies of the software, or if you modify it.
32 |
33 | For example, if you distribute copies of such a program, whether
34 | gratis or for a fee, you must give the recipients all the rights that
35 | you have. You must make sure that they, too, receive or can get the
36 | source code. And you must show them these terms so they know their
37 | rights.
38 |
39 | We protect your rights with two steps: (1) copyright the software, and
40 | (2) offer you this license which gives you legal permission to copy,
41 | distribute and/or modify the software.
42 |
43 | Also, for each author's protection and ours, we want to make certain
44 | that everyone understands that there is no warranty for this free
45 | software. If the software is modified by someone else and passed on, we
46 | want its recipients to know that what they have is not the original, so
47 | that any problems introduced by others will not reflect on the original
48 | authors' reputations.
49 |
50 | Finally, any free program is threatened constantly by software
51 | patents. We wish to avoid the danger that redistributors of a free
52 | program will individually obtain patent licenses, in effect making the
53 | program proprietary. To prevent this, we have made it clear that any
54 | patent must be licensed for everyone's free use or not licensed at all.
55 |
56 | The precise terms and conditions for copying, distribution and
57 | modification follow.
58 |
59 | GNU GENERAL PUBLIC LICENSE
60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61 |
62 | 0. This License applies to any program or other work which contains
63 | a notice placed by the copyright holder saying it may be distributed
64 | under the terms of this General Public License. The "Program", below,
65 | refers to any such program or work, and a "work based on the Program"
66 | means either the Program or any derivative work under copyright law:
67 | that is to say, a work containing the Program or a portion of it,
68 | either verbatim or with modifications and/or translated into another
69 | language. (Hereinafter, translation is included without limitation in
70 | the term "modification".) Each licensee is addressed as "you".
71 |
72 | Activities other than copying, distribution and modification are not
73 | covered by this License; they are outside its scope. The act of
74 | running the Program is not restricted, and the output from the Program
75 | is covered only if its contents constitute a work based on the
76 | Program (independent of having been made by running the Program).
77 | Whether that is true depends on what the Program does.
78 |
79 | 1. You may copy and distribute verbatim copies of the Program's
80 | source code as you receive it, in any medium, provided that you
81 | conspicuously and appropriately publish on each copy an appropriate
82 | copyright notice and disclaimer of warranty; keep intact all the
83 | notices that refer to this License and to the absence of any warranty;
84 | and give any other recipients of the Program a copy of this License
85 | along with the Program.
86 |
87 | You may charge a fee for the physical act of transferring a copy, and
88 | you may at your option offer warranty protection in exchange for a fee.
89 |
90 | 2. You may modify your copy or copies of the Program or any portion
91 | of it, thus forming a work based on the Program, and copy and
92 | distribute such modifications or work under the terms of Section 1
93 | above, provided that you also meet all of these conditions:
94 |
95 | a) You must cause the modified files to carry prominent notices
96 | stating that you changed the files and the date of any change.
97 |
98 | b) You must cause any work that you distribute or publish, that in
99 | whole or in part contains or is derived from the Program or any
100 | part thereof, to be licensed as a whole at no charge to all third
101 | parties under the terms of this License.
102 |
103 | c) If the modified program normally reads commands interactively
104 | when run, you must cause it, when started running for such
105 | interactive use in the most ordinary way, to print or display an
106 | announcement including an appropriate copyright notice and a
107 | notice that there is no warranty (or else, saying that you provide
108 | a warranty) and that users may redistribute the program under
109 | these conditions, and telling the user how to view a copy of this
110 | License. (Exception: if the Program itself is interactive but
111 | does not normally print such an announcement, your work based on
112 | the Program is not required to print an announcement.)
113 |
114 | These requirements apply to the modified work as a whole. If
115 | identifiable sections of that work are not derived from the Program,
116 | and can be reasonably considered independent and separate works in
117 | themselves, then this License, and its terms, do not apply to those
118 | sections when you distribute them as separate works. But when you
119 | distribute the same sections as part of a whole which is a work based
120 | on the Program, the distribution of the whole must be on the terms of
121 | this License, whose permissions for other licensees extend to the
122 | entire whole, and thus to each and every part regardless of who wrote it.
123 |
124 | Thus, it is not the intent of this section to claim rights or contest
125 | your rights to work written entirely by you; rather, the intent is to
126 | exercise the right to control the distribution of derivative or
127 | collective works based on the Program.
128 |
129 | In addition, mere aggregation of another work not based on the Program
130 | with the Program (or with a work based on the Program) on a volume of
131 | a storage or distribution medium does not bring the other work under
132 | the scope of this License.
133 |
134 | 3. You may copy and distribute the Program (or a work based on it,
135 | under Section 2) in object code or executable form under the terms of
136 | Sections 1 and 2 above provided that you also do one of the following:
137 |
138 | a) Accompany it with the complete corresponding machine-readable
139 | source code, which must be distributed under the terms of Sections
140 | 1 and 2 above on a medium customarily used for software interchange; or,
141 |
142 | b) Accompany it with a written offer, valid for at least three
143 | years, to give any third party, for a charge no more than your
144 | cost of physically performing source distribution, a complete
145 | machine-readable copy of the corresponding source code, to be
146 | distributed under the terms of Sections 1 and 2 above on a medium
147 | customarily used for software interchange; or,
148 |
149 | c) Accompany it with the information you received as to the offer
150 | to distribute corresponding source code. (This alternative is
151 | allowed only for noncommercial distribution and only if you
152 | received the program in object code or executable form with such
153 | an offer, in accord with Subsection b above.)
154 |
155 | The source code for a work means the preferred form of the work for
156 | making modifications to it. For an executable work, complete source
157 | code means all the source code for all modules it contains, plus any
158 | associated interface definition files, plus the scripts used to
159 | control compilation and installation of the executable. However, as a
160 | special exception, the source code distributed need not include
161 | anything that is normally distributed (in either source or binary
162 | form) with the major components (compiler, kernel, and so on) of the
163 | operating system on which the executable runs, unless that component
164 | itself accompanies the executable.
165 |
166 | If distribution of executable or object code is made by offering
167 | access to copy from a designated place, then offering equivalent
168 | access to copy the source code from the same place counts as
169 | distribution of the source code, even though third parties are not
170 | compelled to copy the source along with the object code.
171 |
172 | 4. You may not copy, modify, sublicense, or distribute the Program
173 | except as expressly provided under this License. Any attempt
174 | otherwise to copy, modify, sublicense or distribute the Program is
175 | void, and will automatically terminate your rights under this License.
176 | However, parties who have received copies, or rights, from you under
177 | this License will not have their licenses terminated so long as such
178 | parties remain in full compliance.
179 |
180 | 5. You are not required to accept this License, since you have not
181 | signed it. However, nothing else grants you permission to modify or
182 | distribute the Program or its derivative works. These actions are
183 | prohibited by law if you do not accept this License. Therefore, by
184 | modifying or distributing the Program (or any work based on the
185 | Program), you indicate your acceptance of this License to do so, and
186 | all its terms and conditions for copying, distributing or modifying
187 | the Program or works based on it.
188 |
189 | 6. Each time you redistribute the Program (or any work based on the
190 | Program), the recipient automatically receives a license from the
191 | original licensor to copy, distribute or modify the Program subject to
192 | these terms and conditions. You may not impose any further
193 | restrictions on the recipients' exercise of the rights granted herein.
194 | You are not responsible for enforcing compliance by third parties to
195 | this License.
196 |
197 | 7. If, as a consequence of a court judgment or allegation of patent
198 | infringement or for any other reason (not limited to patent issues),
199 | conditions are imposed on you (whether by court order, agreement or
200 | otherwise) that contradict the conditions of this License, they do not
201 | excuse you from the conditions of this License. If you cannot
202 | distribute so as to satisfy simultaneously your obligations under this
203 | License and any other pertinent obligations, then as a consequence you
204 | may not distribute the Program at all. For example, if a patent
205 | license would not permit royalty-free redistribution of the Program by
206 | all those who receive copies directly or indirectly through you, then
207 | the only way you could satisfy both it and this License would be to
208 | refrain entirely from distribution of the Program.
209 |
210 | If any portion of this section is held invalid or unenforceable under
211 | any particular circumstance, the balance of the section is intended to
212 | apply and the section as a whole is intended to apply in other
213 | circumstances.
214 |
215 | It is not the purpose of this section to induce you to infringe any
216 | patents or other property right claims or to contest validity of any
217 | such claims; this section has the sole purpose of protecting the
218 | integrity of the free software distribution system, which is
219 | implemented by public license practices. Many people have made
220 | generous contributions to the wide range of software distributed
221 | through that system in reliance on consistent application of that
222 | system; it is up to the author/donor to decide if he or she is willing
223 | to distribute software through any other system and a licensee cannot
224 | impose that choice.
225 |
226 | This section is intended to make thoroughly clear what is believed to
227 | be a consequence of the rest of this License.
228 |
229 | 8. If the distribution and/or use of the Program is restricted in
230 | certain countries either by patents or by copyrighted interfaces, the
231 | original copyright holder who places the Program under this License
232 | may add an explicit geographical distribution limitation excluding
233 | those countries, so that distribution is permitted only in or among
234 | countries not thus excluded. In such case, this License incorporates
235 | the limitation as if written in the body of this License.
236 |
237 | 9. The Free Software Foundation may publish revised and/or new versions
238 | of the General Public License from time to time. Such new versions will
239 | be similar in spirit to the present version, but may differ in detail to
240 | address new problems or concerns.
241 |
242 | Each version is given a distinguishing version number. If the Program
243 | specifies a version number of this License which applies to it and "any
244 | later version", you have the option of following the terms and conditions
245 | either of that version or of any later version published by the Free
246 | Software Foundation. If the Program does not specify a version number of
247 | this License, you may choose any version ever published by the Free Software
248 | Foundation.
249 |
250 | 10. If you wish to incorporate parts of the Program into other free
251 | programs whose distribution conditions are different, write to the author
252 | to ask for permission. For software which is copyrighted by the Free
253 | Software Foundation, write to the Free Software Foundation; we sometimes
254 | make exceptions for this. Our decision will be guided by the two goals
255 | of preserving the free status of all derivatives of our free software and
256 | of promoting the sharing and reuse of software generally.
257 |
258 | NO WARRANTY
259 |
260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268 | REPAIR OR CORRECTION.
269 |
270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278 | POSSIBILITY OF SUCH DAMAGES.
279 |
280 | END OF TERMS AND CONDITIONS
281 |
282 | How to Apply These Terms to Your New Programs
283 |
284 | If you develop a new program, and you want it to be of the greatest
285 | possible use to the public, the best way to achieve this is to make it
286 | free software which everyone can redistribute and change under these terms.
287 |
288 | To do so, attach the following notices to the program. It is safest
289 | to attach them to the start of each source file to most effectively
290 | convey the exclusion of warranty; and each file should have at least
291 | the "copyright" line and a pointer to where the full notice is found.
292 |
293 | {description}
294 | Copyright (C) {year} {fullname}
295 |
296 | This program is free software; you can redistribute it and/or modify
297 | it under the terms of the GNU General Public License as published by
298 | the Free Software Foundation; either version 2 of the License, or
299 | (at your option) any later version.
300 |
301 | This program is distributed in the hope that it will be useful,
302 | but WITHOUT ANY WARRANTY; without even the implied warranty of
303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304 | GNU General Public License for more details.
305 |
306 | You should have received a copy of the GNU General Public License along
307 | with this program; if not, write to the Free Software Foundation, Inc.,
308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
309 |
310 | Also add information on how to contact you by electronic and paper mail.
311 |
312 | If the program is interactive, make it output a short notice like this
313 | when it starts in an interactive mode:
314 |
315 | Gnomovision version 69, Copyright (C) year name of author
316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
317 | This is free software, and you are welcome to redistribute it
318 | under certain conditions; type `show c' for details.
319 |
320 | The hypothetical commands `show w' and `show c' should show the appropriate
321 | parts of the General Public License. Of course, the commands you use may
322 | be called something other than `show w' and `show c'; they could even be
323 | mouse-clicks or menu items--whatever suits your program.
324 |
325 | You should also get your employer (if you work as a programmer) or your
326 | school, if any, to sign a "copyright disclaimer" for the program, if
327 | necessary. Here is a sample; alter the names:
328 |
329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program
330 | `Gnomovision' (which makes passes at compilers) written by James Hacker.
331 |
332 | {signature of Ty Coon}, 1 April 1989
333 | Ty Coon, President of Vice
334 |
335 | This General Public License does not permit incorporating your program into
336 | proprietary programs. If your program is a subroutine library, you may
337 | consider it more useful to permit linking proprietary applications with the
338 | library. If this is what you want to do, use the GNU Lesser General
339 | Public License instead of this License.
340 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ThrottleBlaster
2 |
3 | A Pi Pico-based solution that reduces the effective frequency of fast CPUs by pulling down the STPCLK# pin at a specified frequency and duty-cycle.
4 |
5 | This allows you to play speed-sensitive games, like Digger on a 1200MHz Athlon!
6 |
7 |
8 |
9 |
10 |
11 | Videos:
12 | - Part 3 (rev 0.3): https://www.youtube.com/watch?v=g4OluJwGDEQ
13 | - Part 2: https://www.youtube.com/watch?v=nGy8OmOe_34
14 | - Part 1: https://www.youtube.com/watch?v=9uNml2j6sy0
15 |
16 | - Mounted on a 5.25 panel: https://www.youtube.com/shorts/n1aFvRNtOcw (by Michael Swimm)
17 | - Controllable by serial: https://www.youtube.com/watch?v=71rln-R2mis (by Michael Swimm)
18 |
19 | Download firmware (ThrottleBlaster.uf2): https://github.com/scrapcomputing/ThrottleBlaster/releases
20 |
21 | Download gerbers (ThrottleBlaster_gerbers_rev.X.X.zip): https://github.com/scrapcomputing/ThrottleBlaster/releases
22 |
23 | A front panel design by Michael Swimm: https://www.thingiverse.com/thing:6619261
24 |
25 | # How it works
26 | The Throttle Blaster is a fancy PWM controller that pulls the CPU's STPCLK# to ground.
27 |
28 |
29 |
30 | It is tailored to the needs of vintage PC enthusiasts, so it drives a 4-digit 7-segment display and is pre-loaded with presets that span several hardware generations.
31 |
32 |
33 | # Features
34 | - Four modes of operation to fit your needs, selected by jumpers:
35 | 1. single push-button control. This is great for re-purposing the Turbo button (NOTE: the on/off turbo switch would need to be replaced with a push-button).
36 | 2. Two-button control (left/right).
37 | 3. rotary-encoder with a push-button. This is great for placing it on a drive bay panel.
38 | 4. analogue potentiometer with/without a push-button. This could be used for configurations without a display.
39 | - 7-segment TM1637-based display that shows the effective frequency. This can be considered optional, but is highly recommended.
40 | - Cycle through preset frequencies or select a frequency at a 1MHz granularity.
41 | - Each preset can be tuned/programmed both in terms of the PWM level but also the PWM frequency.
42 | - Configuration changes are permanently stored in the Pico's flash memory.
43 | - The PWM pulses are generated by the Pico's PIOs and are clock-accurate, with no jitter.
44 |
45 |
46 | # Modes of operation
47 |
48 | ## Mode 1: Rotary Encoder (ROT)
49 |
50 | - The devices starts at the "Presets" state (mode).
51 | - Turn the knob to select a frequency
52 | - Short push to switch to fine-grain frequency selection
53 |
54 |
55 |
56 | ## Initial configuration
57 | - The default CPU frequency is 200MHz. Long push to configure the CPU speed. Select the frequency and short push to get back to the presets.
58 |
59 | ## Mode 2: Single-button (1Btn)
60 |
61 | - The device starts at the "Presets" state.
62 | - Three types of push:
63 | 1. Short push (release immediately)
64 | 2. Medium push (for ~0.5 seconds)
65 | 3. Long push (about 2 seconds)
66 | - Cycle through presets with a short push. A medium push brings us back to the default CPU frequency.
67 | - Long push to enter programming mode.
68 |
69 |
70 |
71 |
72 | ## Mode 3: Two-button (2Btn)
73 |
74 | - The device starts at the "Presets" state.
75 | - Two types of push: long and short
76 | - Three types of actions:
77 | 1. Short Left or Right push (release immediately)
78 | 2. Both Left and Right short push
79 | 3. Both Left and Right long push.
80 | - Cycle through presets with a L or R short push.
81 | - Go to 1-1 manual mode with both L and R short push.
82 | - Long push (L or R) to enter programming mode, or to configure the CPU Frequency (CPUF) when showing the maximum frequency.
83 | - Reset to defaults by long press L and R twice.
84 |
85 |
86 |
87 | ## Mode 4: Potentiometer (POT)
88 |
89 | - Can be used in conjunction with the single-button operation. Turning the potentiometer overrides the preset selected by the button.
90 |
91 | ## Serial Mode (UART): Control by the serial port, works in conjunction with all other modes.
92 |
93 | You can connect to the Throttle Blaster via the serial port and set the Frequency and PWM Period.
94 | This is convenient for launching a game with a `.bat` file that first configures the Throttle Blaster and then launches the game.
95 |
96 | ### UART Circuit
97 | - Connect the Throttle Blaster's Tx pin to the PC's serial port Rx pin (that is pin 2 of the serial connector), the Throttle Blaster's Rx pin to serial Tx (pin 3) and ground to ground (pin 5)
98 |
99 |
100 | ### UART Software
101 | - Serial port settings: 9600 8N1, no flow control
102 | - The string to send is in the form `FP\r`, where:
103 | - `` is the desired effective frequency in MHz (float),
104 | - `` is the PWM Period level (1-256),
105 | - `\r` (also seen as `^M`) is the Carriage-Return character (ASCII 13 0x0d).
106 | - For example `F4.77P8` sets the frequency to `4.77MHz` and the PWM period to `8` which is a around 50us.
107 | - In DOS you can use a terminal emulator, like [Kermit](http://www.columbia.edu/kermit/ftp/archives/msk314.zip), to connect and send the command.
108 | - You could use this one-liner in a batch file: `kermit set port COM1, set speed 9600, output F4.77P8\13`. This will set the frequency to 4.77 MHz, the period to 8 and will send a Carriage-Return character (`\13`).
109 |
110 | > **Note**
111 | > Changing the frequency is not instantaneous. So if you are setting the frequency in a batch file, please consider adding a delay before launching the game.
112 |
113 | > **Note**
114 | > Please note that the serial-port functionality is totally optional. You don't need to populate the MAX3232 IC and its capacitors if you are not planning to use it.
115 |
116 |
117 | ## Preset Buttons (since rev 0.7)
118 | Since revision 0.7 you can also directly select a preset by pushing one of the 8 available buttons.
119 | This functionality is compatible with all other modes of operation.
120 |
121 | Please note that you don't need a Rev 0.7 PCB to use this feature.
122 | You can simply connect buttons to the Pico's GPIOs, with one button pin to the GPIO and the other to ground, see table below:
123 |
124 |
125 | GPIO | 10 | 11 | 12 | 13 | 18 | 19 | 20 | 21
126 | ------------|----|----|----|----|----|----|-----|-----
127 | Freq (MHz) | 4 | 8 | 10 | 25 | 33 | 66 | 133 | Max
128 |
129 | ## Reset Detection (Optional and experimental) (since rev 0.8)
130 | Connect the computer case reset button to `RES1` and another cable from `RES2` to the computer motherboard.
131 | Now when you press the reset button, the Throttle Blaster will detect it and will temporarily set the frequency to maximum for a faster boot.
132 | During this time the MHz display will show "boot" and show a count down until the frequency is restored.
133 |
134 | > **Note**
135 | > Polarity matters: If you connect the reset cables to the RES headers the wrong way your PC won't boot!
136 |
137 | ## Reverse Direction jumper JP3 (since rev 0.4)
138 | Since revision 0.4 the default rotation direction of the knob has been changed and a new jumper JP3 has been added.
139 | Closing the jumper will reverse the direction for both the rotary encoder and the potentiometer.
140 |
141 |
142 | # Presets
143 |
144 | Display | Performance equivalence
145 | ---------|------------------------
146 | 4 | 4.77 MHz IBM PC
147 | 8 | 8 MHz
148 | 10 | 10 MHz
149 | 25 | 25 MHz
150 | 33 | 33 MHz 486
151 | 66 | 66 MHz 486
152 | 133 | 133 MHz Pentium
153 | 450 | 450 MHz Pentium-II
154 | 733 | 733 MHz Pentium-III
155 |
156 | # Firmware
157 |
158 | ## Dependencies
159 | - (Optional) https://github.com/wahlencraft/TM1637-pico
160 | - C++17 compiler
161 |
162 | ## Build Instructions
163 | - Download release (v1.2.1) https://github.com/wahlencraft/TM1637-pico and extract it.
164 | - `cd firmware/ && mkdir build && cd build`
165 | - `cmake -DCMAKE_BUILD_TYPE=Release -DPICO_SDK_PATH=/path/to/pico-sdk/ -DPICO_TM1637_PATH=/path/to/TM1637-pico/ ../src/ && make`
166 | - This will place the firmware into: `ThrottleBlaster.uf2` in the `build` directory.
167 |
168 | ## Installing the firmware
169 | - Unplug the Pico
170 | - Press and hold the small "BOOTSEL" button on the Pico
171 | - While holding the BOOTSEL button, connect the Pico to your PC with a micro-USB cable
172 | - The Pico should show up as a mass-storage device
173 | - Copy the `ThrottleBlaster.uf2` firmware to the drive associated with the Pico
174 | - Safely eject the mass-storage device
175 |
176 | The Pico should boot and you should see the Pico's LED light up.
177 |
178 | # Circuit
179 |
180 | ## Schematic
181 |
182 |
183 |
184 | The Throttle Blaster circuit is fairly simple:
185 | - The Rotary Encoder, the Push Button and the Potentiometer are connected to the Pico's GPIOs.
186 | - The Display is also connected directly to GPIOs
187 | - The STPCLK# pin is driven by a N-channel MOSFET, a 2N7000, and its gate pin connects to the Pico's GPIO via a 1K resistor and the transistor's drain connects to STPCLK# via a 100 Ohm resistor.
188 | - The circuit is powered directly from the PSU's 5V power supply via diode (preferrably a Schottky).
189 | - The serial port circuit relies on a MAX3232 for converting the RS232 levels to Pi-Pico levels.
190 |
191 | ## PCB
192 |
193 |
194 |
195 |
196 | ## Bill Of Materials
197 |
198 | Download gerbers: https://github.com/scrapcomputing/ThrottleBlaster/releases
199 |
200 | Reference | Quantity | Value | Description
201 | ---------------|-------------------|-------------------------------------------------------|------------
202 | N/A | 1 (recommended) | TM1637 based 4-digit 7-segment display | The display of the Throttle Blaster
203 | D1 | 1 | Through-hole diode (preferrably Schottky 1N5817) | Reverse polarity protection
204 | N/A (for Pico) | 2 | 1x17 female through-hole pin-header 2.54mm pitch (Harwin M20-7821746) | For attaching the Pico to the board.
205 | J1 | 1 (optional) | 1x01 male through-hole angled pin-header 2.54mm pitch | For the STPCLK# cable
206 | RES1,2 | 2 (optional) | 1x02 male through-hole angled pin-header 2.54mm pitch | For Reset detection
207 | J2 | 1 (optional UART) | 1x03 male through-hole pin-header 2.54mm pitch | For controlling the Throttle Blaster via serial. (Requires MAX3232)
208 | JP1/JP2 | 1 | 2x02 (or 2x 1x02) male through-hole pin-header 2.54mm | Selects mode of operation.
209 | JP3 | 1 | 1x02 male through-hole pin-header 2.54mm pitch | For the JP3 jumper that flips the rotation direction
210 | SW1/SW2 | 2 (optional) | 1x02 male through-hole pin-header 2.54mm pitch | For the SW1 and SW2 switches
211 | U1 | 1 (optional) | 1x04 male through-hole angled pin-header 2.54mm pitch | For connecting the TM1637 7-segment display.
212 | Jumpers | 2 | 2.54mm pitch Jumpers | For JP1/JP2
213 | Q1 (optional Q2) | 1 (2 for optional RESET detection) | 2N7000 N-channel MOSFET | Pulls down the CPU's STPCLK# pin
214 | R1 | 1 | 1K Resistor SMD 1206 | For the throttle transistor gate.
215 | R2 | 1 | 100 Ohm Resistor SMD 1206 (P-iii CPUs may need 47 Ohms) | Between the throttle pin and the throttling transistor for additional safety. It's value could be lower.
216 | Pot1 | 1 (mode POT) | 10K linear potentiometer | Selects Frequency in Potentiometer mode.
217 | SW1 | 1 (mode 1Btn) | Push button | Selects Frequency in 1Btn mode.
218 | SW3 | 1 (mode 2Btn) | Push button | The right button in 2Btn mode.
219 | Preset Btns P1-P8| 8 (optional) | 1x02 (or one 8x02) male through-hole pin-header 2.54mm| Headers for the 8 preset push-buttons.
220 | SW2 | 1 (mode ROT) | Rotary Encoder with push-button, (ALPS EC11E-Switch) Vertical | Selects Frequency in Rotary mode. Note: These are widely available online using keywords like: "rotary encoder switch Arduino" and they can also be found in kits with fitting knobs.
221 | U2 | 1 | Raspberry Pi Pico |
222 | U3 | 1 | 1x04 horizontal pin header 2.54mm pitch | For connecting to the floppy power connector, for powering the unit.
223 | C1 | 1 (mode POT) | 100pF Ceramic capacitor SMD 1205 | Used to reduce potentiometer noise.
224 | C2,C3,C4,C5,C6 | 5 (optional UART) | 1uF Ceramic Capacitor SMD 1206 | For MAX3232 (serial port)
225 | U4 | 1 (optional UART) | MAX3232 SOIC-16 5.3x10.2mm (Commonly listed as 16-SOIC 3.90mm width) | For controlling the Throttle Blaster via the serial port.
226 |
227 | ## Using the circuit for the first time
228 | - Select the operation mode using jumpers JP1/JP2.
229 | - Connect J1 to your CPU's STPCLK# pin.
230 | - Power it on and you are good to go.
231 | - You can reverse the knob direction by closing JP3 (since rev.0.4)
232 |
233 |
234 | ## Basic Troubleshooting
235 | - Check that the Throttle Blaster's ground is connected to the motherboard's ground
236 | - Check the voltage at the STPCLK# pin:
237 | - Max frequency: The voltage should be matching the CPU's I/O high ~3.3V for most CPUs
238 | - Any other frequency: You should be seing pulses using an oscilloscope, or values between 0 and ~3.3V using a multimeter.
239 | - Confirm that it's working as expected by running benchmarks from [Phil's DOS Benchmark Pack](https://www.philscomputerlab.com/dos-benchmark-pack.html)
240 |
241 |
242 | ## How to find the `STPCLK#` pin
243 | Just look for it in your CPU's datasheet.
244 | This table lists the STPCLK# pin number for your reference:
245 |
246 | CPU | STPCLK# Pin
247 | ----------------------|------------
248 | Pentium MMX | V34
249 | Pentium-iii socket | AG35
250 | Pentium-iii slot1 | B6
251 | Athlon XP | AC1
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 | # How about older CPUs that don't have a STPCLK pin?
262 | 486 or older CPUs don't usually have a STPCLK# pin.
263 | Instead they have the HOLD pin which has somewhat similar functionality and is active high.
264 | Some boards use PWM on this HOLD pin when the Turbo button is pressed.
265 |
266 | The HOLD pin is not pulled down internally by the CPU.
267 | So it is up to the motherboard to pull it down.
268 | The problem is that this is usually actively pulled down by the CPU chipset, meaning that if we try to pull it up with the Throttle Blaster we may damage the chipset.
269 | So I don't think there is a way to get the Throttle Blaster to work universally with older systems.
270 |
271 |
272 | # Change Log
273 | - Rev 0.8c: Fixes bugs introduced in Rev 0.8b.
274 | - Rev 0.8b: Improves responsiveness of rotary encoder. WARNING: This revision is buggy, please upgrade to Rev 0.8c (thanks @nahimov for reporting the bug).
275 | - Rev 0.8: Adds RESET detction circuit (optional) which will temporarily set the speed to max for 20 seconds.
276 | - Rev 0.7: Adds support for 8 preset buttons.
277 | - Rev 0.6: Firmware bug fixes: (i) fix saving MHz/Period adjustments to flash and (ii) one-button mode frequency glitch.
278 | - Rev 0.5: Replaces potentiometer capacitor with SMD and several firmware fixes.
279 | - Rev 0.4: Reverses knob direction and adds jumper JP3 for selecting direction.
280 | - Rev 0.3: Adds UART support in both firmware and PCB.
281 | - Rev 0.2: Adds two-button mode "2Btn".
282 | - Rev 0.1: Initial release.
283 |
284 | # Acknowledgements
285 | - Many thanks to all of you who have reported bugs or asked for clarifications or features requests. By doing so you are helping improve the project! Special thanks to Michael Swimm for extensive testing, reporting bugs, coming up with awesome ideas for new features, and for sharing fancy mounting hardware for the Throttle Blaster!
286 |
287 | # License
288 | The project is GPLv2 except for `Pwm.pio` which comes from the Pi Pico SDK examples and is under `SPDX-License-Identifier: BSD-3-Clause`.
289 |
--------------------------------------------------------------------------------
/firmware/src/Button.cpp:
--------------------------------------------------------------------------------
1 | //-*- C++ -*-
2 | //
3 | // Copyright (C) 2024 Scrap Computing
4 | //
5 |
6 | #include "Button.h"
7 |
8 | /// \Returns true if we "release both", which currently only if both release
9 | /// or if one is released and the other is pressed.
10 | bool bothRelease(ButtonState BS1, ButtonState BS2) {
11 | if ((BS1 == ButtonState::Release || BS1 == ButtonState::MedRelease) &&
12 | (BS2 == ButtonState::Release || BS2 == ButtonState::MedRelease))
13 | return true;
14 | return ((BS1 == ButtonState::Release || BS1 == ButtonState::MedRelease) &&
15 | BS2 == ButtonState::Pressed) ||
16 | ((BS2 == ButtonState::Release || BS2 == ButtonState::MedRelease) &&
17 | BS1 == ButtonState::Pressed);
18 | }
19 |
20 | bool bothLongPress(ButtonState BS1, ButtonState BS2) {
21 | return BS1 == ButtonState::LongPress && BS2 == ButtonState::Pressed;
22 | }
23 |
--------------------------------------------------------------------------------
/firmware/src/Button.h:
--------------------------------------------------------------------------------
1 | //-*- C++ -*-
2 | //
3 | // Copyright (C) 2024 Scrap Computing
4 | //
5 |
6 | #ifndef __BUTTON_H__
7 | #define __BUTTON_H__
8 |
9 | #include "Pico.h"
10 | #include "Utils.h"
11 |
12 | enum class ButtonState {
13 | LongPress,
14 | MedRelease,
15 | Release,
16 | Pressed,
17 | None,
18 | };
19 |
20 | static constexpr const char *getButtonState(ButtonState State) {
21 | switch (State) {
22 | case ButtonState::LongPress: return "LongPress";
23 | case ButtonState::MedRelease: return "MedRelease";
24 | case ButtonState::Release: return "Release";
25 | case ButtonState::Pressed: return "Pressed";
26 | case ButtonState::None: return "None";
27 | }
28 | }
29 |
30 | bool bothRelease(ButtonState BS1, ButtonState BS2);
31 | /// WARNING: Be very careful if you are handling single-button long pressess!
32 | bool bothLongPress(ButtonState BS1, ButtonState BS2);
33 |
34 | template
36 | class Button {
37 | static constexpr const bool OnVal = !OffVal;
38 | int GPIO;
39 | Pico Π
40 | Buffer Buff;
41 | bool LastVal = OffVal;
42 | int LongPressCnt = 0;
43 | bool IgnoreRelease = false;
44 |
45 | public:
46 | Button(int GPIO, Pico &Pi, const char *Name) : GPIO(GPIO), Pi(Pi) {
47 | auto Pull = OffVal == true ? Pico::Pull::Up : Pico::Pull::Down;
48 | Pi.initGPIO(GPIO, GPIO_IN, Pull, Name);
49 | }
50 |
51 | ButtonState get() {
52 | Pi.readGPIO();
53 | Buff.append(Pi.getGPIO(GPIO));
54 | bool Val = Buff.getMean();
55 |
56 | auto GetState = [this](bool Val) {
57 | if (Val == OnVal && LastVal == OffVal) {
58 | LongPressCnt = 0;
59 | return ButtonState::Pressed;
60 | } else if (Val == OffVal && LastVal == OnVal) {
61 | if (!IgnoreRelease) {
62 | if (LongPressCnt >= MedReleaseCntVal)
63 | return ButtonState::MedRelease;
64 | else
65 | return ButtonState::Release;
66 | }
67 | IgnoreRelease = false;
68 | } else if (Val == OnVal && LastVal == OnVal) {
69 | if (++LongPressCnt == LongPressCntVal) {
70 | IgnoreRelease = true;
71 | return ButtonState::LongPress;
72 | }
73 | return ButtonState::Pressed;
74 | }
75 | return ButtonState::None;
76 | };
77 | auto NewState = GetState(Val);
78 | LastVal = Val;
79 | return NewState;
80 | }
81 | };
82 |
83 | #endif // __BUTTON_H__
84 |
--------------------------------------------------------------------------------
/firmware/src/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.13)
2 |
3 | # Build
4 | # -----
5 | # $ mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Release -DPICO_SDK_PATH=/path/to/pico-sdk/ ../src/ && make -j
6 | # For 7-segment display support you need to specify the path to the
7 | # Pico-TM1637 library (https://github.com/wahlencraft/TM1637-pico)
8 | # -DPICO_TM1637_PATH=/path/to/TM1637-pico/
9 | #
10 | # Options
11 | # -------
12 | # o -DDISPLAY_SHIFT_LEFT= If the numbers displayed need shifting
13 | # o -DDISPLAY_BRIGHTNESS= (0-7) Sets the brightness 7 is max, default: 1.
14 | # o -DDISABLE_PICO_LED=on to disable the Pico's blinking LED.
15 | # o -DDBGPRINT=on to enable debug messages
16 | #
17 | # This will place the firmware into: build/ThrottleBlaster.uf2
18 | #
19 | # For example: minicom -b 115200 -D /dev/ttyACM0. Serial connection: 115200 8N1
20 |
21 | set(REVISION_MAJOR 0)
22 | set(REVISION_MINOR 8)
23 |
24 | message("PICO_SDK_PATH = ${PICO_SDK_PATH}")
25 |
26 | # initialize the SDK based on PICO_SDK_PATH
27 | # note: this must happen before project()
28 | include(pico_sdk_import.cmake)
29 |
30 | set(PROJECT_NAME ThrottleBlaster)
31 | project(
32 | ${PROJECT_NAME}
33 | LANGUAGES C CXX ASM)
34 |
35 | set(CMAKE_C_STANDARD 11)
36 | set(CMAKE_CXX_STANDARD 17)
37 |
38 | # initialize the Raspberry Pi Pico SDK
39 | pico_sdk_init()
40 | include_directories("${PICO_SDK_PATH}/src/common/pico_stdlib/include")
41 | include_directories("${PICO_SDK_PATH}/src/common/pico_base/include")
42 | include_directories("${PICO_SDK_PATH}/src/rp2_common/hardware_adc/include")
43 | include_directories("${PICO_SDK_PATH}/src/rp2_common/pico_multicore/include")
44 | include_directories("${PROJECT_BINARY_DIR}/") # for build/config.h
45 | if (DEFINED PICO_TM1637_PATH)
46 | include("${PICO_TM1637_PATH}/PicoTM1637.cmake")
47 | set(PICO_TM1637 1)
48 | endif ()
49 |
50 | if (NOT DEFINED DISPLAY_SHIFT_LEFT)
51 | set(DISPLAY_SHIFT_LEFT 0)
52 | endif()
53 |
54 | if (NOT DEFINED DISPLAY_BRIGHTNESS)
55 | set(DISPLAY_BRIGHTNESS 1)
56 | endif()
57 |
58 | set(CMAKE_CXX_FLAGS_RELEASE "-O2")
59 | set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wall -Werror ${EXTRA_DBG_FLAGS}")
60 |
61 | # rest of your project
62 | file(GLOB SOURCES *.c *.cpp *.h *.def)
63 | add_executable(${PROJECT_NAME} ${SOURCES})
64 |
65 | pico_generate_pio_header(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR}/Pwm.pio)
66 |
67 | target_include_directories(${PROJECT_NAME} PUBLIC "${CMAKE_CURRENT_LIST_DIR}")
68 |
69 | set(LIBS
70 | pico_stdlib
71 | hardware_adc
72 | pico_multicore
73 | hardware_pio
74 | )
75 | if (DEFINED PICO_TM1637_PATH)
76 | set(LIBS PicoTM1637 ${LIBS})
77 | endif ()
78 | target_link_libraries(${PROJECT_NAME} ${LIBS})
79 |
80 |
81 | message("")
82 | message("+---------------+")
83 | message("| Configuration |")
84 | message("+---------------+")
85 | pico_enable_stdio_usb(${PROJECT_NAME} 1)
86 | pico_enable_stdio_uart(${PROJECT_NAME} 0)
87 | if (DISABLE_USB_DBG STREQUAL "0")
88 | pico_enable_stdio_usb(${PROJECT_NAME} 0)
89 | pico_enable_stdio_uart(${PROJECT_NAME} 0)
90 | endif ()
91 | message("DISABLE_USB_DBG = ${DISABLE_USB_DBG}")
92 |
93 | message("PICO_LED = ${PICO_LED}")
94 | message("PICO_TM1637_PATH = ${PICO_TM1637_PATH}")
95 | message("LCD_SHIFT_LEFT = ${LCD_SHIFT_LEFT}")
96 | message("MHZ = ${MHZ}")
97 |
98 | # End of configuration
99 | message("")
100 |
101 | configure_file (
102 | "${PROJECT_SOURCE_DIR}/config.h.in"
103 | "${PROJECT_BINARY_DIR}/config.h"
104 | )
105 |
106 | # Create map/bin/hex/uf2 in addition to ELF.
107 | pico_add_extra_outputs(${PROJECT_NAME})
108 |
--------------------------------------------------------------------------------
/firmware/src/CommonLogic.cpp:
--------------------------------------------------------------------------------
1 | #include "CommonLogic.h"
2 | #include "Debug.h"
3 | #include "pico/time.h"
4 | #include
5 |
6 | void CommonLogic::setMode(Mode NewMode) {
7 | setModeInit(NewMode);
8 |
9 | BeforeMaxMHz = Presets.getMaxMHz();
10 | BeforePeriod = Presets.getPeriod();
11 | BeforeActualKHz = Presets.getActualKHz();
12 |
13 | CurrMode = NewMode;
14 | DBG_PRINT(std::cout << getModeStr(CurrMode) << "\n";)
15 | }
16 |
17 | void CommonLogic::tryWritePresetsToFlash() {
18 | if (Presets.getActualKHz() != BeforeActualKHz ||
19 | Presets.getPeriod() != BeforePeriod ||
20 | Presets.getMaxMHz() != BeforeMaxMHz) {
21 | DBG_PRINT(std::cout << "WriteToFlash:\n";)
22 | DBG_PRINT(std::cout << " Before After\n";)
23 | DBG_PRINT(std::cout << "ActualKHz: " << BeforeActualKHz << " "
24 | << Presets.getActualKHz() << "\n";)
25 | DBG_PRINT(std::cout << "Period: " << BeforePeriod << " "
26 | << Presets.getPeriod() << "\n";)
27 | DBG_PRINT(std::cout << "MaxMHz: " << BeforeMaxMHz << " "
28 | << Presets.getMaxMHz() << "\n";)
29 | Presets.writeToFlash(Flash);
30 | } else {
31 | DBG_PRINT(
32 | std::cout << "Not writing to flash (no change in KHz/Period/MaxMHz)\n";)
33 | }
34 | }
35 |
36 | void CommonLogic::printTxtAndSleep(const char *Str) {
37 | Disp.printTxt(Str);
38 | sleep_ms(PrintSleep);
39 | }
40 |
41 | void CommonLogic::updateDisplay() {
42 | #ifdef PICO_TM1637
43 | switch (getMode()) {
44 | case Mode::Presets:
45 | Disp.printKHz(Presets.getKHz());
46 | break;
47 | case Mode::ConfigMHz:
48 | Disp.printKHz(Presets.getActualKHz());
49 | break;
50 | case Mode::ConfigPeriod:
51 | Disp.printRaw(Presets.getPeriod());
52 | break;
53 | case Mode::Manual:
54 | case Mode::Uart:
55 | Disp.printKHz(DC.getKHz());
56 | break;
57 | case Mode::ConfigMaxMHz:
58 | Disp.printMHz(Presets.getMaxMHz());
59 | break;
60 | case Mode::ResetToDefaults:
61 | Disp.printTxt(MsgResetToDefaults);
62 | break;
63 | case Mode::Boot: {
64 | auto Now = std::chrono::system_clock::now();
65 | auto Seconds =
66 | std::chrono::duration_cast(Now - *ResetTimeOpt);
67 | if (Seconds.count() == 0) {
68 | Disp.printTxt(MsgResetDetected);
69 | } else {
70 | int Remaining = ResetMaxSpeedDuration - Seconds.count();
71 | Disp.printRaw(Remaining);
72 | }
73 | break;
74 | }
75 | }
76 | #endif
77 | }
78 |
79 | void CommonLogic::uartTick(Uart &Uart) {
80 | auto Bytes = Uart.readNonBlocking();
81 | auto NumBytes = Bytes.size();
82 | if (NumBytes == 0)
83 | return;
84 | if (NumBytes >= 1) {
85 | for (char Byte : Bytes) {
86 | // Skip some illegal chars.
87 | if (Byte == '\n')
88 | continue;
89 | UartStr += Byte;
90 | }
91 | }
92 | DBG_PRINT(std::cout << "UartStr: " << UartStr << "\n";)
93 | // Reject messages that are too long!
94 | if (UartStr.size() > MaxUartStrSz) {
95 | DBG_PRINT(std::cout << "Uart: Too many chars:'" << UartStr << "'\n";)
96 | printTxtAndSleep(MsgUartErr);
97 | UartStr.clear();
98 | return;
99 | }
100 | // Look for the EOM character.
101 | if (UartStr.back() != UartEOM)
102 | return;
103 | float MHz = 0;
104 | int Period = 0;
105 | sscanf(UartStr.c_str(), "F%fP%d\r", &MHz, &Period);
106 | int KHz = MHz * 1000;
107 | DBG_PRINT(std::cout << "UART: "
108 | << "MHz=" << MHz << " KHz=" << KHz << " Period=" << Period
109 | << "\n";)
110 | if (KHz == 0) {
111 | setMode(Mode::Uart);
112 | printTxtAndSleep(MsgUartMode);
113 | DC.setMHzToMax();
114 | } else if ((MHz >= MHzLimitLo && MHz <= MHzLimitHi) &&
115 | (Period >= PeriodLimitLo && Period <= PeriodLimitHi)) {
116 | setMode(Mode::Uart);
117 | DC.setKHz(KHz);
118 | DC.setPeriod(Period);
119 | printTxtAndSleep(MsgUartMode);
120 | // Uart.writeBlockingStr("OK\r\n");
121 | } else {
122 | printTxtAndSleep(MsgUartErr);
123 | }
124 | UartStr.clear();
125 | }
126 |
127 | void CommonLogic::resetSenseTick() {
128 | if (ResetTimeOpt) {
129 | auto Now = std::chrono::system_clock::now();
130 | auto Seconds =
131 | std::chrono::duration_cast(Now - *ResetTimeOpt);
132 | if (Seconds.count() > ResetMaxSpeedDuration) {
133 | DBG_PRINT(std::cout << "End of Reset. SvKHz=" << ResetSavedKHz
134 | << " SvPeriod=" << ResetSavedPeriod
135 | << " SvMode=" << getModeStr(ResetSavedMode) << "\n";)
136 | ResetTimeOpt = std::nullopt;
137 | DC.setKHz(ResetSavedKHz);
138 | DC.setPeriodRaw(ResetSavedPeriod);
139 | setMode(ResetSavedMode);
140 | }
141 | }
142 | // Button not pressed, skipping.
143 | if (ResetSense.get() != ButtonState::Pressed)
144 | return;
145 | // Nothing to do if already at max freq.
146 | if (DC.getKHz() == Presets.getMaxKHz() && !ResetTimeOpt)
147 | return;
148 | // If already in booting mode don't save freq/period/mode.
149 | if (getMode() != Mode::Boot) {
150 | ResetSavedKHz = DC.getKHz();
151 | ResetSavedPeriod = DC.getPeriod();
152 | ResetSavedMode = getMode();
153 | }
154 | DBG_PRINT(std::cout << "Reset detected! SvKHz=" << ResetSavedKHz
155 | << " SvPeriod=" << ResetSavedPeriod
156 | << " SvMode=" << getModeStr(ResetSavedMode) << "\n";)
157 | DC.setMHzToMax();
158 | ResetTimeOpt = std::chrono::system_clock::now();
159 | setMode(Mode::Boot);
160 | }
161 |
162 | void CommonLogic::presetBtnsTick() {
163 | auto HandleBtn = [this](auto &Btn, int BtnIdx, bool IsLast) {
164 | switch (Btn.get()) {
165 | case ButtonState::Release:
166 | case ButtonState::MedRelease:
167 | case ButtonState::LongPress: {
168 | DBG_PRINT(std::cout << "PresetBtn " << BtnIdx << " IsLast=" << IsLast
169 | << "\n";)
170 | if (!IsLast)
171 | Presets.setIdx(BtnIdx);
172 | else
173 | // The last preset always sets max frequency.
174 | Presets.cycleMax();
175 | int NewKHz = Presets.getActualKHz();
176 | int NewPeriod = Presets.getPeriod();
177 | DBG_PRINT(std::cout << "NewKHz=" << NewKHz << " NewPeriod=" << NewPeriod
178 | << "\n";)
179 | DC.setKHz(NewKHz);
180 | DC.setPeriod(NewPeriod);
181 | DBG_PRINT(DC.dump();)
182 | break;
183 | }
184 | default:
185 | break;
186 | }
187 | };
188 |
189 | switch (CurrMode) {
190 | case Mode::Presets:
191 | case Mode::Manual:{
192 | for (int BtnIdx = 0, E = (int)PresetBtns.size(); BtnIdx != E; ++BtnIdx)
193 | HandleBtn(PresetBtns[BtnIdx], BtnIdx, /*IsLast=*/BtnIdx == E - 1);
194 | break;
195 | }
196 | default:
197 | break;
198 | }
199 | }
200 |
--------------------------------------------------------------------------------
/firmware/src/CommonLogic.h:
--------------------------------------------------------------------------------
1 | //-*- C++ -*-
2 | //
3 | // Copyright (C) 2024 Scrap Computing
4 | //
5 |
6 | #ifndef __COMMONLOGIC_H__
7 | #define __COMMONLOGIC_H__
8 |
9 | #include "Button.h"
10 | #include "Debug.h"
11 | #include "Display.h"
12 | #include "DutyCycle.h"
13 | #include "Flash.h"
14 | #include "Presets.h"
15 | #include "Uart.h"
16 | #include
17 | #include
18 | #include
19 | #include
20 |
21 | class CommonLogic {
22 | protected:
23 | int SamplePeriod;
24 | int LoopCntSinceSample = 0;
25 |
26 | Display &Disp;
27 | DutyCycle &DC;
28 | PresetsTable &Presets;
29 | FlashStorage &Flash;
30 |
31 | enum class Mode {
32 | Presets,
33 | ConfigMHz,
34 | ConfigPeriod,
35 | Manual,
36 | ConfigMaxMHz,
37 | ResetToDefaults,
38 | Uart,
39 | Boot,
40 | };
41 |
42 | Mode CurrMode = Mode::Presets;
43 |
44 | static constexpr const char *getModeStr(Mode Mode) {
45 | switch (Mode) {
46 | case Mode::Presets: return "Presets";
47 | case Mode::ConfigMHz: return "ConfigMHz";
48 | case Mode::ConfigPeriod: return "ConfigPeriod";
49 | case Mode::Manual: return "Manual";
50 | case Mode::ConfigMaxMHz: return "ConfigMaxMHz";
51 | case Mode::ResetToDefaults: return "ResetToDefaults";
52 | case Mode::Uart: return "UART";
53 | case Mode::Boot: return "Boot";
54 | }
55 | return "BAD";
56 | }
57 | /// Called by setMode(). Can be overriden by implementations.
58 | virtual void setModeInit(Mode NewMode) {}
59 | void setMode(Mode NewMode);
60 | void tryWritePresetsToFlash();
61 | Mode getMode() const { return CurrMode; }
62 |
63 | static constexpr const char *MsgActualFreq = "FrE";
64 | static constexpr const char *MsgPeriod = "PEr";
65 | static constexpr const char *MsgMaxMHz = "CPUF";
66 | static constexpr const char *MsgResetToDefaults = "RES";
67 | static constexpr const char *MsgEscape = "ESC";
68 | static constexpr const char *MsgConfirm = "done";
69 | static constexpr const char *MsgPresets = "PRE";
70 | static constexpr const char *MsgManual = "1-1";
71 | static constexpr const char *MsgUartErr = "UErr";
72 | static constexpr const char *MsgUartMode = "UArt";
73 | static constexpr const char *MsgResetDetected = "boot";
74 |
75 | int BeforeMaxMHz = 0;
76 | int BeforeActualKHz = 0;
77 | int BeforePeriod = 0;
78 |
79 | std::vector