啟動初始化
正在下載時
其中兩個ComboBox是分別用于選擇和顯示串口端口號和波特率的。配置好正確的端口號和波特率,選擇要更新的固件。點(diǎn)擊開始下載。就開啟一個線程等待下位機(jī)發(fā)送傳輸請求。待收到下位機(jī)的請求后進(jìn)入文件傳送。
if (serialPort.ReadByte() != C)//下位機(jī)沒有請求傳送文件。則{//通知主線程。更新固件失敗
Debug.WriteLine("Can't begin the transfer."); DownloadResultEvent.Invoke(false, new EventArgs()); serialPort.Close();}
//收到下位機(jī)請求后發(fā)送第一個初始化包,告知下位機(jī),傳輸文件的文件名和大小
sendYmodemInitialPacket(STX, packetNumber, invertedPacketNumber, data, dataSize, path, fileStream, CRC, crcSize);
//等待下位機(jī)發(fā)送應(yīng)答信號
//超過串口組件規(guī)定的接收時間沒有收到應(yīng)答,則表示更新失敗
if (serialPort.ReadByte() != ACK)
{
Debug.WriteLine("Can't send the initial packet."); DownloadResultEvent.Invoke(false, new EventArgs());
// return false;
}
if (serialPort.ReadByte() != C)//接收到'C'下位機(jī)請求則表示下位機(jī)請求進(jìn)入正式的文件數(shù)據(jù)傳輸流程
{
DownloadResultEvent.Invoke(false, new EventArgs()); return;// false;
}
文件傳輸
do{
/* if this is the last packet fill the remaining bytes with 0 */
fileReadCount = fileStream.Read(data, 0, dataSize);
if (fileReadCount == 0) break; //最后讀取得字節(jié)數(shù)低于規(guī)定讀取的,則把發(fā)送的數(shù)據(jù)包用0補(bǔ)齊
if (fileReadCount != dataSize)
for (int i = fileReadCount; i < dataSize; i++)
data[i] = 0;/* calculate packetNumber */ packetNumber++;//每發(fā)送完一個數(shù)據(jù)包,則累計(jì)
if (packetNumber > 255)//最大允許發(fā)送255個數(shù)據(jù)包,即文件大小不得超過255K.
packetNumber -= 256;
Console.WriteLine(packetNumber);
/* calculate invertedPacketNumber */
invertedPacketNumber = 255 - packetNumber;
/* calculate CRC */
Crc16Ccitt crc16Ccitt = new Crc16Ccitt(InitialCrcValue.Zeros);
CRC = crc16Ccitt.ComputeChecksumBytes(data);
/* send the packet */
sendYmodemPacket(STX, packetNumber, invertedPacketNumber, data, dataSize, CRC, crcSize);
//計(jì)算當(dāng)前下載進(jìn)度
int progress = (int)(((float)dataSize * packetNumber) / fileStream.Length * 100);
if (progress > 100)
progress = 100;
//將進(jìn)度以事件的形式通知給主線程 NowDownloadProgressEvent.Invoke(progress, new EventArgs()); /* wait for ACK */
if (serialPort.ReadByte() != ACK)
{
Debug.WriteLine("Couldn't send a packet."); DownloadResultEvent.Invoke(false, new EventArgs());
return;// false;
}} while (dataSize == fileReadCount);
主線程響應(yīng)進(jìn)度事件
private delegate void NowDownloadProgress(int nowValue); \
private void NowDownloadProgressEvent(object sender, EventArgs e)
{
int value = Convert.ToInt32(sender);
NowDownloadProgress count = new NowDownloadProgress(UploadFileProgress); this.Invoke(count, value); }
private void UploadFileProgress(int count)
{
DownloadProgressBar.Value = count;//更新進(jìn)度條
}
總結(jié)
1.在子線程中不能操作非自身線程所創(chuàng)建的UI控件,所以在子線程完成UI交互的方式,使用事件的方式,通知創(chuàng)建UI控件的父線程。由父線程響應(yīng)事件來更新UI。2.線程的傳參的形式可采用線程類的方式。把線程中調(diào)用的主方法和需要的參數(shù)寫在一個類里。再開辟線程時,對需要使用到的類中的成員變量進(jìn)行賦值。然后開啟線程。線程類的成員變量
private string path;
public string Path
{get {return Path;} set { path = value; } }
private string portName;
public string PortName { get { return portName; } set { portName = value; } }
private int baudRate;public int BaudRate { get { return baudRate; } set { baudRate = value; } }
private System.IO.Ports.SerialPort serialPort = new System.IO.Ports.SerialPort();public event EventHandler NowDownloadProgressEvent;public event EventHandler DownloadResultEvent;
開啟子線程進(jìn)行通信
if (button.Text == "開始下載")
{ button.Text = "正在下載";
ymodem = new Ymodem.Ymodem();
ymodem.Path = pathTextBox.Text.ToString();
ymodem.PortName = SerialPortComboBox.SelectedItem.ToString();
ymodem.BaudRate=Convert.ToInt32(BaudRateComboBox.SelectedItem.ToString());
downloadThread = new System.Threading.Thread(ymodem.YmodemUploadFile);
ymodem.NowDownloadProgressEvent += new EventHandler(NowDownloadProgressEvent);
ymodem.DownloadResultEvent += new EventHandler(DownloadFinishEvent); downloadThread.Start();
}