单片机BootLoader上位机简介

BootLoader上位机开发语言为C#,选择uart通信,需要用到几个常用的串口控件:串口号,波特率,数据位和打开串口按钮。另外需要一个打开HEX的按钮,一个保存bin文件的按钮。打开一个BLE.HEX文件,上位机对hex文件进行解析,然后显示在窗口,最后也可以保存成bin文件,再来就是一个下载按钮,点击按钮则开始下载程序。

单片机bootloader有什么用(单片机Bootloader上位机开发之HEX转bin)(1)

上位机界面

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有什么用(单片机Bootloader上位机开发之HEX转bin)(2)

打开hex文件

单片机bootloader有什么用(单片机Bootloader上位机开发之HEX转bin)(3)

保存bin文件

hex与bin文件比较,hex文件会有跳存储地址情况,则在空的地址填补十六进制 FF。这样既可生成完整的bin。

单片机bootloader有什么用(单片机Bootloader上位机开发之HEX转bin)(4)

左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>();//串口数据数据帧识别缓存空间

// 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 = "";

}

}

}

,