精品!單片機Bootloader上位機開發之HEX轉bin

單片機BootLoader上位機簡介

BootLoader上位機開發語言為C#,選擇uart通信,需要用到幾個常用的串口控件:串口號,波特率,數據位和打開串口按鈕。另外需要一個打開HEX的按鈕,一個保存bin文件的按鈕。打開一個BLE.HEX文件,上位機對hex文件進行解析,然後顯示在窗口,最後也可以保存成bin文件,再來就是一個下載按鈕,點擊按鈕則開始下載程序。

精品!單片機Bootloader上位機開發之HEX轉bin

上位機界面

HEX文件解析工作

點擊打開hex文件按鈕,選擇hex文件,上位機解析hex文件,在hex裡面存儲的是字符型十六進制的碼,其中需要用到一步很重要的轉換,字符型十六進制轉換為十六進制值,即char stringHex[] = “48” 轉為stringHex = 0x48;C#處理這個問題用到轉換做法:如hexString = "FF"; byte value = byte.Parse(hexString, System.Globalization.NumberStyles.HexNumber); , 其中System.Globalization.NumberStyles.HexNumber為固定用法。

精品!單片機Bootloader上位機開發之HEX轉bin

打開hex文件

精品!單片機Bootloader上位機開發之HEX轉bin

保存bin文件

hex與bin文件比較,hex文件會有跳存儲地址情況,則在空的地址填補十六進制 FF。這樣既可生成完整的bin。

精品!單片機Bootloader上位機開發之HEX轉bin

左hex和右bin文件比較

C# 程序部分代碼覽閱

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Linq;

using System.Text;

using System.Windows.Forms;

using Microsoft.Win32.SafeHandles;

using System.Runtime.InteropServices;

using System.IO;

using System.Diagnostics;

using System.IO.Ports;

using System.Threading;

namespace Boot

{

public partial class Bootloader : Form

{

bool ReceiveByte_Busy = false;

bool serialPort_Closing = false;//串口正在關閉標誌

Int32 ReceiveByte_Cnt = 0;//串口操作,接收字節計數

Int32 SendByte_Cnt = 0;//串口操作,發送字節計數

public List<byte> BufferData = new List<byte>();//串口數據數據幀識別緩存空間/<byte>/<byte>

// public FormTM tmfrm = null;//new FormTM();

public Thread thrd = null;

public FileStream writefile = null;

byte[] buffer = new byte[1024 * 1024 * 5]; // 打開文件hex to bin緩存

int bufferAdr; // 打開文件hex to bin緩存指針

public Bootloader()

{

InitializeComponent();

}

private void Form1_Load(object sender, EventArgs e)

{

#region 串口設置初始狀態

try

{

foreach (string com in System.IO.Ports.SerialPort.GetPortNames())//自動獲取串口號名稱

{

this.cmbboxCom.Items.Add(com);

}

cmbboxCom.SelectedIndex = 0;

//默認串口設置顯示

cmbBaudRate.Items.Add("1200");

cmbBaudRate.Items.Add("2400");

cmbBaudRate.Items.Add("4800");

cmbBaudRate.Items.Add("9600");

cmbBaudRate.Items.Add("19200");

cmbBaudRate.Items.Add("38400");

cmbBaudRate.Items.Add("115200");

cmbBaudRate.SelectedIndex = 3;

cmbDataBit.SelectedIndex = 0;

}

catch

{

MessageBox.Show("找不到串口連接!", "Error");

}

#endregion

}

private void btnOpen_Click(object sender, EventArgs e)

{

#region 串口點擊設置

if (!serialPort1.IsOpen)

{

string sComNum = cmbboxCom.Text;

string sBaudRate = cmbBaudRate.Text;

string sDataBit = cmbDataBit.Text;

try

{

serialPort1.PortName = sComNum;

serialPort1.BaudRate = Int32.Parse(sBaudRate);

serialPort1.DataBits = Int32.Parse(sDataBit);

serialPort1.ReadTimeout = 500;

serialPort1.WriteTimeout = 500;

serialPort1.Open();

if (serialPort1.IsOpen)

{

btnOpen.Text = "關閉";

cmbboxCom.Enabled = false;

cmbBaudRate.Enabled = false;

cmbDataBit.Enabled = false;

}

}

catch (Exception ex)

{

MessageBox.Show(ex.Message);

}

}

else

{

try

{

serialPort1.Close();

if (!serialPort1.IsOpen) // 如果已經關閉串口

{

btnOpen.Text = "打開";

cmbboxCom.Enabled = true;

cmbBaudRate.Enabled = true;

cmbDataBit.Enabled = true;

this.cmbboxCom.Items.Clear();

foreach (string com in System.IO.Ports.SerialPort.GetPortNames()) //重新自動獲取串口號名稱

{

this.cmbboxCom.Items.Add(com);

}

}

}

catch { }

}

#endregion

}

private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)

{

if (serialPort_Closing == true)//如果正在關閉,忽略操作,直接返回,儘快的完成串口監聽線程的一次循環

{

return;

}

try

{

ReceiveByte_Busy = true;//串口接收忙碌標誌

int ReceiveNums = serialPort1.BytesToRead;//記錄緩存數量

ReceiveByte_Cnt += ReceiveNums;

this.Invoke((EventHandler)(delegate

{

txtbRcvArea.Text = ReceiveByte_Cnt.ToString("D");

if (chkbHexDisplay.CheckState == CheckState.Checked)

{

byte[] buf = new byte[ReceiveNums];//聲明一個臨時數據組存儲當前串口數據

serialPort1.Read(buf, 0, ReceiveNums);//讀取緩衝數據

ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadSavDat), buf);

BufferData.AddRange(buf);//串口數據寫到自定義緩衝區,準備進行數據幀識別處理

int idx = 0;

for (; idx < BufferData.Count - 3; idx++)

{

if (BufferData[idx] == 0x39 && BufferData[idx + 1] == 0xD7 && BufferData[idx + 2] == 0x11)

{

if (BufferData.Count - idx >= 33)

{

byte[] tmb = new byte[33];

BufferData.CopyTo(idx, tmb, 0, tmb.Length);

}

else

{

break;

}

}

}

BufferData.RemoveRange(0, idx);

string tempstr = string.Empty;

for (int i = 0; i < ReceiveNums; i++)

{

tempstr += buf[i].ToString("X2") + " ";

}

if (chkbPauDisp.CheckState == CheckState.Unchecked)

{

txtbRcvArea.AppendText(tempstr);//追加到接收數據顯示框中

}

}

else

{

serialPort1.Encoding = System.Text.Encoding.GetEncoding("GB2312");//解決亂碼問題,國標2312編碼格式

if (chkbPauDisp.CheckState == CheckState.Unchecked)

{

txtbRcvArea.AppendText(serialPort1.ReadExisting());

}

}

// ledRcvStat.LEDSwitch = true;

// ledRcvStat.Invalidate();

// stat_timer.Enabled = true;

}));

ReceiveByte_Busy = false;

}

catch (Exception er)

{

MessageBox.Show("錯誤: RecieveData" + er.Message, "錯誤");

}

}

void ThreadSavDat(object o)

{

byte[] buf = o as byte[];

if (writefile != null && writefile.CanWrite)

{

writefile.Write(buf, 0, buf.Length);

}

}

private void button1_Click(object sender, EventArgs e)

{

OpenFileDialog ofd = new OpenFileDialog();

ofd.Title = "請選擇要打開的文本文件";

ofd.InitialDirectory = @"C:\\Users\\SpringRain\\Desktop";

ofd.Multiselect = true;

ofd.Filter = "HEX文件|*.hex|文本文件|*.txt|所有文件|*.*";

ofd.ShowDialog();

string path = ofd.FileName; //獲得用戶選中的文件的路徑

//list.Add(path);//將文件的全路徑存儲到泛型集合中

string fileName = Path.GetFileName(path);//獲得了用戶打開文件的文件名

//listBox1.Items.Add(fileName);//將文件名放到ListBox中

tbHexPath.Text = fileName;

if (path == "")

{

return;

}

FileStream fsRead = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Read);

StreamReader HexReader = new StreamReader(fsRead); //讀取數據流

string szLine;

int startAdr;

int endAdr;

endAdr = 0;

bufferAdr = 0;

txtbRcvArea.Text = "";

while (true)

{

szLine = HexReader.ReadLine(); //讀取Hex中一行

if (szLine == null) { break; } //讀取完畢,退出

if (szLine.Substring(0, 1) == ":") //判斷首字符是”:”

{

if (szLine.Substring(1, 8) == "00000001") { break; } //文件結束標識

if ((szLine.Substring(8, 1) == "0") || (szLine.Substring(8, 1) == "1"))//直接解析數據類型標識為 : 00 和 01 的格式

{

int lineLenth;

string hexString;

hexString = szLine.Substring(1, 2);

lineLenth = Int32.Parse(hexString, System.Globalization.NumberStyles.HexNumber); // 獲取一行的數據個數值

hexString = szLine.Substring(3, 4);

startAdr = Int32.Parse(hexString, System.Globalization.NumberStyles.HexNumber); // 獲取地址值

for (int i = 0; i < startAdr - endAdr; i++) // 補空位置

{

hexString = "FF";

byte value = byte.Parse(hexString, System.Globalization.NumberStyles.HexNumber);

buffer[bufferAdr] = value;

bufferAdr++;

}

for (int i = 0; i < lineLenth; i++) // hex轉換為byte

{

hexString = szLine.Substring(i * 2 + 9, 2);

byte value = byte.Parse(hexString, System.Globalization.NumberStyles.HexNumber);

buffer[bufferAdr] = value;

bufferAdr++;

}

endAdr = startAdr + lineLenth;

}

}

}

txtbRcvArea.Text = byteToHexStr(buffer, bufferAdr);

}

private void button3_Click(object sender, EventArgs e)

{

SaveFileDialog sfd = new SaveFileDialog();

sfd.InitialDirectory = @"C:\\Users\\SpringRain\\Desktop";

sfd.Title = "請選擇要保存的文件路徑";

sfd.Filter = "BIN文件|*.bin|文本文件|*.txt|所有文件|*.*";

sfd.ShowDialog();

//獲得用戶要保存的文件的路徑

string path = sfd.FileName;

//獲得了用戶打開文件的文件名

string fileName = Path.GetFileName(path);

rbHexPath.Text = fileName;

if (path == "")

{

return;

}

FileStream fsWrite = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write);

// byte[] buffer = Encoding.Default.GetBytes(txtbRcvArea.Text);

fsWrite.Write(buffer, 0, bufferAdr);

MessageBox.Show("保存成功");

}

//字節數組轉16進制字符串

public static string byteToHexStr(byte[] bytes,int len)

{

string returnStr = "";

if (bytes != null)

{

for (int i = 0; i < len; i++)

{

returnStr += bytes[i].ToString("X2");

}

}

return returnStr;

}

private void button4_Click(object sender, EventArgs e)

{

txtbRcvArea.Text = "";

}

}

}


分享到:


相關文章: