ATSHA204 Device Library

From this link you can get the datasheet of the device, you will need it while reading this post.

Things to mention in this post:
* Atsha204 Device

In this post, we are writing Atsha204 Ilibrary using HAL I2C library.

About Atsha204

As mentioned in the datasheet ;

-The Atsha204 device have 2 types of communication interfaces. They are I2C and One-Wire.
-Atsha204 have 4.5Kb EEPROM size but the Data zone have 4Kb of it rest of it belongs to the Configuration Zone and One time Programmable area.
-Do not forget that device type is 16-bit.

First of all , the device needed to be configured before using the Data Zone.

So Let’s start with the Configuration Zone Explanation;

-The device have the device specific serial number for the user definition of the applications. First 2 bytes of the serial number is the model and other 6 bytes of the serial number is device specific
-The addresses of the configs given in the figure which is taken from the datasheet.
-The important part of the configuration zone is the Slot Config areas to the using the Data and OTP Zones.
-The Device comes from the product line with default configurations, which are showing in the figure above.

Let’s Continue with the Slot Configurations:

-The Slot Configs are given in the figure according to the bit number.
-Different configurations gives us to the different operations, In this tutorial we just use the Data Zone so we need to configure all of them with the value 0x00. If we want to write encrypted, it is enough to set just WriteKey. But we do not using it right now.

-After the Slot Configurations we need to check Lock Data and Lock Config areas because they are giving us to the access of the Data and OTP zone to write and read operations.

-For Lock Data and Lock Config fields 0x55 means unlocked state. To writing in to the Data Zone first of all, we need to configure the Configuration Zone with the Slot Configuration and Lock the Lock Config field. Do not forget that it is important that Configuration in the Every part of Slot Configuration areas represents the Every Data Zone areas seperatly and make sure to write to config values well.

-After Locking to LockConfig field of the Configuration Zone, you can write in to the Data Zone but you can not read it. Once you Lock to LockData field you will achieve the read access as well.

  • DO NOT FORGET THAT, ONCE YOU LOCK THE DEVICE YOU CAN NOT UNLOCK IT. BE CAREFUL FOR THAT.

If you get the logic behind the stage, Let’s go into the frame packets to communicate with the device;

To Communicate with the device, we need to prepare our packet format , device have the specific type of the packet frame.

Byte 0# : The first byte of the frame represents the Command Flag which is constant value.

Byte 1# : The second byte of the frame represents the size of the packet which are only Count, Param1, Param2, Data and CRC values.

Byte 2# : The third byte of the frame represents the OpCode which can be read, write, reset, or something else with the device. You can see the OpCodes in the below figure :

Byte 3,4,5 # : This bytes related to the functions they can be changed.

Byte N-1,N # : This bytes are represents CRC values of the packet. CRC value must be 2 byte.

We all settle now ,

Let’s Code

The First function of the code is the CRC function which is important for us to packet frame while communicating with the device.

/**
  * @func	ATSHA204_Calculate_crc
  *	@brief	Calculate CRC
  *	@param	length					: Length of the Command_t struct without crc2 and function command
  *	@param	data					: The data pointer that crc will be calculated
  *	@param	crc						: CRC bytes pointer
  * @retval	none
  */
static void ATSHA204_Calculate_crc(uint8_t length, uint8_t *data, uint8_t *crc)
{
	uint8_t counter;
	uint16_t crc_register = 0;
	uint16_t polynom = 0x8005;
	uint8_t shift_register;
	uint8_t data_bit, crc_bit;

	for (counter = 0; counter < length; counter++)
	{
		for (shift_register = 0x01; shift_register > 0x00; shift_register <<= 1)
		{
			data_bit = (data[counter] & shift_register) ? 1 : 0;
			crc_bit = crc_register >> 15;
			crc_register <<= 1;
			if (data_bit != crc_bit)
				crc_register ^= polynom;
		}
	}
	crc[0] = (uint8_t)(crc_register & 0x00FF);
	crc[1] = (uint8_t)(crc_register >> 8);
}

The Second function of the Library is the function that execute the command functions which are related to the OpCodes. We only use Lock, Read and Write commands for this lib.

/**
  * @func	ATSHA204_Commands
  *	@brief	Determines which operations to be performed according to the command type.
  *	@param	pATSHA204a				: ATSHA204 device pointer
  *	@param	COMMAND_FUNC			: Specifies the command type
  * @retval	status 					: Return ATSHA204 status.
  */
static ATSHA204_StatusTypeDef ATSHA204_Commands(ATSHA204_Device *pATSHA204a, ATSHA204_Commands_te COMMAND_FUNC) {
	ATSHA204_StatusTypeDef status = ATSHA204_OK;
	command.functionCommand_u8 = ATSHA204_COMMAND;

	switch(COMMAND_FUNC) {

		case COMMAND_DERIVEKEY:
		break;

		case COMMAND_DEVREV:
		break;

		case COMMAND_GENDIG:
		break;

		case COMMAND_HMAC:
		break;

		case COMMAND_CHECKMAC:
		break;

		case COMMAND_LOCK:
			command.count_u8				= 7 + sizeof(command.pData);
			command.opcode_u8				= COMMAND_LOCK;
			command.param1.bits._bit0_1  	= (atsha204_variable.zone == 0) ? ZONE_LOCK_CONFIG : ZONE_LOCK_DATA_OTP;
			command.param1.bits._bit2		= 0;
			command.param1.bits._bit3		= 0;
			command.param1.bits._bit4		= 0;
			command.param1.bits._bit5		= 0;
			command.param1.bits._bit6		= 0;
			command.param1.bits._bit7		= 1;
			command.param2_u16				= 0;
			ATSHA204_Calculate_crc(command.count_u8-2, (uint8_t*)&command.count_u8, (uint8_t*)&command.crcl);
			status = ATSHA204_SendI2C_Command(pATSHA204a, (uint8_t*)&command, sizeof(command));
		break;

		case COMMAND_MAC:
		break;

		case COMMAND_NONCE:
		break;

		case COMMAND_PAUSE:
		break;

		case COMMAND_RANDOM:
		break;

		case COMMAND_READ:
			command.count_u8				= 7 + sizeof(command.pData);
			command.opcode_u8				= COMMAND_READ;
			command.param1.bits._bit0_1  	= atsha204_variable.zone;
			command.param1.bits._bit2		= 0;
			command.param1.bits._bit3		= 0;
			command.param1.bits._bit4		= 0;
			command.param1.bits._bit5		= 0;
			command.param1.bits._bit6		= 0;
			command.param1.bits._bit7		= (atsha204_variable.byte == 0) ? 0 : 1;
			command.param2_u16				= atsha204_variable.address;
			ATSHA204_Calculate_crc(command.count_u8-2, (uint8_t*)&command.count_u8, (uint8_t*)&command.crcl);
			status = ATSHA204_SendI2C_Command(pATSHA204a, (uint8_t*)&command, sizeof(command));
		break;

		case COMMAND_UPDATEEXTRA:
		break;

		case COMMAND_WRITE:
			command.count_u8 				= 7 + sizeof(command.pData);;
			command.opcode_u8 				= COMMAND_WRITE;
			command.param1.bits._bit0_1  	= atsha204_variable.zone;
			command.param1.bits._bit2		= 0;
			command.param1.bits._bit3		= 0;
			command.param1.bits._bit4		= 0;
			command.param1.bits._bit5		= 0;
			command.param1.bits._bit6		= 0;
			command.param1.bits._bit7		= (atsha204_variable.byte == 0) ? 0 : 1;
			command.param2_u16				= atsha204_variable.address;
			uint8_t paramSize = (atsha204_variable.byte == 0) ? 4 : 32;
			for(uint32_t i = 0; i < paramSize; i++)	{
				command.pData[i] = *(atsha204_variable.pData+i);
			}
			ATSHA204_Calculate_crc(command.count_u8-2, (uint8_t*)&command.count_u8, (uint8_t*)&command.crcl);
			status = ATSHA204_SendI2C_Command(pATSHA204a, (uint8_t*)&command, sizeof(command));
		break;

		default:
		return ATSHA204_ERROR;

	}
	memset(&command.functionCommand_u8,0,12);
	return status;
}

The device gives us several features with the commands as well as the commands there are other features of the device like reset, sleep, ıdle, command and wake up. This functions represents the types of features.

/**
  * @func	ATSHA204_LL
  *	@brief	Read data from flash memory
  *	@param	MODE					: Specifies the ATSHA204 device mode.
  *	@param	pATSHA204a				: ATSHA204 device pointer
  *	@param	COMMAND_FUNC			: Specifies the command type
  * @retval	status 					: Return ATSHA204 status.
  */
static ATSHA204_StatusTypeDef ATSHA204_LL(ATSHA204_Mode_te MODE, ATSHA204_Device *pATSHA204a, ATSHA204_Commands_te COMMAND_FUNC) {
	ATSHA204_StatusTypeDef status = ATSHA204_ERROR;
	uint8_t wakeupData[40] = {0};
	switch(MODE) {

		case ATSHA204_RESET:
		break;

		case ATSHA204_SLEEP:
			status = (ATSHA204_StatusTypeDef)HAL_I2C_Master_Transmit(pATSHA204a->hi2c, pATSHA204a->DevAddress, (uint8_t*)ATSHA204_SLEEP, 1, 1000);
			HAL_Delay(5);
			return(status);
		break;

		case ATSHA204_IDLE:
		break;

		case ATSHA204_COMMAND:
			return (ATSHA204_StatusTypeDef)ATSHA204_Commands(pATSHA204a, COMMAND_FUNC);
		break;

		case ATSHA204_WAKE:
			status = (ATSHA204_StatusTypeDef)HAL_I2C_Master_Transmit(pATSHA204a->hi2c, pATSHA204a->DevAddress, wakeupData, sizeof(wakeupData), 1000);
			HAL_Delay(5); // waiting until the tWHI completed. It is enough to more than 2.5ms.
			status = (ATSHA204_StatusTypeDef)HAL_I2C_Master_Receive(pATSHA204a->hi2c, pATSHA204a->DevAddress, pATSHA204a->wake_data, sizeof(pATSHA204a->wake_data), 1000);
			return status;
		break;

		default:
		return status;
	}
	return status;
}

I2C Tx and Rx function which is using for the sending packets.

/**
  * @func	ATSHA204_SendI2C_Command
  *	@brief	Read data from flash memory
  *	@param	*pATSHA204a				: ATSHA204 Device pointer
  *	@param	*packet					: Transmitting data packet pointer
  *	@param	packetSize				: Transmitting data packet size
  * @retval	status 					: Returns ATSHA204 status
  */
	ATSHA204_StatusTypeDef status = ATSHA204_ERROR;
static ATSHA204_StatusTypeDef ATSHA204_SendI2C_Command(ATSHA204_Device *pATSHA204a, uint8_t *packet, uint32_t packetSize) {
	ATSHA204_LL(ATSHA204_WAKE, pATSHA204a, 0);
	status = (ATSHA204_StatusTypeDef)HAL_I2C_Master_Transmit(pATSHA204a->hi2c, pATSHA204a->DevAddress, packet, packetSize, 1000);
	HAL_Delay(5);
	status = (ATSHA204_StatusTypeDef)HAL_I2C_Master_Receive(pATSHA204a->hi2c, pATSHA204a->DevAddress, pATSHA204a->readBuffer, packetSize, 1000);
	ATSHA204_LL(ATSHA204_SLEEP, pATSHA204a, 0);
	return status;
}

I2C Init function to handle the parameters via struct for reducing the parameter count.

/**
  * @func	ATSHA204_Init
  *	@brief	Initialize ATSHA204 device parameter.
  *	@param	me						: ATSHA204 device pointer
  *	@param	hi2c					: Specifies ATSHA204 device I2C interface.
  *	@param	devaddress				: Represent ATSHA204 device address
  *	@param	timeout					: Timeout value of the SHA204 device.
  * @retval	none
  */
void ATSHA204_Init(ATSHA204_Device *me, I2C_HandleTypeDef *hi2c, uint16_t devaddress, uint16_t timeout) {
	me->hi2c 			= hi2c;
	me->DevAddress 		= devaddress;
	me->Timeout			= timeout;
}

To Lock the LockData and LockConfig fields in the configuration zone.

/**
  * @func	ATSHA204_Lock
  *	@brief	Write either LockConfig or LockData to 0x00, thereby changing the permissions in the designation zone.
  *			Thia command fails if the designated zone is already locked.
  *	@param	me						: ATSHA204 device pointer
  *	@param	zone					: specifies zones types as Data zone, Configuration zone or OTP zone
  * @retval	none
  */
ATSHA204_StatusTypeDef ATSHA204_Lock(ATSHA204_Device *me, ATSHA204_Zone_te zone) {
	atsha204_variable.zone		= zone;
	return (ATSHA204_StatusTypeDef)ATSHA204_LL(ATSHA204_COMMAND, me, COMMAND_LOCK);
}

Read OpCode function.

/**
  * @func	ATSHA204_Read
  *	@brief	Read word from one of the memory zones of the device
  *	@param	me						: ATSHA204 device pointer
  *	@param	zone					: Specifies zones types as Data zone, Configuration zone or OTP zone
  * @param	address					: specifies number of bits to read
  * @param	address					: specifies number of bits to read
  * @retval	none
  */
ATSHA204_StatusTypeDef ATSHA204_Read(ATSHA204_Device *me, ATSHA204_Zone_te zone, uint16_t address, uint16_t size) {
	atsha204_variable.address 		= address;
	atsha204_variable.zone			= zone;
	atsha204_variable.size			= size;
	ATSHA204_StatusTypeDef status 	= ATSHA204_ERROR;
	atsha204_variable.byte			= 0;
	uint16_t counter = 0;
	uint8_t readBytes = 4;

	while(size > 3) {
		atsha204_variable.byte 	= (size >= 32)	?	1	:	0;
		readBytes 				= (size >= 32)	?	32	:	4;
		status = (ATSHA204_StatusTypeDef)ATSHA204_LL(ATSHA204_WAKE, me, 0);
		status = (ATSHA204_StatusTypeDef)ATSHA204_LL(ATSHA204_COMMAND ,me, COMMAND_READ);
		status = (ATSHA204_StatusTypeDef)ATSHA204_LL(ATSHA204_SLEEP, me, 0);
		if(zone == CONFIG_ZONE) {
			for(uint8_t k = 0; k < readBytes; k++)	{
				me->config_zone_data[k+counter] = me->readBuffer[k+1];
			}
			counter += (size >= 32)	?	32	:	4;
		} else  {
			for(uint8_t k = 0; k < readBytes; k++)	{
				me->slotData[k+counter] = me->readBuffer[k+1];
			}
			counter += (size >= 32)	?	32	:	4;
		}
		atsha204_variable.address 		+= (size >= 32) ? 	8 	: 	1;
		size						 	-= (size >= 32) ? 	32 	: 	4;
	}
	return status;
}

Write OpCode function.

/**
  * @func	ATSHA204_Write
  *	@brief	Read data from flash memory
  *	@param	me						: ATSHA204 device pointer
  *	@param	zone					: Specifies zones types as Data zone, Configuration zone or OTP zone
  *	@param	address					: Address of first word to be written within the zone.
  *	@param	pData					: Data pointer to be written within the zone
  *	@param	size					: Data pointer size
  * @retval	none
  */
ATSHA204_StatusTypeDef ATSHA204_Write(ATSHA204_Device *me, ATSHA204_Zone_te zone, uint16_t address, uint8_t *pData, uint16_t size) {
	ATSHA204_StatusTypeDef status 	= ATSHA204_ERROR;
	atsha204_variable.address 		= address;
	atsha204_variable.size			= size;
	atsha204_variable.zone			= zone;
	atsha204_variable.pData			= pData;
	atsha204_variable.byte			= 0;

	while(size > 3)	{
		atsha204_variable.byte		=	(size >= 32) ? 1	:	0;
		status = (ATSHA204_StatusTypeDef)ATSHA204_LL(ATSHA204_COMMAND, me, COMMAND_WRITE);
		atsha204_variable.address 	+= 	(size >= 32) ? 8 	: 	1;
		atsha204_variable.pData		+= 	(size >= 32) ? 32 	: 	4;
		size 						-= 	(size >= 32) ? 32	:	4;
	}
	return status;
}

For getting the configuration zone parameters you can use this function.

/**
  * @func	ATSHA204_GetConfig
  *	@brief	Read Configuration Datas from devices
  *	@param	me						: ATSHA204 device pointer
  * @retval	status					: Returns ATSHA204 status
  */
ATSHA204_StatusTypeDef ATSHA204_GetConfig(ATSHA204_Device *me) {
	ATSHA204_StatusTypeDef status = ATSHA204_OK;
	atsha204_variable.byte		= 0;
	status = ATSHA_GetSerialNumber(me);
	status = ATSHA204_Read(me, CONFIG_ZONE, CONFIG_BASE_ADDRESS, CONFIG_SIZE);

	for (uint8_t i = 0; i < 32; i++)
		me->_configData.slotConf[i] = me->config_zone_data[i + 20];

	me->_configData.lockValue 	= me->config_zone_data[86];
	me->_configData.lockConfig 	= me->config_zone_data[87];
	return status;
}

For getting the serial number you can use this.

/**
  * @func	ATSHA_GetSerialNumber
  *	@brief	Read Serial number from device
  *	@param	me						: ATSHA204 device pointer
  * @retval	status					: Returns ATSHA204 status
  */
ATSHA204_StatusTypeDef ATSHA_GetSerialNumber(ATSHA204_Device *me) {
	ATSHA204_StatusTypeDef status = ATSHA204_ERROR;
	uint8_t counter = 0;
	atsha204_variable.byte		= 0;
	memset(me->readBuffer, 0, MAX_READ_WRITE_SIZE);

	status = (ATSHA204_StatusTypeDef)ATSHA204_LL(ATSHA204_WAKE, me, 0);
	status = ATSHA204_Read(me, CONFIG_ZONE, 0, 4);
	status = (ATSHA204_StatusTypeDef)ATSHA204_LL(ATSHA204_SLEEP, me, 0);
	for(uint8_t k = 0; k < 4; k++)	{
		me->_configData.serialNum[k+counter] = me->readBuffer[k+1];
	}
	counter += 4;
	status = (ATSHA204_StatusTypeDef)ATSHA204_LL(ATSHA204_WAKE, me, 0);
	status = ATSHA204_Read(me, CONFIG_ZONE, 2, 4);
	status = (ATSHA204_StatusTypeDef)ATSHA204_LL(ATSHA204_SLEEP, me, 0);
	for(uint8_t k = 0; k < 4; k++)	{
		me->_configData.serialNum[k+counter] = me->readBuffer[k+1];
	}
	return status;
}

So, Thats all for this post. Any questions are welcome.

You can check the GitHub Link,

Click Here For GitHub Link,

Leave a Reply

Your email address will not be published.