Monday, May 31, 2021

ProMicro HID Keyboard Emulator

Quick one this time. The Arduino ProMicro (examples here and here) is based on an Atmel 16u4 MCU and has HID keyboard emulation ready to go--use the keyboard.h functions in your sketch and go. It also has a USB port. That makes creating PC keyboard emulators, which work in parallel to your normal PC keyboard, a breeze. 

A good tutorial for this is here.


Arduino Pro Micro


 I will be using something like this for the continuing PNG sequencer project (part I post for that ongoing project is here) so this time I got a couple of HID emulators working using junk box parts and plastic cases I had lying around.


I used a couple of Hohner harmonica cases to hold the proMicro, switches, and DS3231 timer IC.

The "dtime" button selects the current time off a DS3231 I2C real time clock and fakes the PC into thinking someone typed this date-time in--I'll test it right now!  5/31/2021 16:17:22  Ah!  Works!!

The other 2 buttons can be set to produce a text string, anything you want.


The ProMicro is held in place with a 3D printed case; download the SVG file here. It took about 20 minutes to print. The plastics throughout are easily/sloppily augered out with a dremel grinder.

I used the cheapest parts possible. The ProMicro and DS3231 breakout boards are a $2-$3USD  AliExpress clones. The I2C based RTC holds time OK as long as it has USB power--after that it goes out of time pretty quickly, although doesn't reset itself to zilch. So it kinda works, but not great. 

I guess you get what you pay for right? After a power outage I upload the code into the ProMicro again which gets the clock working accurately; the code sends the time of the last compile to the DS3231 by default.  

Good enough for now, but for the sequencer this has to be fixed.  I assume (?) that if I use a better quality DS3231 timer, better battery etc., this will work; we will see.


You can get the ino Arduino sketch for this emulator below. 

There are many different RTC DS3231 Arduino libraries out there; I used Adafruit's which you can get here. Instructions about how to install this library into Arduino IDE is here.

I assume readers can figure out the ProMicro's wiring--the switches are momentary NO's connected from D pins to ground--from the code. the DS3231 is connected to Power, ground, and ProMicro pins 2 (SDA) and 3 (SCL).   

Until next time, don't breathe the fumes.


==========================


#include "RTClib.h"

#include <Keyboard.h>


String getdt();


RTC_DS3231 rtc;


char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};


int pin = 10;  // 

int mspin = 9;

int datepin = 7;




void setup()

{

  

 /*

///////////////////RTC CLOCK BS//////////////////////////////

arduinos don't have RTC


we are using RTC3231 from Maxim.

for pro micro here are I2C pins

2 > SDA, 3 > SCL


Library used is this one:

https://learn.adafruit.com/adafruit-ds3231-precision-rtc-breakout/arduino-usage

be careful, there are lots of other 3231 Arduino libraries.  Others probably won't

work with this sketch


*/


    //RTC setup:

    Serial.begin(9600);

    if (! rtc.begin()) 

    {

        Serial.println("Couldn't find RTC");

        Serial.flush();

        abort();

    }


  if (rtc.lostPower()) 

    {

    Serial.println("RTC lost power, let's set the time!");

    // When time needs to be set on a new device, or after a power loss, the

    // following line sets the RTC to the date & time this sketch was compiled

    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));

    }


  /* UNCOMMENT BELOW TO SET DATE TIME ON RTC TO TIME SKETCH WAS COMPILED.

  // When time needs to be re-set on a previously configured device, the

   following line sets the RTC to the date & time this sketch was compiled

  */

  

  rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));


  

  /* 

  UNCOMMENT AND CHANGE NEXT LINE TO SET THE TIME:  y m d h m s

otherwise compile time is blown into RTC at sketch upload.

  */

  

 // rtc.adjust(DateTime(2021, 3, 19, 8, 56, 0));

  

  

  pinMode(pin, INPUT_PULLUP);  // Set the button as an input

  pinMode(mspin, INPUT_PULLUP);  // Set the button as an input

  pinMode(datepin, INPUT_PULLUP);  // Set the button as an input

  

  digitalWrite(pin, HIGH);  // Pull the button high

  digitalWrite(mspin, HIGH);  // Pull the button high

  digitalWrite(datepin, HIGH);  // Pull the button high


  Keyboard.begin();


}




void loop()

{


 

  if (digitalRead(pin) == 0)  // if the button goes low

  {

    

    Keyboard.print("yer-text-0-here");  // 

    delay(1000);  // delay so we have poor mans debounce

  }


    if (digitalRead(mspin) == 0)  // if the button goes low

  {

    

    Keyboard.print("yer-text-1-here");   

    delay(1000);

  }

  


   if (digitalRead(datepin) == 0)  // if the button goes low

  {

    

    String gg = getdt();  


    {

    Keyboard.print(gg);  

    }

       

   delay(1000);

  }

   


}  


String getdt()

{


 DateTime now = rtc.now();


    int hour = now.hour();

    String hourStr = String(hour);

    if (hourStr.length() == 1)

       {

        hourStr = "0" + hourStr;

       }

    

    int minute = now.minute();

    String minuteStr = String(minute);

    if (minuteStr.length() == 1)       

       {

        minuteStr = "0" + minuteStr;

       }


    

    int second = now.second();

    String secondStr = String(second);

    if (secondStr.length()== 1)

       {

        secondStr= "0" + secondStr;

       }

       


    String TimeStr;

    TimeStr = hourStr + ":" + minuteStr + ":" + secondStr;


    int day = now.day();

    String dayStr=String(day);


    int month = now.month();

    String monthStr=String(month);


    int year = now.year();

    String yearStr=String(year);


    String DateStr;

    DateStr = monthStr + "/" + dayStr + "/" + yearStr;


    String FinalString;

    FinalString = DateStr + " " + TimeStr;

    return FinalString;

Thursday, May 20, 2021

AVR based Digital LFO--Written in Embedded C--Welcome to the Land of the Missing Blog Readers

Finally! after weeks coding, building and debugging a fully digital voltage controlled LFO module, it's done, it works, and ready to be racked.

Mod Synth LFO module based on an Atmel 328P, written entirely in embedded C.


The land of lost readers--what? This module was developed entirely in embedded C, read more about that in an earlier post; here. Many thanks to audio diy legend Grumble aka "BigDutchEd" for inspiring the switch to embedded C!

I figure most hobbyists won't want to build this LFO, or even read its underlying code, since it's not based on Arduino hardware and/or the Sketch programming language, Micropython, CircuitPython, or other higher level languages aimed at those of us who would rather "just build things".  

That's understandable and of course is A-OK, but it's not where I am coming from.  

For me this entire electronics thing is about learning. Otherwise, I'd build kits or even better, buy a lot of ready to rack audio gear from these guys.

But! since I am not a professional EE, pros will probably read my C code and laugh. I am stuck. Nevertheless, if you are in the .00001% and want to see how this works, or maybe even build it (yeh, right?) go to my github page here. It contains eagle files, wiring diagrams, PDFs, all the current C code, and all the rest.

Regardless of getting < 10 views for posts like this I am going to solder on.....

After developing in C for a bit, I don't see myself going back to a higher level language for MPU and MCU programming with alactrity. I feel (oddly/unexpectedly?) that coding in Pure C is easier than Arduino Sketch once you get the hang of it. Embedded C can be straightforward, for AVR MPU's how to write code is extremely well documented, and of course, Embedded C is really frustratingly fun to debug....

But above all: I have complete control of everything. What else in life is like that?  

ready to test....


About the DLFO: I tried to make operation of this module really simple--a 0-5V CV input to control output frequency, an output jack, a toggle switch to select course output frequency; a momentary switch (inspired by Mutable Instruments) that selects a waveform and lights a corresponding LED, and pots to control frequency and offset voltage at output. 

Overall I am OK with how this module came out, but more important: I am happy with what I learned designing, debugging, and building it. The Atmel 328P MPU is a relatively slow 8 bit processor and doesn't lend itself well to a project like this; I had to hack around its speed limit, but that was a good learning process in itself. More info about the basics of how I made this work and software design choices is here. The simple 328P PCB--which cannot run sketches BTW--is discussed here.

I now have a far better understanding of things like timers, interrupts, switch debouncing, and common peripheral protocols like I2C and SPI. For a pro engineer this is extremely basic stuff, but for hobbyists like me, maybe not so much?

The board in the center is a "mini AVR board", post is here.  Left is the PCB that lives behind the front panel.  Right are 2u Frac "blanks" for the front panel design.


Does this LFO make balloon animals? 
No. The triangle wave still only goes to 22hz, but i can probably figure out a way to improve this (ha! software based hardware builds--they are never done.)  This LFO has an 8 bit grainy sound which is cool in its own way. Easily cleaned up with some lubrication if it needs to sound more analog.

I can also add other waveforms in the future--sawtooth? random? Stairstep? In my addled mind I think this won't be hard to code--but finding the time may be. Overall, I tried to make the code as modular as I could, which is what I think C is designed for. Hence the quick and simple front panel art. Hence a separate function for each waveform. hence the super simple ISR's. But in the end, everything will probably get redone.

Furthermore, the front panel was built on a PCB; not metal, to save time and cost. How-to posts for that are here and here.  


For this prototype I printed out the PCB for the buffer circuit and taped it to a blank front panel.  Then drilled out the panel. Otherwise I end up with 4 leftover PCBs front panels from the fab house. instead I am left with 4 2u blanks I can use for other things!

 
Of course, you can also build LFO's using analog components (eg: here) but what is the fun in that?   

The new "pots board" used for this DLFO (again--github for schems etc. is here) is extremely simple; it's a few op amp buffers and home for switches, jacks, and LED's.  Also it has a socket for a DAC (MCP4921--SPI--very useful and easy to use DIP IC) which is not a peripheral that comes built into a 328.  

If I was going to make a bunch of these I'd make make it +/- 12V "eurorack" and SMD; it should work with 12V without any modification. The board could probably work with 3-4HP due to its simple hardware design if the SMD parts were laid out correctly. Not here/not now though...



The MCU and pots board are at a right angle. Good for one-off prototyping of embedded C projects, but I wouldn't do this for something more serious.


Also on the bench it's easy to test....unsolder a wire and hook it to a scope or logic probe.



OK enough C-thing.  I will use it in my rack for a bit before I add more waveforms and make more software mods--but with so much time and so little to do that may never happen.  We will see....

If I get around to that, I will do another post and update github. In the meantime, well, if you've made it this far, thanks for reading--and be warned: there is more C crap coming.



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.

 


    


ProMicro HID Keyboard Emulator

Quick one this time. The Arduino ProMicro (examples here and here ) is based on an Atmel 16u4 MCU and has HID keyboard emulation ready to go...