本帖最后由 cry1109 于 2020-6-8 09:30 编辑
思路很简单,C#制作一个上位机工具,将读取的bin文件通过串口下发至单片机,一帧数据包含:7字节的Modbus协议帧头+200字节数据更新包(最后一帧少于等于200字节)+2字节的CRC校验码。单片机在boot中解析协议,协议解析无误后,将数据更新包写入Flash中,然后返回特定的数据。上位机根据返回的数据判断本次数据是否写入成功,如果写入成功继续下发新的更新包,如果写入失败重复发送本次更新包。通讯间隔100ms。 开发环境:Visio Studio 2015 一.新建windows窗体应用程序:
 二.绘制基本界面:
 在工具箱中的公共控件下找到以下三个控件Label、ComboBox、Button,拖拽到Form中。鼠标点击控件后可以在属性栏中修改控件的相应属性。
 选中修改串口波特率对应的CommboBox控件,点击Items属性,输入相应的波特率值,保存。其他控件的属性就是改改名字,外形大小、颜色等。
 然后再放两个TextBox控件显示加载信息等,以及几个按钮。最后界面如下:
 三.添加串口控件以及文件对话框:在工具箱中找打组件下的SerialPort控件,也就是串口控件;对话框下的OpenFileDialog控件,拖拽到Form下的空区域中
现在所用到的控件都已经加到窗体中了,基本工作已经完成了。接下来开始撸代码了。 四.代码撸起来1.搜索可用串口,并显示在CommboBox中显示串口名称。双击搜索端口按钮,会自动跳转到代码编辑处,在button_Clik事件函数下添加更新端口的函数。 - private void button2_Click(object sender, EventArgs e)
- {
- string[] ArryPort = System.IO.Ports.SerialPort.GetPortNames();
- comboBox2.Items.Clear();
- for (int i = 0; i < ArryPort.Length; i++)
- {
- comboBox2.Items.Add(ArryPort[i]);
- }
- }
2.打开/关闭串口。双击启用端口按钮,加入以下代码。 - private void button1_Click(object sender, EventArgs e)
- {
- if (button1.Text == "启用端口")
- {
- try
- {
- serialPort1.PortName = comboBox2.Text;
- serialPort1.Open();
- comboBox2.Enabled = false;
- button2.Enabled = false;
- button1.Text = "关闭端口";
- }
- catch
- {
- System.Media.SystemSounds.Beep.Play();
- MessageBox.Show("端口打开失败", "错误");
- }
- }
- else
- {
- try
- {
- serialPort1.Close();
- button2.Enabled = true;
- comboBox2.Enabled = true;
- button1.Text = "启用端口";
- }
- catch
- {
- System.Media.SystemSounds.Beep.Play();
- MessageBox.Show("关闭串口失败", "错误");
- }
- }
- }
3.修改串口波特率。双击串口波特率对应的CommboBox,加入以下代码。 - private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
- {
- serialPort1.BaudRate = Convert.ToInt32(comboBox1.Text);
- }
4.在Form1_Load中创建串口接收线程,并且初始化串口波特率,串口的其他参数配置使用默认即可。串口接收也可以使用数据接收事假,类似于STM32的串口接收中断。下面的代码是创建串口接收线程,以线程的方式接收串口数据。 - private void Form1_Load(object sender, EventArgs e)
- {
- serialPort1.BaudRate = 115200;
- comboBox1.Text = "115200";
- Thread ReadSerialPort = new Thread(new
- ParameterizedThreadStart(SerialPortReadThread));
- ReadSerialPort.IsBackground = true;
- ReadSerialPort.Start();
- }
如果使用事件的方式接收数据,按一下步骤操作即可:
5.打开.bin文件。双击文件按钮,加入以下代码: - string BinText;
- OpenFileDialog MyFileDialog;
- private void button7_Click(object sender, EventArgs e)
- {
- BinText = "";
- MyFileDialog = new OpenFileDialog();
- MyFileDialog.InitialDirectory =
- Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
- MyFileDialog.Filter = @"|*.bin";
- if (MyFileDialog.ShowDialog() == DialogResult.OK)
- {
- string filePath = MyFileDialog.FileName;
- FileStream Myfile = new FileStream(filePath,FileMode.Open,FileAccess.Read);
- BinaryReader binreader = new BinaryReader(Myfile);
- MyPublicPara.FileLength = (int)Myfile.Length;//获取bin文件长度
- MyPublicPara.Binchar = binreader.ReadBytes((int)Myfile.Length);
- textBox4.Text = MyFileDialog.SafeFileName;
- textBox4.Text += " "+Myfile.Length+"Byte\r\n\r\n";
- }
- }
到这一步,我们已经可以打开串口、打开指定的bin文件了,接下来就是通过串口把bin文件发出去。 6.发送bin文件。 点击开始更新按钮,加入以下代码: - public delegate void ClearProgressValue();
- Progress progressForm;
- List<Progress> ListForm = new List<Progress>(); //创建窗体集合
- private void button8_Click(object sender, EventArgs e)
- {
- byte[] SysRestar_CMD = { 0x01, 0x10, 0x36, 0x51, 0x00, 0x02, 0x04, 0x13, 0x52, 0x00, 0x62, 0x00, 0x00 };
- ushort crc_value = ModbusCRC16(SysRestar_CMD, 13);
- SysRestar_CMD[11] = (byte)((crc_value >> 8) & 0xff);
- SysRestar_CMD[12] = (byte)(crc_value & 0xff);
- if (serialPort1.IsOpen)
- serialPort1.Write(SysRestar_CMD, 0, 13);
- textBox5.Text = "";
- BinText = "";
- MyPublicPara.UpdataCount = 0;
- progressForm = new Progress();
- progressForm.StartPosition = FormStartPosition.CenterScreen;
- if (ListForm.Count != 0)
- {
- ListForm[0].Close();
- ListForm.Clear();
- }
- progressForm.Show();
- ListForm.Add(progressForm); //将更新进度窗体加入集合中
- button8.Enabled = false;
- Delay(2000);
- if ((MyPublicPara.FileLength > 10000) && (MyPublicPara.FileLength < 100000) && (serialPort1.IsOpen))
- {
- Thread SendReadFile = new Thread(new ParameterizedThreadStart(SendBinFileThread));//创建下发更新文件线程
- SendReadFile.IsBackground = true;
- SendReadFile.Start();//启动线程
- }
- }
开始更新后,先下发一个复位指令,让单片机复位进入boot程序,延迟2s后创建下发bin文件的线程,开始更新。指令格式按照modbus协议,不清楚modbus协议的百度之。这里提供一个modbus的16位CRC校验计算公式,可以用来计算或者校验一帧数据的CRC,如下: - public static ushort ModbusCRC16(byte[] data,int length)
- {
- int len = length - 2;
- ushort crc_value = 0xFFFF;
- for (int i = 0; i < len; i++)
- {
- crc_value ^= (ushort)data[i];
- for (int j = 8; j != 0; j--)
- {
- if ((crc_value & 0x0001) != 0)
- {
- crc_value >>= 1;
- crc_value ^= 0xA001;
- }
- else
- {
- crc_value >>= 1;
- }
- }
- }
- return crc_value = (ushort)(((crc_value & 0x00ff) << 8) | ((crc_value & 0xff00) >> 8));
- }
7.开始下发bin文件。
|