采用TCP協(xié)議實現(xiàn)PIC18F97J60 ethernet bootloader
TCP/IP Stack
Microchip TCP/IP Stack是免費的,廣泛應(yīng)用于PIC單片機中。由于有遠程更新程序的需求,我決定開發(fā)基于TCP協(xié)議的ethernet bootloader, 主要使用了Microchip TCP/IP Stack的TCP模塊。最終我開發(fā)出來的ethernet bootloader 在PIC18F97J60上驗證通過。整個實現(xiàn)上分兩部分,一部分是單片機端的基于TCP協(xié)議的bootloader程序,我將其命名為PhnBoot_v2.0, 另外一部分是同樣基于TCP協(xié)議與單片機互動的PC端通信程序,我將其命名為PhnLoader_v2.0。我還定義了PhnBoot_v2.0和PhnLoader_v2.0之間傳輸數(shù)據(jù)的通信協(xié)定。下面將細說我是如何實現(xiàn)的。
通信協(xié)定
單片機端PhnBoot_v2.0和PC端PhnLoader_v2.0之間的通信數(shù)據(jù)包采用以下協(xié)定
定義如下:
STX - Start of packet indicator
ETX - End of packet indicator
LEN - The length of true data
DATA - General data 16 bytes, only first LEN of datas are true
CMD - Base command
ADDR - Address up to 24 bits ( ADDRL , ADDRH , ADDRH)
具體有以下Base command:
RD-VER: 0x00 -- Read Version Information (最終版本刪除了此命令)
RD_MEM: 0x01 -- Read Program Memory (最終版本刪除了此命令)
ER_MEM: 0x03 -- Erase Program Memory
WR_MEM: 0x02 -- Write Program Memory
WR_CFG: 0x04 -- Write Configuration Registers
PhnLoader_v2.0 功能
定義好了通訊協(xié)定, 接著就按照協(xié)定去實現(xiàn)PhnLoader_v2.0。 PhnLoader_v2.0的具體功能包括選擇IP地址,端口和協(xié)議類型, 目前只支持TCP協(xié)議, 創(chuàng)建TCP服務(wù)器,加載應(yīng)用程序Hex文件,Parse 應(yīng)用程序的Hex文件,一行一行解讀Hex文件,一旦收到連接請求,建立TCP連接,一旦收到應(yīng)用程序更新請求,立刻按照通訊協(xié)定采用TCP協(xié)議發(fā)送Hex記錄到單片機,接收單片機發(fā)送回來的Response,發(fā)送完畢后斷開TCP連接,發(fā)送期間出現(xiàn)問題就立馬結(jié)束發(fā)送。
PhnLoader_v2.0 主要代碼段
PhnLoader_v2.0是用C#實現(xiàn)的,是我在利用空余時間自學(xué)C#后寫的,上面提到的功能都實現(xiàn)了。
private void btnDownload_Click(object sender, EventArgs e)
{
btnDownload.Enabled = false;
pBarLoading.Visible = false;
if (!this.connect())
{
Debug.WriteLine("Udp server building unsuccessfully");
textBoxStatus.ForeColor = Color.Red;
textBoxStatus.AppendText("Udp server building unsuccessfullyrn");
textBoxStatus.ForeColor = Color.Black;
btnDownload.Enabled = true;
return;
}
try
{
loaderReader = new StreamReader(textBoxFile.Text);
}
catch (Exception ex)
{
Debug.WriteLine("Error: " + ex.Message);
textBoxStatus.ForeColor = Color.Red;
textBoxStatus.AppendText("Read hex file unsuccessfullyrn");
textBoxStatus.ForeColor = Color.Black;
loaderReader.Close();
loaderServer.Close();
btnDownload.Enabled = true;
return;
}
loaderFrame = new SerialFrame();
DateTime startTime = DateTime.Now;
IPEndPoint clientPoint = new IPEndPoint(IPAddress.Any, 0);
if (!loaderServer.Read(readyMsg,timeSpan))
{
Debug.WriteLine("Error: Timeout receive ready message from bootloader");
textBoxStatus.ForeColor = Color.Red;
textBoxStatus.AppendText("Timeout receive ready message from bootloaderrn");
textBoxStatus.ForeColor = Color.Black;
loaderServer.Close();
loaderReader.Close();
btnDownload.Enabled = true;
return;
}
if (!erase())
{
textBoxStatus.ForeColor = Color.Red;
textBoxStatus.AppendText("Erase unsuccessfullyrn");
textBoxStatus.ForeColor = Color.Black;
loaderReader.Close();
loaderServer.Close();
btnDownload.Enabled = true;
return;
}
pBarLoading.Refresh();
pBarLoading.Visible = true;
pBarLoading.Value = 0;
pBarLoading.Maximum = loaderLines;
pBarLoading.Step = 1;
string recordLine;
Address_U = 0;
bool isNextLineUserID = false;
bool isNextLineConfigBits = false;
textBoxStatus.AppendText("rnDownloading hex file ...rn");
try
{
while (loaderReader.Peek() >= 0)
{
pBarLoading.PerformStep();
recordLine = loaderReader.ReadLine();
if (recordLine.Contains(EXTEND_TOKEN) == true)
{
if (recordLine.Contains(USER_ID_TOKEN) == true)
{
isNextLineUserID = true;
continue;
}
else if (recordLine.Contains(CONFIG_BITS_TOKEN) == true)
{
const int ADDR_U_START_INDEX = 9;
const int ADDR_U_LENGTH = 4;
string addrU = recordLine.Substring(ADDR_U_START_INDEX, ADDR_U_LENGTH);
Address_U = Convert.ToInt32(addrU, 16) << 16;
isNextLineConfigBits = true;
continue;
}
else
{
const int ADDR_U_START_INDEX = 9;
const int ADDR_U_LENGTH = 4;
string addrU = recordLine.Substring(ADDR_U_START_INDEX, ADDR_U_LENGTH);
Address_U = Convert.ToInt32(addrU, 16) << 16;