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.
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.
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:
|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:
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:
a = (freq * 268435456) / 25000000
b = int(a)
c = hex(b)
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?