Saturday, August 10, 2013

using the atxmega32e5 under linux

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.


compiler


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.

avr-libc


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 :)

Example


Here is an example that demonstrates the following:

  • switch to the internal 32Mhz oscillator
  • configure an USART on ports D6 and D7 at 9600 baud and run a simple echo follower
  • blink a LED on port A0


// (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;
}

Code


The code can be found on github, together with the files mentioned here for convenience.

Hardware


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.

More fun


I'm still exploring this nice little microcontroller. Be sure to check back later on my blog for new adventures!



No comments: