Posts Tagged ‘ Sine Synthesizer ’

Arduino – Si5351 Powered Signal Generator

Arduino-Si5351 Signal Generator

Arduino-Si5351 Signal Generator

Device Description

For a long time I wanted a general purpose signal generator. Now Direct Digital Synthesizer hardware is available on a single programmable chip. The Analog Devices AD9851 is used in many ham radio projects, also widely used is the Silicon Labs Si5351. Either of these can be obtained from many sources such as Adafruit, Sparkfun, or on EBay and there’s lots of information on the internet. Even Amazon has them. A few dollars gets a DDS chip that will tune continuously from audio to VHF mounted on a small breakout board. I purchased an Si5351 board from Etherkit because they offer a version with a TCXO.

Silicon Labs makes the Si5351 in several variations. It’s intended use is as a multiple output clock generator with up to eight individually programmed frequencies. Most commonly available breakout boards though, use the A version with three outputs. Digikey has the bare chips for less than $1. Connectors are optional, most boards are set up for SMA female jacks. The signal generator I built brings out all three outputs but I used good old RCA phono jacks. SMA connectors are wonderful but the cables to use them are pricey. My box will be used to check and align receivers so precision impedance control is less important.

AD9851 chips have a real Digital/Analog converter on board, it uses an amplitude lookup table to produce a fair sine wave. The data sheet says:

“The AD9851 uses an innovative and proprietary angle rotation algorithm that mathematically converts the 14-bit truncated value of the 32-bit phase accumulator to the 10-bit quantized amplitude that is passed to the DAC. This unique algorithm uses a much-reduced ROM look-up table and DSP to perform this function.”

which sounds suspiciously like the quarter wave sine synthesis I experimented with a few years ago. I’ll have to revise my WordPress pages about quarter wave DDS techniques to include the phrase “angle rotation algorithm”. The price for pure sine wave complexity is, the device has only a single output. In contrast, the Si5351 outputs a square wave on all it’s ports – it is intended to serve as a programmable frequency clock source but for my purposes I don’t care and the available multiple outputs are attractive.
I breadboarded the Etherkit breakout along with one of Paul Stoffregen’s Teensy-LCs I had on hand for the controller. A Teensy-LC has an ARM Cortex M0 heart clocked at 48 Mhz. An ordinary 16 Mhz Arduino would probably work in this circuit but would have trouble with sweep. I used basic software from N6QW to get started and my oscilloscope soon showed it was working. Then a series of enhancements to the N6QW sketch followed:
  • expand to controlling all three outputs
  • setting up frequencies using rotary encoder and LCD menus
  • save and restore setup in EEPROM
  • add output power change to the menus
  • add  output OFF to the power menu
  • add sweep capability with menu control

The Box

A thin metal gift card box was cut up to form an enclosure for the generator. It is about a quarter inch larger than the usual Altoids tin in all three dimensions. I needed the extra volume to fit in a battery and charger removed from a cheap phone power pack. These booster packs usually contain a single 18650 cell and it just fits.

Arduino-Si5351 Signal Generator Interior

Arduino-Si5351 Signal Generator Interior

The components are, top to bottom, blue 16×2 LCD board supporting the Teensy-LC. The entire unit can be 5 volt powered either from the Teensy USB jack or from the battery charger, I added a fat diode to isolate the two sources.  Both the Teensy and the Si5351 board are using their on board voltage regulators. The LCD is five volt, but the Teensy-LC does not have 5 volt tolerant inputs. There are 2.2k resistors between the LCD data pins and the Teensy to smooth over that discrepancy, these would not be necessary if the LCD was a 3.3 volt unit. The pot below the Teensy adjusts the LCD contrast.
At left below the LCD is the Adafruit (Bourns) rotary encoder. It just clears the battery. Right of the encoder is a strip of perf board with the three output select pushbuttons mounted. The LCD, encoder and buttons are Adafruit parts.
At the top of the open box bottom is the salvaged 18650 cell. The measured drain of the whole circuit is less than 100 milliamps so the battery should last half a day at least. The gooey glob at the center is a 2.5 amp fuse. To the right of the battery is the on/off switch and isolation diode. The salvaged Lithium charge/boost circuit is the green board at bottom right. It has it’s own separate USB jack because the USB data leads are not accessible. To program the unit you have to plug into the Teensy USB jack.
Finally the blue board in the bottom is the Etherkit breakout. The actual Si5351 chip is at the left end, next to the TCXO. There are three RCA output jacks mounted in the left side of the box, the fourth jack next to the handle is for syncing the scope when sweeping.


On power up the unit reads saved frequency settings from EEPROM. The display then shows frequency, current port selected, and the output power setting.

Click one of the three buttons to change the port selected to display on the LCD.

Rotating the encoder knob will change the frequency digit under the flashing cursor. Press click the encoder knob to change the digit under the cursor, you can set the cursor to change digits from 1 Hz to 10 MHz.

Hold one of the port select buttons down and turn the encoder knob to change output power for that port. The chip has choices of 8 milliamps, 6 mA, 4 mA, 2 mA, and OFF. Silicon Labs’ spec for driver impedance is 50-85 ohms.

If any of the above settings are changed, the software waits ten seconds, then copies the current settings into EEPROM. In the event the unit gets confused, it can be restored to last saved settings by cycling the power switch. Also it can be returned to default settings by holding down all three port select buttons, while powering up.

Arduino-Si5351 Signal Generator Frequency/Power

Arduino-Si5351 Signal Generator Frequency/Power Setting


Sweep Function

Sweep is accessed from a separate menu. Press down the encoder knob for more than two seconds (a long press) and release to enter sweep parameters for the currently selected port.  A short click of the encoder knob will advance the menu through the sweep choices, currently +/-  0, 1000, 5000, 15000, 50000, or 150000 Hz. The unit sweeps from frequency minus that amount to frequency plus that amount so the total width of sweep is twice the setting. Sweeping is done by reprogramming frequencies in 20 steps between the limits.

A second long press of the encoder knob will return to the frequency menu. The letters “sw” appear on the LCD to indicate that that port is set up to sweep. All three ports can be set up but only the port currently selected in the display will be sweeping at any given time.

A pulse is available at a phono jack on top of the box to trigger an oscilloscope at start of each sweep iteration.

Sweep parameters are not currently saved in EEPROM.

Arduino-Si5351 Signal Generator Sweep

Arduino-Si5351 Signal Generator Sweep Setup Screen


The Software

I am using the Si5351 library from Etherkit. I added a function to completely disable the output drivers to save battery when a port is set to “OFF” so I will include the library in the zip file.
Most of the sketch, implementing multiple port and the menus, was straight forward. The one thing that gave me fits was the quadrature nature of the rotary encoder. This is a mechanical switch and it glitches. Not a problem if you are say, setting an analog output from 0 to 1023, you probably wouldn’t notice but if you need to change a digit by exactly one, glitches are intolerable.  Conventional debouncing a switch usually just waits a certain amount of milliseconds after a transition is detected, read again and proceed. There are debounce libraries for doing this. I had no luck. I finally found an post by Oleg Mazurov about treating the quadrature pulses as a Gray code. It helped a lot and I posted about my findings in the Adafruit Feather forum.
The Adafruit rotary encoder outputs four state transitions on each of it’s 24 rotary detent positions. To get one increment per detent, I tried skipping four state reads with a software counter but it was still glitching if the knob was inadvertently touched. What finally worked was changing the Gray code decode table to disallow all but one positive change and one negative change. The allowed changes remaining are in the middle of the detent so glitches near the resting position are unlikely.
Sweep is accomplished by reprogramming a port frequency on the fly. Tests indicate this is slow and the 20 step sweep takes about 65 milliseconds which limits the sweep rate to about 15 per second. I found a very good way to time something is to write a 1 to some output pin at the beginning, then write a 0 at the end. It’s easy to see exactly how much time the intervening code took by measuring the pulse on an oscilloscope. The send_frequency function takes 3.5 milliseconds. Looking at the Si5351 library, the set_freq code is long and complex. It might be possible to speed this up by using the set_freq_manual call but I’m not smart enough to do that. Porting this code to a 16 Mhz Arduino might make the sweep unusably slow.

Addendum April 4, 2018: Amplitude Modulation Experiment

I thought I might try generating an AM modulated signal by summing three generator outputs.  A 1.1 MHz signal modulated  at 1 KHz would have a 1.100 MHz carrier, a 1.101 MHz upper sideband at half the carrier amplitude, and a 1.099 MHz sideband at half the carrier amplitude. I breadboarded a resistive combiner as follows:
Resistive Combiner

Resistive Combiner

The Si5351 was set to equal drive power on all three outputs.  I examined the output on the oscilloscope, channel 1 connected to AM Out, channel 2 to Audio Out with sync taken from Channel 2. The result is not encouraging. It does output an amplitude modulated signal but the waveforms are bizarre. I see a blocky square wave changing at a 1000 Hz rate but there is some phase problem I can’t control.  This is the best I could capture:

AM Modulated Signal

AM Modulated Signal

Connecting an audio amp and speaker does show a 1 KHz tone if the phases happen to line up just right. I found that setting any one of the generators to be one Hz off frequency results in a rolling pattern with about a one per second beat note in the speaker.

This method of faking Amplitude Modulation is certainly not precise or controllable. Setting one of the outputs off frequency by a few Hz does give a useful warbling tone in an AM receiver.


A zip archive is available including sketch source, the Si5351 library, and a schematic
Jan 4, 2018 V1.1 including the sweep function
 Jan 5, 2018 V1.11 Sweep bug would not change freq while in sweep mode. Cleaned up schematic.

Arduino Iambic Keyer – 2017

Last spring I started doing what I call “Morse Walking”. Going out with headphones on, beeping morse code. I’m doing this hoping I will get good enough at morse to actually use it on the air without making a fool of myself. I previously made two battery operated Arduino based keyers here and here that can generate random morse characters. But they are awkward, the 2015 version requires an external battery and the 2016 model won’t fit in my pocket. Obviously I needed another project.

Arduino Feather keyer

Arduino Feather keyer


I decided to use an Adafruit Feather, it has an Atmel 32u4 processer, built in USB interface, a charger/converter for a 3 volt lithium battery, and plenty of pins brought out. The idea was to cut down the previous keyer software to fit the 32u4 equipped with only the hardware I needed for battery powered practice. Like many of my projects, putting it all into a small box proved to be difficult. Not many parts in the schematic but that Altoids tin is only 2 1/4″ x 1 1/2″ and about 1/2″ deep.

Arduino Feather Iambic Keyer Schematic

Arduino Feather Iambic Keyer

Note the unusual wiring at the headphone jack. Ring is grounded while tip carries the signal. Sleeve is not connected. This puts the two headphone elements in series and presents a higher impedance to the output circuit. The ring normal contact can then signal power enable to the Feather when the phones are plugged in.

I sawed about a quarter inch off the breadboard end of the Feather to make it fit, along with two phone jacks, in an Altoids Small tin. I wired up the Feather along with a quadrature rotary encoder which I thought would be cool to use as a volume control. Porting the previous keyer sketch to the 32u4 proved more difficult than I expected. The encoder required major changes to the keyer code as volume control is now in software and requires manipulation of the sine wave synthesizer tables. I got that part mostly working but set the project aside in May.

So now it’s November and I’ve picked up the little keyer again. The software has been revamped and now has these features:

  1. Characters to be sent are buffered in an asynchronous circular queue so memory buttons or keyboard characters can be “typed ahead”.
  2. USB serial terminal is supported as a keyboard for sending or function programming.
  3. Paddle generated morse is interpreted and printed as ASCII letters on the serial terminal.
  4. Four programmable memories available with 251 character capacity each.
  5. Memories programmable from serial terminal or from the paddles.
  6. Random code practice modes, letters, letters and numbers, letters numbers and punctuation.
  7. Adjustable character spacing in code practice mode (Farnsworthiness).
  8. Morse character speed settable 10 to 45 WPM.
  9. Sidetone frequency settable 100 to 1200 Hz.
  10. Synthesized sine wave sidetone with leading and trailing edge envelope shaping.
  11. Memories and operating parameters stored in EEPROM and easily reset to defaults.
  12. Stand alone operation from LiPo battery.

I went for an hour walk today with the keyer in my shirt pocket and it performed flawlessly so I consider it done.

This is a photo of the unit with battery removed.

Arduino Feather keyer battery removed

Arduino Feather keyer battery removed


I had to move the JST connector to the side to get clearance, the battery would not fit under the rotary encoder. There are four memory buttons, plus the back switch on the encoder used for Function. I’m using my usual analog read routine to debounce the button switches. Metal cap switches from Adafruit, work much better than the 6mm plastic buttons I used in previous versions.

You can see here the battery just fits.

Arduino Feather keyer with battery

Arduino Feather keyer with battery


I spent quite a bit of time working on the waveform generation setup. The encoder routine generates a number 0-64 which is used to scale the synthesizer waveform. Four 16 byte amplitude tables must be regenerated in RAM each time the volume is changed. Initially the sine waves were horribly distorted so I made a spread sheet helped to see what was happening. I was able to work out code that produced a good stepped morse element.  There is very little audible clicking now at beginning or end of the morse elements.

This photo is a dit at about 25 WPM.

Arduino Feather keyer waveform

Arduino Feather keyer waveform

The software, detailed operating instructions, schematic, and spreadsheets can be downloaded from Dropbox.
Any future revisions will appear here

Version 1.0 12/3/2017

Version 1.2 12/6/2017 Was running out of memory, rearranged startup and moved stuff to PROGMEM

Jim Harvey –


More Fun With Direct Digital Synthesis: Adafruit Feather 32u4

Feather Port with Volume Control

Long walks are a good thing and I’ve taken along one of my morse code keyers running in practice mode to pass the time. Neither of the keyers I made fit pockets very well so I decided to build yet another one, battery operated, with headphone only output. The target container is an Altoids Small tin easily carried in a shirt pocket.

An Adafruit Feather 32u4 Basic has built in USB and an on board battery charger so it is a good start for this project. I ported my previous 32u4 code easily. Reference my previous page on the 32u4 experiments here. Driving headphones directly from a Pulse Width Modulated output is different from my previous projects where I used a small audio amplifier to drive a speaker. Headphones have a much lower impedance than an amp so RC filter components change radically. Implementing a volume control would be a challenge. I tried a 500 ohm trim pot and that worked fairly well with some but not all of the headphones I tried. But I couldn’t find a pot with a shaft that would fit in the Altoids tin.

With PWM Direct Digital Synthesizer generating a sine wave I might be able to throttle the PWM output with software. I have an Adafruit Rotary Encoder that fits the Altoids tin if I’m careful so I worked that into the demo program.  Rotary Encoders are basically mechanical, therefore subject to contact bounce like any other switch. I Googled up a half dozen different Encoder sketches and all of them would glitch badly. I finally found code by Oleg Mazurov that uses a Grey coding technique to ignore invalid inputs. It works well. I was able to get that code running on the Feather and contributed the sketch to the Adafruit Feather forum.

This photo shows the slightly truncated Feather in the Altoids Small tin. The encoder is the green object in the center of the lid. There are a couple of Oscilloscope umbilicals attached and the battery is not yet installed.

Changing the apparent volume of the DDS output is a process of multiplying the Sine table values by a volume parameter between zero and one. I copy the Sine table into RAM and apply this transform. But since I use only integer math in the sketch, the method is, read the value from flash then multiply by 0-63 volume, then divide the result by 63. Close enough.

One more improvement: in the original sketch, even when the volume is set to zero the PWM signals are 50% high and 50% low, averaging zero as far as the Sine wave coupled through the output capacitor is concerned. That means the output pin is still driving full voltage at the PWM frequency. With the low load impedance it’s a significant drain on the DC power and I’m using a very small battery. The remedy was to lower the zero crossing base line in step with lowering the amplitude of the Sine table.  A new volatile variable maxSine passes the necessary correction to the DDS Interrupt Service Routine. The ISR doesn’t like having it’s variables changed on the fly. Rotating the Encoder makes a scratchy sound like a dirty potentiometer but it’s fine when you stop adjusting.

I measured 18 milliamps constant draw from the USB supply before implementing the zero crossing shift. With the shift, current varies from 10 ma at zero volume to 17.6 ma at maximum volume. The 150 mah battery should last 6-10 hours, way more than my morse code attention span.

My example sketch with all the experimental options in the previous version is downloadable from Dropbox. A short video showing the waveforms produced is on YouTube.



Arduino Iambic Keyer 2016 – Part 3: Operation

Memory Keyer 2016

Arduino Iambic Keyer - Keyboard and Paddles

Arduino Iambic Keyer – Keyboard and Paddles


This describes the operation of an Arduino sketch and appropriate hardware that serves as an iambic morse code keyer. This version runs on a PJRC Teensy3.2 board and libraries. It will not compile to an Atmel (traditional Arduino) processor without major changes. I chose the Cortex M4 based Teensy because it has built in Real Time Clock, a real DAC, built in USB, and lots of memory. I added a 16×2 liquid crystal display and batteries for stand alone operation.


  1. Characters to be sent are buffered in an asynchronous circular queue so memory buttons or keyboard characters can be “typed ahead”.
  2. PS2 and serial terminal keyboards supported.
  3. Paddle generated morse is interpreted and printed as ASCII text.
  4. Seven 50 character memories. Each is programmable from keyboards or from paddles.
  5. Random code practice modes, letters only, letters and numbers, letters numbers and punctuation.
  6. Sending speed settable 10 to 45 WPM. Limits can be changed by recompiling.
  7. Sidetone frequency settable 100 to 1200 Hz. Limits can be changed by recompiling.
  8. Synthesized sine wave sidetone with leading and trailing edge envelope shaping.
  9. Memories and operating parameters stored in EEPROM, are easily reset to defaults.
  10. Stand alone operation from batteries. Based on tests, a 4400 MAH battery will last at least 36 hours.
  11. A Liquid Crystal Display.

Outside the Box

This keyer has five I/O connectors:

  1. A 3 conductor jack for the paddle or straight key.
  2. A 3 conductor jack for transmitter keying with an optocoupler open collector output on the tip. Line level sidetone audio is connected to the ring.
  3. An external speaker jack connected to a 2.5 watt audio amplifier.
  4. A mini DIN connector for PS2 keyboard. USB keyboards will work with an adapter.
  5. A micro USB port for programming and serial terminal connection. This also powers the unit and charges the battery.

There are eight push button switches, a volume control, and an LED:

  1. A Function button.
  2. Seven push buttons to activate individual memories.
  3. The volume control feeds a Class D amplifier for the speaker. It does not affect the level on the line output connection.
  4. The LED follows the transmit keying signal.

There is an on/off switch that also serves to reboot the processor into setup mode.

Liquid Crystal Displays have a limited space for messages so information has to be presented sequentially. Switching on power displays clock status then “Keyer Ready” is displayed on the LCD and on the serial port. The unit is then ready for normal operation.

Straight Key Operation

On power up, if the unit senses a two conductor plug in the paddle jack, it will go into Straight Key mode and just pass keying through to the transmit circuit, with side tone. Memories and other features are not available as the morse engine is never started.

Normal Iambic Operation

The dual paddle jack is wired dit paddle on tip, dah paddle on ring of the mini stereo connector. The external speaker jack will work with stereo headphones or a speaker. On the transmit jack, there is an open collector optocoupler connected to tip and line level side tone on the ring.

Software will attempt to translate paddle sent morse into ASCII which will display on the LCD and is also sent to the serial port. Decode success depends on how good your fist is. The keyer cannot decode morse received over the air.

Keyboard Operation

The keyer will accept and send characters entered from a PS2 keyboard or from a terminal emulator program such as PuTTY, or even the Arduino IDE serial window. It’s better to use an emulator that supports line at a time transmission as that gives you an opportunity to fix typing mistakes. Opening a terminal window on a USB connected PC also lets you view and log status messages sent from the keyer. PS2 support also works with a USB keyboard and appropriate USB to PS2 adapter.

All characters in the Wikipedia morse code table are supported except the underscore and the dollar sign. From either serial or PS2 keyboards, enter normal text in lower case. Entering text in upper case will suppress the next intercharacter gap, thus running two characters together. This can be used to send prosigns. For example, As (wait) or Sk (end of contact). Message text entered from the paddles will always be treated as all lower case.

Serial or PS2 keyboards use a one at a time command mode, activated by typing a back slash followed by a single character. Keyboard commands are:

  1. \+ or \= increase sending speed one Word Per Minute
  2. \- decrease sending speed one Word Per Minute
  3. \u increase sidetone frequency by 5%
  4. \d decrease sidetone frequency by 5%
  5. \w save current sidetone frequency and WPM to EEPROM memory
  6. \1, \2 … \7 send stored text as though a memory button was pressed.


Seven programmable 50 character memories are available by pressing buttons. The sketch uses a 64 byte circular buffer to queue sendable text from either keyboard or from EEPROM memory, the buffer is asynchronous so memory messages can be stacked with interspersed keyboard text. Memory messages are read as needed from EEPROM so each consumes only a single byte of circular buffer space. Memories can also be queued from the keyboards by entering commands \1 … \7.

Message memory can be programmed from compiled defaults, from either keyboard, or directly from the paddles if your sending is good which is useful in stand alone operation. To program a message into memory, press and hold the Function button, then press and release the desired memory button, then release Function. Enter your text from paddles or keyboard, then exit programing mode by clicking Function again.

Normal Operation Display

Operating parameters can be viewed on the LCD by holding the Function button. Parameters also print to the serial port. The rotating menu display will show:

  • Time and date both GMT and local
  • Sending speed and side tone frequency
  • Battery status
  • The first 16 characters of each programmable message.

Each item will be on the LCD for one second. Releasing Function stops menu rotation but does not erase the LCD which allows more time to read a particular display.

Changing Words Per Minute

Since speed may need to be adjusted according to conditions, there is a provision to change WPM on the fly. Just hold the Function button down and close a paddle. Dits increase speed, dahs decrease. On releasing Function, the changed speed writes to EEPROM.

You can also change speed from the keyboards by entering \+ to increase or \- to decrease. Entering \= will also increase WPM if you forgot to shift. If adjusting by keyboard, you must enter \w to write the new setting to EEPROM.

Changing Side Tone Frequency

This can be done from the keyboards by sending \u to raise frequency or \d to lower frequency. As with WPM, enter the command \w to save the change. Tone frequency can be set in stand alone operation from the Startup Menu described below.

Battery Status

Software constantly monitors battery voltage and the PowerBoost charger status signals. These can be seen in the operating display. Voltage is shown in hundredths with no decimal point. A fully charged battery will show about 4.20 volts, if the battery drops below 3.40 volts “LOW BATT” will show on the battery status and the LCD back light will flash. If voltage drops below the boost regulator LB threshold, about 3.25 volts the status display will show “DEAD BATT”.

Startup Mode

Arduino sketches have a separate startup() section for initializing things. It is executed once when the processor resets, just before entering the main event loop(). This keyer takes advantage of separate startup to recall and optionally change important system parameters. A normal power on sequence copies the following items from EEPROM to RAM:

  • Words Per Minute
  • Side Tone frequency
  • LCD Back light brightness
  • GMT – Local time offset

Memory messages are also stored in EEPROM but are not read into RAM. Startup then reads the Real Time Clock into the system clock.

Startup then checks to see if Code Practice mode (see below) is requested, if so, Code Practice begins as soon as the memory button is released.

Startup Menu

If Startup sees the Function button held down, it enters Startup Menu mode. Eight parameters rotate on the LCD, seven of these can be entered and changed.

  1. Sending speed Words Per Minute
  2. Side Tone frequency
  3. LCD Back light brightness
  4. System clock time (GMT)
  5. System date (also GMT)
  6. GMT – Local offset in hours
  7. Reset all stored parameters to defaults
  8. Display battery voltage and charge status

For Startup Menu purposes, three of the memory buttons are redefined. M5 is Enter, M6 is Up, M7 is Down. Click the Enter (M5) button while a menu item is on the display to activate change mode for that item. For the first three menu items and for GMT offset, Up (M6) and Down (M7) act directly on the displayed number.

Once entered, Time and Date can be set by repeatedly pressing the Enter button until the cursor is beneath the digit needing change. Once there Up and Down operate as expected. The RTC will be updated when the Time or Date menu is exited.

After a change, exit the menu item by pressing and holding Enter (M5) for longer than 2 seconds. Menu rotation will continue where it left off.

Entering menu 7 will reset all EEPROM stored parameters including the seven memory messages to defaults specified in the file “canned.h”. You can change these defaults by editing that file with the Arduino IDE and recompiling. Canned.h appears as one of the tabs at the top of the Arduino editor window.

The last Startup Menu display shows the current battery voltage and charge status. This information is also sent to the serial connection, at the menu rate of rotation, about five times per minute. A terminal emulator program like PuTTY or Minicom on the PC, can log these battery messages in a text file, you can later load the text into a spread sheet and with some amount of fussing create a charge or discharge curve for the battery. To get a discharge curve though you have to disable charging through the USB connection by cutting the red wire in the USB cable.

Startup Menu mode is exited by clicking the Function button again. Changed parameters will write to EEPROM and normal operation starts.

Synchronize the Real Time Clock

Internal system time is maintained by software in the PJRC libraries. System time is initialized at bootup from the crystal controlled, battery backed Real Time Clock inside the processor chip. You can hack set the RTC from the Startup Menu, but there is a way to synchronize the RTC with a PC clock over a USB serial connection. If the PC is itself synchronized with an Internet NTP server, the result will be within 1 or 2 seconds of WWV.

Accurate clock synchronization requires the Arduino sketch be ready to accept and process a time update at the exact moment the PC sends it. Connect the keyer to the PC with a USB cable, power up the keyer while holding the Function button down, then while the LCD displays “Enter Setup Mode”, paste the following Linux command into a shell;

date +T%s\n > /dev/ttyACM0

Release the Function button and the RTC synchronizes. There is a processing sketch included with the PJRC Time library that can be used to synchronize the clock from a Windows computer.

Code Practice Mode

Holding one of the first three memory buttons down on boot up puts the keyer in Code Practice Mode. Characters generated are based on tables in the Wikipedia article on Morse Code.

  • M1 Send letters only
  • M2 Send letters and numbers
  • M3 Send letters, numbers, and punctuation

Characters are sent in groups of five. If a serial terminal window is open it will display the sequential group number as well as the ASCII characters themselves.

Practice Mode Commands

In practice mode, the first four memory buttons adjust the delay between characters in increments of two element (dot width) times. M1 adjusts by zero, M2 by two, M3 by four, M4 by six elements giving a listener additional time to decode the sounds. Each step slows the average WPM by about 10 percent.

Pressing the M5 button pauses practice and the display back light will begin blinking. Pressing M5 again resumes but will likely have mutilated any character that was in progress. M6 increases sending speed and M7 lowers sending speed, one WPM per click. The changed speed is only effective until the keyer is reset, it is NOT written to EEPROM.

Display Mode

Normal Operating Displays are also available in Code Practice mode by pressing Function. The display will not start until the current five letter code group is completed.

Battery Status

If the battery voltage drops below 3.40 volts, the display back light will begin flashing.


Many thanks to Richard Chapman KC4IFB whose September 2009 QEX article provided the inspiration and base code for this sketch. His iambic keyer code feels exactly like my original WB4VVF Accukeyer. Also see Rarons Blog for a discussion of the tree method for decoding and encoding morse characters. It was very helpful in building efficient translation tables. The circular queue was implemented with help from examples at Paul Stoffregen encouraged me to try the Teensy3.2 on the PJRC forum.


Revision History

February 25, 2016       MemoryKeyerTeensy3.2_V1.0    Initial sketch
March 9, 2016             MemoryKeyerTeensy3.2_V1.1.0  Rework battery alarm logic, bug fixes.
March 16, 2016           MemoryKeyerTeensy3.2 V1.1.1   Workaround fix LCD does not have backslash in its font.


Still More Fun With Direct Digital Synthesis: TeensyLC with DAC

I posted a link to my previous page on setting up PWM Direct Digital Synthesized sine waves on PJRC’s Teensy 2.0, a 32u4 Arduino. Paul Stoffregen (the PJ in PJRC) commented that I should try porting my DDS demo sketch to a TeensyLC. “You won’t need PWM” he said. “Use the Digital Analog Converter” he said. I was a bit hesitant because TeensyLC has an ARM processor running at 48 Mhz. Quite a bit more power than the Atmel 16 Mhz chips and more I/O capability, so worth a look.  PJRC supplies an Arduino IDE add on called Teensyduino which configures support for PJRC boards so simply doing an analogWrite to pin A12 (took me a while to actually find A12) starts the DAC instead of starting PWM. Teensyduino has an included library IntervalTimer which sets a repetitive timer running and attaches that to an Interrupt Service Routine which you supply.  So an LC port should be simple as the dirty work of configuring the timer is taken care of.

Arduino PWM based DDS uses a fixed sample rate of 31 or 62 Khz and the interrupt generated sine wave has to fit into that constraint. That means low frequency audio tones are relatively smooth as there are lots of sample periods to go around even with the 256 step tables. High audio frequencies on the other hand, suffer distortion as the shorter period audio wave can’t fit in enough sample periods.

In the TeensyLC, the DAC always gets a fixed number of bits so you change the interrupt frequency to change the frequency of the generated audio tone. For instance, using the 256 step table, to generate a 1000 Hz tone requires 1000 * 256 interrupts per second. That’s about one every four microseconds, which even with the 48 Mhz processor clock, is going to use a LOT of CPU time. The shortest period I could set with IntervalTimer was 5 microseconds, my experiments with the demo program showed a maximum audio frequency of 650 Hz with the 256 step table, the 128 table limited at 1300 Hz, and the 64 step table maxed out at 2600 Hz.

Another down side to the DAC method stems from the fact that the audio frequency granularity is determined by the 1 microsecond granularity of the IntervalTimer function multiplied by the number of steps in the sine table.  The demo program shows this while sweeping. Low audio frequencies sweep smoothly but the higher frequencies have very noticeable jumps. Higher sine table sizes show this effect much more.

But the output wave form is much better than PWM. The following photos were shot using the Quarter Wave symmetry version of the demo sketch. Full table version traces were identical. All these were taken at a set audio frequency of 440 Hz. The filter was a single 10K resistor in series with the output pin, and a 0.01 ufd capacitor to ground. You can see the wave period changing a bit with the different table sizes. That is due to the granularity issue.

TeensyLC DAC Bottom - Raw, Top - Filtered 256 Step Table, Quarter Wave

TeensyLC DAC Bottom – Raw, Top – Filtered
256 Step Table, Quarter Wave


TeensyLC DAC Bottom - Raw, Top - Filtered 128 Step Table, Quarter Wave

TeensyLC DAC Bottom – Raw, Top – Filtered
128 Step Table, Quarter Wave


TeensyLC DAC Bottom - Raw, Top - Filtered 64 Step Table, Quarter Wave

TeensyLC DAC Bottom – Raw, Top – Filtered
64 Step Table, Quarter Wave


TeensyLC DAC Bottom - Raw, Top - Filtered 32 Step Table, Quarter Wave

TeensyLC DAC Bottom – Raw, Top – Filtered
32 Step Table, Quarter Wave Symmetry


TeensyLC DAC Bottom - Raw, Top - Filtered 16 Step Table, Quarter Wave Symmetry

TeensyLC DAC Bottom – Raw, Top – Filtered
16 Step Table, Quarter Wave Symmetry

I will probably use the TeensyLC in my next version of morse keyer.  I have to find a 16×2 LCD that will work at 3.3 volts, figure out how to interface a 5 volt PS2 keyboard, and how to charge and connect a 5 volt battery pack. The LC (and the 32u4) have USB serial built in which will make interfacing a terminal window on a logging computer a lot easier.

I found a useful web page showing how you implement quarter wave sine symmetry DDS on an FPGA and I took this opportunity to streamline the quarter wave ISR. You have to be familiar with bit banging to understand it now.

The demo sketches, full table and quarter table versions, and the Libre spreadsheet I used to develop the quarter wave half step offset tables are included in the archive at:

More Fun With Direct Digital Synthesis: 32u4 and Fast PWM

32u4 Port

My 2015 winter project will be an expanded version of the Morse Code keyer I built last year. I now have a PJRC Teensy 2.0 controller board (from Sparkfun) to use. The Atmel 32u4 based Teensy 2.0 is even smaller than the Pro Minis I have been using. It is attractive because it has USB built in for program loading and serial transmission. And there’s lots of I/O pins available which will make an LCD easy to connect. I note that everybody making a 32u4 “Arduino” is defining a different pinout from the chip. The Adafruit Feather is quite ambitious, has it’s own IDE addon and can be bought with Bluetooth or WiFi  included.

This breadboard photo shows the Teensy 2.0 at top left, Sparkfun Pro Mini at center, Sparkfun RedBoard top right, and a MicroCenter Pro Mini clone at bottom right.



PJRC has ported many of the standard Arduino libraries to their 32u4 Teensy form factor and these are included in your Arduino IDE when you download and install the Teensyduino addon. But my keyer sketch uses Direct Digital Synthesis to form a nice sine wave side tone which requires direct setup of a Pulse Width Modulation timer inside the AVR chip. My implementation is based on code from Jeff Whitlatch, KO7M.  I used timer 2 in last winters UNO based keyer as that’s the timer that does tone() in the Arduino UNO and I completely superseded that function. Reading the data sheet on 32u4 shows that it doesn’t even *have* a timer 2. It has timer 0 (8 bit), timers 1 and 3 (16 bit), and timer 4 (10 bit). Digging into the Teensy Arduino libraries shows that PJRC is using timer 3 for tone() so I needed a port that just changed the timer used. Comparing the 32u4 data sheet with the Mega328 data sheet showed that the only difference besides the timer number would be in the Waveform Generation Mode bits. 32u4 has four WGM bits, Mega328 has 3. I used a mode setting that turns the 16 bit 32u4 timer 3 into an 8 bit timer and Success! The port was working in my test program.

Fast PWM

Phase Correct PWM in an AVR processor uses the timer in a manner that has it count from 0 to 255 then count back down to zero for each interrupt cycle. The PWM signal goes high on the way up and low on the way down. For an 8 bit counter, that’s 510 counts per cycle (read the data sheet to find out why it skips two) so with no clock scaling the sample rate is 16 MHz/510 or 31.4 KHz. In Fast PWM mode the timer counts from 0 to 255 then rolls over back to zero. A PWM wave on the output pin goes high on the way up and low on overflow. This results in some jitter on the PWM pulses but the sample rate is twice as fast, 16 MHz/256 or 62.5 KHz. I decided to try Fast PWM in the test program. It required only a change in the math that calculates the sample rate, and a slightly different set of WGM bits.

The following images show comparisons of the 31.4 KHz (Phase Correct) and the 62.5 KHz (Fast) rate tone waveforms from the test program. The PWM signal is filtered by a single 10k resistor and a 0.01 ufd capacitor to ground at the scope connection.

Comparison of Phase Correct PWM with Fast PWM

Comparison of Phase Correct PWM with Fast PWM at 100 Hz


Comparison of Phase Correct PWM with Fast PWM at 1000 Hz

Comparison of Phase Correct PWM with Fast PWM at 1000 Hz


Comparison of Phase Correct PWM with Fast PWM at 5000 Hz

Comparison of Phase Correct PWM with Fast PWM at 5000 Hz


Comparison of Phase Correct PWM with Fast PWM at 10000 Hz

Comparison of Phase Correct PWM with Fast PWM at 10000 Hz


There is a clear improvement at the higher sample rate in the amount of “fuzz” on the waveform. The fuzz is at the sample rate and is the result of the 0.01 ufd capacitor integrating the PWM square wave. At twice the rate, the capacitor has half the time to charge or discharge. You can see at 5 KHz, the Fast PWM signal is much closer to a sine wave than Phase Correct. At 10 KHz, both are falling apart. A better output filter that cuts off the sample frequency would help but for morse keyer side tone from a tiny speaker, the average amateur radio operators ear won’t know the difference. I may try a switched capacitor filter some time in the future just to see. Watch this space.

Revision History

2015/12/08   The photos above are all from the 32u4 port. The program uses quarter wave symmetry to reduce the table size by a factor of 4. Two sketches, one running on UNO or other Mega328 controllers, the other for the PJRC 32u4 Teensy are at:

I also back ported the changes to an earlier sketch that did not take advantage of symmetry. Scope traces were identical to the 32u4 port. Since the complete 360 degree cycle is specified, the code may be modified to generate arbitrary non-sinusoidal waveforms by manipulating the tables. Download at:

Each of these zip archives has a copy of the spreadsheet I used to construct the sine tables.



Fun With Direct Digital Synthesis

You know that good feeling you get when a project comes together? Thats what I felt when I saw this sine wave on my oscilloscope.  It’s a Pulse Width Modulation (PWM) signal generated by an Arduino using a Direct Digital Synthesis (DDS) technique and a lookup table of amplitude values. It was the result of searching for a way to improve the tone quality of a morse code keyer project and I had gotten half wave, then quarter sine wave symmetry working in a demonstration sketch.

Quarter Wave Symmetry

Quarter Wave Symmetry
1000 Hz 256 Step Table

Digital to Analog Conversion is best done with optimized precision circuits. The relatively simple processor in an Arduino does not have a DAC device but it can do Pulse Width Modulation which can, in most cases, do the job adequately.

Pulse Width Modulation is widely documented. Basically it is a technique for generating an analog voltage from a digital signal. The device generates a high speed pulse train with varying pulse widths.  This is fed through a filter circuit which averages the energy in the pulses.  Fat pulses average to a higher voltage, skinny pulses average to a low voltage. By changing the pulse width over time, digital software can create a good analog representation of an arbitrary periodic signal. Other interesting applications are possible.

An Arduino generates PWM by setting up a timer. This is a counter with a circuit to trigger an interrupt at a specified count. The counter is incremented by the processor clock or, through a prescaler, a submultiple of the processor clock.  PWM is achieved by the Interrupt Processing Routine (ISR) manipulating the specified count at which the timer fires. DDS applications synthesize an arbitrary waveform by using a lookup table of values that the ISR accesses sequentially to change the terminal count.

Common non-DDS applications might be dimming an LED , feeding an RC servo, or controlling the speed of a motor, and the Arduino environment has a convenient analogWrite() function that handles all the details for you. Hybrid cars use PWM to control the traction motors.

I found many articles on generating DDS sound using the Arduino platform. I picked one from KO7M as a base to learn more about the method. Read Jeff’s weblog entry with source code here. His sketch sets up timer 2 for PWM with a 32 KHz base frequency.  Timer 2 is normally used by the Arduino for the tone() function and since my aim is to supersede the tone() function with something better, his example was a perfect starting point. In this article, I will only describe the changes I made in Jeff’s Interrupt Service Routine (ISR) and in the tables. Details of setting up the timers are covered in the source code and in many Internet sources. Here is a drawing of the Arduino connections:

Schematic for DDS Demo

Schematic for DDS Demo

Only five external parts needed including a button switch that stops and starts the sweep.

My test bed started out simple but it grew some (gruesome?).

Test Bed

Test Bed

Part 0: Analyzing and Parting Out the KO7M Sketch

After confirming that the sketch actually worked, I added code to sweep the frequency range from 50 to 2000 HZ so I could hear what it would sound like, and observe fidelity on my oscilloscope. Later I also added lines to generate a pulse at the start of the sine wave on a different pin. Using this pulse as an external sync source allowed the scope to display a steady waveform. I removed the sync code in the examples in this post for clarity.

This is the ISR from the original sketch (with small changes by me). TuningWord is the only parameter passed from the main part of the program. It is adjusted to set the particular PWM output frequency desired:

ISR(TIMER2_OVF_vect) {
  byte sine_table_index;
  static uint32_t phase_accumulator;
  // Update phase accumulator and extract the sine table index from it
  phase_accumulator += tuning_word;
  sine_table_index = phase_accumulator >> 24;  // Use upper 8 bits as index

  // Set current amplitude value for the sine wave being constructed.
  OCR2A = pgm_read_byte_near(sine256 + sine_table_index); 

Note Jeff is storing his sine table in flash memory. If you’re not familiar with that technique, read this tutorial. It is a good method for reducing the RAM footprint of an Arduino sketch. You can find my version of Jeff’s sketch here.  This is the 1000 Hz output from the original sketch with floating point math:

Original Floating Point

Original Floating Point
1000 Hz 256 Table

Part 1: Integer Conversion and Multiple Sine Tables

At this time, all floating point math in the example was converted to integer. To my surprise, it still works. There is more jitter on the waveforms due to integer truncation, but for keyer sidetone it is acceptable and the sketch sized dropped by about half.

I wanted to experiment with multiple lookup tables to get an idea of the minimum memory footprint possible while still producing acceptable sound. Libre Office Calc easily produced data with a 0-255 range for tables of length 256, 128, 64, 32 and 16 samples. See my spreadsheet here. Calc could also graph the tables to show how rough the waveform might be.  It was a simple matter to paste a table column into VI and join the values into C style  statements. These lines of data were inserted into the sketch and wrapped with #ifdef and #endif statements. At the top of the sketch is this code:

// Table sizes. Uncomment one and only one
//#define Table256
//#define Table128
#define Table64
//#define Table32
//#define Table16

Which allows me to choose a table size to evaluate at compile time. The wrapped tables look like:

#ifdef Table16
// 16 samples per period
#define Bits 12               // # of bits to shift 
PROGMEM prog_uchar sineTable[16] = {
#endif // Table16

Bits is a constant definition that controls how far the ISR has to right shift the phase accumulator variable so the number that remains does not exceed the sine table size.

The interrupt handler changed little and looks like this:

ISR(TIMER2_OVF_vect) {
byte sineTableindex;
static unsigned int phaseAccumulator;

  // Update phase accumulator and extract the sine table index from it
  phaseAccumulator += tuningWord;
  // Right shift because we're only using the most significant bits of the INT
  sineTableindex = phaseAccumulator >> Bits;

  // Look up current amplitude value for the sine wave being constructed.
  OCR2A = pgm_read_byte_near(sineTable + sineTableindex);

Code for this full table sketch is here. These are waveforms from the Integer version:

Integer Version

Integer Version
Three Table Sizes

Part 2: Using Half Wave Symmetry of Sine Waves to Reduce Table Size

During my Google research I found articles pointing out that the size of the sine table could be reduced by a factor of two or four if you exploited the symmetry of a sine wave. Several pages described how to do this in FPGA hardware but I could not find a good example of symmetry use in C code and nothing at all for an Arduino. I tackled symmetry in two stages.

For half wave symmetry it is only necessary to duplicate the first half of the wave for the second half, inverting the second half as it is generated. First you need to know which half is being processed during the interrupt. The Most Signifigant Bit (MSB) of the phase accumulator variable has that information. It will be zero in the first half of the sine wave, one in the second half. I added a variable to record this bit and use a mask of 0x8000 to extract the bit. Then a new constant MSBMask was created inside the table definitions to turn off the high order MSB during table lookup. The tables now look like this example:

#ifdef Table16
// 16 samples per period
#define Bits 12
#define MSBMask 0x07
PROGMEM prog_uchar sineTable[8] = {
#endif // Table16

Note the table sizes are half the original. This 16 stepe per period table is now only 8 bytes long.  The modified ISR with half wave symmetry now looks like:

ISR(TIMER2_OVF_vect) {
byte sineTableindex;                     // calculated index into sine table     
static unsigned int phaseAccumulator;    // running total of phase offset
unsigned int whichHalf;                  // for sine symmetry switching

  // Update phase accumulator and extract the sine table index from it
  phaseAccumulator += tuningWord;
  whichHalf = phaseAccumulator & 0x8000; // record which half of wave

  // Right shift because we're only using the most significant bits
  // of the INT. Leave only enough bits for one half table range
  sineTableindex = (phaseAccumulator >> Bits) & MSBMask;

  // Set current amplitude value for the sine wave being constructed.
  OCR2A = pgm_read_byte_near(sineTable + sineTableindex);

  // invert the second half of sine wave
  if(whichHalf) OCR2A = 255 - OCR2A;

The only additions to the ISR were the whichHalf variable, MSBMask which is defined with the sine table to remove the MSB, and the if statement at the end which does the actual inversion.

Code for the half wave symmetry version is here. These photos are from the half wave version:

Half Wave Symmetry Version

Half Wave Symmetry Version
Three Table Sizes

Part 3: Using Quarter Wave Sine Symmetry to Reduce Table Size

Exploiting quarter wave symmetry is more complicated. It is a left/right symmetry while half wave symmetry is up/down. First you have to know which quarter of the wave is being generated. The second most significant bit in the phase accumulator variable has this information. It will be 0 if in the first or third quarter, 1 if second or fourth. A new state variable and a mask of 0x4000 records this bit.  Since we are using less of the lookup tables, the MSBMask is smaller, it now needs to discard two MSB bits. The tables now look like:

#ifdef Table16
// 16 samples per period
#define Bits 12
#define MSBMask 0x03
PROGMEM prog_uchar sineTable[5] = {
  128, 177, 218, 245, 255
#endif // Table16

Note that the sixteen sample table has been reduced to five (not four) bytes. The 256 level table is only 65 bytes long! The odd byte at the end was necessary to work around a glitch – the table read from flash would look up the same value twice at the beginning and end of the even quadrant. Adding one to the index and one extra table byte solves that issue. The ISR now looks like this:

ISR(TIMER2_OVF_vect) {
byte sineTableindex;                     // calculated index into sine table     
static unsigned int phaseAccumulator;    // running total of phase offset
unsigned int whichHalf;                  // for sine symmetry
unsigned int whichQtr;                   // for sine symmetry
static boolean pulsed;                   // for unique scope sync

  // Update phase accumulator and extract the sine table index from it
  phaseAccumulator += tuningWord;
  whichHalf = phaseAccumulator & 0x8000; // record which half of wave
  whichQtr =  phaseAccumulator & 0x4000; // record which quarter of wave

  // Right shift because we're only using the most significant bits
  // of the INT. Leave only enough bits for one quarter table range
  sineTableindex = (phaseAccumulator >> Bits) & MSBMask;

  // Look up current amplitude value for the sine wave being constructed.
  if(whichQtr) {                         // in second or fourth quarter
  // +1 works around a glitch where same data at the quarter 
  // transitions was looked up twice in a row
    OCR2A = pgm_read_byte_near(sineTable + (MSBMask - sineTableindex + 1));
  else {                                 // in first or third quarter
    OCR2A = pgm_read_byte_near(sineTable + sineTableindex);
  // invert the second half of sine wave
  if(whichHalf) OCR2A = 255 - OCR2A;

This code does quarter wave symmetry first, then wraps half wave symmetry around that for the final output.  The changes to add quarter wave are the whichQtr variable and it’s masking line, and the if(whichQtr) statement which inverts the table lookup left to right. MSBMask in in the byte read statement because it is the number of steps that exactly form a quarter of the wave form.

Code for the quarter wave symmetry sketch is here. Here are scope traces of the quarter wave symmetry version:

Quarter Wave Symmetry Version

Quarter Wave Symmetry Version
Three Table Sizes


These changes to the original Interrupt Service Routine allowed the table sizes to be reduced by a factor of four with hopefully, not too much additional overhead. Any ISR overhead added is executed 32000 every second so you have to be conservative.  CPU time in the ISR is traded for a smaller program.

All three of these sketches can be useful for arbitrary waveform synthesis by constructing an appropriate table.  The base sketch would work for any periodic waveform, including those that have no symmetry either up/down or left/right. A sawtooth wave for example.  Half wave symmetry would be good for representing an underdamped or overdamped square wave.  Quarter wave symmetry works for sine waves, as shown here, or a triangle wave.

The oscilloscope traces are interesting by themselves. These sketches and a scope could be used to demonstrate microcontroller techniques at your local science fair. My scope is an 80’s vintage Tektronix clone. On the screen you can see the generated sine waves and by zooming in, see the artifacts of PWM happening at a 32 KHz rate. Use the coarse 16 step table and the staircase model of DDS is clear. You can view the raw PWM by simply disconnecting the 0.1 Ufd integrating capacitor from the scope input. Interesting though, disconnecting the filter capacitor does not affect the audio much because your ears can’t respond to 32 Khz sound.

DDS quantization is most visible at low frequencies, using the smaller tables. Here you can clearly see the 16 steps and 32Khz fuzz on the trace.

50 Hz 16 Step Waveforms

50 Hz 16 Step Waveforms


The 64 step sine synthesizer with quarter wave symmetry was successfully added to my keyer project.  I see no degradation of the morse timing at 50 WPM.  A brief video is here.  Also I recorded MP3s of the tone before and after.  The tone produced by the stock Ardino tone() function is here.  The tone from the 64 step (16 values) DDS function is here.