I like to define it a "tool" since I built it for checking Half H-Bridges and Check / Set servo positions for all my robotics needs. The Super Square Waves Generator (SSWG) can deliver 3 different kinds of square waves signals, at same time, taking advantage of specific Core Independent Peripherals on Microchip® PIC® microcontrollers.
For this project I've used a PIC16F15376 (8bit, 32MHz) starting from an example I've written years ago for the Microchip® PIC16F15376 Curiosity Nano board. This article is mostly for people loving this kind of microcontrollers since there is also lot of technical stuff about usage of certain peripherals.
The output signals are:
- NCO - Numeric Controlled Oscillator (1 output) : frequency range from 1Hz to ~8MHz with great frequency accuracy and continuous frequency variation at a minumum 1Hz steps. The NCO is useful for great accuracy and/or great frequency values when you don't need great variations of duty cycle (say devices needing a stable clock).
- PWM - Pulse Width Modulation (2 outputs having phase opposition) up to 2MHz. Duty cycle is always variable (resolution is greater for the lowest frequencies and lower for the highest frequencies). Frequency and Duty cycle accuracies are great so the PWM is the choice when you need a great duty cycle resolution but you don't need a very specific frequency, so this kind of signal is useful for testing H-Bridges for motor control or for circuitry needing a simply PWM regulation (like light dimmers or one-direction motor drivers).
- Servo (1 output): this fixed frequency output (50Hz) does not use a dedicated PIC® microcontroller module but it's delivered by toggling an IO using a Timer Interrupt, then the frequency is not so accurate but Servo does not need accuracy! This output is ideal for testing servos by varying the high-level pulse duration, so, for example, you can put your servos at center value for mounting them in your final applications, or simply check if they works or check their maximum escursions.
The good thing, as I written previously, is that those outputs are completely independent from each other and are delivered at same time on 4 different IOs.
UsageAt start-up, display will show settings for the NCO: what display is showing is only related on how user controls (buttons, encoder, potentiometer) will act since, as I said, outputs are all working independently: it's a simple "menu page" for the specific function.
You can change settings for the 3 kinds of signals (NCO/PWM/Servo) by pressing a button (Switch 2): at every press you'll enter the menu for editing parameters for the signal. By entering a menu, an indication of menu name will be showed in the display for a fraction of seconds, and then a different led will turn on for indicating the active menu (there are 3 leds: one for each menu).
Another button (Switch 3) will prevent (lock) the possibility to edit parameters: this is valid for all signals even the respective menu is not showed, this is good for preventing unintentional changes, expecially when changing from a menu to another if you are using more than output in your testing.
The lock/unlock status will be showed permantently by a fourth led (Led0) and for a fraction of seconds also on the display. This led will blink in the "lock" condition and will be permantenly on in the "non lock" condition (=changes allowed). In the lock condition only the menu button (Switch2) will work allowing the user to see parameters settings for each signal but, as I said, without possibilities to change them.
A description of menus usage will follow giving also some informations on how some features are obtained using the various peripheral this PIC® microcontroller has.
NCO menuThe NCO output is obtained using the NCO (Numerically Controlled Oscillator) module.
Since other PIC® microcontrollers could have more than one NCO module, the actual module of this microcontroller, for compatibility issues, is referred as NCO1 in the register names of datasheet.
The NCO module can work using various clock sources: I've made the choice to select between only 2 sources: the System Clock (32MHz) and the internal oscillator (MFINTOSC 31kHz) running at 31.25kHz, so you can have better accuracy in the range 1Hz-31250Hz by choosing the Lower frequency (LO setting) oscillator, and greater frequency values (with good accuracy too) if you need them, by choosing the Higher frequency oscillator (HI setting).
There are 2 working modes for the NCO you can choose from: the Fixed Duty cycle (FD) that delivers 50%, fixed, duty cycle (in this case the LO setting will have a maximum frequency value of 15625Hz instead of 31250Hz), and the Pulse Frequency (PF) that have a very little possibility of duty cycle variation depending upon the choosen frequency.
By rotating the encoder, frequency will vary in a range depending on the selected oscillator (LO or HI) and the mode (FD or PF). Increment/decrement frequency value per encoder tick is defined by the St (Steps) value showed on the second row of the display: you can change the steps value by pressing the encoder stem, maximum step value rely on the selected oscillator.
A button (Switch 0) will toggle between the low frequency oscillator (LO) and high frequency oscillator (HI). Another button (Switch 1) will toggle between a 50% Fixed Duty Cycle (FD) and a (very) limited variable duty cycle (PF - aka Pulse Frequency mode).
As said, frequency ranges depends upon selected oscillator and mode:
Duty cycle variation in PF mode is made using the potentiometer and obtainable values are a function of the selected frequency and oscillator so it's very difficult to make a quick calculation (we're using an 8bit microcontroller) and then I choosed to show only the actual value of the N1PWS (NCO1 Pulse Width Selection bits) bit group of the NCO1CLK register. This value can go from 1 to 7 but maximum value is limited depending frequency and oscillator selections.
Actually you'll need an oscilloscope for checking the precise duty cycle value for the NCO in PF mode: I've not made further efforts in showing duty cycle percent value on display for this mode since the NCO is intended for giving a precise, stable, clock (at 50% duty cycle) to a subsystem, so there is no need for duty cycle variation and, for me, the actual implementation goes far away the intended function. If you need greater duty cycle resolution and visualization on the display, there is the PWM mode.About Square Waves Shapes
The maximum frequency NCO can output on this microcontroller is, theoretically, ~32MHz using the PF mode (precisely is 31'999'968Hz) and ~16MHz in FD mode (precisely 15'999'984Hz), but in the reality, you can't obtain over ~16MHz from the output (Say 8Mhz! Continue to read).
For frequencies above 2MHz square wave appears to be slightly distorted as you can see from image captured from the oscilloscope (the 8MHz one looks almost sinusoidal!):
Above 8MHz the situation get worse and the output signal is absolutely unuseable:
For this reason I've limited by default the maximum NCO output at 8MHz (probably waveform shapes could be adjusted with some filtering - I'm not an analog signal expert, suggestions well accepted in the comments) but, if you want to have the full range output, you can tie the RD0 port to GND first than turn on the device: this setting is only taken at start-up so it will not change if you remove or add the RD0 connection after device is started.
The weird waveform shape problem is probably causated by the fact that the real CPU speed is not 32MHz as we set in the configuration but 8MHz: in fact this class of PIC® microcontrollers execute 1 simple assembler intruction in 4 clock cycles, so the real CPU frequency, compared to system having 1 clock cycle per instruction, is 8MHz (8Mips - and we're not considering some special instructions requires more than 4 clock cycles).
With this CPU speed probably is really impossible to handle such high NCO frequencies for giving an output signal on the physical pin (even this task is not perfomed by the CPU). Probably those frequencies are useable as "internal" signals for driving other peripheral but not as physical outputs (anyway I didn't tried).
Additionally, I've tried following things (without success) in order to having a better square wave shape at highest frequencies:
- Set the output as open drain with weak-pullup activated
- Set the output as open drain with external pullup
- Used a Buffer or an inverter on the output (situation get worst)
- Used a different system oscillator
- Used a different power source
If you've other suggestions, please, leave a comment.
PWM menuThe main PWM signal is obtained using the PWM3 + Timer2 module but no physical output was configured for the PWM3 module: I use the "internal" PWM signal for feeding the input of the CWG (Complementary Waveform Generator) module and then obtain 2 different PWM signals having opposite phases (Half-Bridge modality, CWG1CON0.mode=0b100), routed on two different IOs. In this way I've obtained 2 mayor advantages:
- I've 2 PWM opposite PWM signals, allowing me to feed an H-Bridge for motor control
- The two opposite PWM signals will have a deadband allowing an H-Bridge to work properly without having same level signals overlapping
For light dimming purposes or motor speed variation in only 1 direction, only one output is needed.
PWM frequency is a function of the Timer 2 Period Register (PR2), so for varying the Frequency I'll change the PR2 value following the formula:
For using the PWM function, clock has to be set to FOSC/4 (8MHz) and I've set the prescaler value to 1:4 : this allows to obtain frequencies between 7813Hz and 2MHz.
Using an 1:1 prescaler will allow to obtain frequencies in the range 31.25kHz÷ 8Mhz and we can go up to a 1:128 prescaler obtaining the smaller frequencies in the range 244Hz÷63kHz, so the full range, allowing changing prescaler value is from 244Hz to 8MHz, but I choosed to remain prescaler fixed at 1:4 : the waveforms shapes are the same of the NCO, so the last frequency for having a good square wave is 2MHz and personally I don't need a PWM under 7KHz since I use it for controlling motor speed.
In PWM menu are showed: the actual Frequency, the Duty Cycle percent value (calculated as usual on the High level pulse) and the Duty Cycle resolution for the selected frequency expressed in bits (a value of 10bits means 2^10 possible duty cycle values). Lowest frequencies have the greatest resolution.
For H-Bridges I usually use frequencies slightly above the human-udible frequencies (above 20kHz) so the motors will not disturb us with strange noises and for this frequencies the resolution is very good being 8 or 9 bits.
Frequency is changed using the encoder. Since PWM module is made for giving us a good control over the duty instead of frequency, the frequency variation is not continue so you'll have different step of frequency variations and so you cannot choose a very specific frequency (is really not needed for driving H-bridges or varying lamps brightness).
By varying frequency (obtained changing the PR value for Timer2), duty cycle max resolution bits will change following the formula:
Rotating the potentiometer you'll change the duty cycle from 0 to 100% within a resolution defined by the above mentioned bits value and following the formula:
Remember that 0% duty means signal always at low level and 100% means signal always at high level.
In this menu encoder button and Switches 0 and 1 have no function.
I'd consider to use one unused button in this menu for changing the Timer 2 prescaler value in order to obtain some more-extended frequency variation as said before but this implies to complicate a little bit more the calculations for showing values on display.
Even if at 2MHz formula says there is a resolution of 2 bits (4 possible values for the duty cycle), in the reality the waveform doesn't change...
ServoWARNING! For testing servos don't use power from the PICkit™programmer or from the VBUS pin if you're using the Curiosity Nano board you'll damage it! Use always a suitable external power supply since Servos can draw a lot of current!
As I already written, the 50Hz servo signal is obtained by toggling an IO on a Timer interrupt. More precisely I'm using the Interrupt on Timer 1 set to give an overflow signal every 50μS so I can have a maximum resolution of 50μS on the servo rotation (a rotation of about 6÷7 degrees).
I tried to lower this value but entire system becomes very slow since there are too many interrupts to check at very strictly times, with an 8MHz 8bit CPU and then the main program is leaved behind causing the system stuck.
Rotating the encoder, the High Level pulse is changed from about 500mS to about 2500mS allowing the servo rotating from ~90 to ~-90 degrees (180°).
Note: not all servo can reach 180° excursion, so if you see servo not moving after a certain setting and you hear some strange noise instead, means servo can not move to this position and is drawing lot of current being very dangerous, so move it back quickly or the internal motor/circuitry will blow up.
Pushing the encoder stem will bring servo to center position (1500mS / 0 degrees). Switch0 will set pulse at 650mS (about 76°), Switch1 will set pulse at 2350ms (about -76°): I found those values didn't cause overload on all servo I tested but please check: if you hear strange noise return quick to center!
Potentiometer is not used in this menu.
Due to this operational mode (toggling an IO on an interrupt while doing a lot of other stuff instead of using a dedicated module) the accuracy of showed frequency and angle is very low (you can check by yourself using an oscilloscope) but standard servo commands does not require high precision and, anyway, the center position is the most accurate, and this was, for me, the most important feature.
Lot of times happened building robots you must assemble the servos with shaft in central positionBuilding the circuit
Personally I built the circuit on a single-sided protoboard having dimensions of 160x100mm so I didn't designed a PCB.
Anyway you have 2 different options for building the circuit, each option will require a different firmware:
- Like I done: a bare PIC16F15376 on a protoboard. In this case you can solder the 8MHz Crystal and capacitors on pins 13 and 14 and then use the SSWG_100_xtal.hex firmware OR don't mount the Crystal and capacitors and then use the SSWG_100_intosc.hex firmware. You choose. I haven't find differencies on square wave outputs.
- Build the circuit around the PIC16F15376 Curiosity Nano and upload the SSWG_100_intosc.hex firmware. In this case is very important to follow the considerations I've written in the Curiosity Nano paragraph below. Anyway I didn't tried this.
Other than schematic you can download a Reference Table for pins used that reports corrispondence between function to PIC16F15376 and PIC16F15376 Curiosity Nano pins. I think this table is more useful than the schematic itself.
Uploading the HEX on the PIC16F15376This paragraph is only for the bare PIC16F15376 so is NOT for the Curiosity Nano board.
For uploading the provided HEX on the PIC16F15376 you can use a PICkit™3 (PG164130), PICkit™4 (PG164140) or the newer (faster and more capable) PICkit™5 (PG164150). The MPLAB® Snap (PG164100) supports this PIC®: see the next paragraph for consideration on how to use it.
PICkit™2 (PG164120 / DV164121) is no more supported in the programming environments (and probably soon neither the PICkit™3).
Since I don't have other Microchip® programmers, I can't say if other programmers like the Real ICE, PM3 and so on will work. Anyway I think that ICD3, 4 and 5 could work since the ICD programmers follows the PICkit™ ones. but I'm not sure
For programming the PIC® microcontroller you'll need the software MPLAB® IPE that comes with the MPLAB® X IDE installation (download). IPE stands for Integrated Programming Environment. Launch MPLAB® IPE with your original Microchip® programmer connected and then select the PIC16F15376 from the Device list:
Now check that your programmer can deliver the 5V to the board from the Power Menu (if this menù is not available, enable the advanced mode from the Settings menu - a window will appear requiring a password: write microchip):
Return to Operate menu, connect the programmer to the board and then click Connect button. MPLAB® IPE will connect to the programmer and then to the microcontroller showing status in the log window:
If you're using the PICkit™3 and some red writings appears in the log:
means PICkit™3 is not able to power the board/microcontroller since has low current output (it's a problem all PICkit3 users faced often), so return to the Power menu, uncheck the Power target circuit from PICkit™3 setting and then give the board voltage from an external power supply
Somebody solves this issue leaving the PICkit™3 power and putting an high value capacitor on the VDD line, but for me this never worked.
After connection is successful, load the HEX file (download available at the end of this article) pressing Browse button next to the Hex File textbox:
Now you can hit the Program button and wait.
PICkit™3 will require a while, PICkit™4 will be very fast, PICkit™5 will finish first than you press the button (impressive!). The first time you use the programmer with this specific PIC® microcontroller class (enhanced midrange in this case), additional time will required to load some stuff on the programmer.
At end of programming, leaving the programmer attached, program will not start since the programmer hold the microcontroller in the reset state by default, you can disable this behaviour from the Settings menu by selecting Release from Reset:
This paragraph was added on 05-11-2024 after I made some tests with this programmer as I promised in the first revision.
The MPLAB® Snap (PG164100) can be used for programming this PIC®. This programmer is the cheapest original Microchip® one. Anyway this tool cannot power the PIC® microcontroller and cannot use the standard, high-voltage programming on MCLR pin, so there are two things you must keep in mind:
- Powering the board / the PIC® microcontroller from a suitable power supply (in this case the PIC16F15376 must be powered at 5V on Vdd pin)
- The LVP (Low Voltage Programming) must be used: this means that if you're using a PIC® microcontroller that was previously programmed with the LVP flag OFF in the config word, cannot be reprogrammed using the the MPLAB® Snap: the new PIC® microcontrollers doesn't have this issue since they are sold with the LVP flag ON, so they can be programmed with this tool.
So: power the board, connect the MPLAB® Snap, launch the MPLAB® IPE and press the Connect button after selected the right PIC® microcontroller, a message would appear indicating the IPE is changing the firmware on the SNAP for using this microcontroller:
In case an error appears saying: "You have entered an invalid value (null) for the Voltage Level on the Snap Power property page. Connection Failed" try to change the Device Pack in the Settings by selecting a previous version:
Then retry to connect to the device. Then select the firmware end program. I updated the Github Repository with the new HEX (v. 1.00b) having the LVP flag ON.
Building the Circuit around the PIC16F15376 Curiosity NanoSince I designed the circuit to be used for the bare PIC16F15376 IC, there are some things you must pay LOT of attention using the PIC16F15376 Curiosity Nano board.
First of all, all the Curiosity Nano boards don't need an external programmer since they've a PKOB (PICkit On Board) programmer included, made specifically for programming the target PIC® microcontroller, so programming them is very easy: when you connect a Curiosity Nano board to the PC, a new Storage Device appears: you must only drag and drop the .HEX file to this device.
Curiosity Nano boards doesn't have the Primary External Oscillator, so you must use the SSWG_100_intosc.hex firmware. Then follow those steps:
- You MUST NOT POWER the entire SSWG circuit from the 5V coming out from the Curiosity Nano (Read: VBUS obtained from the USB) or you'll burn the board! So you must power the circuit from an external power supply but for doing this you must tie the VOFF pin to GND (this will disable the onboard regulator) and then give the 5V on the VTG pin (NOT VBUS). If you give the 5V on VTGpin without putting VOFF to GND, the board will fry!
- Follow the schematic paying attention to connecting components on the right pins of the Curiosity Nano board (you must follow the pin names, not numbers!). Here is the PIC16F15376 Curiosity Nano pinout for reference:
I also prepared a Reference Table with PINs definitions (you can download this in PDF format):
- RC0 and RC1 pins on the Curiosity Nano are not routed to the microcontroller by default since they can be used for the secondary oscillator. In order to use those pins you must put a solder blob on some pads under the board with RC0 and RC1 writings. Follow the Curiosity Nano manual.
- RE0 is already connected to an onboard led (LED0 on my schematic) so you can save a LED.
- RE2 is already connected to the onboard switch (SWITCH0) on my schematic so you can save a switch.
I want to remind you this one more time: The VOFF thing is the most important! Pay attention to this!
ChallengesWhen I started to use the potentiometer for setting duty cycle values in NCO and PWM a problem raised: since potentiometer position is absolute, when you change the duty cycle value in the PWM menu and then return to NCO in PF mode, potentiometer will be read at actual position varying the previous set value so I thought was better to not read the potentiometer when changing a menu until you decide to turn it: if you rotate the potentiometer in the PWM menu, then you go in the NCO menu / PF mode, the previous NCO duty cycle value will retained even if the potentiometer changed position; if you want to change the duty cycle you'll turn the potentiometer again. Anyway since duty cycle resolutions are a function of frequency, value will change anyway changing the frequency. This task looks very simple to implement but it required me some time to get working perfectly!
Another problem are the not perfect timings on the Servo Output: only the 1500 is very accurate but others are not (and some values don't produce variations). I did some corrections on values but anyway are still not perfect.
About the Source codeI didn't included the source code, but only the pre-compiled HEXs for a simple reason: many of project based on PIC® microcontrollers I released for free in the past became commercial products without my authotization, without asking, without asking me if I wanted to partecipate, without giving me a candy and this is very sad to me since I spend lot of time and money.
So I had an idea: I included an easter egg in the firmware!
In order to find the easter egg you must build the circuit and.. do something! If you find it, take a picture and send it to me (you can contact me on my Instagram), say me also how you obtained it... then I'll send you the source code.
ConclusionsMaybe the SSWG is not the perfect tool for generating Square Waves due to overmentioned issues (Square Waves shapes not so perfect over a certain Frequency and lack of accuracy on servo output), but is cheap and I use it a lot, so I thought Hackster was the best place where to talk about it and collect opinions, ideas and solutions.
Comments