FN-M16P MP3 player (Part 2)

Part 1, 2

As seen in the previous post, the MCU (e.g. arduino) and the module are talking each other through a serial communication (UART) at a quite modest rate of 9600 baud. Data is exchanged as packets of 10 bytes according to the following structure:

Byte#	Function		Value	Comments
0	Start Byte		0x7E	constant
1	Version Info		0xFF	my module returns 8, any input value will work
2	Number of bytes		0x06	constant
3	Command 		0xXX	See datasheet
4	Command feedback	0xXX	0x00 or 0x01
5	Parameter		0xXX	parameter MSB, see datasheet
6	Parameter		0xXX	parameter LSB, see datasheet
7	Checksum		0xXX	checksum MSB
8	Checksum		0xXX	checksum LSB
9	End command		0xEF	constant

The checksum is computed over the 6 bits proceeding it. Computing the checksum is fairly easy: sum the 6 bytes and negate them. Then map the Most Significant and the Least Significant Bytes (MSB & LSB).

16 bits can be used for the parameter settings. Various cases happen:

  • Some commands do not require a parameter, then enter any byte value
  • Some commands require an 8 bit parameter, then enter this parameter as the LSB and enter any value for the LSB
  • Some commands require two 8 bit parameters, then enter each parameter as the LSB and the LSB
  • Some commands require a 16 bits parameter, then map the MSB ((value >> 8) & OxFF) and LSB (value & OxFF) from the parameter and enter then as the parameter MSB and LSB
  • One command requires a 4 bits parameter and a 12 bits parameter, then apply a 4 bits shift to the first parameter and map them to the MSB, the 4 next bits are shifted from the second parameter and the remaining bits are mapped to the LSB.

The command feedback is optional and may be necessary in harsh environments. However, the risk is low that the UART will fail to exchange data at 9600 bauds so that I did not implement this option in my code. Surprisingly, the checksum is not mandatory and the checksum bytes might be omitted, leading to 8 bytes long frames. However, I maintained this minimal integrity protection level in the data  communication.

Next is a link to a spreadsheet that I used in my early testing which you may find helpful for debugging. >Link<

While most commands do not echo data from the device, some do. This is the case for the commands echoing the current sound level, the number of tracks on device, etc. Data is transmitted back once the full incoming frame is read and decoded by the device (half duplex transmission). Errors are reported as command 0x40.

The library that I built uses general purpose functions that relate to the “sound tracks reader” mode and to the “sound tracks manager” mode. In its early version, it contains a light management of errors.

Next is a copy of the command launcher function

/*
Execute command using its parameters
The returned value is passed by reference and the function returns error codes (DFP_ERR_NONE=0=no error)
*/
uint8_t PlainDFP::execCmd(uint8_t CMD, uint8_t paramMSB, uint8_t paramLSB, uint16_t *retValue) 
{
	uint8_t error = DFP_ERR_NONE;
	uint8_t vBuffer[10];
	/* Build the command line */
	vBuffer[0] = 0x7E; 					/* Start byte (constant) */
	vBuffer[1] = 0xFF; 					/* Version byte: any value */
	vBuffer[2] = 0x06; 					/* Data length (checksum not included) always 6 */
	vBuffer[3] = CMD;  					/* Actual command */
	vBuffer[4] = 0x00; 					/* Feedback request: 0=no, 1=yes */
	vBuffer[5] = paramMSB; 				/* Parameter MSB */
	vBuffer[6] = paramLSB; 				/* Parameter LSB */
	vBuffer[7] = 0xFF; 					/* Check sum MSB */
	vBuffer[8] = 0xFF; 					/* Check sum LSB */
	vBuffer[9] = 0xEF; 					/* End byte (constant) */
	/* Compute and record checksum */
	int16_t checksum = checkSum(vBuffer);
	vBuffer[7] = ((checksum >> 8) & 0xFF); 		/* Check sum MSB */
	vBuffer[8] = (checksum & 0xFF); 			/* Check sum LSB */
	/* Send the command line to the module */
	for (uint8_t i = 0; i < 10; i++)
	{
		_serial->write(vBuffer[i]);
	}
	/* If the command is supposed to echo a feed back */
	if (CMD > 0x20)
	{
		delay(100);
		if (_serial->available() >= 10) 
		{
			/* Record answer in buffer */
			for (uint8_t i = 0; i < 10; i++)
			{
				vBuffer[i] = _serial->read();
			}
			/* Flush exta bytes from receive buffer */
			while (_serial->available())
			{
				_serial->read();
			}
			/* Compute checksum from receive buffer and compare it to the computed checksum */
			checksum = checkSum(vBuffer);
			if (checksum == ((vBuffer[7]  << 8) || vBuffer[8]))
			{
				*retValue = ((vBuffer[5] << 8) || vBuffer[6]);
			}
			else
			{
				error = DFP_ERR_INV_CHK_SUM; 
			}
			/* Device returns an error data with this command */
			if (vBuffer[3] == 0x40)
			{
				error = DFP_ERR_DEV_ERROR;
			}
		}
		else
		{
			error = DFP_ERR_MIS_ANSWER; 
		}
	}
	return(error);
}

And next is the checksum function

/*
Compute check sum from a 10 bytes long buffer
*/
int16_t PlainDFP::checkSum(uint8_t *vBuffer)
{
	int16_t result = 0;
	for (uint8_t i = 1; i < 7; i++)
	{
		result += vBuffer[i];
	}
	result *= -1;
	return(result);
}

 

 

Leave a Reply

You must be logged in to post a comment.