Wednesday, 22 July 2015

Printing/Reading floats on an AVR in Atmel Studio

As part of my lab power supply project I have been coding the control software that runs on the power supply module and drives the DACs/ADCs as well as other things. The plan is to use provide a text based control interface over USB using a USB to UART chip (MCP2200).

I need to print floating point values as strings and parse strings containing floating point values. Turns out this isn't as easy as I hoped!

I am using Atmel Studio 6 and (sensibly) by default it doesn't include code for formatting or parse floats inside printf or scanf. The code to do this is large and would bloat out applications that don't need it.

The way this works is you have to include additional libraries that contain the version of printf and scanf which contain the extra formatting code. So first of all you have to include the libprintf_flt.a and libscanf_flt.a libraries in the linker options. So you right-click your project, choose properties then select the Toolchain tab. Find the AVR/GNU Linker options and select Libraries. Click the gree add icon in the top panel and add printf_flt (the linker adds the lib prefix and .a suffix). Do the same for scanf_flt. If you only need printf and not scanf support you can leave whichever one you don't need out.


When I first did this I found I got link errors like this:


e:/program files (x86)/atmel/atmel toolchain/avr8 gcc/native/3.4.1061/avr8-gnu-toolchain/bin/../lib/gcc/avr/4.8.1/../../../../avr/lib/avr5\libm.a(mulsf3.o): In function `__mulsf3':
(.text.avr-libc.fplib+0x2): relocation truncated to fit: R_AVR_13_PCREL against symbol `__fp_round' defined in .text.avr-libc.fplib section in e:/program files (x86)/atmel/atmel toolchain/avr8 gcc/native/3.4.1061/avr8-gnu-toolchain/bin/../lib/gcc/avr/4.8.1/../../../../avr/lib/avr5\libm.a(fp_round.o)
  
I searched for quite a bit before I found that the problem is you have to have libm at the BOTTOM of the list. Then it links fine! Thanks for that Atmel!

Then you have to tell the linker to use the float version of the function. For printf you go to the General group under the AVR/GNU Linker and select the last option which is 'Use vprintf library'


For some reason they didn't include a checkbox for scanf. To add scanf you go to Miscellaneous group under the linker options and add the option -Wl,-u,vfscanf manually.



Then you should be able to use printf and scanf with the %f format string and it work correctly.

Why do they make it so hard?

Wednesday, 1 July 2015

Atmel Studio 6.2 and atmega328p Serial Comms

As part of my ongoing Lab Power Supply project I thought that it was time I learned to use Atmel Studio. While Arduino is pretty quick to get things going I really started to hit its limits in the dummy load project. Just managing lots of Windows gets hard and I ran into problems when I tried using microprocessors not supported by the IDE.

I also want to experiment with JTAG ICE debugging and I think Atmel Studio will make this much easier than other approaches (such as using WinAVR and the GDB command line).

My plan was to experiment with using a USB to UART chip in conjunction with an isolator chip to create a galvanically isolated USB port for the power supply.

Atmel Studio 6.2

I downloaded the code from the Atmel web site. It is a huge download at just over 500MB. Then when it downloaded it needed to download and install the Visual Studio IDE. Finally the thing installs and the first thing it does is pops up and tells me there is an update the the ASF (Atmel Software Framework) which then downloads another 140MB or so.

I'm quite familiar with Visual Studio as I use it for C++ development during my day job. Atmel Studio felt pretty similar. I was able to quickly create a C++ project for an ATMEGA328P. Weirdly it pretty much immediately warned me that some of the ASF won't work in a C++ project and I should use C. Undeterred I pushed on. 

I tried the ASF thing but found it pretty confusing. Before you can begin you have to choose a board. I am basically using the micro in a breadboard so I wasn't sure what to choose. There was a 'generic' "user board template megaAVR' board option so I went with that. The list of libraries in the ASF wizard is pretty confusing too - things that would have been there out of the box in Arduino appear to be libraries. For example there is a ADC driver, a Delay routines service a GPIO and an IOPORT service (no I don't know what the difference is either). Each of these opens a little folder and has a link to documentation and shows you the headers for that option. 

Amusingly there is a unit test framwork which appears to be C unit. I later found you have to use this in a separate project from your target project. Unit testing would be very nice.

A nice way of describing the user documentation is sparse. I haven't yet discovered the sample code (if there is any). Basic tasks often require direct access to registers etc.

When I have gone looking I found that things which are basic in Arduino aren't so basic in Atmel Studio. Also you lack lots of library support for things and people tend to code their own. It will be much more work using this but I am hoping it is worth it. Also there is an option to use the Arduino libraries from within Atmel Studio but I haven't explored this yet.

I compiled the empty project, set a breakpoint and hit F5. It popped up a dialog asking me to choose the target but the only option was a simulator. Running in a simulator is nice for unit-testing I suppose. This seemed to work as expected and the debugger seemed like it would be useful if the project actually did something.

Programming the Chip

Unfortunately Atmel Studio only comes as a Windows tool so I am running it on a Windows 7 VM under parallels on my Mac. Initially I plan to use an AVRasp programmer but later will try out a JTAG programmer.

Annoyingly Atmel Studio doesn't support this programmer directly so you have to configure avrdude as an external tool and use that to program the chip. I installed WinAVR which adds avrdude to the path. The command line options are described quite well here

I found this would not detect my usbasp device. I had to go to this site , download the drivers and then right-click the device in Device Manager and update the drivers from the downloaded ones.

To add avrdude to the Atmel Studio project you go to tools -> external tools and add something like this (see image). First browse to the avrdude exe, then for the parameters add:

 -c usbtiny -p atmega328p -F -U flash:w:"$(ProjectDir)Debug\$(ItemFileName).hex":i

De-select the 'close on exit' check box as otherwise if something goes wrong you won't see the error. Here is what it looks like:


Serial

My plan is to eventually control each Lab PSU channel using text commands sent over a USB->UART bridge. For now I just want to be able to print something. On the Arduino this is easy but pretty basic.

I tried searching the documentation and while there were links to USART stuff it all seemed to be related to specific board configurations I wasn't running. Apparently there is a ReMORSE example app somewhere for the ATMEGA328P but I am buggered if I can find it.

I Googled for examples and found that to make serial work you essentially have to provide functions for getting or putting a single character to the serial interface and then wire these into a FILE structure. From there you can replace the stdin or stdout globals with your own so when you call printf() or scanf() etc it uses the serial port.

The example code I found would basically do this:

FILE uart_io = FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW);

This uses a macro to set the fields of the FILE structure during initialization. When I did this I got this error:

C:\Users\fred\Documents\Atmel Studio\6.2\LabPSU\LabPSU\LabPSU.cpp(33,18): error: sorry, unimplemented: non-trivial designated initializers not supported

It turns out that this macro uses a language feature that never made it into the C++ spec and hence you get this error. I found there was a function that did something similar however so I could do this instead:

    FILE uart_io;
    memset(&uart_io,sizeof(uart_io),0);
    fdev_setup_stream(&uart_io,serialWrite,serialRead,_FDEV_SETUP_RW);


Now I needed functions to emit or consumer characters from the serial interface. Most of the code I found on the net did this with busy wait loops which seemed positively medieval. I found a library by Peter Fleury that implemented putting/getting characters from the serial interface using interrupts and a ring buffer here. This seemed much better!

I had to create bindings to allow this to interface with the functions expected by the FILE structure so I created a pair of functions like this, I'm not worrying about errors as I don't think there is anything that can be done anyway,

int serialWrite(char c, FILE *fp)
{
    uart_putc(c);
    return 1;
}

int serialRead(FILE *fp)
{
    return uart_getc();
}



Then I registered these using the snippet above. Once that is done I can do things like this and the output should turn up on the serial port.


    int i=0;
    
    while(1)
    {
        printf("Testing... %d\r\n",i++);   
    }

When I first compiled this and programmed it onto the chip nothing happened. No output and no signs of life. I ended up creating a blinking LED program just to make sure I was programming the chip correctly and this worked.

Eventually I found another copy of this library but this copy included an example program. In that he included avr/interrupt.h and called the function sei() before using the UART functions. I added this code and it worked!

Note in the code example above I am printing \r\n as for whatever reason without this the output moves to the next line but doesn't return to the start of the line. Given the output is going to a Mac I would have thought just \n would be fine. Anyway no matter. This looks like a starting point.

Isolated Interface

The bit I didn't mention is my hardware setup. I plan to use an MCP2200 USB bridge chip from Microchip. I bought a breakout board for one of these from RS (for like $20! Cheap!). Initially I just connected it to a computer (my Mac) and typed characters at it while watching what happened on the RX port. My Mac already had a driver for it so I just plugged it in and used screen to connect to /dev/tty.usbmodem1411. That was pretty easy.

There is some software from Microchip for configuring the device but this is Windows only. I installed the drivers onto my Windows 7 VM and ran the tool. I configured some of the GPIOs to blink LEDs and this seemed to work. Overall this looked really good.

Next I connected my microcontroller to it and used the VDD and GND pins to power the micro controller. This worked and I could see the test serial output from the micro controller via the screen tool.

Now for this to work in m lab power supply it needs to be electrically isolated from the USB power. I bought one of the Analog devices ADuM1201 chips from RS. They are relatively cheap and very simple. You have two power and ground ports and you have an in and an out port on each side. The two sides are isolated from each other but the data signal goes through.

I powered one side of the chip from the MCP2200 breakout and the other side from a bench PSU (my Jaycar PSU configured for 5V). When I power up the bench PSU I can see the comms on the screen tool. If I put my scope on the pins I can see the traffic. Interestingly if I connect the ground to the opposite side from where I am connecting the signal the voltage floats around as you would expect.

This looks super easy and I think is the way to go.