The Atmel AVR atxmega*e5 is an interesting chip. However it is still rather new so getting it to work on my linux environment took some tinkering.
\
\
Compiling requires a compiler that knows the CPU. In the previous post I just cheated and used a mostly compatible CPU.
\
My Debian comes default with gcc 4.7.3 which does not support the new atxmega*e5 CPU’s yet. Luckily someone already packaged the gcc 4.8.1 compiler source, so it was just a matter of recompiling Debian’s gcc-avr package against the gcc 4.8.1 source package. If you don’t feel like that using the atxmega*D4 as cpu type during compilation is doable.
\
Besides the compiler, avr-libc also needs to be adapted. It is avr-libc that provides the io description headers for the CPU’s. There is no avr-libc for linux yet that does this. Luckily Atmel’s AVR Studio product also uses gcc internally, and they already support the atxmega*e5 CPU’s.
\
I took the files iox8e5.h, iox16e5.h and iox32e5.h from avr studio and copied these to /usr/lib/avr/include/avr/ .
\
Also /usr/lib/avr/include/avr/io.h needs to be updated to add the new CPU’s:
\
--- io.h.old 2013-08-10 09:51:40.963241968 +0200
+++ io.h 2013-08-10 13:29:12.682545107 +0200
@@ -382,12 +382,6 @@
# include <avr/iox32a4.h>
#elif defined (__AVR_ATxmega32D4__)
# include <avr/iox32d4.h>
+#elif defined (__AVR_ATxmega8E5__)
+# include <avr/iox8e5.h>
+#elif defined (__AVR_ATxmega16E5__)
+# include <avr/iox16e5.h>
+#elif defined (__AVR_ATxmega32E5__)
+# include <pavr/iox32e5.h>
#elif defined (__AVR_ATxmega64A1__)
# include <avr/iox64a1.h>
#elif defined (__AVR_ATxmega64A1U__)
\
Besides that, some other files are needed. I took the files crtx8e5.o, crtx16e5.o and crtx32e5.o from avr studio and copied these to /usr/lib/avr/lib/avrxmega2/
With those two in place, compilation works :)
\
\
Here is an example that demonstrates the following:
\
\
// (c) 2013 Joost Yervante Damad <joost@damad.be>
// License: GPL3
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
static void usart_write(uint8_t data)
{
USARTD0.DATA = data;
if(!(USARTD0.STATUS & USART_DREIF_bm)) {
while(!(USARTD0.STATUS & USART_TXCIF_bm)); // wait for TX complete
}
USARTD0.STATUS |= USART_TXCIF_bm; // clear TX interrupt flag
}
static inline void init_oscillator() {
// enable 32Mhz internal oscillator
OSC.CTRL |= OSC_RC32MEN_bm;
// wait for it to be stable
while (!(OSC.STATUS & OSC_RC32MRDY_bm));
// tell the processor we want to change a protected register
CCP=CCP_IOREG_gc;
// and start using the 32Mhz oscillator
CLK.CTRL=CLK_SCLKSEL_RC32M_gc;
// disable the default 2Mhz oscillator
OSC.CTRL&=(~OSC_RC2MEN_bm);
// enable 32kHz calibrated internal oscillator
OSC.CTRL|= OSC_RC32KEN_bm;
while (!(OSC.STATUS & OSC_RC32KRDY_bm));
// set bit to 0 to indicate we use the internal 32kHz
// callibrated oscillator as auto-calibration source
// for our 32Mhz oscillator
OSC.DFLLCTRL = OSC_RC32MCREF_RC32K_gc;
// enable auto-calibration for the 32Mhz oscillator
DFLLRC32M.CTRL |= DFLL_ENABLE_bm;
}
static inline void init_usart() {
// enable clock out on port PC7
PORTCFG.CLKOUT = (PORTCFG.CLKOUT & ~PORTCFG_CLKOUTSEL_gm) | PORTCFG_CLKOUT_PC7_gc;
// set PC7 as output
PORTC.DIRSET = PIN7_bm;
// set PD7 as output for TX0
PORTD.DIRSET = PIN7_bm;
PORTD.OUTSET = PIN7_bm;
// remap USARTD0 to PD[7-4]
PORTD.REMAP |= PORT_USART0_bm;
// set baud rate 9600: BSEL=12, BSCALE=4
// as found in table in
// Atmel-42005-8-and-16-bit-AVR-Microcontrollers-XMEGA-E_Manual.pdf
USARTD0.BAUDCTRLA = 12; // BSEL
USARTD0.BAUDCTRLB = 4 << USART_BSCALE_gp; // BSCALE
// disable 2X
USARTD0.CTRLB = USARTD0.CTRLB & ~USART_CLK2X_bm;
// enable RX and TX
USARTD0.CTRLB = USARTD0.CTRLB | USART_RXEN_bm | USART_TXEN_bm;
// enable async UART 8N1
USARTD0.CTRLC = USART_CMODE_ASYNCHRONOUS_gc | USART_PMODE_DISABLED_gc | USART_CHSIZE_8BIT_gc;
USARTD0.CTRLC &= ~USART_SBMODE_bm;
USARTD0.CTRLD = 0; // No LUT
// set interrupt level for RX
USARTD0.CTRLA = (USARTD0.CTRLA & ~USART_RXCINTLVL_gm) | USART_RXCINTLVL_LO_gc;
}
static inline void init_interrupts() {
// Enable PMIC interrupt level low
PMIC.CTRL |= PMIC_LOLVLEX_bm;
// enable interrupts
sei();
}
static volatile uint8_t echo_char = 42;
int main( void )
{
init_oscillator();
// enable clock out on port PC7
PORTCFG.CLKOUT = (PORTCFG.CLKOUT & ~PORTCFG_CLKOUTSEL_gm) | PORTCFG_CLKOUT_PC7_gc;
// set PC7 as output
PORTC.DIRSET = PIN7_bm;
init_usart();
init_interrupts();
// set PA0 as output
PORTA.DIRSET = PIN0_bm;
// blink LED on PA0 with 1 second on, 1 second off
// write echo_char on USART on D7; defaults to 42(*)
while (1) {
usart_write(echo_char);
PORTA.OUTSET = PIN0_bm;
_delay_ms( 1000 );
usart_write(echo_char);
PORTA.OUTCLR = PIN0_bm;
_delay_ms( 1000 );
}
}
// USART RX receive interrupt handler
ISR(USARTD0_RXC_vect) {
echo_char = USARTD0.DATA;
}
\
The code can be found on github, together with the files mentioned here for convenience.
\
\
I have some extra atxmega32e5 breakout boards available. You can find them in my tindie store.
\
| breakout on a breadboard with USB-serial adapter |
\
In addition, the eagle schematic and board files are available on github as well.
\
\
I’m still exploring this nice little microcontroller. Be sure to check back later on my blog for new adventures!
\
\
\
tags: C - C++ - atxmega - atxmega32e5 - avr - electronics - lang:en - programming - tindie