單片機BootLoader上位機簡介
BootLoader上位機開發語言為C#,選擇uart通信,需要用到幾個常用的串口控件:串口號,波特率,數據位和打開串口按鈕。另外需要一個打開HEX的按鈕,一個保存bin文件的按鈕。打開一個BLE.HEX文件,上位機對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為固定用法。
hex與bin文件比較,hex文件會有跳存儲地址情況,則在空的地址填補十六進制 FF。這樣既可生成完整的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 = "";
}
}
}
閱讀更多 小武哥編程 的文章