啟動(dòng)初始化
正在下載時(shí)
其中兩個(gè)ComboBox是分別用于選擇和顯示串口端口號(hào)和波特率的羞海。配置好正確的端口號(hào)和波特率,選擇要更新的固件曲管。點(diǎn)擊開(kāi)始下載却邓。就開(kāi)啟一個(gè)線程等待下位機(jī)發(fā)送傳輸請(qǐng)求。待收到下位機(jī)的請(qǐng)求后進(jìn)入文件傳送翘地。
if (serialPort.ReadByte() != C)//下位機(jī)沒(méi)有請(qǐng)求傳送文件申尤。則{//通知主線程。更新固件失敗
Debug.WriteLine("Can't begin the transfer."); DownloadResultEvent.Invoke(false, new EventArgs()); serialPort.Close();}
//收到下位機(jī)請(qǐng)求后發(fā)送第一個(gè)初始化包衙耕,告知下位機(jī)昧穿,傳輸文件的文件名和大小
sendYmodemInitialPacket(STX, packetNumber, invertedPacketNumber, data, dataSize, path, fileStream, CRC, crcSize);
//等待下位機(jī)發(fā)送應(yīng)答信號(hào)
//超過(guò)串口組件規(guī)定的接收時(shí)間沒(méi)有收到應(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ī)請(qǐng)求則表示下位機(jī)請(qǐng)求進(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ā)送完一個(gè)數(shù)據(jù)包橙喘,則累計(jì)
if (packetNumber > 255)//最大允許發(fā)送255個(gè)數(shù)據(jù)包时鸵,即文件大小不得超過(guò)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交互的方式厅瞎,使用事件的方式饰潜,通知?jiǎng)?chuàng)建UI控件的父線程。由父線程響應(yīng)事件來(lái)更新UI和簸。2.線程的傳參的形式可采用線程類的方式彭雾。把線程中調(diào)用的主方法和需要的參數(shù)寫(xiě)在一個(gè)類里。再開(kāi)辟線程時(shí)锁保,對(duì)需要使用到的類中的成員變量進(jìn)行賦值薯酝。然后開(kāi)啟線程。線程類的成員變量
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;
開(kāi)啟子線程進(jìn)行通信
if (button.Text == "開(kāi)始下載")
{ 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();
}