Is there a LoRaWAN source for AVR?


#1

Hey all,

Currently I'm working on getting the LoRaWAN stack with support for an sx1272 onto my ATxMega256A3U.
Now I'm wondering what would be the smartest way to go about this.
So far I've found a number of sx1272 drivers, but they all seem to be focused on ARM and not AVR.
Does anyone have experience with putting sx1272 drivers on AVR or know about a source code that does this?

Right now I'm trying to rebuild the semtech sx1272 source Firmware Driver V3.3 and LoRaWAN Stack to work on the ATxMega but that will be a buttload of error-heavy work :confused:


(Matthijs Kooijman) #2

Have a look at https://github.com/matthijskooijman/arduino-lmic

It's aimed at the Arduino environment, including the AVR. However, I think ATXMega is AVR32?


#3

ATxMega uses the standard AVR. Sadly the hardware guy did not attach the DIO to the MCU so I need to think of something for that. (And have only have one non-interrupt pin available left on the MCU). In any case I'm a bit closer, so thank you.


(Michal) #4

Hello,
Few weeks ago I have written hal.c for AtXmega32E5. I'll post it below. It's really bad written because I didn't think someone will use it. If you have any questions just ask.

/*
* hal.c
*
* Created: 21.07.2016 10:45:39
*  Author: Michal Misiek
*/
#include "hal.h"
#include "lmic.h"
>#include <asf.h>
>#include <util/delay.h>
>#include <stdio.h>

>#include "time.h"
#include "uart.h"

// -----------------------------------------------------------------------------
// I/O

#define DIO0_PIN PIN0_bm
#define DIO0_PORT PORTD
#define DIO0_PORT_IN (PORTD.IN)
#define DIO1_PIN PIN1_bm
#define DIO1_PORT PORTD
#define DIO1_PORT_IN (PORTD.IN)
#define DIO2_PIN PIN4_bm
#define DIO2_PORT PORTD
#define DIO2_PORT_IN (PORTD.IN)

#define RESET_PIN PIN5_bm
#define RESET_PORT PORTD
#define NSS_PIN PIN4_bm
#define NSS_PORT PORTC
#define MOSI_PIN PIN7_bm
#define MOSI_PORT PORTC
#define MISO_PIN PIN6_bm
#define MISO_PORT PORTC
#define SCK_PIN PIN5_bm
#define SCK_PORT PORTC



void hal_io_init ()
{
	//DIO as input, need for interrupts
	DIO0_PORT.DIRCLR = DIO0_PIN;
	DIO1_PORT.DIRCLR = DIO1_PIN;
	DIO2_PORT.DIRCLR = DIO2_PIN;
	PORTD.PIN0CTRL = PORT_OPC_PULLDOWN_gc;
	PORTD.PIN1CTRL = PORT_OPC_PULLDOWN_gc;
	PORTD.PIN4CTRL = PORT_OPC_PULLDOWN_gc;


	// RXTX i RESET as output
	// RXTX_uC_PORT.DIRSET = RXTX_uC_PIN;
	RESET_PORT.DIRSET = RESET_PIN;

}

//Switching antenna
// val == 1  => tx 1
void hal_pin_rxtx (u1_t val) {
}

// set radio RST pin to given value (or keep floating!)
void hal_pin_rst (u1_t val) {
	if(val == 0 || val == 1) { // drive pin
		RESET_PORT.DIRSET = RESET_PIN;
		if(val){
			RESET_PORT.OUTSET = RESET_PIN;
			} else {
			RESET_PORT.OUTCLR = RESET_PIN;
		}
		} else { // keep pin floating
		RESET_PORT.DIRCLR = RESET_PIN;
	}
}

const u8_t dio_pins[]  = {DIO0_PIN, DIO1_PIN, DIO2_PIN };
static bool dio_states[NUM_DIO] = {0};

// -----------------------------------------------------------------------------
// SPI

static void spi_init_pins(){
	PORTC.DIRSET    =    NSS_PIN | MOSI_PIN | SCK_PIN; // wyj┼Ťcia SPI
	PORTC.DIRCLR    =    MISO_PIN;                     // wej┼Ťcie SPI
	PORTC.OUTCLR    =    SCK_PIN | MISO_PIN | MOSI_PIN | NSS_PIN;
	SPIC.CTRL = SPI_DORD_bm;
	//PORTC.REMAP     =    PORT_SPI_bm;
}
static void spi_enable_module(){
	SPIC.CTRL       =    SPI_ENABLE_bm|               // w┼é─ůczenie SPI
	SPI_MASTER_bm|               // tryb master
	SPI_MODE_0_gc|               // tryb 3
	SPI_PRESCALER_DIV64_gc;      // preskaler
}



void hal_spi_init () {
	spi_init_pins();
	spi_enable_module();
}

void hal_pin_nss (u1_t val) {
	NSS_PORT.DIRSET = NSS_PIN;
	if (val){
		NSS_PORT.OUTSET = NSS_PIN;
		}	else {
		NSS_PORT.OUTCLR = NSS_PIN;
	}
}

// perform SPI transaction with radio
u1_t hal_spi (u1_t out) {
	SPIC.DATA = out;
	while(SPIC.STATUS == 0);
	u1_t res = SPIC.DATA;
	#if _spi_debug
	/*SendChar('\n');
	SendChar(out);
	SendChar(':');
	SendChar(res);
	SendChar('\n');*/
	#endif
	return res;
}

// -----------------------------------------------------------------------------
// TIME



void hal_time_init () {
	TCC4.CTRLB = TC45_WGMODE_NORMAL_gc;
	//32MHz/1024 = 31250kHz -> 32us
	TCC4.CTRLA = TC45_CLKSEL_DIV1024_gc;
	EVSYS_CH0MUX=EVSYS_CHMUX_TCC4_OVF_gc;
	TCC5.CTRLB = TC45_WGMODE_NORMAL_gc;
	TCC5.CTRLA=TC45_CLKSEL_EVCH0_gc;
}

u8_t lastmicros=0;
u8_t addticks=0;
u8_t ticks = 0;


u4_t hal_ticks () {
	//hal_disableIRQs();
	// LMIC requires ticks to be 15.5?s - 100 ?s long
	// Check for overflow of micros()
	if ( TCC4.CNT  < lastmicros ) {
		addticks += 65536;
	}
	lastmicros = TCC4.CNT;
	//hal_enableIRQs();
	return TCC4.CNT + TCC5.CNT * 65536;

}

// Returns the number of ticks until time. Negative values indicate that
// time has already passed.
static s4_t delta_time(u4_t time) {
	u8_t t = hal_ticks();
	s4_t d = time - t;
	if (d<=1) { return 0; }
	else {
		return (u4_t)(time - hal_ticks());
	}
}

void hal_waitUntil (u4_t time) {
	while( delta_time(time) != 0 ); // busy wait until timestamp is reached
}

// check and rewind for target time
u1_t hal_checkTimer (u4_t time) {
	// No need to schedule wakeup, since we're not sleeping
	return delta_time(time) <= 0;
}



void interrupt_init(void){
	/*PORTD.DIRCLR = PIN2_bm | PIN1_bm | PIN0_bm;
	PORTD.PIN2CTRL  =    PORT_OPC_PULLDOWN_gc|
	PORT_ISC_RISING_gc;
	PORTD.PIN1CTRL  =    PORT_OPC_PULLDOWN_gc|
	PORT_ISC_RISING_gc;
	PORTD.PIN0CTRL  =    PORT_OPC_PULLDOWN_gc|
	PORT_ISC_RISING_gc;   // przerwanie wywo┼éuje zbocze opadaj─ůce


	PORTD.INTMASK  =    DIO0_PIN;               // pin E5 ma generowa─ç przerwania INT0
	DIO1_PORT.INTMASK  =    DIO1_PIN;
	DIO2_PORT.INTMASK = DIO2_PIN;              // pin E6 ma generowa─ç przerwania INT1
	PORTC.INTCTRL   =    PORT_INTLVL_HI_gc|    // poziom HI dla przerwania INT0 portu E
	PORT_INTLVL_LO_gc;    // poziom LO dla przerwanie INT1 portu E* /
	PMIC.CTRL       =    PMIC_HILVLEN_bm|       // w┼é─ůczenie przerwa┼ä o priorytecie HI
	PMIC_LOLVLEN_bm;       // w┼é─ůczenie przerwa┼ä o priorytecie LO
	sei();
	hal_enableIRQs();*/


}
static uint8_t irqlevel = 0;



//Check DIO pins, but maybe there's more
void hal_io_check() {

	if (dio_states[0] != (PORTD.IN & DIO0_PIN)) {
		dio_states[0] = !dio_states[0];
		if (dio_states[0])
		//SendNum(0);
		radio_irq_handler(0);
	}

	if (dio_states[1] != (PORTD.IN & DIO1_PIN)) {
		dio_states[1] = !dio_states[1];
		if (dio_states[1])
		//SendNum(1);
		radio_irq_handler(1);
	}

	if (dio_states[2] != (PORTD.IN & DIO2_PIN)) {
		dio_states[2] = !dio_states[2];
		if (dio_states[2])
		//SendNum(2);
		radio_irq_handler(2);
	}


}


void hal_disableIRQs () {
	cli();
	irqlevel++;
}

void hal_enableIRQs () {
	//SendNum(irqlevel);
	if(--irqlevel == 0) {
		sei();
		hal_io_check();
	}
}

void hal_sleep () {
	// Not implemented
}

// -----------------------------------------------------------------------------



void hal_init () {
	// configure radio I/O and interrupt handler
	//interrupt_init();
	hal_io_init();
	// configure radio SPI
	hal_spi_init();
	// configure timer and interrupt handler
	hal_time_init();

}

void hal_failed (const char *file, u2_t function, u2_t line) {
	SendString("HAL Failed\n");
	SendString(file);
	SendChar(':');
	SendNum(line);
	SendChar(':');
	SendString("   ");
	SendString(function);
	SendChar('\n');
	hal_disableIRQs();
	//hal_pin_nss(0);
	//PORTC.DIRSET = PIN4_bm;
	//PORTC.OUTCLR = PIN4_bm;
	while(1);
}

(Davezap) #5

Hi I've been porting the IBM LMICv6 to SAM4E I know it's not the Mega 256 chip you mentioned but as most of the problems I had were just with GCC and Atmel Studio I think it's applicable.

here, currently supports SX1272 and SX1276 class A&B LoRa
https://www.research.ibm.com/labs/zurich/ics/lrsc/lmic.html

First off their is a bug in the code that will report "#if with no expression" just change such #if's to #ifdef

Then you re-implement all the functions in hal.c for your chip.
Change the defines in hal.h to the appropriate settings.

Then in your main app #include "LMIC_1.6/LoRaWAN.h" and call LoRaWAN_INIT()

burito1's note about hal_sleep () is true, just leave it blank as the code seems to arrive at it in oslmic.c: os_runloop() and never return - after having disabled the interrupts - I'm not how it expected to work that way.

You will find the device, router_id, and AES key set in LoRaWAN.c

That's all the referenced github port is doing, using the same IBM implementation + Arduino specific HAL.

disclaimer, I'm still working on this myself and have not yet got my other radio ready for p2p communication or a gateway to test with but will soon. But I've got it to the point of sending the join requests.

[edit]
Found the #if bug again.

static int decodeBeacon (void) {
    ASSERT(LMIC.dataLen == LEN_BCN); // implicit header RX guarantees this
    xref2u1_t d = LMIC.frame;
    if(
#if CFG_eu868
        d[OFF_BCN_CRC1] != (u1_t)os_crc16(d,OFF_BCN_CRC1)
#elif CFG_us915
        os_rlsbf2(&d[OFF_BCN_CRC1]) != os_crc16(d,OFF_BCN_CRC1)
#endif

Should be

static int decodeBeacon (void) {
    ASSERT(LMIC.dataLen == LEN_BCN); // implicit header RX guarantees this
    xref2u1_t d = LMIC.frame;
    if(
#if defined(CFG_eu868)
        d[OFF_BCN_CRC1] != (u1_t)os_crc16(d,OFF_BCN_CRC1)
#elif defined(CFG_us915)
        os_rlsbf2(&d[OFF_BCN_CRC1]) != os_crc16(d,OFF_BCN_CRC1)
#endif