Sunday, September 7, 2014

Minima Controller Shield checkout

UPDATED:

My Minima controller Arduino shield checks out as functional.  With two Si570 devices on the board, powering them from the Arduino regulator is pushing things, so I have isolated the 3V3 rail from the Arduino and provided my own regulator.  However, with only a single Si570 on the board, I am within limits on the Arduino regulator.  Therefore, I jumpered the power input pin back to the Vin pin on the Arduino to supply power to the shield.  This is the brown jumper wire seen below.  The yellow wire is the ground to my scope probe.



Measuring the voltage off the regulator and the Si570 look good.  Probing the output of the Si570 as seen above, we have the following default frequency of 56.32 MHz output seen below.



For those following along, the schematic of the shield looks like this.  There really isn't anything to it.  Just a multiplexer in front of two Si570's, voltage regulation and breaking out some of the I/O pins to separate cable connections for the Minima.




Now to go figure out how to talk to the multiplexer and the Si570.

I removed the shield from the Arduino Uno and connected up a bus pirate to see if I can talk to the PCA9546.  When searching the I2C address space for devices, initially I got a "response" from all possible addresses.  Clearly this is wrong...

Probing the PCA9546 I found that the voltages were correct, but that the reset pin was held low.  As seen from the schematic above, this is connected to the reset button and the ICSP reset pin.  However, this pin on the shield is not pulled up to any voltage rail and currently relies on the connected UNO to supply this voltage.  So, rather than power the shield from the bus pirate, I plugged it back into the UNO and reinstalled my brown power jumper and just monitored SCL and SDA pins with the bus pirate.



Now interrogating the bus yielded the following results:

Bus Pirate v3
Firmware v4.2 Bootloader v4.2
DEVID:0x0447 REVID:0x3043 (B5)
http://dangerousprototypes.com


I2C>(1)
Searching 7bit I2C address space.
   Found devices at:
0xE0(0x70 W) 0xE1(0x70 R) 0xFE(0x7F W) 0xFF(0x7F R)

Checking the PCA9546 datasheet, we see the following:


If you look at the schematic above, you will notice that I connect A0-A2 to ground, so the device address should be 0x70 (this value is the 0xE0 shifted right one bit to remove the read/write bit) and the read/write addresses should be 0xE0 for write and 0xE1 for read.

So, it looks like the PCA9546 can be seen on the bus and so now it might be time to write some actual code.

Saturday, September 6, 2014

Minima Controller Shield Build

I have completed (as far as I went) the build of my Minima Controller Shield, but have yet to complete testing of the board.  I populated only a single Si570, the edge and ICSP stackable headers and the header for my OLED display.



The prototype board needs a little work.  I need to move the VFO SMA edge connector away from the USB connector.  This will work for a prototype, but needs to move in the final version for sure.



The OLED display is a 3V3 device and I have it sharing the same I2C bus as the first Si570 for the VFO.  There is also a 5V set of connections for an LCD display with an I2C backpack for example.  This shares the same I2C bus as the PCA9546 multiplexer input, so it should allow 5V displays to work without driver modifications.   I will need to customize the 3V3 OLED driver to take into account that it is connected to the PCA9546 multiplexer.  I used this device because it allows voltage level conversion with a 5V input side and four 1.8 - 5V or more output side I2C buses.  It also allows me to address multiple devices that do not allow you to change their I2C address such as the Si570.  Without it, one would be limited to a single Si570 on a bus.



Ok, off to do some voltage testing and then to see if I can address the PCA9546.  If so, I will get busy on my display driver changes and testing the Si570.  I plan to roll my changes back into my Si570 driver to allow multiple instances behind a multiplexer as a compile time option.

Thursday, September 4, 2014

Small Progress on the Minimal Controller Shield

I had a little time this evening to start stuffing my prototype Minima controller board with parts.  Got the Si570, voltage regulator, reset button and PCA9546 I2C multiplexer installed.  The rest is connectors and a few R and C 0805 parts.  Getting there...



Progress on laying out my RF board for the Minima has been stalled for a couple weeks.  I hope to get back on that before too much more time has passed.

Tuesday, September 2, 2014

Direct Digital Synthesis (DDS) Part 2

Continuing on the theme of DDS in software, my previous post illustrated a simple audio sinusoidal generator using DDS techniques.  I promised to turn this baseline code into something more useful and here I will illustrate using this technique to generate a WSPR beacon message.

Here we will not be building a complete WSPR beacon, but only the components that deal with generating the WSPR audio signal in software.

I have done a similar beacon previously for the Parallax Propeller processor.  This will be a similar initial implementation that sends a pre-encoded message using Joe Taylor's wsprcode.exe application.  I encoded the message "KO7M CN87 7" and here is that message:

C:\Users\Jeff>wsprcode "KO7M CN87 7"
Message: KO7M CN87 7

Source-encoded message (50 bits, hex): 8B CC 46 9D 56 B1 C0

Data symbols:
      1 1 0 1 0 1 0 1 1 1 1 0 1 1 1 0 0 1 1 0 0 1 0 1 1 1 0 0 0 1
      1 1 1 0 0 0 1 1 0 0 1 0 1 0 0 1 1 0 1 1 1 1 0 1 1 1 1 0 0 0
      0 1 0 1 0 0 0 0 0 1 1 0 1 1 0 0 1 0 0 1 0 0 1 0 1 1 0 0 0 1
      0 1 0 1 1 0 1 0 0 0 1 1 0 1 0 1 0 1 0 1 1 1 0 1 0 1 1 0 0 1
      0 1 0 1 0 0 0 1 1 0 0 1 1 1 0 1 0 1 1 1 0 0 0 1 0 1 0 1 0 0
      1 0 1 1 1 0 1 1 0 0 0 1

Sync symbols:
      1 1 0 0 0 0 0 0 1 0 0 0 1 1 1 0 0 0 1 0 0 1 0 1 1 1 1 0 0 0
      0 0 0 0 1 0 0 1 0 1 0 0 0 0 0 0 1 0 1 1 0 0 1 1 0 1 0 0 0 1
      1 0 1 0 0 0 0 1 1 0 1 0 1 0 1 0 1 0 0 1 0 0 1 0 1 1 0 0 0 1
      1 0 1 0 1 0 0 0 1 0 0 0 0 0 1 0 0 1 0 0 1 1 1 0 1 1 0 0 1 1
      0 1 0 0 0 1 1 1 0 0 0 0 0 1 0 1 0 0 1 1 0 0 0 0 0 0 0 1 1 0
      1 0 1 1 0 0 0 1 1 0 0 0

Channel symbols:
      3 3 0 2 0 2 0 2 3 2 2 0 3 3 3 0 0 2 3 0 0 3 0 3 3 3 1 0 0 2
      2 2 2 0 1 0 2 3 0 1 2 0 2 0 0 2 3 0 3 3 2 2 1 3 2 3 2 0 0 1
      1 2 1 2 0 0 0 1 1 2 3 0 3 2 1 0 3 0 0 3 0 0 3 0 3 3 0 0 0 3
      1 2 1 2 3 0 2 0 1 0 2 2 0 2 1 2 0 3 0 2 3 3 1 2 1 3 2 0 1 3
      0 3 0 2 0 1 1 3 2 0 0 2 2 3 0 3 0 2 3 3 0 0 0 2 0 2 0 3 1 0
      3 0 3 3 2 0 2 3 1 0 0 2

Decoded message: KO7M CN87 7              ntype:  7

I grabbed the channel symbols and turned them into a byte array, thus:

uint8_t symbols[] = 
{
  3,3,0,2,0,2,0,2,3,2,2,0,3,3,3,0,0,2,3,0,0,3,0,3,3,3,1,0,0,2,
  2,2,2,0,1,0,2,3,0,1,2,0,2,0,0,2,3,0,3,3,2,2,1,3,2,3,2,0,0,1,
  1,2,1,2,0,0,0,1,1,2,3,0,3,2,1,0,3,0,0,3,0,0,3,0,3,3,0,0,0,3,
  1,2,1,2,3,0,2,0,1,0,2,2,0,2,1,2,0,3,0,2,3,3,1,2,1,3,2,0,1,3,
  0,3,0,2,0,1,1,3,2,0,0,2,2,3,0,3,0,2,3,3,0,0,0,2,0,2,0,3,1,0,
  3,0,3,3,2,0,2,3,1,0,0,2
};

// Maximum number of symbols in a WSPR message

const uint32_t iSymbolMax = (sizeof(symbols) / sizeof(uint8_t));

I then set up a couple of constants and a table of DDS tuning words for the WSPR channel symbols.  Remember that for a SSB transmitter, the audio tones that are used to modulate it for WSPR transmission are in the range of 1400-1600 Hz.  The WSPR band is 200 Hz wide and so I have chosen to put my beacon signal in the middle of the band.  Any frequency can be used in this 200 Hz range, but consideration should be made for the width of the signal when choosing to be near the band edge.

// Tuning word is set to the following multiplier times the tone frequency
const double multiplier = pow(2,32) / ref_frequency / 256;

// Tuning word delta for 1.4648 Hz delta between symbols
const double delta = 1.4648 * multiplier;

// Array of pre-calculated tuning words for each WSPR symbol spaced
// 1.4648 Hz apart. Arbitrarily chose symbol 2 for 1500 Hz as W1JT does not
// define it in his documentation.
volatile const uint32_t tuning_words[] = 
{
  (dds_frequency * multiplier - (delta * 2)),
  (dds_frequency * multiplier - delta),
  (dds_frequency * multiplier),
  (dds_frequency * multiplier + delta) 
};

I am going to use timer 2 to generate a 32 kHz clock (reference frequency) for the DDS and generate a 1500 Hz tone.

// DDS frequency is chosen to be in the middle of the 1400-1600 Hz band
// Any value in this range is appropriate, but should be chosen to stay within
// the band while allowing for 1.4648 Hz above and 2.9296 Hz below plus
// sidebands.
int dds_frequency = 1500;

// The reference frequency is subject to the accuracy of the 16 MHz
// crystal oscillator.
const double ref_frequency = (16000000/510);

So, once we enable interrupts, a 1500 Hz tone will be generated.  The four WSPR channel symbols are 1.4648 Hz apart and are of 683 ms duration.  Since we are executing an interrupt service routine every 32 us, it will take 21427 interrupts to generate one WSPR channel symbol.  Once we have seen this many interrupts, we move on to the next symbol in the WSPR message until all 162 symbols have been generated.  At this point, we disable interrupts and the tone generation stops.

// Timer 2 interrupt service routine (ISR) is used to generate
// the timebase reference clock for the DDS generator at 32kHz.
ISR(TIMER2_OVF_vect)
{
  // Keep track of how long this symbol has been playing and when to move
  // to next symbol. WSPR symbol length = 8192 / 12000 * 1000 ms = 682.6666 or
  // 683 ms.  This function is called about every 32 us.  By keeping track of
  // how many times this function is called we can calculate the length of
  // each symbol.
  //
  // Clock is 16000000 / 510 = 31372.5490 Hz = 31.875 us per tick
  // .683 / 3.1875e-5 = 21427 counts.  This may need be adjusted based on
  // accuracy of main 16 MHz clock.
  if (cTone++ >= 21427)
  {
    cTone = 0;  // Reset tone length counter
    iSymbol++;  // Carry on with next symbol
  }
  
  // Update 24 bit phase accumulator and extract the sine table index from
  // top 8 bits of phase accumulator.
  phase_accumulator += tuning_word;
  sine_table_index = phase_accumulator >> 16;  // 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);
  
  // If we have sent the entire 162 symbol WSPR msg, kill the timer
  // and reset to first symbol.
  if (iSymbol >= iSymbolMax)
  {
    TIMSK2 = 0;  // Disable timer 2 to stop tone
    iSymbol = 0; // Reset back to beginning of symbol set
  }
  
  // Get the next tuning word based on the current symbol
  tuning_word = tuning_words[symbols[iSymbol]];
}

So, putting all this together, I have a functional WSPR beacon using software DDS.  This is not a complete beacon solution as WSPR messages have to be started within a couple seconds of an even minute.  But, the code if started on an even minute is easily decoded by Joe Taylor's fine software.  Here is a screen shot of an Argo waterfall display of the signal:

Here is a screen shot of the signal decode:


Where am I going with all this?  Tying all this back to recent work on the Minima transceiver, I am proposing that the CW sidetone hardware be replaced by a software DDS sidetone generator.  In doing so, not only can we provide CW sidetone services, but the audio tone can be modulated in interesting ways to easily integrate digital modes such as WSPR into the Minima project.  On transmit, the CW sidetone is used to generate CW signals by feeding the audio signal into the transmitter audio chain.  The same technique is used for digital modes.

In my next post, I will flesh out the WSPR beacon to allow specification of the message without requiring the message be pre-encoded using wsprcode.exe.  Stay tuned...

Here is the complete source code listing thus far.  If you wish to just compile it and use it, please change the symbols[] array to contain your own WSPR message.

// Sine wave generator using DDS techniques implementing WSPR beacon
// Jeff Whitlatch - ko7m

#include "avr/pgmspace.h"

// Single period sine wave table.
// Amplitudes are 0-255 with 128 as zero crossing.  256 samples per period.
PROGMEM prog_uchar sine256[] =
{
  128,131,134,137,140,143,146,149,152,155,158,162,165,167,170,173,
  176,179,182,185,188,190,193,196,198,201,203,206,208,211,213,215,
  218,220,222,224,226,228,230,232,234,235,237,238,240,241,243,244,
  245,246,248,249,250,250,251,252,253,253,254,254,254,255,255,255,
  255,255,255,255,254,254,254,253,253,252,251,250,250,249,248,246,
  245,244,243,241,240,238,237,235,234,232,230,228,226,224,222,220,
  218,215,213,211,208,206,203,201,198,196,193,190,188,185,182,179,
  176,173,170,167,165,162,158,155,152,149,146,143,140,137,134,131,
  128,124,121,118,115,112,109,106,103,100,97,93,90,88,85,82,
  79,76,73,70,67,65,62,59,57,54,52,49,47,44,42,40,
  37,35,33,31,29,27,25,23,21,20,18,17,15,14,12,11,
  10,9,7,6,5,5,4,3,2,2,1,1,1,0,0,0,
  0,0,0,0,1,1,1,2,2,3,4,5,5,6,7,9,
  10,11,12,14,15,17,18,20,21,23,25,27,29,31,33,35,
  37,40,42,44,47,49,52,54,57,59,62,65,67,70,73,76,
  79,82,85,88,90,93,97,100,103,106,109,112,115,118,121,124
};

uint8_t symbols[] = 
{
  3,3,0,2,0,2,0,2,3,2,2,0,3,3,3,0,0,2,3,0,0,3,0,3,3,3,1,0,0,2,
  2,2,2,0,1,0,2,3,0,1,2,0,2,0,0,2,3,0,3,3,2,2,1,3,2,3,2,0,0,1,
  1,2,1,2,0,0,0,1,1,2,3,0,3,2,1,0,3,0,0,3,0,0,3,0,3,3,0,0,0,3,
  1,2,1,2,3,0,2,0,1,0,2,2,0,2,1,2,0,3,0,2,3,3,1,2,1,3,2,0,1,3,
  0,3,0,2,0,1,1,3,2,0,0,2,2,3,0,3,0,2,3,3,0,0,0,2,0,2,0,3,1,0,
  3,0,3,3,2,0,2,3,1,0,0,2
};

// Maximum number of symbols in a WSPR message
const uint32_t iSymbolMax = (sizeof(symbols) / sizeof(uint8_t));

// Useful macros for setting and resetting bits
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))

// DDS frequency is chosen to be in the middle of the 1400-1600 Hz band  Any value
// in this range is appropriate, but should be chosen to stay within the band while
// allowing for 1.4648 Hz above and 2.9296 Hz below plus sidebands.
int dds_frequency = 1500;

// The reference frequency is subject to the accuracy of the 16 MHz crystal oscillator.
const double ref_frequency = (16000000/510);

// The following formula will calculate the necessary tuning word for a given output freq
// tuning_word = pow(2,32) * dds_frequency / ref_frequency;

// These must all be marked as volatile as they are used in an interrupt service routine
volatile byte sine_table_index;
volatile uint32_t phase_accumulator;
volatile uint32_t tuning_word;
volatile uint16_t iSymbol;
volatile uint16_t cTone;

// Tuning word is set to the following multiplier times the tone frequency
const double multiplier = pow(2,32) / ref_frequency / 256;

// Tuning word delta for 1.4648 Hz delta between symbols
const double delta = 1.4648 * multiplier;

// Array of pre-calculated tuning words for each WSPR symbol spaced 1.4648 Hz apart.  
// Arbitrarily chose symbol 2 for 1500 Hz as W1JT does not define it in his documentation.
volatile const uint32_t tuning_words[] = 
{
  (dds_frequency * multiplier - (delta * 2)),
  (dds_frequency * multiplier - delta),
  (dds_frequency * multiplier),
  (dds_frequency * multiplier + delta) 
};

// Setup
void setup()
{
  // PWM output for timer2 is pin 10 on the ATMega2560
  // If you use an ATMega328 (such as the UNO) you need to make this pin 11
  // See https://spreadsheets.google.com/pub?key=rtHw_R6eVL140KS9_G8GPkA&gid=0
  pinMode(10, OUTPUT);      // Timer 2 PWM output on mega256 is pin 10
  
  // Set up timer2 to a phase correct 32kHz clock
  timer2Setup();

  iSymbol = cTone = 0;     // Set initial symbol and symbol length
  
  // Calculate the tuning word for the first symbol before we enable interrupts
  tuning_word = tuning_words[symbols[iSymbol]];
  
  // disable interrupts to avoid timing distortion
  //cbi (TIMSK0, TOIE0);    // Disable timer 0.  Breaks the delay() function
  sbi (TIMSK2, TOIE2);      // Enable timer 2.
}

// Nothing to do here.  Everything is interrupt driven
void loop()
{
}

// Setup timer2 with prescaler = 1, PWM mode to phase correct PWM
// See th ATMega datasheet for all the gory details
void timer2Setup()
  TIMSK2 = 0;
  TCCR2A = _BV(COM2A1) | _BV(COM2B1) | _BV(WGM20);
  TCCR2B = _BV(CS20);
}

// Timer 2 interrupt service routine (ISR) is used to generate
// the timebase reference clock for the DDS generator at 32kHz.
ISR(TIMER2_OVF_vect)
{
  // Keep track of how long this symbol has been playing and when to move to next symbol
  // WSPR symbol length = 8192 / 12000 * 1000 ms = 682.6666 or 683 ms.  This function is
  // called about every 32 us.  By keeping track of how many times this function is called
  // we can calculate the length of each symbol
  //
  // Clock is 16000000 / 510 = 31372.5490 Hz = 31.875 us per tick
  // .683 / 3.1875e-5 = 21427 counts.  This may need be adjusted based on accuracy of
  // main 16 MHz clock.
  if (cTone++ >= 21427)
  {
    cTone = 0;  // Reset tone length counter
    iSymbol++;  // Carry on with next symbol
  }
  
  // Update 24 bit phase accumulator and extract the sine table index from top 8 bits
  phase_accumulator += tuning_word;
  sine_table_index = phase_accumulator >> 16;  // 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);
  
  // If we have sent the entire 162 symbol WSPR msg, kill the timer
  // and reset to first symbol.
  if (iSymbol >= iSymbolMax)
  {
    TIMSK2 = 0;  // Disable timer 2 to stop tone
    iSymbol = 0; // Reset back to beginning of symbol set
  }
  
  // Get the next tuning word based on the current symbol
  tuning_word = tuning_words[symbols[iSymbol]];

}

Monday, September 1, 2014

Arduino Timers

In taking my DDS code to the next level, I needed to dig into Arduino timers and gain a fuller understanding of how they work.  The ATMega328 has three timers known as Timer 0, Timer 1 and Timer 2.  Each timer has two outputs and corresponding output compare registers that determine when the output is toggled.

Each of the timers has a prescaler that generates the timer clock by dividing the system clock (16 MHz) by a selectable value 1, 8, 63, 256 or 1024.  Timer 2 has a different set of prescale values from the other timers.  Each timer output has a corresponding output compare register that can be used to generate interrupts.

Each timer has a select-able mode.  The PWM modes are "Fast PWM" and "Phase Correct PWM".  Each timer can run from 0 to 255 or from 0 to a fixed value.  Timer 1 is a 16 bit counter that has additional modes to support timer values up to 16 bits.  Each output may optionally be inverted.

A timer, once enabled will run and can generate interrupts on overflow and/or matches against either output compare register.  Each timer has a set of registers that control the behavior of that timer.

TCCRnA, TCCRnB: Timer/Counter Control Registers - Holds the main control bits for the timer.  It should be noted that the "A" and "B" on the end does not correspond to the outputs A and B.  These registers hold several groups of bits:

  • WGM: Waveform Generation Mode - These bits that are split between TCCRnA and TCCRnB control the overall mode of the timer.
  • CS: Clock Select - These bits control the clock prescaler.
  • COMnA, COMnB: Compare Match Output - Enable, disable, invert output A or B respectively.

OCRnA, OCRnB: Output Compare Register - Sets the levels at whch outputs A or B respectively will be affected.  When the timer value matches the register value, the corresponding output will be modified as specified by the mode.

Timer Pins
It is at best confusing which timer controls which pin, not to mention that it varies between different processors.  For the Mega328, the following table describes the Output Compare register, silk screened pin number on the Arduino board, pin number on the chip and name of the pin.

Timer OCR  Board pin Chip pin Name 
0  OC0A 6 12 PD6 
OC0B 5 11 PD5 
1 OC1A 9      15   PB1 
OC1B   10      16   PB2 
2 OC2A 11 17   PB3 
      OC2B 3 5   PD3

Timers are initialized by the Arduino to set the prescaler to divide the clock by 64.  Timer 0 is set to Fast PWM while Timer 1 and Timer 2 are initialized to Phase Correct PWM.

Internally Arduino uses Timer 0 to implement the millis() and delay() library functions.  Changing the frequency of this timer will affect these functions.

There are a couple of modes for each timer that will be discussed separately.

Fast PWM

This is the simplest PWM (pulse-width-modulated) mode.  The timer repeatedly counts from 0 to 255.  The timer output turns on when the timer is at 0 and turns off when the timer matches the output compare register value.  The higher the output compare register value, the higher the duty cycle.  Both timer outputs will have the same frequency but can have the different duty cycles as set by OCRnA or OCRnB.

The output frequency for an 8 bit timer is determined by the system clock (16 MHz) divided by the currently set prescaler value divided by 256.  The last division by 256 is because the timer runs from 0 to 255 before it overflows.  For example, assume a prescaler set to divide by 64:

Frequency = 16 MHz / 64 / 256 = 16000000 / 64 / 256 = 976.5625 Hz
Duty Cycle Output A = OCRnA+1 / 256.
Duty Cycle Output B = OCRnB+1 / 256

As can be seen from the duty cycle calculation, Fast PWM holds the output high one cycle longer than the value in the Compare Match Output register OCRnA/OCRnB.  The motivation behind this is that for Fast PWM counting to 255, the duty cycle can be from 0 to 256 cycles.  The output compare register however can only hold the values 0 to 255.  The solution is to keep the output high for OCR+1 cycles. so an OCR value of 255 is 100% duty cycle, but an OCR value of 0 is a 1/256% duty cycle.  This is in contrast to Phase Correct PWM where an OCR value of 0 is a 0% duty cycle and 255 is a 100% duty cycle.

Phase Correct PWM

In this mode the timer counts from 0 to 255 and then back down to 0.  The output turns off as the timer hits the OCR value on the way up and turns it back on at the OCR value on the way back down.  This results in a more symmetrical output, the frequency of which will be 1/2 the value for Fast PWM mode because the timer runs both directions.

Again assuming a prescaler value of 64:

Frequency = 16 MHz / 64 / 255 = 16000000 / 64 / 255 / 2 = 490.196 Hz
Duty Cycle Output A = OCRnA / 256.
Duty Cycle Output B = OCRnB / 256

Notice that frequency is divided by 255 instead of 256 and that the duty cycle calculations do not add one as seen above.

This is important

Suppose that a timer is set to Fast PWM mode and is set up to count to an OCRnA value of 3.  In this case the timer will take the values 012301230123...  Note that there are 4 clock cycles in each timer cycle.  Thus, the frequency will be divided by 4.  The duty cycle will be a multiple of 25% (1/4) since the output can be high for 0, 1, 2, 3, or 4 cycles out of the four.  Similarly, if the timer counts up to 255, there will be 256 clock cycles in each timer cycle and the duty cycle will be a multiple of 1/256.  In other words, Fast PWM divides by N+1 where N is the maximum timer value (either OCRnA or 255).

In the case of Phase Correct PWM mode and the same OCRnA value of 3, the timer values will be 012321012321...  There are six clock cycles in each timer cycle (012321).  Therefore the frequency will be divided by 6 in this case and the duty cycle will be a multiple of 33% since the output can be high for 0, 2, 4, or 5 of the 6 clock cycles.  Again, if the timer instead counts up to 255 and back down, there will be 510 clock cycles in each timer cycle and the duty cycle will be a multiple of 1/255.  In other words, phase-correct PWM divides by 2N where N is the maximum timer value.

Tips

You need to both enable a pin for output and enable the PWM mode on the pin in order to get any output.

Different timers use the control bits and prescaler differently.  Be sure to check the datasheet for the processor in use to know the appropriate settings for the timer.

Some combination of bits do not work together.  For example, toggle mode does not work with Fast PWM to 255 or with output B.

Be sure you have correctly set the necessary bits in the correct control register.

Check that you are using the correct output pins for the given timer on a given processor.


Sunday, August 24, 2014

Corrections to DDS code

Many thanks to my friend Eldon, WA0UWH who pointed out the fact that my 0-255 amplitude table in my previous post has some 256 values in it.  Oh sigh...  I neglected to take into account that the range of sine values is -1 to +1 inclusive.  I added a generic mapping function to handle the mapping correctly.  I have updated my previous post with the changes.  This also fixed the gliches seen in my integrated output.  See the last scope trace screen shot.  I have updated this photograph as well, so all evidence of temporary insanity has been removed...

Thanks Eldon!

Saturday, August 23, 2014

Direct Digital Synthesis (DDS)

UPDATED

With all the recent work on software for the Si570 device, this got me thinking a bit about the process of direct digital synthesis.  I went back and read again a great tutorial on the topic by Analog Devices MT-085 Fundamentals of Direct Digital Synthesis and decided to experiment with a software DDS using the Arduino as a test platform.

Basically the architecture of a DDS system has a stable clock driving a look up of sinusoidal information where one or more cycles of a sine wave (or for that matter, any other arbitrary waveform) are fed in sequence to a digital to analog converter to produce the final output.  Each of these look ups can be stored in a read-only memory and the value fetched represents the corresponding digital amplitude of the signal at each clock tick.

For my experimentation, I will construct a sinusoidal waveform using 8 bit data representing the amplitude of the signal at each clock tick.  To construct the table of sinusoidal amplitude information, I will retrieve a value from 0-255 which will map to a signal where the sinusoidal waveform crosses zero at the middle of this range (e.g. 128) and 256 samples for one period of the waveform.

Each address in the look up table corresponds to a phase point on the sine wave from 0 to 360 degrees.  In reality only data for 90 degrees (one quadrant of a circle) would be required as the quadrant is indicated by the two most significant bits.  The look up table contains the corresponding digital amplitude information for one complete cycle of a sine wave used to drive a DAC (digital to analogue converter).

If the look up table contained an entry for each bit of a 32 bit index into the table, there would be 2^32 output values before the index overflowed.  A sine wave thus constructed would have a frequency equal to the input clock frequency divided by 2^32 as it would take that many data points to reconstruct the sine wave.  Let's call this the M=1 case (one increment per clock tick).

If we increment through the table a little faster, say M=2 then the table index will roll over twice as fast and the output frequency would be doubled.  For an n-bit index (in DDS systems this is called a phase accumulator) there are 2^n possible phase points (data points in our look up table).  Let's call M the amount that the phase accumulator (index) is incremented on each clock cycle.  If fc is the clock frequency, then the frequency of the output sine wave is determined by the following formulae, also known as the DDS tuning equation.

    fout = (M * fc) / (2^n)

The frequency resolution of a DDS system is equal to fc/2^n.  If n = 32, this represents a resolution greater than one part in four billion. (2^32 = 4,294,967,296).  This is rather impractical and in general unnecessary.  In most DDS systems not all of the bits of the phase accumulator are used in the look up table and only the upper (most significant) bits are used.  This can significantly reduce the size of the look up table without affecting the frequency resolution.  This truncation of the phase does however add a small amount of phase noise to the final output.

There is a lot more information in the reference I mention above (MT-085) and well worth a read by anyone interested in DDS technology.

The first task at hand is to construct a table of 256 sine values representing the amplitude of the sinusoid for one sine period.  I wrote a little C# application to generate the necessary data structure text:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace GenSineData
{
  class Program
  {
    static void Main(string[] args)
    {
      Console.WriteLine("PROGMEM prog_uchar sine256[] = ");
      Console.WriteLine("{");
      Console.Write("  ");

      for (int i = 0; i < 256; i++)
      {
        double angle = 360.0 / 256.0 * Convert.ToDouble(i);
        double radians = angle * Math.PI / 180;
        double sin = Math.Sin(radians);
        
        // Map range from -1 to +1 to 0 to 255
        int amplitude = Convert.ToInt32(map(sin, -1, 1, 0, 255));
        Console.Write(amplitude.ToString() + (i != 255 ? "," : ""));
        if (i > 0 && (i+1) % 16 == 0)
        {
          Console.WriteLine();
          Console.Write("  ");
        }
      }
      Console.WriteLine("};");
    }

    // Map from one range to another
    static double map(double x, double in_min,
                      double in_max, double out_min, double out_max)
    {
      return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
    }
  }
}

The output of this application can be pasted into my Arduino code producing a program memory-based lookup table.

PROGMEM prog_uchar sine256[] =
{
  128,131,134,137,140,143,146,149,152,155,158,162,165,167,170,173,
  176,179,182,185,188,190,193,196,198,201,203,206,208,211,213,215,
  218,220,222,224,226,228,230,232,234,235,237,238,240,241,243,244,
  245,246,248,249,250,250,251,252,253,253,254,254,254,255,255,255,
  255,255,255,255,254,254,254,253,253,252,251,250,250,249,248,246,
  245,244,243,241,240,238,237,235,234,232,230,228,226,224,222,220,
  218,215,213,211,208,206,203,201,198,196,193,190,188,185,182,179,
  176,173,170,167,165,162,158,155,152,149,146,143,140,137,134,131,
  128,124,121,118,115,112,109,106,103,100,97,93,90,88,85,82,
  79,76,73,70,67,65,62,59,57,54,52,49,47,44,42,40,
  37,35,33,31,29,27,25,23,21,20,18,17,15,14,12,11,
  10,9,7,6,5,5,4,3,2,2,1,1,1,0,0,0,
  0,0,0,0,1,1,1,2,2,3,4,5,5,6,7,9,
  10,11,12,14,15,17,18,20,21,23,25,27,29,31,33,35,
  37,40,42,44,47,49,52,54,57,59,62,65,67,70,73,76,
  79,82,85,88,90,93,97,100,103,106,109,112,115,118,121,124
  };

Importing this data into Excel and graphing it, we find a nice sinusoidal waveform with zero crossings at the value 128 and all amplitude values between 0 and 255 using only 256 data points and 8 bit amplitude values.



So far, so good...

To compute the necessary tuning word for the DDS for the desired output frequency:

  M = (2^32) * desired_frequency / clock_frequency

For my purposes, I intend to use a clock_frequency of approximately 32kHz by dividing down the Arduino master clock of 16MHz.  I will use a timer interrupt at 32kHz and a 32 bit phase accumulator.  The upper 8 bits will be used as the index into the above sine wave table used to drive the Arduino PWM DAC.  Of course a low pass filter will be required to remove high frequency components of the audio output.

Ok, so now we put together a little Arduino test application to pull all these concepts together.  I am using an ATMega2560 for my testing but any Arduino would work so long as the pin differences are taken into account between the platforms.  More on this later.


Here is my DDSTest sketch:

// Sine wave generator using DDS techniques
// Jeff Whitlatch - ko7m

#include "avr/pgmspace.h"

// Single period sine wave table.
// Amplitudes are 0-255 with 128 as zero crossing.  256 samples per period.
PROGMEM prog_uchar sine256[] =
{
  128,131,134,137,140,143,146,149,152,155,158,162,165,167,170,173,
  176,179,182,185,188,190,193,196,198,201,203,206,208,211,213,215,
  218,220,222,224,226,228,230,232,234,235,237,238,240,241,243,244,
  245,246,248,249,250,250,251,252,253,253,254,254,254,255,255,255,
  255,255,255,255,254,254,254,253,253,252,251,250,250,249,248,246,
  245,244,243,241,240,238,237,235,234,232,230,228,226,224,222,220,
  218,215,213,211,208,206,203,201,198,196,193,190,188,185,182,179,
  176,173,170,167,165,162,158,155,152,149,146,143,140,137,134,131,
  128,124,121,118,115,112,109,106,103,100,97,93,90,88,85,82,
  79,76,73,70,67,65,62,59,57,54,52,49,47,44,42,40,
  37,35,33,31,29,27,25,23,21,20,18,17,15,14,12,11,
  10,9,7,6,5,5,4,3,2,2,1,1,1,0,0,0,
  0,0,0,0,1,1,1,2,2,3,4,5,5,6,7,9,
  10,11,12,14,15,17,18,20,21,23,25,27,29,31,33,35,
  37,40,42,44,47,49,52,54,57,59,62,65,67,70,73,76,
  79,82,85,88,90,93,97,100,103,106,109,112,115,118,121,124
  };

// Useful macros for setting and resetting bits
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))

// DDS frequency and reference clock
double dds_frequency = 0.0;
const double ref_frequency = (16000000/500);

// These must all be marked as volatile as they are used in an interrupt service routine
volatile byte sine_table_index;
volatile uint32_t phase_accumulator;
volatile uint32_t tuning_word;

void setup()
{
  // PWM output for timer2 is pin 10 on the ATMega2560
  // If you use an ATMega328 (such as the UNO) you need to make this pin 11
  // See spreadsheet here
  pinMode(10, OUTPUT);      // Timer 2 PWM output on mega256 is pin 10
  
  // Set up timer2 to a phase correct 32kHz clock
  timer2Setup();

  // disable interrupts to avoid timing distortion
  cbi (TIMSK0,TOIE0);    // Disable timer 0.  Breaks the delay() function
  sbi (TIMSK2,TOIE2);    // Enable timer 2.

  // Set up initial DDS frequency and calculate the timing word
  dds_frequency = 1000;
  tuning_word = pow(2,32) * dds_frequency / ref_frequency;
}

// Nothing to do here.  Everything is interrupt driven
void loop()
{
}

// Setup timer2 with prescaler = 1, PWM mode to phase correct PWM
// See the ATMega2560 datasheet for all the gory details
void timer2Setup()
{
  // Clock prescaler = 1
  sbi (TCCR2B, CS20);      // 001 = no prescaling
  cbi (TCCR2B, CS21);
  cbi (TCCR2B, CS22);

  // Phase Correct PWM
  cbi (TCCR2A, COM2A0);    // 10 = clear OC2A compare match
  sbi (TCCR2A, COM2A1);

  // Mode 1
  sbi (TCCR2A, WGM20);     // See table 20-8 in datasheet
  cbi (TCCR2A, WGM21);
  cbi (TCCR2B, WGM22);
}

// Timer 2 interrupt service routine (ISR) is used to generate
// the timebase reference clock for the DDS generator at 32kHz.
ISR(TIMER2_OVF_vect)
{
  // 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); 

}

Connecting a powered speaker to pin 10 allows me to hear the 1kHz tone.  The PWM signal on pin 10 looks like this, though when this picture was taken, a 1.5kHz tone was being generated.





So, far so good...  A low pass filter will be required or at least some level of integration to turn this into a sine wave.  Since we are sampling at 32kHz, Nyquist says that the highest frequency we can produce is 16kHz.  So by setting a simple RC low pass filter cutoff frequency near this frequency should be adequate.  I will choose 15kHz for simplicity.

  cutoff frequency = 1/ (2 * pi * R * C)

Using a 1000 ohm resistor for R:

  15000 = 1/(2000 * pi * C)
  C = 1/(30000000 * pi)
  C = 1.06103295e-8 farad or 0.0106103295 microfarads.  (.01 uF will do thanks...)

The following is the result using this simple integrator:





Fun!  Ok, so next installment, I hope to do something useful with all this...

More to come...

Monday, August 11, 2014

Si570 Arduino Shield PCB is off to be manufactured

I finished up the Si570 shield for the Arduino today and sent the gerber files off the the fab house today.  They will be on the panel to be produced on 13 Aug, so hopefully I will have some prototype PCBs back soon.



Update: My prototype boards shipped yesterday (Friday) so hopefully they will be in hand by Monday.