I built a firmware updater app for a device and the host app connects to the device via a serial connection. The app has been working on small apps (~18KB) and I recently upped the firmware size to ~200KB.
Now the host app (C#) hangs on a serial port write and pausing the program in debug shows a Debugger Break.
The code pictured below reads a .HEX file line by line (not pictured) and writes blocks of 256 bytes one byte at a time using UART (serial comm). After 256 bytes, a checksum is sent for the block, and the loop is left to set up the next transfer.
What could be the reason for the app to hang on the write command? Is there some port or buffer maintenance that has to be done between blocks? I can see the byte counter for the block (0 to 255) and it doesn't hang on the same byte number.
This app is connecting to an ARM processor on a STM32F417IGT dev board.
Thanks for your help!
Complete write function, in code:
public void WriteNewAppToFlash(SerialPort _serialPort)
{
int byte_read = 0;
long checksum = 0;
var ff = new byte[] { 0xFF };
// ------------------------------------------------------------------------------
// -------- WRITE MEMORY --------------------------------------------------------
// ------------------------------------------------------------------------------
// for Address
int baseAddress = 0x08004000;
int offset = 0;
// for string from HEX file
string line;
int length;
int type;
int hexChecksum = 0;
bool sendAddress = true;
int counter = 0; // Counting the number of lines in the file
int byteCounter = 0; // Counting nmumber of bytes in the current block
// Read the file and process one line at a time
System.IO.StreamReader file = new System.IO.StreamReader(path);
while ((line = file.ReadLine()) != null)
{
if (sendAddress == true)
{
/*
-------------------------------------------------------------------------------------------------------
SEND WRITE COMMAND
-----------------------------------------------------------------------------------------------------*/
// Send 0x43 (erase memory) and 0xBC
var writeMem = new byte[] { 0x31 };
var ce = new byte[] { 0xCE };
_serialPort.Write(writeMem, 0, 1);
//Console.WriteLine("writeMem = 0x" + BitConverter.ToInt32(writeMem, 0).ToString("X"));
_serialPort.Write(ce, 0, 1);
//Console.WriteLine("CE = 0x" + BitConverter.ToInt32(writeMem, 0).ToString("X"));
// Receive ACK byte
byte_read = _serialPort.ReadByte();
//Console.WriteLine("ACK = 0x" + byte_read.ToString("X"));
//Console.WriteLine("");
if (byte_read == NACK)
{
//Console.WriteLine("NACK received for WRITE MEMORY start");
//Console.WriteLine("");
}
// -- end SEND 0x31 and 0xCE and wait for ACK -----------------------------------------
-------------------------------------------------------------------------------------------------------
SEND CURRENT ADDRESS AND CHECKSUM TO FLASH MEMORY
-----------------------------------------------------------------------------------------------------*/
Byte[] currentAddr = BitConverter.GetBytes(baseAddress + offset);
// Increment offset by 0x100 (256 bytes)
offset = offset + 0x00000100;
//int msb;
// Reset Checksum and XOR address
checksum = 0;
foreach (byte b in currentAddr)
{
checksum ^= b;
}
//Console.WriteLine("cksum = " + checksum);
Byte[] cksum = BitConverter.GetBytes(checksum);
// Send address, MSB first, LSB last
_serialPort.Write(currentAddr, 3, 1);
_serialPort.Write(currentAddr, 2, 1);
_serialPort.Write(currentAddr, 1, 1);
_serialPort.Write(currentAddr, 0, 1);
// Send checksum of address bytes
_serialPort.Write(cksum, 0, 1);
// Receive ACK byte
byte_read = _serialPort.ReadByte();
//Console.WriteLine("ACK = 0x" + byte_read.ToString("X"));
//Console.WriteLine("");
if (byte_read == NACK)
{
//Console.WriteLine("NACK received for WRITE MEMORY address received");
//Console.WriteLine("");
}
// -- end addr or increment ---------------------------------------------------------
sendAddress = false;
// Send number of bytes, always 256, the last group will be padded with 0xFF
_serialPort.Write(ff, 0, 1);
hexChecksum = 0;
} // end IF for WRITE COMMAND and ADDRESS
/*
-------------------------------------------------------------------------------------------------------
WRITE 256 BYTES FROM HEX FILE TO FLASH MEMORY
-----------------------------------------------------------------------------------------------------*/
// FIRST CHARACTER in HEX FILE
// The colon indicates the start of a "record"
// Remove colon from beginning of string
line = line.Substring(1, line.Length - 1);
//Console.WriteLine(line);
// Create byte array from string
var bytes = GetBytesFromByteString(line).ToArray();
// Assign values to variables from byte array
type = bytes[3];
/* Next TWO CHARACTERS in HEX FILE 00-data
are the record type: 01-EOF
02-
03-
04-
05- */
// Check type for data = 00
if (type == 0)
{
// The first two characters represent the number of data bytes for the record
// Obtain length, convert to int (counter for sending data)
length = bytes[0];
for ( int i = 0; i < length; i++ )
{
// Send individual characters to device
_serialPort.Write(bytes, 4 + i, 1);
//Thread.Sleep(100);
// increment counter
byteCounter++;
if( byteCounter % 32 == 0 )
{
Thread.Sleep(40);
}
// Add byte to checksum
hexChecksum ^= bytes[4 + i];
//Console.WriteLine("cum checksum = " + hexChecksum);
// If counter == 256, reset counter, send checksum
if (byteCounter == 256)
{
sendAddress = true;
byteCounter = 0;
//Console.WriteLine("checksum = " + hexChecksum.ToString("X"));
// Convert checksum to a byte value and send
hexChecksum = hexChecksum ^ 0xFF;
byte csByte = Convert.ToByte(hexChecksum);
//Console.WriteLine("CSBYTE = " + Convert.ToInt32(csByte));
Byte[] csByte_arr = BitConverter.GetBytes(csByte);
_serialPort.Write(csByte_arr, 0, 1);
// Receive ACK byte
byte_read = _serialPort.ReadByte();
//Console.WriteLine("ACK = 0x" + byte_read.ToString("X"));
//Console.WriteLine("");
if (byte_read == NACK)
{
//Console.WriteLine("NACK received for write memory DATA received");
//Console.WriteLine("");
}
} // end IF byteCounter == 256
//Console.WriteLine(byteCounter);
} // end FOR loop
}
else if (type == 5)
{
// Address thingy
//Console.WriteLine("Hit address thingy");
//Console.WriteLine("");
}
else if (type == 1) // Check for end of file
{
// End of file
while (byteCounter != 0)
{
// Send individual bytes to device
_serialPort.Write(ff, 0, 1);
// increment counter
byteCounter++;
// Add byte to checksum
hexChecksum ^= 0xFF;
//Console.WriteLine("cum checksum = " + hexChecksum);
if (byteCounter == 256)
{
byteCounter = 0;
hexChecksum = hexChecksum ^ 0xFF;
byte csByte = Convert.ToByte(hexChecksum);
//Console.WriteLine("CSBYTE = " + Convert.ToInt32(csByte));
Byte[] csByte_arr = BitConverter.GetBytes(csByte);
_serialPort.Write(csByte_arr, 0, 1);
// Receive ACK byte
byte_read = _serialPort.ReadByte();
//Console.WriteLine("ACK = 0x" + byte_read.ToString("X"));
//Console.WriteLine("");
if (byte_read == NACK)
{
//Console.WriteLine("NACK received for write memory DATA received");
//Console.WriteLine("");
}
}
}
} // end ELSE if TYPE == 1
counter++;
} // end WHILE loop for loading hex file
file.Close();
//System.Console.WriteLine("There were {0} lines.", counter);
//Console.WriteLine("");
// -- end WRITE MEMORY ------------------------------------------------------
} // end WriteNewAppToFlash
Changed the code to write a block of 256 bytes. It now writes two blocks and will not write the third block. The program executes to the Write line, the byte buffer is full, but it will not initiate the write.
if (byteCounter >= 255)
{
// Convert checksum to a byte value
hexChecksum = hexChecksum ^ 0xFF;
byte csByte = Convert.ToByte(hexChecksum);
Byte[] csByte_arr = BitConverter.GetBytes(csByte);
do
{
// send byte array
_serialPort.Write(buffer256, 0, 256);
Thread.Sleep(70);
// send checksum
_serialPort.Write(csByte_arr, 0, 1);
// Receive ACK byte
byte_read = _serialPort.ReadByte();
Thread.Sleep(100);
if (byte_read == NACK)
{
//Console.WriteLine("NACK received for write memory DATA received");
//Console.WriteLine("");
}
} while (byte_read != ACK);
// Clear buffer, reset byte count, set flag to send write cmd and send new addr
Array.Clear(buffer256, 0, buffer256.Length);
byteCounter = 0;
sendAddress = true;
}
I eventually got this app to work, but the exact reason for it working is unclear. Here is what I looked at and what changes I made:
Switched from transmitting single bytes and then a checksum at the end of 256 bytes, to placing 256 in a buffer and then transmitting all at once, followed by the checksum. This method was failing at first, but turned out to be faster than the previous method.
Changed the order of processing lines of HEX code and checking that a block is ready to send (see code below). The difference is small, but that more than anything seems to have allowed the whole thing to work. Before, it was checking for 256 bytes to send, sending it and then putting the data from a line in the next block of 256. I stepped through the code and didn't see anything that would make it fail, but it must have done something.
Since then the app has worked great and I no longer have any qualms about using the .NET SerialPort class with embedded devices.
// BLOCK WRITE TO MEMORY
if (type == 0)
{
// Length of line is stored at byte 0, in this case 0x10, or 16 bytes of data
length = bytes[0];
// Add data from current line to buffer of 256 bytes
for (int i = 0; i < length; i++)
{
// Stuff all bytes from line into buffer of 256 bytes
buffer256[byteCounter++] = bytes[4 + i];
// Add byte to checksum
hexChecksum ^= bytes[4 + i];
}
// When buffer is full, send block of 256 bytes and checksum, reset variables for next block
if (byteCounter >= 255)
{
// Convert checksum to a byte value
hexChecksum = hexChecksum ^ 0xFF;
byte csByte = Convert.ToByte(hexChecksum);
Byte[] csByte_arr = BitConverter.GetBytes(csByte);
// Send byte array
_serialPort.Write(buffer256, 0, 256);
// For testing
// Console.WriteLine("block number [{0}]", ++blockCount);
//send checksum
_serialPort.Write(csByte_arr, 0, 1);
//Receive ACK byte
byte_read = _serialPort.ReadByte();
Console.WriteLine("block/ACK = [{0}] | {1}", ++blockCount, byte_read);
while (byte_read != ACK)
{
Array.Clear(buffer256, 0, buffer256.Length);
hexChecksum = 0;
lineCount = 0;
// reprocess the previous 16 lines stored in the line buffer
for ( int j = 0; j < 16; j++ )
{
line = lineBuffer[j];
line = line.Substring(1, line.Length - 1);
var bytesLocal = GetBytesFromByteString(line).ToArray();
length = bytesLocal[0];
for (int i = 0; i < length; i++)
{
buffer256[byteCounter++] = bytesLocal[4 + i];
hexChecksum ^= bytesLocal[4 + i];
}
}
// Convert checksum to a byte value
hexChecksum = hexChecksum ^ 0xFF;
byte csByteLocal = Convert.ToByte(hexChecksum);
Byte[] csByte_arrLocal = BitConverter.GetBytes(csByteLocal);
// Send byte array
_serialPort.Write(buffer256, 0, 256);
//send checksum
_serialPort.Write(csByte_arrLocal, 0, 1);
//Receive ACK byte
byte_read = _serialPort.ReadByte();
Console.WriteLine("block/ACK = [{0}] | {1}", ++blockCount, byte_read);
}
// Clear buffer, reset byte count, clear checksum, set flag to send write cmd/send new addr
Array.Clear(buffer256, 0, buffer256.Length);
byteCounter = 0;
hexChecksum = 0;
lineCount = 0;
sendAddress = true;
}
} // end BLOCK WRITE TO MEMORY
User contributions licensed under CC BY-SA 3.0