LoRa Mac Node with EFM32PG custom board + SX1276 or RFM95 module

Hi All,

I could port the code for LoRa Mac Node onto EFM32PG custom board with SX1276/RFM95 changeable module. The LoRa radio module goes on socket, SX1276 is 915Mhz and RFM95 is 868Mhz versions.
I am using RFM95 868Mhz with ABP for now and it does transmit and I can see the uplink message under my Laird geateway on TTN, but that happens only one time when the powered up. I have a few questions:

  1. In the board.c file, under BoardInitMCU() there is a function call CalibrateSystemWakeupTime( ).How important is this? I tried to single step through this in dubug mode, it does start a timer, but it does not generate OnCalibrateSystemWakeupTimeTimerEvent, and after 5 tries it sends RTC into low power mode and does not wakeup. But when I comment out the CalibrateSystemWakeupTime(), the code goes further and works good and transmits the message.

2.Further I need to transmit the message every 15sec, where can I set up this?

Thank you in advance!
Silas

your board does not need to calibrate with CalibrateSystemWakeupTime(), this function is for STM32 only.

can you show your implementation code for EFM32PG?

1 Like

Hi Nestorayuso

Thanks for clarification on CalibrateSystemWakeupTime() :slight_smile:

here is my code in Board.c

void BoardInit(void)
{

BoardInitEMU();
BoardInitCMU();
BoardInitHFXO();
BoardInitLFXO();
BoardInitHWPins();

}

void BoardInitMcu( void )
{

BoardInit();
BoardInitPeriph();

if( McuInitialized == false )
{
	RtcInit();
}

SpiInitRadio( &SX1276.Spi );
SX1276IoInit();

if( McuInitialized == false )
{
    McuInitialized = true;
    if( GetBoardPowerSource( ) == BATTERY_POWER )
    {
        CalibrateSystemWakeupTime( );
    }
}

}

The BoardInit() initializes EMU, CMU, HFXO, LFXO and HWPins and then initializes the Peripherals. Then RTC is initilzed. The following is the RTCInit code

 void RtcInit( void )

{
if( RtcInitalized == false )
{
// Reset overflow counter
rtcOverflowCounter = 0;

	// Calculate overflow interval based on RTC counter width and frequency
	rtcOverflowInterval   = ((0x00FFFFFF+1) / RTC_TICKS_PER_MS);
	rtcOverflowIntervalR  = ((0x00FFFFFF+1) - (rtcOverflowInterval * RTC_TICKS_PER_MS)); // remainder

	RtcInitalized = true;

	rtcFreq = CMU_ClockFreqGet(cmuClock_RTCC);


	RTCC_Init_TypeDef rtccInit = RTCC_INIT_DEFAULT;
	RTCC_CCChConf_TypeDef rtccInitCompareChannel = RTCC_CH_INIT_COMPARE_DEFAULT;

	rtccInit.cntWrapOnCCV1 = true;        /* Clear counter on compare match */
	rtccInit.presc = rtccCntPresc_1;


	/* Setting the compare value of the RTCC */
	//RTCC_ChannelInit(1, &rtccInitCompareChannel);
	//RTCC_ChannelCCVSet(1, RTC_COUNT_BETWEEN_WAKEUP);

	// Disable all rtc interrupts
	RTCC_IntDisable(RTCC_IEN_OF | RTCC_IEN_CC1);
	RTCC_IntClear(RTCC_IFC_OF | RTCC_IFC_CC1);

	//RTCC_CounterReset();

	// Enable interrupt on overflow
	RTCC_IntEnable(RTCC_IF_OF);

	// Enable interrupts
	NVIC_SetPriority(RTCC_IRQn, 4);
	NVIC_ClearPendingIRQ(RTCC_IRQn);
	NVIC_EnableIRQ(RTCC_IRQn);

	// Enable RTC
	RTCC_Enable(true);
}

}

Thanks
Silas

1 Like

Ok, I fouind the following defines:

#define APP_TX_DUTYCYCLE                    15000   // Milliseconds between two transmissions
#define APP_TX_DUTYCYCLE_RND                1000    // Random delay for application data transmission duty cycle [ms]
#define APP_DEFAULT_DATARATE                DR_5    // SF7 - BW125
#define APP_PORT                            2       // Application port
#define APP_DATA_SIZE                       16      // Size of packets to transmit in this example
#define APP_DATA_MAX_SIZE                   64      // Size of user data buffer
#define APP_ADR_ON                          1       // Whether we use Adaptive Data Rate or not
#define APP_CONFIRMED_MSG_ON                0       // Whether this example will transmit confirmed or unconfirmed packets

So I guess my second question is answered :slight_smile:

I also found this post - very useful read :+1:
TTN Fair Access Policy

Thanks
Silas

Hi Nestorayuso

Following is the RtcInit() code, actually it is RTCC for EFM32PG. It seems the code on the Main() after sending the first message in “case DEVICE_STATE_SEND:” sets the DeviceState=DEVICE_STATE_SLEEP and then send to TimerLowPowerHandler() (EM2), although the statement TimerStart(&TxNextPacketTimer) should have started the timer which would wake the device up when the time = APP_TX_DUTYCYCLE + RandomDelay expires, but this never happens, seems the RTCC is not running or the interrupt is not firing… :frowning:

Further, just the outline: in case of my product, the EM sleep mode is handled by the main product code, and it is also responsible for writing the new data frame and when the data is different from the previous data sent, it will set the flag for DataReady and approximately every 4 or 5 min the LoRa packet will be sent out.

Would you please help.:slight_smile:

 void RtcInit( void )
{
	if( RtcInitalized == false )
	{
		// Reset overflow counter
		rtcOverflowCounter = 0;

		// Calculate overflow interval based on RTC counter width and frequency
		rtcOverflowInterval   = ((0x00FFFFFF+1) / RTC_TICKS_PER_MS);
		rtcOverflowIntervalR  = ((0x00FFFFFF+1) - (rtcOverflowInterval * RTC_TICKS_PER_MS)); // remainder

		RtcInitalized = true;

		rtcFreq = CMU_ClockFreqGet(cmuClock_RTCC);


		RTCC_Init_TypeDef rtccInit = RTCC_INIT_DEFAULT;
		RTCC_CCChConf_TypeDef rtccInitCompareChannel = RTCC_CH_INIT_COMPARE_DEFAULT;

		rtccInit.cntWrapOnCCV1 = true;        /* Clear counter on compare match */
		rtccInit.presc = rtccCntPresc_1;


		/* Setting the compare value of the RTCC */
		RTCC_ChannelInit(1, &rtccInitCompareChannel);
		RTCC_ChannelCCVSet(1, 64000);

		/* Enabling Interrupt from RTCC */
		RTCC_IntEnable(RTCC_IEN_CC1);
		NVIC_ClearPendingIRQ(RTCC_IRQn);
		NVIC_EnableIRQ(RTCC_IRQn);

		/* Initialize the RTCC */
		RTCC_Init(&rtccInit);
	}
}

be sure to use a 32.768KHz crystal LFXO, not an internal LFRC:

	CMU_ClockEnable(cmuClock_CORELE, true);
	CMU_ClockSelectSet(cmuClock_LFA, cmuSelect_LFXO);
	CMU_ClockDivSet(cmuClock_RTCC, cmuClkDiv_1);
	CMU_ClockEnable(cmuClock_RTCC, true);

in your code, the variable rtcFreq is useless.
also set the compare value to 32768-1

	RTCC_ChannelCCVSet(1, 32768-1);

show me your RTCC_IRQHandler() function

Hi Nestorayuso

Thanks for your reply. Here is the RTCC_IRQHandler(). With a break point on the RTCCC_IntClear, the program execution does stop there with compare value set to 32767. That means the RTCC and IRQ working fine. Further I set the function GetBoardPowerSource() to return USB_POWER so that the code does not put the device into sleep mode through function TimerLowPowerHandler() and keeps looping through the main -> DEVICE_STATE_SLEEP. But the event OnTxNextPacketTimerEvent() never comes up!! Seems the following code lines does not start the timer for next transmission, what am I missing?? Can you please help… Thank you so much help…

Silas

        // Schedule next packet transmission
        TxDutyCycleTime = APP_TX_DUTYCYCLE + randr(-APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND);
        TimerSetValue(&TxNextPacketTimer, TxDutyCycleTime);
        TimerStart(&TxNextPacketTimer);



void RTCC_IRQHandler(void)
{
	uint32_t flags = RTCC_IntGetEnabled(); //get flags for only the enabled interrupts
	RTCC_IntClear(RTCC_IFC_OF | RTCC_IFC_CC1);

	// Check for overflow
	if (flags & RTCC_IF_OF)
		rtcOverflowCounter++;

	// Check if timer expired
	if (flags & RTCC_IF_CC1)
	{
		RTCC_IntDisable(RTCC_IEN_CC1);
		TimerIrqHandler();
	}
}

Please send me the complete rtc-board.c

To set a new timer, a call to RtcSetTimeout() is done ant this function sets a new compare value with RTCC_ChannelCCVSet(1, xxx).

The new compare value must be lower than the overflow size of the RCTT counter. If it is higher, you must save it somewhere to set the compare value later whenever doesn’t overflow.

Hi Nestorayuso,

Thanks for your reply. I have attached two files here, rtc-board.c and timer.c. In the original version of the rtc-board.c, in the code for RtcGetTimerValue() was partly commented ?!? I tried with uncommenting the lines but it did not help … rtc-board.txt (7.4 KB) timer.txt (9.8 KB)
For the first power-up cycle, the TimerIrqHandler() in timer.c does fire up and the message sent out without any issue, But after that it just goes into the loop on Main->TimerLowPowerHandler() in timer.c, I have set GetBoardPowerSource() to return as USBpower, so that does not go into low power mode. But there is no timer event and not next message…

TimerTime_t RtcGetTimerValue( void )
{
	TimerTime_t t = 0;

	// Add time based on number of counter overflows
//	t += rtcOverflowCounter * rtcOverflowInterval;

	// Add remainder if the overflow interval is not an integer
//	if ( rtcOverflowIntervalR != 0 )
//	{
//		t += (rtcOverflowCounter * rtcOverflowIntervalR) / RTC_TICKS_PER_MS;
//	}

	// Add the number of milliseconds for RTC
	t += ( RTC->CNT / RTC_TICKS_PER_MS );

	// Add compensation for crystal temperature drift
	t += tempCompAccumulator;

	return t;
}

This is resolved with Nestor’s help :slight_smile:
Thank you so much @nestorayuso

1 Like