In this installment of Scope Noob I’m working with Direct - TopicsExpress



          

In this installment of Scope Noob I’m working with Direct Digital Synthesis using a microcontroller. I was pleasantly surprised by some of the quirks which I discovered during this process. Most notably, I had a chance to look at errant triggers solved by using holdoff and a few timing peculiarities introduced by my use of the microcontroller. Here’s a video synopsis but I’ll cover everything in-depth after the break. Direct Digital Synthesis DDS is a method of creating analog signals from a set of digital inputs. I was inspired to give it a try after watching [Bil Herd’s] post and video on DDS. But he was using programmable logic and I thought I’d give it a try with a microcontroller. R/2R Ladder A simple R/2R Ladder is the key to this experiment. It’s a network of resistors in a 1:2 ratio with one another. I would recommend targeting 10k and 20k resistors; what I had handy was 500 and 1k which worked well for this. Eight bits of the microcontroller drive the digital inputs of the ladder, with a single output that is an analog voltage between 0V and Vcc. My code examples for each of these experiments are available in this repository. The code was written for an ATmega328p (which is what the Trinket Pro is rockin’) but it’s vanilla enough to easily port for anything. My first run is a simple ramp signal using code that loops through an 8-bit variable and is also used as the output value. When it overflows the ramp begins again. uint8_t counter = 0; while(1) { PORTC = counter; PORTB = counter >> 6; //Shift the counter so MSB is on PB0 and PB1 ++counter; } Notice that there are several yellow blips on that ramp signal. I’ll get into that in a little bit, but with a working “hello world” for the DDS I wanted to refine my methods by using hardware interrupts. Setting up Timer2 at the system clock rate of 16MHz with a counter resolution of 256bits still allows me to generate a period of 440 Hz: ISR(TIMER2_COMPA_vect) { static uint8_t counter = 0; PORTC = counter; //Set PORTC PORTB = counter >> 6; //Set PORTB counter++; } It is worth noting that those blips in the signal are still there, just a bit harder to see on the above screenshot. As you can see, this signal is clocked at 438Hz, quite close to my target of 440Hz. That frequency is an ‘A’ in pitch. I figure I might morph this into an audio project eventually and if that happens I’ll wanted a signal that sounds better than a ramp wave does. I Saw the Sine This setup is well prepared for generating more interesting signals. All that’s needed is a better set of data to push to the ports than an incrementing counter. The most common way of doing this is to use a lookup table. I found a website that will generate sine wave values based on your parametric needs. I loaded up one with 256 bits of resolution so that I could still use the counter overflow as the index of the array. ISR(TIMER2_COMPA_vect) { static uint8_t counter = 0; //Declare static this will only be instatiated the first time PORTC = sine[counter]; //Set PORTC PORTB = (sine[counter++]) >> 6; //Set PORTB and increment the counter } That’s quite a pretty curve, but now we’re still seeing those signal anomalies and there’s a new issue. When I zoom in on the waveform I sometimes get a double signal. This looks like a sine wave inverted and overlaid on itself. Not knowing what caused this I pinged [Adam Fabio] to see if he had any insights. He mentioned that it’s probably an issue of the scope triggering when it shouldn’t be. The DS1054z that I’m using has two settings in the trigger menu that may be able to help with this. The first is a toggle that tells the scope to ignore noise when triggering. I was able to get this to work a little bit depending on my timebase and trigger voltage but it wasn’t a sure fix. The other option was Holdoff. Learning about Holdoff Holdoff is a feature that allows you to dial in a “blackout” window where the scope will not trigger. The best discussion of the feature that I found is [Dave Jones’] video regarding holdoff. He shows several use cases but mine is one of the easiest. I know the timing that my signal uses and I calculated that a holdoff just a bit longer than 2.1ms should be enough for the scope to trigger at the start of each period. Splitting Microcontroller Ports It’s finally time to figure out what’s going on with those blips in the signal. Here you can see that I’m measuring one of those blips which is about 1.024 us wide. That’s actually quite a lot and so I started probing around with channel two to see what’s going on. Very quickly I figured out that the blips always occur between bit 5 and bit 6 of the R/2R ladder. In this case I’m really glad I used the Trinket Pro because otherwise I wouldn’t have had these blips to play around with. Normally I would use all eight bits on a single port but this board doesn’t offer that. Because of this I’m using PC0-PC5 and PB0-PB1. The blip occurs because of latency between writing PORTC and writing PORTB. The C code that I wrote in the Interrupt Service Routine is very concise and beautiful C code. But the assembly generated from it shows why I’m getting large blips: You can see that there are numerous instructions between the write to PORTC and the subsequent write to PORTB. This was simple to trace down because of my probing using the scope. The best part is that you don’t have to resort to writing your own assembly, instead you can just craft your C code with timing in mind: ISR(TIMER2_COMPA_vect) { static uint8_t counter = 0; static uint8_t prewindC = 0; static uint8_t prewindB = 0; PORTC = prewindC; //SET PORTC PORTB = prewindB; //SET PORTB prewindC = sine[counter]; prewindB = (sine[counter++]) >> 6; } In the interest of brevity, here is the resulting assembly and the new blip measurement (click to enlarge): The result of coding with the microcontroller in mind shortens the blip in the signal by about 5 times! It is perhaps possible to further reduce this by half by using in-line assembly as there is still one instruction call in between port writes. But I think this a fantastic example of an oscilloscope saving you time in troubleshooting. A Rant about Microcontroller Choice Obviously working with a bare chip rather than a breakout board would have allowed me to use 8-bits on one port. But lets assume you were required to work with this restriction. With most 8-bit controllers you’ll hit another gotcha that I experienced here: to write to PORTB using one instruction I have to blow out the entire PORTB register even though I’m only writing 2 bits. The reason for this is that you cannot write both digital 1 and digital 0 to the port using bitwise instructions. The best you could do is to first set your target bits to a known value, then to use a second instruction to write the bitwise values you seek. Obviously if you’re dealing with timing this is not optimal. Most 32-bit microcontrollers (and some 8 or 16 bit varieties) have a workaround for this. Above is a paragraph from the TI datasheet for a TM4C123 chip. It shows that this processor allows single-instruction write without overwriting the entire port because it has a mask feature in the upper bits of the register. You can use an assignment operator and only the bits set in the mask register will be affected. Homework I don’t have a firm plan yet for next week so I can’t tip you off about the topic. Help me along by suggesting an area for me to explore by leaving a comment below. Filed under: Hackaday Columns, Microcontrollers December 09, 2014 at 05:01AM
Posted on: Tue, 09 Dec 2014 15:08:10 +0000

Trending Topics



Recently Viewed Topics




© 2015