采用TCP协议实现PIC18F97J60 ethernet bootloader
扫描二维码
随时随地手机看文章
TCP/IP Stack
Microchip TCP/IP Stack是免费的,广泛应用于PIC单片机中。由于有远程更新程序的需求,我决定开发基于TCP协议的ethernet bootloader, 主要使用了Microchip TCP/IP Stack的TCP模块。最终我开发出来的ethernet bootloader 在PIC18F97J60上验证通过。整个实现上分两部分,一部分是单片机端的基于TCP协议的bootloader程序,我将其命名为PhnBoot_v2.0, 另外一部分是同样基于TCP协议与单片机互动的PC端通信程序,我将其命名为PhnLoader_v2.0。我还定义了PhnBoot_v2.0和PhnLoader_v2.0之间传输数据的通信协定。下面将细说我是如何实现的。
通信协定
单片机端PhnBoot_v2.0和PC端PhnLoader_v2.0之间的通信数据包采用以下协定
定义如下:
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 功能
定义好了通讯协定, 接着就按照协定去实现PhnLoader_v2.0。 PhnLoader_v2.0的具体功能包括选择IP地址,端口和协议类型, 目前只支持TCP协议, 创建TCP服务器,加载应用程序Hex文件,Parse 应用程序的Hex文件,一行一行解读Hex文件,一旦收到连接请求,建立TCP连接,一旦收到应用程序更新请求,立刻按照通讯协定采用TCP协议发送Hex记录到单片机,接收单片机发送回来的Response,发送完毕后断开TCP连接,发送期间出现问题就立马结束发送。
PhnLoader_v2.0 主要代码段
PhnLoader_v2.0是用C#实现的,是我在利用空余时间自学C#后写的,上面提到的功能都实现了。
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;