I’ve been playing around with the MicroChip AVR128DA48 Curiosity Nano board for a few weeks now. This is a development board for the AVRxxxDAnn family (xxx is the size of the flash rom, 16k,32k,64k, and 128k, nn is the pin count, 28,32,48 and 64).
These devices are available in 28pin DIP, SOIC and SSOP, and 32, 48, and 64 pin VQFN and TQFP packages. The SMT packages have pin spacing of 1.27mm to 0.4mm depending on the package size. The 1.27mm spaced SOIC, 0.8mm spaced TQFP, and maybe the 0.65mm spaced SSOP are not too hard to hand solder, however the 0.5mm spaced TQFP (48 and 64 pin packages) are better soldered using paste applied with a stencil and a hot air station than by hand with a soldering iron. (Digikey carries stencils for various sized SMT IC footprints.) Still, it CAN be done if you have a good magnifier, a very narrow tipped soldering iron, and 0.2mm (or thinner!) solder wire. SMT to DIP adapter boards for these parts are available, look for ones with a good solder mask layer between the pins, and either gold or tin plating on the pads. A steady hand is also required!
I purchased some extra chips in the 32 and 48 pin TQFP packages. I’ve soldered 100 pin 0.5mm packages before (Atmega2560) so I have some hope of being able to work with these. I’ve also ordered some stencils and solder paste, just in case!
In addition to the AVR128DA parts, I’ve ordered some of the ATmega4808/4809 parts in both 32 pin TQFP, and 40 pin DIP packages. The ATmega4809PF is an interesting throwback. Housed in a 40pin DIP package (and available for $2.02 in single pieces), it’s ideal for bread boarding. Cheaper than the ATmega1284, and with more features, this may be the ‘goto’ part for most QRP rig front panels. This part is actually using the same die as the 48 pin TQFP/QFN parts, with 8 of the port pins not connected. Microchip recommends disabling the unused port pins in software to save power. BTW, the Arduino Nano Every which uses the ATmega4809 is advertised to run at 20mhz, but it really is clocked at the 16mhz rate out of the box. You’ll have to reflash the boot loader (or the fuses) to get it to go at the 20mhz rate.
The 32 pin ATmega4808-AU is the same size as the ATmega328p used on the Arduino Uno, with 50% more flash memory, and more I/O. It’s also cheaper, and should be considered for any application where you’re currently using the older AVR.
The AVR128DA family has some advantages over the ATmega480x family. The internal clock oscillator can be synchronized against an external 32khz crystal, which is also used as an RTC clock source. The ‘480X family also has the external crystal oscillator for use as an RTC, but no built in automatic clock synch for the HF main oscillator. Max clock frequencies for the ‘DA family goes up to 24mhz, selectable under software control. Also the available 64 pin package provides a few more Usarts, A/D inputs, as well as more I/O pins.
The ‘480x family runs at either 16 or 20 mhz, selected by a fuse bit in flash. There are software configurable clock dividers, but the base frequency can’t be changed under program control.
You have two choices of how to write code for these parts, either use the Arduino IDE, or ‘bare metal’ C/C++ code. I’ve used the Arduino IDE in the past to create rapid prototypes for AVR based projects as a contract software engineer. Often, I’ve been criticized for doing this, other software engineers have pointed out that Arduino is for hobbyist’s, and isn’t suitable for production work. For the most part, I’d say ‘Balderdash!”. The Arduino platform is really C++ and a collection of libraries and macros. It uses the GNU GCC compiler and binary tools, along with the GNU C library. You do have to bend over backwards a bit to save the resulting binary and linker outputs which are deleted whenever the IDE is closed. One thing that the Arduino framework brings to the table is a set of routines that enable the programmer to do single bit manipulation of I/O pins without having to know about the port structure. The pinMode(), digitalWrite(), and digitalRead() functions make use of a pin to port lookup, and then perform the necessary read/modify write using bit masks to isolate the single port pins. However, doing this yourself directly isn’t difficult, and will result in faster code operation than letting the framework perform a lot of extra work under the hood. We’ll look at this a bit later.
The port structure of the older Atmega AVR’s consisted of just three registers for each port. For example, considering PORTA, there was the PORTA, DDRA, and PINA registers. PORTA is the output register for the port, each bit corresponding to one of the I/O bits in the port. Setting a bit places a high on the output pin. setting a zero places a low on that pin (assuming the pin direction has been set to output). The DDRA register controls the direction of each bit, a zero makes the port pin an input, a one sets it as an output. Writing a one to the PORTn register when the DDRn register has set that pin to an input will activate the internal pull up, a zero will disable the pull up. Finally, there is the PINn register. This is where we read the input of the port pin. If the PIN direction is set to OUTPUT, then the PINn register will reflect the state of the output, if the direction is input, the PIN register will reflect the actual signal level present on the port hardware pin.
The new AVR processors have the port structure first defined on the XMEGA AVR’s about a decade ago. We now have more registers, and they are logically defined in a C structure. So we have PORTn.DIR (the DDR register), PORTn.DIRSET (register to set the direction to output on a pin), PORTnDIRCLR (register to clear the direction to input on a pin), and PORTn.DIRTGL (register to toggle or switch the direction on a pin).
Similarly we now have the PORTn.OUT, PORTn.OUTSET, PORTn.OUTCLR, and PORTn.OUTTGL registers for outputting to a pin.
Input is via the PORTn.IN registers, and we have also have the PORTn.INTFLAGS, PORTn.PORTCTRL, PORTn.PINCONFIG registers. These latter registers have the interrupt flags, hardware slew control, and pin control (invert, pullup, and interrupt sense options). There are also a few other registers that allow configuration of multiple port bits at a time.
Finally, there are the VPORT registers. There a four of these for each port, DIR,OUT, IN, and INTFLAGS. The VPORT registers allow accessing the ports in I/O space instead of data space. This means using the IN, OUT, SBI and CBI instructions, instead of LD and ST. If you use the VPORT register names the GCC compiler can optimize the generated code to make use of the faster instructions. However, if you are not writing your code purely in assembler, the optimizations of using the VPORT registers may not buy you that much.
Now that we’ve seen how the port structure logic has changed, we can see how to program the ports to act the same way as the Arduino ports. Lets assume bit 0 on PORTA is an output and bit 1 on PORTB will be an input. This might be the same as having IO pin 0 on the Arduino as an output, and IO pin 9 as an input. Instead of writing in the Arduino framework:
digitalWrite(0, digitalRead(9)); //echo state of pin 0 onto pin 9
We can write the following C/C++ code:
PORTA.DIRSET = 0x01; //set bit 0 of porta as output
PORTB.DIRCLR = 0x02; //set bit 1of portb as input
//if PORTB bit 1 is true then set PORTA bit 0
if(PORTB.IN & 0x02) PORTA.OUTSET = 1;
//else clear PORTA bit 0
else PORTA.OUTCLR = 1;
Note that we must keep track of the ports and bits assigned for each I/O function, while on the Arduino we need only concern ourselves with the absolute pin numbers. However, what you don’t see in the Arduino code is all of the lookup and decoding that the framework must do to act on the required ports and pins, along with masking off the unused bits. There will be MUCH more code generated by the Arduino functions shown than the raw metal coding. Yes, there is one less line of source code in the Arduino version than in our bare metal version. But ours will run MUCH faster and take up less flash space!
Another wrinkle with these parts is the portmux. Rather than hard wiring each of the internal interfaces to specific pins, the portmux allows reassigning them to available pins. This is done by having two or three alternate choices for the Usart, SPI, I2C and Timer output pins. Actually, it’s not possible to make use of ALL of the interface options, as there are fewer available pins than pin functions. It’s the portmux feature that allows you to decide which functions you will be using. Those who have designed systems using any of the ARM micro controllers will be familiar with the portmux feature. Note that the A/D and D/A devices ARE hard wired to specific pins on the part, and are NOT routed via the portmux. The pins used by the analog subsystems can still be used for digital I/O, provided that the analog inputs attached to them are not selected (just as with the older AVR parts).
Right now there are two Arduino boards using the ATmega4809 parts, the Nano Every, and the new Uno Wifi. There is a third party Arduino board package that supports all of the “bare metal” ATmega480x parts (MegacoreX), and a new package for the AVRxxxDA parts has just been released as well (DxCore). Both are still in beta version, functional, but probably with some bugs. Both will support a boot loader, but the use of an external programmer is recommended instead. At the moment, the Arduino IDE supports 3 different programmers (via AVRDude), the Microchip mEDBG which is the embedded UPDI programmer supplied with various Microchip/Atmel dev boards AND the Arduino Nano Every. It also supports the jtag2UPDI, which is a modified Arduino that emulates the UPDI programmer protocol, and the Atmel-ICE in UPDI mode.
For programming these parts using ‘bare metal’ C/C++, you will need the latest version of AVR-GCC with support for the parts. The best way to get this set up on your PC would be to install either Atmel Studio, or MPLAB-X IDE. Atmel Studio only runs on Windows, while MPLAB-X IDE is available for Windows, Mac, and Linux. Both support several Microchip programmers, the SPARK, PicKit4, and the Atmel ICE.
I’ve mentioned the available programmers briefly in my last post.
The Microchip SPARK is a bare board programmer module. It doesn’t come with the required USB cable, or the target cable, you will have to buy the first, and make up the second. Still, the SPARK cost’s only $25, and MicroChip does offer a $10 off coupon for it from time to time. You can download an STL file to 3D print an enclosure for the SPARK from Thingiverse.
The PicKit4 is Microchips’s latest programmer. It will support almost every device that they make, and this includes the PIC, dsPIC, AVR, and SAM families of micro controllers. It costs about $60, and comes with a USB cable.
The Atmel ICE also supports just about every device made by Atmel. It has separate connectors for SAM and AVR devices. Recent firmware updates allow the Atmel ICE to also program and debug PIC parts too. The bare board (no cables) costs about $60. A 3D printable enclosure designs can be downloaded from thingiverse. The basic kit comes in an enclosure, and includes the USB cable, and a suitable target cable for AVR devices. This costs about $100. The full kit includes an additional cable and adapter set for programming SAM devices, this costs $140. If you want to go with the Atmel ICE, I’d recommend buying the bare board, and 3D print your own enclosure for it. It uses a standard micro USB cable, which you probably already own. You can make a target cable by getting a SWD debugger ribbon cable, a 6 pin ISP ribbon cable, and breakout boards for the SWD and ISP connectors. All available from Adafruit.
I have both the Pickit4 and the Atmel ICE, if I was just starting out with these devices and didn’t have a programmer, I’d probably get the SPARK.
Writing bare metal code from scratch can be a bit daunting, at least getting started. Atmel has an online tool called AtmelStart, that will generate a project framework that will get you started. You can import the generated project into either Atmel Studio, or MPLAB-X. You can also just use the provided makefile to build the code using either the GNU tools, or the IAR professional tools.
AtmelStart has you select the development board, or microcontroller chip that you are using, and then pick which interface drivers you wish for it to create. In most cases you are given a choice of interrupt driven, or polling drivers. The Usart serial drivers can be configured to interface with the GCC printf() I/O streams. I’d recommend selecting at least the USART and TWI (I2C) drivers if you intend to use those functions. You can also have AtmelStart build the clock configuration code, this will remove some additional head scratching the first time you try using one of these new parts. I’d probably not bother with configuring the port initialization with AtmelStart. The code it generated required some modification for my use, and I think I did a better job setting this part up from scratch than the framework did.
You should also search the Microchip website for project code examples. I found some very good examples for I2C, and RTC code there.
I’ll be showing some of my example code in a later post. All of my project code will be put up on Github when ready. Right now, I’m experimenting with getting a GLCD driver working, along with a rotary encoder driven menu system, and an Si5351 synthesizer.