YARM-Dev Embedded Notes Blog

YARM-Dev Embedded Notes Blog. [Sun Aug 20 2017]

Atmel Studio 7 Step by Step Configuration

Atmel Studio 7 Installation and YARM library import.

  • You must download and install Atmel Studio 7 from here
  • Launch Atmel Studio 7 then select "File -> New -> Project". Inside the "New Project" window click on and select "GCC C ASF Board Project"
  • Type in the name of your project, and click "OK".
  • A new windows appear. Now you must choose the MCU.
  • Select inside "Device Family" pop-up the family "SAML21"; then select the "ATSAML21E18B" device and click OK.
  • The new project will be created.
  • Now is time to insert the "Atmel Software Framework (ASF)" modules.
  • Select "Project -> ASF Wizard" from the menu, after a while, on the left side of the window will appear a list of the available modules. With the help of the "Search for modules" text box, search and then "Add >>" the following modules:
    • Generic board support (driver)
    • Delay routines (service) [cycle]
    • EXTINT - External Interrupt (driver) [callback]
    • PORT - GPIO Pin Control (driver)
    • RTC - Real Time Counter Driver (driver) [count_callback]
    • SERCOM I2C - Master Mode I2C (driver) [polled]
    • SERCOM SPI - Serial Peripheral Interface (driver) [callback]
    • SERCOM USART - Serial Communication (driver) [polled]
    • SYSTEMS - Core Systems Driver (driver)
    • Standard serial I/O (stdio) (driver)
  • N.B. Some of this modules can be configured using a pop-up menu on the right of the name. The correct value to set is inside the square bracket of each modules. Click on "Apply". At this point your project is ready to use this libraries.
  • Inside the project window, right click on the "src" folder inside the "Solution Explorer" window on the right. Choose "Add -> Existing Item" to copy the source library inside your project.
  • Remenber to add inside in "Properties -> Toolchain -> ARM/GNU Linker -> Miscellaneous" the option: "-lc -u _printf_float" to visualize the float value with "printf".

See the screenshot of the entire procedure in this PDF file.

The YARM_lib library

YARM_lib library at a Glance.

The YARM_lib code initializes and manages the ATA8510 module. Primitives are available to allow the management of the transmission the reception and selectively switch from IDLE mode, TX and POLLING mode.
The ATA8510 module is seen from the MCU SAML21 as a SPI SLAVE device. The library takes care of initializating the SPI, and its management. A unique function, YARM_SendCmd , is used to send commands to the radio module. The function also introduces a certain delay, provided by the DS, specific for each command.
The first function to call is:

    /* Configure SPI and PowerUp ATA8510 */
    YARM_Init();
This function takes care to configure the pin I/O used by SPI, performs the configuration of the SPI and initializes the module ATA8510 according to a sequence and precise timing, operating on the pins: NRESET8510_PIN and NPWRON_PIN .
At this point, the radio module can be configured in an operating mode:

	YARM_SetIdleMode();
	if ( ConsoleOn) PrintSysError("YARM_SetIdleMode");
	delay_ms(1);
The state IDLE is the state in which the radio device receives commands.

	YARM_SetPollingMode();
	if ( ConsoleOn) PrintSysError("YARM_SetPollingMode");
The state POLLING instead puts the radio module in receipt of a group of predefined channels.

	/* 1. Data setup to transmission. */
	txLength = PrepareTxData();
	/* 2. Write the TX preamble. */
	YARM_WriteTxPreamble( YARM_WriteTxPreambleBuffer_LEN, &TxPreambleBuffer[0]);
	if ( ConsoleOn) PrintSysError("YARM_WriteTxPreamble");	
	delay_us( 100);
	/* 3. Write the data in to the TX fifo. */
	YARM_WriteTxFifo( txLength, sd.data);
	if ( ConsoleOn) PrintSysError("YARM_WriteTxFifo");
	delay_ms(100);
	/* 4. Configure the radio in TX mode, set the channel. */
	YARM_SetSystemMode( YARM_RF_TXMODE, WEATHER_TX_CHNL);
	if ( ConsoleOn) PrintSysError("YARM_SetSystemMode TXMode");
	delay_ms(100);
	
	if ( ConsoleOn) printf("TXLoop Started\r\n");
		
	/* 5. Loop waiting for the flag event_state. */
	event_state = 0;
	while( !event_state);
	event_state = 0;
	
	/* 6. Loop again for the bit EOTA. */
	i = 0;
	do {
		i++;
		delay_us( 3000);
		YARM_GetEventBytes( TxRxEvent);
	} while (((TxRxEvent[1] & 0x10) == 0)&&(i <40));

	if ( 1 /*ConsoleOn*/) {
		TERM_TEXT_GREEN;
		printf("-------- END Transmission \r\n");
		TERM_TEXT_DEFAULT;
	}
	/* 7. Configure the module in IDLE mode. */
	YARM_SetIdleMode(); 
	if ( ConsoleOn) PrintSysError("YARM_SetIdleMode");
	delay_ms( 1);
    

How to send float and Int: Data formatting for YARM Tx

Data formatting for simple Tx.


	/* Data union to manage different format as array of chars
	*/
	typedef union
	{
		uint8_t data[32];
		struct {
			float t;
			float p;
			float h;
			uint32_t SerialNumber[4];
		} wval;
	} SENSOR_DATA_t;

	SENSOR_DATA_t sd;
	#define SENSOR_DATA_LEN	(sizeof(SENSOR_DATA_t))
	/* **************************************************************** */
The TX Fifo is a unsigned char buffer. In most cases we use int or float I use to define a union to allocate, in different format, the same space.
When you need to clear the entire buffer you can use a simple loop:

	for ( i=0; i<SENSOR_DATA_LEN; i++) {
		sd.data[i]=0;
	}
And when you need to update the t value you can do the job in this manner:

	sd.wval.t = temperature;
The MCU serial number is stored here:

	/* Pointer to the MCU 128 bit unique serial number
	*/
	volatile uint32_t *ser_ptr1 = (volatile uint32_t *)0x0080A00C;
	volatile uint32_t *ser_ptr2 = (volatile uint32_t *)0x0080A040;
	/* **************************************************************** */
We can load the numbers into an array for our purpose:

	/* MCU ID */
	SerialNumber[0]=*ser_ptr1;
	SerialNumber[1]=*ser_ptr2++;
	SerialNumber[2]=*ser_ptr2++;
	SerialNumber[3]=*ser_ptr2++;
And load the values in our structure for sending:

	sd.wval.SerialNumber[0]=SerialNumber[0];
	sd.wval.SerialNumber[1]=SerialNumber[1];
	sd.wval.SerialNumber[2]=SerialNumber[2];
	sd.wval.SerialNumber[3]=SerialNumber[3];
At this point you want to send you data. The correct sequence is:
	
	/* 1. Write the TX preamble. */
	YARM_WriteTxPreamble( YARM_WriteTxPreambleBuffer_LEN, &TxPreambleBuffer[0]);
	if ( ConsoleOn) PrintSysError("YARM_WriteTxPreamble");
	delay_us( 100);
	
	/* 2. Write the data in to the TX fifo. */
	YARM_WriteTxFifo( txLength, sd.data);
	if ( ConsoleOn) PrintSysError("YARM_WriteTxFifo");
	delay_ms(100);

	/* 3. Configure the radio in TX mode, set the channel. */
	YARM_SetSystemMode( YARM_RF_TXMODE, WEATHER_TX_CHNL);
	if ( ConsoleOn) PrintSysError("YARM_SetSystemMode TXMode");
	delay_ms(100);
    
The function:

	YARM_WriteTxFifo( txLength, sd.data);
it's called with two parameters: the length of the data to transmit and the pointer to the data.

Using the AES module to reduce the Atmel UID

Using the AES module to reduce the Atmel UID

The 128 bit long MCU UID is too big to be sent every time. But we need this ID to uniquely identify our YARM module. A solution I found is to use the AES module as cryptographic hashing function, to hash the MCU UID. Taking only 32bit of the result for maintain a good uniqueness.

#include "aes.h"
#include "UIDGen_lib.h"

//
#define AES_BUF_SIZE 4
static uint32_t AES_output_data[AES_BUF_SIZE];
/** Cipher 128 bits key array. */
const uint32_t key128[4] = {
	0xC1A0C1A0,
	0xC1A0C1A0,
	0xC1A0C1A0,
	0xC1A0C1A0
};
/* AES configuration */
struct aes_config g_aes_cfg;
/* AES instance*/
struct aes_module aes_instance;

The AES module is used as criptographic engine, to use it we must set a 128bit long secret KEY.

/**
 * \brief This function receive the 128 bit long UID and return a 32bit version.
 * 
 * \param[in]  long_uid the pointer to the 128bit UID
 * \param[out] short_uid the pointer to the 32bit short UID
 * \return     0 for OK
*/
uint32_t UID_Gen( uint32_t*long_uid, uint32_t*short_uid)
{
	aes_get_config_defaults(&g_aes_cfg);
	aes_init(&aes_instance,AES, &g_aes_cfg);
	aes_enable(&aes_instance);

	/* Configure the AES. */
	g_aes_cfg.encrypt_mode = AES_ENCRYPTION;
	g_aes_cfg.key_size = AES_KEY_SIZE_128;
	g_aes_cfg.start_mode = AES_AUTO_START;
	g_aes_cfg.opmode = AES_ECB_MODE;
	g_aes_cfg.cfb_size = AES_CFB_SIZE_128;
	g_aes_cfg.lod = false;
	aes_set_config( &aes_instance,AES, &g_aes_cfg);

	/* Set the cryptographic key. */
	aes_write_key( &aes_instance, key128);

	/* The initialization vector is not used by the ECB cipher mode. */

	aes_set_new_message( &aes_instance);
	/* Write the data to be ciphered to the input data registers. */
	aes_write_input_data( &aes_instance, long_uid);
	aes_clear_new_message( &aes_instance);
	/* Wait for the end of the encryption process. */
	while (!(aes_get_status( &aes_instance) & AES_ENCRYPTION_COMPLETE)) {
	}
	aes_read_output_data( &aes_instance, AES_output_data);
    
	*short_uid=AES_output_data[2];
	
	return 0;
}
The uint32_t UID_Gen( uint32_t*long_uid, uint32_t*short_uid) function take a pointer to the 128bit uid and return a pointer to a 32bit UID. The AES module generate a 128 bit encripted array, but the function return only 32bit of it.
To use this function insert inside the main:

// Pointer to UID
volatile uint32_t *ser_ptr1 = (volatile uint32_t *)0x0080A00C;
volatile uint32_t *ser_ptr2 = (volatile uint32_t *)0x0080A040;
uint32_t SerialNumber[4];
uint32_t ShortUID;

int main( void)
{
	....

	/* MCU ID */
	SerialNumber[0]=*ser_ptr1;
	SerialNumber[1]=*ser_ptr2++;
	SerialNumber[2]=*ser_ptr2++;
	SerialNumber[3]=*ser_ptr2++;
	
	UID_Gen( SerialNumber, &ShortUID);
	....
}
After this call, you can use the new UID returned in ShortUID to identify the YARM module.
To use the AES module, you must include it on the project. Open the ASF menu and select "ASF Wizard". From "Available Modules" select "AES - Advanced Encryption Standard Module (driver) [polled]"