Saturday, April 24, 2021

Minimalist Atmel 328 Development Board--How to Build One

Hello again: if you've been following the last few posts you see that I am trying to use Arduino's IDE a lot less, and learning to program MPU's like the Atmel 328P using
"Embedded C", as opposed to the Arduino Sketch language.  



I've had some success, such as using C to create an LFO prototype...read about first steps for the DLFO  project here.  It works on the bench.  Update 5-1-21 the whole damn Digital LFO--dev board discussed below, buffer board for incoming CV, switch debouncing that toggles waveform select, LEDs to show which waveform you're using etc etc. works!! I am happy! Post coming soon. 

Question: if I stop using Arduino's IDE, can I stop using their hardware as well? Why/Why not?

Turns out it's pretty easy to roll your own simple AVR based dev board.  Using bits of the Arduino Uno R3 schematic, a few examples found online (eg: here), and a few Atmel PDFs (the one here for instance--very useful reference) I came up with a minimal parts count AVR 328 PCB for future projects.

If you want to build one of these yourself, you can get the Eagle files (the free version of Eagle should be able to load these files), gerbers, and more, from my github (go here).

Schematic and board look like this:


 

But does it work? 

Programming is done with an Atmel ICE; the IDE used for writing the blink code is Atmel Studio 7, as in the previous posts.

Let's send this dev board's gerbers off to China....I usually use these guys....wow that was fast, they are back.  

Let's stuff the board:



On the bench, connected to power and so on, it looks like this:





The power etc. checks OK.   

Next, with a brand new 328 chip popped in we can upload this simple blink C program to test:

#include <avr/io.h>

#include <stdio.h>


void Delay(void);


int main(void)

{

//DDRB = 0x20; // set bit 5 of DDR register which makes PB5 an output

    DDRB = 0x02;  // use PB1

while(1)

{

PORTB = 0xFF; // switch LED on

Delay();

PORTB = 0x00; // switch LED off

Delay();

}

}


void Delay(void)

{

volatile unsigned long count = 15000;


while (count--);

}

And...nothing. Huh? This is a really simple design, why won't this work?  

It took me about 5 minutes to figure this out.  

The Atmel ICE's ICSP connector is pinned like this:


...but when designing the  ICE connector no one at Amtel (?) marked where pin one is. Turns out I had the connector upside down!

On my board, the MISO pin goes to the top left. So the tab you see on the connector faces upwards. 

After this fix, the MPU is now seen by the ICE programmer and we can upload code.  yeh!

Let's blink.....


Cool!  Good enough for now--"not much time for blogging/too much work to do...." 

Next up, I'll see if I can make the recently "coded at the bench" DLFO (again, post for initial software DLFO build is here)  work with this development board. I created a 2nd PCB for the DLFO to accommodate its CV buffers and whatnot and should have that back soon.

Stay tuned and don't breathe the fumes!

Saturday, March 27, 2021

Atmel 328 based Digital LFO, written in AVR-C--WORKS!

Hello again, and welcome to the newly downgraded binary mind of AudioDiWHY. This time I'm building a Digital LFO to put some of the Pure C skills I've been learning over the past couple of months to the test. Good news--on the bench this Digital Low Frequency Oscillator--DLFO--works!  

If you want to look at the code, download the github zipped C code here and read on.....

The breadboard DLFO is all software and an Atmel 328P MCU--with a bare minimum of external components.....


Best I can remember, the first synthesizer module I ever "designed" was an analog LFO (read more here). For that I cobbled together circuit fragments found online and experimented with the circuit on the bench. I did this until I had something I liked. 

Now, many years later, I wanted to see if I could do this again but with minimal analog circuitry. And to make things even more challenging, all code for this Digital LFO is written using AVR C.  

I also wanted control voltages to determine the LFO's output frequency, something the analog circuit couldn't do.

The AudioDiWhy analog LFO, circa 2003, with a new front panel.....with SuperD! Livery--thanks Iggy for analog assist!


OK, first up: I needed a development platform. The main MCU used for the project was an 8 bit Atmel 328P; it's reasonably simple to program, well documented, inexpensive, can be found ready to use on a USD$10 Arduino Uno R3 clone (which means not having to wire up a crystal and so on). 

The 328P has 10 bit ADC's already built in; good! I still needed an external DAC, and SPI is easy to use, so I used a Microchip MCP4921. It's 12 bits--that means the waveforms the DLFO creates will have 2^12 or a maximum of 4096 voltage divisions. That resolution I figure will be good enough.

 

A-DiWHY: "Hey Mom! Check out the 4921 it's PDIP! It's SPI! It's easy to breadboard!" (Mom: "What???")

The rest of the development platform I used is the same one I've used for the past few posts: a Windows 10 NUC PC; Atmel Studio 7, and an Atmel Ice programmer. 

UNO to 4921 DAC wiring. For CV analog in, controlling the frequency at output, I used PortC pin 1


But! the 328P doesn't natively support I2S, 44.1K sample rates, or anything like that.  How am I going to make a voltage to low frequency converter out of it?  

Turns out, there are lots of ways.....

First, I needed a steady clock upon which to base the rest of the design. There are 3 timers in the 328P-- 2x 8-bit timers and one 16-bit timer. I was a bit intimidated by AVR timers at first, but after some reading it turns out it's actually not that complex. The  timer can be set up with just a few lines of code--we want to use "CTC mode" for this project--a great web page about AVR timers is here.  

The code fragment I ended up using:


//********TIMER************

// Set the Timer 0 Mode to CTC

TCCR0A |= (1 << WGM01);


// Set the value that you want to count to

OCR0A = 0xFF;


TIMSK0 |= (1 << OCIE0A);    //Interrupt fires when counter matches OCR0A value above.


sei();         //enable interrupts  

// in atmel s7 sei() is flagged as red squiggle due to 

//"intellisence" beautifying,but will still compile. Doh!



    //TCCR0B |= (1 << CS02); DO NOT USE, this turns off clock

TCCR0B |= (1 << CS01);  

TCCR0B &= ~(1 << CS00);

    //********TIMER************

OK what next: we need the timer to signal the LFO to do something.  For that, I needed to use Interrupts. Wait: Interrupts? They always intimidated me while using Arduino sketch--they never made much sense and I followed code examples until they sort of worked--but here, I had to know what I was doing. 

Good news: turns about again, after some reading: not that complex.  A good video about how 328P timers can be used to create interrupts is here; a good web page is here

Each time the 8 bit timer (I used timer 0, but any of the 3 would have worked) I had the code throw a hardware interrupt. That creates the steady heartbeat for the rest of the design.

Now we have a counter that counts upwards until timer0 matches the value in register OCR0A, then, it throws an interrupt and starts the process over. 

Next  I declared a volatile unsigned variable 16 bits wide called "c".  Remember in C you have to think carefully about variable casting!  The interrupt routine is a simple increment statement for this global variable:

ISR (TIMER0_COMPA_vect)  // timer0 overflow interrupt

{

c++;

}

(There is something strangely poetic about literally using c++ in a program written entirely in C right?  Wait, I need more fumes....)

OK we now have a timer that creates an interrupt on a regular basis, incrementing a global variable--what's next?  

Lots of ways to go here, but I decided a cheap and dirty way to control frequency from CV is to allow a certain number of interrupts to pass before incrementing or decrementing a value to be sent to the DAC.  If I wanted the waveform to increment from say 2000 to 2001, I write the 12 bit value for 2001 to the 4921 after waiting for a certain amount of interrupts to occur.  More interrupts means a lower frequencly waveform at output, less interrupts means a higher frequency. 

That means: for a square wave, I can send 0xFFF for on and 0x000 for off after some number of interrupts pass. For a ramp, increment by one, and then when 4096 is reached, start over at zero.  For a triangle, count up then down again.

If you're still trying to figure out my approach, let's look more closely at an example--let's create a ramp wave. 

For the first 12 interrupts of a ramp wave looks like this:


By incrementing the global variable c by 1, 4096 times, we can get a pretty decent ramp wave.


And in general the scheme for determining frequency at output works like this:

....let interrupts pass, then take action in code to send data to a DAC. Here we see a really simple triangle wave being created with a few bits and a few interrupts. In reality, we have 4096 steps along the Y axis and time, in the form of our interrupt "heatbeat", exists along the X axis. 

A problem with this methodology: CV control natively operates "backwards"--a CV ADC reading of 1023  says "let 1023 interrupts pass before incrementing the DAC value" which means 5V at CV in produces the slowest LFO frequency.  That's not what we expect from a run of the mill audio synthesizer LFO so that needs to be fixed.  

Again, lots of ways to fix this--I could have used an external op amp inverter for CV instance, but trying to do this all in software I subtracted 1024 from the value read at the ADC input--easy!:

uint16_t rate = 0;

rate = 1024-freq;

That was really simple, and worked.....now CV fully CCW is slowest and CV "fully CW" is fastest.

Next, let's get some output going.....after studying the 3921 datasheet I created a simple .h and .c file to control the DAC to do the heavy lifting at output. Find that in the github zip, here. With all this, all sorts of waveforms can be generated, and I can reuse the 4921.c and .h files in other projects--cool!.

The final part of this is to control the maximum frequency the 5V CV generates.  This took some trial and error and going over and over the math, but eventually I found an unexpected trick: I can add to the value sent to the DAC to increment the maximum speed at the expense of the waveform's resolution.  There are limits here of course--I can run the triangle wave so fast that it starts to distort because too much data is dropped--but overall this software kludge (?) worked, and for the square wave, the value of this "add" has the added benefit of determining the maximum frequency to about 5%.

count = count + WFMFREQ;

uint8_t CMSB = count >> 8;

uint8_t CLSB = count & 0xFF;

write4921(CMSB,CLSB);

OK, so what's next?  I need to create clamped hardware buffers for CV to not fry anything inside or outside this circuit.  I will also probably tie the "rate" value WFMFREQ to a pot or switch. Update--no, I solved this a different way, post for final software build is coming soon.

In the meantime, the DLFO in its current baby-step form produces decent waveforms--I have created functions for Tri, square, and ramp, and creating more--saw, random, expo, etc., should be pretty easy.







Or maybe just stop here--do I really need more LFO's?  Not sure. These last 10 days or so was an exceptionally good learning experience and now perhaps I am no longer a full "C newbie". Getting another stupid LFO to work in my rack almost doesn't matter at this point.  Update 4-8-21: right, but I had to see this through. PCBs designed and off for fab. Assuming I have a morning or two, I will build it to see how this design can be further refined.

OK enough C for one day. If you want to stretch out your brain a bit, maybe you want to put Sketch aside and try something like this. Leave the fumes aside for a week? For me anyway, it was a lot of fun. AHOLA!





Monday, March 8, 2021

AD9833VCO: Putting C to work!

Last time we looked at libraries, written in C, to assist in the difficult transition from Arduino Sketch programming language and IDE to using purely C for coding the microcontrollers in our audio projects. 

This time, let's use this new (for me anyway) methodology to create a low cost, low parts count audio voltage controlled oscillator ("VCO") using Analog Device's AD9833 and an Arduino Uno.  

DiWHY: Like so many Audio DiY'ers, I am a huge fan of Arduino and its super easy Sketch Language.  But why make things easy?  I'd like to know more about the programming languages used to write sketch itself--C and C++--and hopefully gain a deeper understanding of how embedded systems work. 

History-onics: To understand this post you may want to skim my first post about using C instead of Sketch, here; related posts include creating a development platform and setting up an UNO for C programming, here, and writing/cobbling together C libraries for communication (SPI, I2C to name two): here.

All hail!! Dennis Ritchie, the father of C. Did he know that someday C would be a dominant language for embedded systems? 


As far as C programming: no way around it, you have to learn it at least to an intermediate level if you really want to stop programming in sketch and use Pure C.  

But if I can do it anyone can. Covid? No commute! I have time! 

I took an online coarse (C++ although I am coding here in C) and did a lot of reading; for me, C required more forethought and attention to detail when creating working code vs. languages without type casting like PHP.  

But when writing programs for MPUs like the ATTINY85, with its limited memory and relatively scant on-board peripherals, maybe programming discipline is a good thing. 

Indeed, after working with C a bit, I can see why its brevity and modularity make it a good fit for embedded systems and a top choice for professionals. And having to think carefully about variables before using them kept me honest while coding, which isn't the worst thing. It's all good--whatever works for you? read more here.

For this proof of concept I am using an Atmel 328P and its built in 10 bit ADC peripheral to read control voltages.  The MCU then uses the SPI protocol to change the output frequency of an Analog Devices 9833 function generator IC. For convenience the Atmel MPU is on an Arduino UNO R3 but it could have almost as easily been on a breadboard with minimal support components.


AD9833 breakout board--3 waveforms, tops out at about 12Mhz, frequency accurate to 28 bits--$7USD or so; are you kidding me??


The AD9833 is a popular audio IC, and most of the AD9833 projects online use the Arduino sketch language and a preexisting Arduino library like the one here to build a 9833 function generator. Or a MIDI controlled AD9833 VCO (post here--but I don't think you can call that a VCO? This is a MIDI-CO? DCO. Cool!)  Without doubt, there is a large number of Arduino AD9833 audio projects already in existence.

OK, let's do this but without Sketch....

Good news, i got this to work!

 the UNO's analog input  uses an ADC C library, here;  MCU to IC uses an SPI C library (here). Tif you want to try to make this work, get an AD9833 breakout board datasheet here--it's a tricky chip programmatically which will save you tiny SMD soldering.  


Main.c calls all of it. It's all on github, here.

For debugging I used the UNO's USB hooked up to the Windows 10 system using the printf library (here) and the free Windows terminal program "tera term".

To review: the development platform used:

  • Atmel Studio7, get that here.
  • Atmel ICE programmer, information here.
  • An Arduino Uno clone (I could have used a Atmega328P chip on a breadboard, but I had some clone Unos so it seemed easiest to just use that....) 
  • An analog devices AD9833 breakout board.

Hardware used to test (on a breadboard, and I am terrible at breadboarding, but this is really easy) is really simple:    

On the bench, looks like this:

No, this VCO isn't 1V/octave and can't go from 0 to 60 in 2.3 seconds.  Maybe later.  I am just seeing if I can get the basics to work. 


The built-in Analog to digital converter on an UNO is 10 bit (0-1023; maps from 0V to 5V at input); in this proof of concept maps the ADC value to frequency, so the VCO goes from about 1hz to about 1K, here is a snippet of that:

     while (1) 

    {     

    CV = analogRead10bit();

     adfreq = get_ad_freq(CV);

             adfreq = adfreq + 0x4000;

             LSB_L = (adfreq & 0xFF00) >> 8;

     LSB_R = adfreq & 0x00FF;

 

      SPI_TransferTx16(LSB_L,LSB_R); //freq LSB (L,R)adjust freq slightly  

      SPI_TransferTx16(0x40,0x00); // freq MSB (L,R) // adjust freq greatly  

      SPI_TransferTx16(192,0);  //phase val 0

      SPI_TransferTx16(32,0); // exit reset mode  


}

}

C-CODE: I've added a github repository, "AD9833POC", where I capture all the code used so far. Get that here

I also created a short Python program that allows you to extract the 16 bit LSB freq0 register setting in hex from your desired frequency--I found this code very useful while debugging; add 0x4000 to the output and it's ready to be fed into the AD9833:

def frequency(freq):

a = (freq * 268435456) / 25000000
b = int(a)
c = hex(b)
print(b)
print(c)
frequency(535)

Going forward, I can see a lot ways to have fun with this core VCO: for instance, make 4-6 of this simple circuit for some sort of FM based patchable tone generator?  I also found that if the CV is left floating, it makes a pretty crazy random low frequency tone generator....might be fun to see what can be done with that....and I see a few things on the web about using the AD9833 as an LFO. Before this goes into your rack you'll need to buffer the CV input and AD9833 output, and also boost the amplitude of the AD9833's sine and triangle waveforms since they are like 0 to 1.5V P/P, but that's easy enough, it's a few op amps and resistors.

Further thoughts: a 14 bit ADC would do increase the frequency response to 16Khz or so, without having to modify the code a lot, since everything would stay in the LSB frequency0 register for the 9833.  Yes, you have to dig into the AD9833 datasheet a lot to know what that means....  but as a proof of concept this is good enough. I have so many projects waiting around right now, now sure what I'll try next!  

C-ya later? overall these "pure C" posts/learnings have taken me a long way....

Getting this far, in terms of setting up the AVR C environment, learning the C language, cobbling together the libraries, etc., has been a lot of work. No doubt: Arduino audio projects are almost always easier and quicker to craft and debug vs. a project purely written in C. 

No harm in going to back to Sketch..... 

But after I got over the steep part of this it became a bit easier...the optimized C code runs fast and takes up almost no processor memory-- I can read Arduino libraries that used to seem like black boxes and pretty much understand how they work--I feel good about this, and I see myself coding more in C in the coming weeks and months. 

And.....do I dare say it? Someone has to say it: Solving programming puzzles in C is super frustratingly fun!

This has helped me with my day job as well, since now I can read open source C and C++ programs that have nothing to do with audio and try to understand what's going on there as well. Who would have thought? 

 

Wednesday, February 24, 2021

AVR (Atmel 8 bit MPU) and C: Get Your Library Card!

Time for another post about moving away from Arduino "Sketch" coding for AVR microprocessors  and digging into Arduino Sketch's underlying language: C/C++. 

I have heard this called "Pure C" or "Embedded-C".  This means: you aren't creating .ino files with the Arduino IDE; Instead you code .c and .h files using the Atmel Studio 7 IDE or something like it.  Then, you compile the C code, and finally send it to the AVR MPU using a programmer like an Atmel Ice.

the toolset: breadboard, IC, Atmel ICE programmer, and a PDIP based UNO R3 clone. 

I always try to engineer things that are easy to reuse--"good engineering is modular engineering." 

To that end, this week, I tried to write, borrow, or generally cobble together AVR-C code for some of the more popular protocols used to communicate between the MCUs, MPUs, peripherals, and PC's. I2C, SPI, UART and the 10 bit ADCs built into ATmega328P's come to mind.   

OK, how to do this? First: to make everything work I had to understand the protocols themselves. I found myself using a Pulseview logic analyzer to figure the basics out. 

And I had to understand at a deep level how the AVR's registers are designed, what the important AVR registers do, and so forth. 

All of this took time. So, if you like to study datasheets, scratch your head a lot, and learn new things, Pure C or AVR-C or embedded C whatever you want to call it is a blessing.   

If not, if you just want to your electronics project up and running fast, with minimal digging and puzzling over details, walk, don't run to Arduino and get their latest IDEs. Use that instead.  

  

Note the broken logic probe parts. Cheap probes suck.

But here's the upside: in studying the datasheets you can learn what the IC is really capable of, and sometimes it's more than what you find in an existing Arduino library or sketch examples. For instance, the ATmega 328P AVR's on board ADC's give you the ability, if you use C, to change the gain of the incoming analog signal (no need for voltage dividers). Take a look here, the "Bits:4" section. I don't recall that the sketch language gives you hooks into that? If so I never used it. I will use it now.


I2C RTC clock chip (DS3231, datasheet here).  If you want to learn how I2C really works, try writing code using Embedded C for this chip. Tricky!

I also had to read up on how to make the protocols work in the real world.  Call up wire.h in Arduino and follow examples found online?  Nope. In the C world, SPI is pretty easy to code, to the point where bitchy forum moderators made fun of someone for suggesting a library for it needs to exist (here), but none of this is completely easy!

I2C is more complex, and required some studying of the protocol as well as the I2C chips datasheet. For example, to read the 3231 RTC chip using I2C, a fairly complex dance of bytes have to be sent to the chip and back to do useful things: 


In this case, the 3231's I2C address needs to be bit shifted (address << 1) then a zero or one added to the LSB to determine if the I2C instruction is to write or read. That's how I2C works--not simple.

A square wave created with Pure-C and a DAC. Worked.

With libraries in hand, I tried to use them to do things like read ADC peripherals and create square waves or send waveforms to a scope using different DAC ICs.  

Overall I got the code to work so I assume at least the basics of my these libraries are OK..... 

To get the libraries: they are on github: I2C here, SPI here, AVR onboard ADC is here, and here is printf to Serial (useful for debugging) is here. I will update these with fixes and more functionality, so you download any of these you may want to check back from time to time.

I also have example code used to test the libraries but that's specific to chips I had in my parts box like the AD9833. I will post all that at some point as well.

MCP4728 quad DAC dev environment....

Update: 4-5-21 I added an Embedded C library for Microchip's I2C quad DAC: MCP4728.  Get the library at my github Repos here. The 4728.c and .h files work, for the simple example included.   

If you come up with additions or mistakes comment, etc. Have fun!

Sunday, February 7, 2021

Atmel Pure C Studio Setup and Debugging, burning Arduino Bootloaders using Atmel Ice; Why (C)ool Heads Prevail

Hello again from the land of audio ones and zeros. 

I am continuing to move away from using the Arduino IDE for any microcontroller programming needed.  

This time I continue to move towards a more C-language approach to electronic design. I mostly am doing this to learn how MPUs and peripherals really work "at a deep level"; Arduino's IDE sometimes shields you from that. 

The move to C won't necessarily save time or make projects easier to finish however. The AudioDiWHY mantra: why make things easy?

I began this learning process a long time ago--post here.  

For this pursuit I am using Arduino Unos and Atmel Studio 7 as well as an Atmel ICE programmer

Leaving the Arduino IDE we quickly see something new (for me anyway):  C programming is hard! Pointers?  References? Registers? Mallocs? Dereferencing?  Ampersands?  .h files. At first, you have to love it. No one else will.

I have decided to try to write my own library for I2C....I can use one that's already done (e.g. here) but it is more educational to write my own or at least modify an existing one to the point I can understand how it--and I2C--works.  Update: 2-24-21 libraries are done, or at least work for the basics.  See the post here.

Clockwise: Bona-Fide Uno, Atmel Ice Programmer, Sacrificial Uno Clono

I didn't get far. Early on in this process I bricked my first Uno. I messed with its fuses (info here--be careful) and yes, it stopped working and could no longer be programmed. 

Much swearing ensued, and then I realized--why am I cursing?  It turns out I had a few extra 328P's, and the clone used a through hole MPU (so, I got lucky).  So I removed and discarded the blown out chip. I put in a new one. Fixed! 

Can I save the broken MPU?  Probably, information about that (for ATTINY anyway) is here. Is it worth the time?  The blown up MPU is a USD$2.50 part. Nope.  


(Which begs the question: why the tantrums?  It must be other things in my life right (like Covid?) Can I blame it on my parents? Why not. Growing up my dad worked on cars all the time and was constantly swearing at them, kicking them, getting in a terrible mood because of them. And he loves cars! WTF? Whatever.  It was how he rolled, but for me, it serves no purpose; it's not cathartic and it makes me a way worse tech.) 

Enough!

OK on to the next problem. After working perfectly for a few days, the Clone Uno's UART stopped working. Why? well it could be a lot of things, perhaps related to aforementioned fuse issues, but maybe not. 

I want to fix this logically.  

For starters, maybe it's not the MPU/MCU at all. Is it the terminal program (TeraTerm: good program, free, here) I am using?  

Hooking up a few known good UART devices into the PC running TeraTerm--yields, at best, mixed results. Sometime I saw data in the terminal output, sometimes not. The program should run without issue 100% of the time. So yes, that might be it.  

I booted my old Linux system, here, I know Putty works on the laptop...but the old laptop won't boot. Overall not my day. I'll fix that later.

Back to the W10 system. Welcome to Microsoft Windows hell, where everything that could be easy isn't....these guys are too smart for their own good and light cigars with $500 bills. Sometimes I'd get cryptic messages like "you don't have rights to use COM port 20", even when running TeraTerm with full rights. This one got me stuck for a bit. COM20? Why isn't Tera Term using COM3? 

In windows you use Device Manager to change your COM port configuration but that appeared to be no help until I found the webpage here. You have to show "hidden devices" to delete or modify the "invisible COM ports". 

So: run device manager (start > run > devmgmt.msc)  and then "View > hidden devices".  Yes, I had 18--count 'em--18 hidden com ports.  Right click > Deleted them. 

Wow, that felt good.

Plugged back in the UNO. COM3! Yes!!!!! Now find the COM port in device manager: right click > Port Settings tab > Advanced.  I can change the COM port number now. 

To make sure it all worked, I set the Uno's USB serial port from COM3 to COM4.  Yes, that works too, so this is fixed.

You need device manager's "advanced properties" to show you your stale COM ports.

So why did this COM20 setting occur?  Speculation: I must have messed up my com ports while experimenting with FDTI chips (post here). 

OK, TeraTerm and my COM port configuration are fixed. Does everything work now? No! The Windows term program works again, but UART on the Uno clone still doesn't work.

Looking at the UART output with Sigrok Pulseview--post about getting Pulseview working is here--right off the pins on the UNO clone--the UART signals from the microcontroller appear to be OK.  But out of the UNO's USB port it is still broken. 

To fix that's let's look at how the UNO works for serial communication.  A geniuine UNO board has a second atmel MPU: a 16u2, to do its USB to serial conversion. So there are 2 MPUs on an Uno, and the 16u2 is there primarily to convert USB to serial. I didn't know this; so is it an issue with the 16u2? 

I don't know how to check that. There is an ICSP connector for the 16u2, but if I brick this 2nd CPU I have no idea how I'd fix it, and I need to keep moving here. Maybe I messed up the UART on the 328P <> 16u2 when screwing around with my newbie C I2C code?

OK, let's send everything back to stock Arduino using Atmel Studio. If the USB serial signal works in an Arduino environment it might tell us something more about our issue. And: Burning the Arduino bootloader and setting the 16u2 and 328P MPUs back to their initial Arduino settings is a good thing to know how to do.   

Turns out with Atmel Ice, and maybe other programmers, it's not hard. Good video describing how to do this is here.  

You hook the Atmel ice programmer (ICE is a different programmer than the programmer in the video but the process is exactly the same) up to the UNO, then flash the appropriate optiboot*.hex file into it.  

OK where do you find the optiboot hex file? For me, on my Windows 10 system, it was here:

C:\Program Files\WindowsApps\ArduinoLLC.ArduinoIDE_1.8.42.0_x86__mdqgnx93n4wtt\hardware\arduino\avr\bootloaders\optiboot\optiboot_atmega328.hex

But you knew that right?  

Anyway: using Atmel Studio, flash the UNO and then choose the above hex file. That gets Arduino bootloader back into the microprocessor.

But you're not done, you need to set the fuses (carefully):

And: Remember that 0 (no X below) is active with AVR fuses!  You knew this as well right?

UNO ARDUINO FUSE SETTINGS:

BODLEVEL = 2V7

RSTDISBL = [ ]

DWEN = [ ]

SPIEN = [X]

WDTON = [ ]

EESAVE = [ ]

BOOTSZ = 256W_3F00

BOOTRST = [X]  <--HAD TO CHANGE THIS; use Optiboot bootloader 

CKDIV8 = [ ] <--HAD TO CHANGE THIS; Uno expects /8 clock 

CKOUT = [ ]

SUT_CKSEL = EXTXOSC_8MHZ_XX_16KCK_14CK_65MS  <--HAD TO CHANGE THIS; use external Crystal on Uno board

EXTENDED = 0xFD (valid)

HIGH = 0xDE (valid)

LOW = 0xFF (valid) 


Yes, some fuse settings were wrong after programming in the hex file, so I held my breath and reprogrammed the fuses again

With that done, the UNO clone works again as an Arduino....good.  Lesson learned: if you use an UNO to practice Pure C programming, make sure it has a PDIP chip (some use SMD, not easy to replace) so if you "brick" the chip you can swap in a new one easily.

I can run a serial test ino file, and now I can see serial out in the TeraTerm program. Fixed! 

So we know the hardware is OK.

Let's try running test code again using some "pure C" UART code from Atmel studio. Ha!!! Works now! Simple C serial UART test code for Atmel Studio, targeting an UNO, where printf()'s will show up on your serial terminal program set to 8N1, is posted here. You can use that to test your setup if you ever want to experiment with UARTS, Atmel microcontrollers, and AVR C.
 
BTW The SPI > serial arduino bootloader/hardware/dual MPU UNO paradigm is complex, more so than I realized when I started this project. Read more here. Interesting.   

And, happy day: Keeping kool I fixed my ancient Linux system as well, but that's outside the scope of AudioDiWhy. Putty works again.....the clone UNO  to Linux putty works again as well.....here's a poor photo of the term and MPU UART at work:

Nice to see text on the right!  I was lost, now I'm found.


Finally I needed to get debugging working. Inline bebugging (good vid is the concept is here) is not well implemented in the Arduino IDE, so I have been using Atmel's setup for this for a long time now....Atmel uses a proprietary debugging protocol called "Debugwire" for inline debugging for some of their chips. Compare this to industry standard, JTAG--which isn't supported on an UNO.  

Sadly DebugWire is not well documented for us makers--e.g.: good luck finding an Atmel document saying how the protocol is laid out. But it is still useful once set up correctly.

Getting this going for a newbie like me was a bit terrifying at first because I read all over that it's easy to brick your UNO with DebugWire. But again--the Atmel 328P is a $2.50 part.  If I blow it up so be it.  It's the cost of learning how this technology really works.

Here's how to get DebugWire going.

On my Uno clone I had to cut the trace you see below; debugwire won't work without this trace cut.  

that's it--other than this, you can use DebugWire to program and debug an UNO without issue.

DeBugWire works with the indicated trace cut, but Arduino IDE won't.


Really good blog post about the hardware mod for this here. A takeaway: You get into Atmel Studio as always, but choose "DebugWIRE" as your tools for Atmel studio and not "ISP".  

Here is how to do that:

First make sure your project builds without errors.

Once that's done, in Atmel Studio 7, left double click on the project you want to debug in solution explorer (by default "solution explorer" is the column on the right).  A set of tools appears in a column on the left. 

Click on "build" then, choose your programmer. Then from the dropdown to the right of that, choose "DebugWire".  Click OK.

The software will ask you if you want to change your fuse settings; terrifying!  But let the software do this for you. For me, it works. And--when you are done w/ debugwire make sure to switch back to normal ICSP programming mode again ("debug >  Disable DebugWIRE and disconnect"). This resets the fuses back to how they were. 

You can only get out of debugWire when you are in debugwire....otherwise this option isn't available.  So I try to always end my DebugWire session after I am done debugging, otherwise things get confusing.

BTW: If you want to use the UNO for Arduino again you have to reconnect the trace you just cut and reburn the bootloader. 

Before I did anything the reset trace looked to me like it was already cut, but using a continuity tester, it wasn't. Had to cut it.

With that cut, yep, debug wire works. Followed all the instructions as per the video here to get basic debugging going. It worked and it's useful, you can see your variable values at breakpoints, as well as see how all registers are set (Debug > windows > IO). 

Good luck doing any inline debugging with the Arduino IDE--you can't--so arguably inline debugging alone is reason enough to switch to Atmel studio + Atmel ICE for 328P projects, perhaps.

One more thing: for any sort of logic projects it's useful I think to have LEDs with 330ohm resistors in series in your junk box. I have found myself using these frequently when doing the above MPU/MCU  bench work. Instead of having to breadboard dumb LED/resistor pairs every time, I built 10 of these on a single perf board. This took maybe 20 minutes and has saved a lot of time since.  

"You need Schoolin"



OK, onward to my I2C library....time to brick something again.....then back to zero. Ons and Offs!  How much work could this be? The main takeaway for me this time: getting upset about an electronics issue makes no sense. Keep cool, take breaks, and figure it out. The sun will come up tomorrow regardless. See ya next time.

 


    


Sunday, January 24, 2021

AudioDiWHY: Son of Manifesto and a Return to Digital

 Another year, another month of COVID lockdown. It's time for reflection, right? Rereading the first post of this blog (here), which I haven't done for a while: a main focus of AudioDiWhy was to get away from computers/networking/automation (which is the crux of my day job) and learn more about the great analog circuits of old. 


The SH5 remains one of my favorite electronic instruments of all time, and is all analog (no MPUs on board). One reason I started down the audioDiWhy path: a humble attempt to understand what makes the SH5 sound so damn good.


But I have found that any idea once realized with analog circuitry can be done easily and probably for less money using digital.  That doesn't mean the design will sound as good as a analog circuit circa 1975, but at the end of the day, the digital approximation can probably be built.

And for us makers, the rapid improvements in affordable MPUs and MCUs makes a digital approach even more self-evident?   

To wit: I recently read about the Raspberry Pi Pico (here). Cool! 

This MPU is fast (but....other MCUs are as well--Teensie 4.1 for instance is apparently faster) and  inexpensive ($4USD, are you kidding me? but you can get Nano clones for less). However, the Pico has a breakthrough feature for Makers: PIO, read about that here; I will have to dive into this in a later post. Apparently there are things PIO can easily do (such as accurately controlling NeoPixels regardless of CPU speed) that I find really interesting. 

I will use C to program it, and assenbly language for the PIO part, I hope, since that seems to be the way my maker life is heading.

Wait! there's more: I have had in the back of my mind a four to six v/oct-LFOs/VCOs design that fit behind a 1u Frac panel (1u because I am seriously running out of room for anything new in my studio). With analog SMD tech, op amps, tempcos, FETS etc., forget it, maybe? Even with Curtis VCO ICs it would take a lot of design work to debug and get 4x LFO/VCOs to fit into a space this small. Using analog discreet components would take even more PCB space and might not be possible.

But with a high powered super tiny MPU, 4x AD9833 ICs (already messed with using python and FTDIchips; post here) some op amps to buffer input and output--yes, knowing some analog design is very useful--and embracing SMT on the DIY bench (post here)--this is doable as I see it.

Unimaginable a few years ago, for me anyway, with the skill set I had before 2018. Today, four years later, sure.

But! the main thing I have to remind myself: make sure it doesn't sound crappy.  Digital designs replacing the great sounding circuits of old makes sense economically, but AudioDiwhy isn't about saving money (and often, saving time). Perhaps a wobbly triangle wave from an old school LFO sounds better than the pristine output from a AD's function generator chip. I need to keep this in mind.

So--I am teaching myself more about C, C++, and drilling further into Python and Javascript than I thought I ever would. The masterful work by Mutable Instruments proves to me you can design audio in an almost all digital domain and make your project sound damn great. You just have to be smart about it and like so many other things in  life, be willing to dig in, think about things, experiment, make mistakes, and work hard.

Digital again.  Crap. I keep wanting to beat back my need for the elegance of ones and zeros, and it keeps coming back like an STD.  You can't fight city hall right?        

Here are some of the projects I am considering in the coming months (some are already underway; some are new).  All are digitally based. 

  • The PNG based CV sequencer. Already started--post is here.
  • An AD98933 based FM VCO.  4 carriers, 6 is better. Bonus points--make it track v/octave.  Again already started--I have one osc working, but need to build more. Read about it here; I have a ways to go.
  • Find and verify, cobble together, and/or write basic C libraries for my favorite I2C and SPI chips (already started, go here).
  • A digital LFO to compete with one of the first circuits I ever "designed", that project is already started, part I is here. Update 4-10-21 PCBs are out for fab. 
  • A random voltage generator based on the API you find here.  
  • Redo the random gate (here) but make the reset feature work--I like the way this circuit came out but it can get stuck on a "long step".  Use AVR-C to fix this or maybe just improve the existing sketch code now that I have a slightly better understanding of timers and interrupts.
  • The video here  describes using Lava Lamps to generate randomness. I love lavalamps! Do the same for a home studio using an SBC and a digital camera.
  • A digital delay. Of course it's been done but I'd like to know how it really works, and no better way to do that then design and build one myself.'
  • Same delay idea, but a simple digital circuit for replaying control voltages.
  • A device that injects WAV sound effects into audio calls (burps and farts?  Laff track?  Applause). Endless fun for Zoom calls, right? Drive your family and friends nuts!!
  • Like everyone, I get tons of spam emails and texts. Turn them into music (sad?) somehow.
I will add to this list from time to time, but that's the roadmap for the next few months.  I won't be posting as often in the next several weeks as I try to amass better skills to pull off some of these projects. As per my pychiatrist girlfriend: maybe that isn't the worst thing. 

OK that's it for now, for the updated manifesto. I don't get a lot of comments since I figure my blog is so self centered, but I will say it anyway: if you have cool digital audio things you'd like to see me build leave a comment and I might design and build it.  Thanks for reading my blog and making it this far.






Thursday, December 31, 2020

Analog Devices AD9833: Accurate, Fast Sine, Triangle and Square waves using Python

This is hopefully the last of the FTDI232H posts? Not many readers for these FTDI posts so far; guess over the holidays we have better things to do (not me?). 

Really, it's a pretty cool little board.... 


To wrap up we will use affordable breakout boards to make function generator accurate to 10Mhz+. To make this go, you will need an FTDI232H breakout board, Python 3.6+ and the PYFTDI module, and an AD9833 breakout board; the Bob's are available from Amazon, AliExpress, and all the other usual places. 

You may want to first read about getting started with the FTDI 232H breakout board in the last two posts, here and here.

I hate breadboarding so a small PCB for this project may come around one of these days?


I already got basic AD9833 SPI going at the end of the post here; this time I will flesh it out so you can edit values for the waveform and frequency directly in the Python code; out comes the waveform at the frequency specified on your scope--accurately.  

(Get the python code here from Github, but here is the 12-26-20 version:)

from pyftdi.spi import SpiController

############user changes these###############

user_freq = 1000

wave = 3 #1--sine   2--pulse  3--tri

##########################################


#pinout from H232 for SPI


'''

#WE WANT TO BE ABLE TO ENTER A FREQ TO SHOW ON SCOPE.

# Instantiate a SPI controller

# We need want to use A*BUS4 for /CS, so at least 2 /CS lines should be

# reserved for SPI, the remaining IO are available as GPIOs.



def get_dec_freq(freq):

    bignum = 2**28

    f = freq

    clock=25000000 #if your clock is different enter that here./

    dec_freq = f*bignum/clock

    return int(dec_freq)



padded_binary = 0

bits_pushed = 0

d = get_dec_freq(user_freq)


print("freq int returned is: " + str(d))


#turn into binary string.

str1 = bin(d)

#print(str1)


#get rid of first 2 chars.

str2 = str1[2:]

#print(str2)


#pad whatever we have so far to 28 bits:

longer = str2.zfill(28)

#print("here is 28 bit version of string")

#print(str(longer))

#print("here is length of that string")

#print(len(str(longer)))


lm1 = "01" + longer[:6]

lm2 = longer[6:14]

rm1 = "01" + longer[14:20]

rm2 = longer[20:]

# print(lm1 + " " + lm2  + " " + rm1 + " " + rm2)



def str_2_int(strx):

    numb = int(strx, 2)

    return numb


lm1x = str_2_int(lm1)

lm2x = str_2_int(lm2)

rm1x = str_2_int(rm1)

rm2x = str_2_int(rm2)

print(str(lm1x) + " " + str(lm2x)  + " " + str(rm1x) + " " + str(rm2x))


##########

#freq0_loadlower16 = [80,199]

#freq0_loadupper16 = [64,0]

#64 0 80 198



spi = SpiController(cs_count=2)

device = 'ftdi://ftdi:232h:0:1/1'

# Configure the first interface (IF/1) of the FTDI device as a SPI master

spi.configure(device)


# Get a port to a SPI slave w/ /CS on A*BUS4 and SPI mode 2 @ 10MHz

slave = spi.get_port(cs=1, freq=8E6, mode=2)



freq0_loadlower16 = [rm1x,rm2x]

freq0_loadupper16 = [lm1x,lm2x]


cntrl_reset = [33,0]



phase0 = [207,0]


#new waveforms here


cntrl_reset = [33,0]


if wave == 3: # tri

    cntrl_write = [32,2]  #tri

    print("tri")

if wave == 2:

    cntrl_write = [32,32]  #square

    print("square")

if wave == 1:

    cntrl_write = [32,0]  #sine

    print("sin")


send2_9833 = cntrl_reset + freq0_loadlower16 + freq0_loadupper16 + phase0 + cntrl_write


print(send2_9833)


qq = bytearray(send2_9833)

# Synchronous exchange with the remote SPI slave

#write_buf = qq

#read_buf = slave.exchange(write_buf, duplex=False)

slave.exchange(out=qq, readlen=0, start=True, stop=True, duplex=False, droptail=0)

slave.flush()


The only tricky thing to getting the python to go was formulating the 14 and 12 bit words for 9833 data, then shoving that correctly into bytearrays. I tried masks, bit shifting and other things, but none of it worked (works fine in C, but not in Python). The main problem apparently is that Python treats ints as binary data behind the scenes, so 0b01110000 is always treated as 0b0111. So much for using masks--it's all fine unless you need to pad your 1's and 0's to 28 or 32 bits as you do with the 9833. 

To get around this, I ended up doing something damn kludgy or dodgey or dicey or just not smart: I changed the 1's and 0's to strings (python is good at converting data types), used standard python tools to extract and manipulate the 1's and 0's to what was needed by the 9833; used zfill to make the string 28 chars long, and added 2-3 bits to the front of each string as needed by the 9833 to signify if the data is control, frequency, phase or whatever. Then at the last minute I converted the string back to integers to shove into a byte array. 

Complex and ugly but it worked.

Is there a more elegant Python way to code this?  There must be but I can't figure it out, if you have ideas, enter it into the comments because I am curious.

To use the code, change the frequency and waveform values at the top then run it.  That's it.

It would of course be easy to use something like the argparse module to make these arguments into a command line statement but what is here is good enough for me.

The only thing left to do, to get a really cool little USB driven function generator, is to up the gain at output (easy, it's an op amp) and perhaps make it bipolar (not as easy, but certainly possible; it means creating a -5V signal somehow from the existing 5V signal).  Also the 9833 doesn't do ramp waves--sorry, but it would be possible to add more circuitry to convert the triangle wave to ramp I figure.

Of course there are less than a million AD9833 based gizmos already on the web, here and using arduino only, here for instance.  But I am not sure there is another that uses Python and is USB driven? If there already is, oh well.  I didn't look around that much and it was fun coding all this.  

I am going to not post for at least a few weeks while I take some online classes and try to sharpen some programming skills. After that more geeky posts. I feel things heading into less analog territory in 2021?  Probably. See ya next year!


Minimalist Atmel 328 Development Board--How to Build One

Hello again:  if you've been following the last few posts you see that I am trying to use Arduino's IDE a lot less, and learning to ...