Отправить файл прошивки устройству через COM порт - C#
Формулировка задачи:
День добрый.
Я совсем недавно взялся за с#, и в качестве первого задания хочу написать программу автоматической настройки модемов. По работе бывает, что приносят нам модем с заводскими настройками, и надо быстренько все перебить на свои (то есть хочется вместо забивания команд ручками по бумажке, подключить модем, нажать одну кнопку и пойти пить чай).
Правда вылезла проблема. Если с тем, как кидать команды модему я вроде разобрался, то остается вопрос как его перепрошивать.
То есть вбивая команды, в определенный момент времени, устройству нужно сказать "AT+WDWL" (то есть принимай файл), и в терминале, используя протокол передачи "1K Xmodem", кинуть ему некий файл прошивки на 100 с чем-то килобайт.
И как это сделать на с# - ума не приложу... Естественно пошел гуглить, но толи народу это малоактуально, толи я неправильно вопрос формулирую - ничего толкового найти не получается.
Если кто сталкивался - подскажите пожалуйста.
ps
Не уверен, что смогу отвечать сегодня, но завтра точно буду на месте.
Решение задачи: «Отправить файл прошивки устройству через COM порт»
textual
Листинг программы
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO.Ports; using System.Threading; using System.Threading.Tasks; namespace ArcadiaUtility_v3._0._0._0 { public class WriteArcadia { //События public event EventHandler<BarEventArgs> barOnEvent; //Включение и установки прогресса public event EventHandler barStepEvent; //Шаг прогресса public event EventHandler barOffEvent; //Отключение прогресса public event EventHandler<ExceptionEventArgs> exceptionEvent;//Обработка ошибок public event EventHandler<BarEventArgs> correctWriteEvent; //Считанные данные корректны public event EventHandler correctApplEvent; //Данные соответствуют устройству //Поля private SerialPort port; //Переменная порта private List<byte> inData; //Переменная входных данных для записи private int mcuType; //Тип МК private string swVersion; //Версия ПО private int configSize; //Макс. размер конфигурации private int moduleType; //Тип устройства //Конструктор public WriteArcadia(string portName, List<byte> data) { port = new SerialPort() { PortName = portName, BaudRate = 38400, ReadTimeout = 500 }; inData = data; } //Внешний метод записи(в отдельном потоке) public void WriteModule() { //Коррекция размера(до заполнения блока) int fileSize = (inData[14] * 0x100 + inData[15]) * 2; fileSize = (fileSize % 0x40 == 0) ? fileSize : fileSize + (0x40 - fileSize % 0x40); CorrectFileSize(fileSize); //WriteData(); Task.Factory.StartNew((Action)delegate { WriteData(); }); } //Метод коррекции загруженного файла по размеру private void CorrectFileSize(int corrLength) { if (inData.Count == corrLength) return; if (inData.Count < corrLength) inData.AddRange(new byte[corrLength - inData.Count]); else inData.RemoveRange(corrLength, inData.Count - corrLength); } //Внутренний метод записи private void WriteData() { byte[] connectData = { 0xa5, 0xee, 0x02, 0x95, 0x49, 0x02, 0x90, 0x20, 0x25 }; byte[] writeAsc = { 0xa5, 0xee, 0x02, 0x95, 0x4f, 0x43, 0x90, 0x00, 0x00 }; int startAddress=0; try { port.Open(); List<byte> tempList = new List<byte>(); //Цикл попыток соединения for (int i = 0; i <= 10; i++) { if (!ReadBlock(40, connectData, tempList)) { Thread.Sleep(200); if (i == 10) { throw new CommonException("Устройство не отвечает!", "Ошибка соединения"); } } else break; } //Фильтрация считанного блока tempList.RemoveRange(0, 7); tempList.RemoveAt(tempList.Count - 1); //Получение основной информации об устройстве mcuType = tempList[0]; moduleType = BitConverter.ToInt16(new byte[] { tempList[2], tempList[1] }, 0); swVersion = BitConverter.ToString(tempList.GetRange(3, 3).ToArray(), 0); configSize = tempList[12]; startAddress = tempList[8]; //Событие для вывода информации об устройстве if (correctApplEvent != null) correctApplEvent(this, EventArgs.Empty); //Проверка на соответствие модуля //размеру загруженного фала конфигурации if (mcuType == 0x00 && inData.Count > 0x2800) { string msg = "Размер данных не соответствует типу устройства!"; throw new CommonException(msg, "Ошибка типа устройства"); } //Стирание устройства if (!CommonErase(mcuType, configSize)) { throw new CommonException("Устройство не отвечает!", "Ошибка стирания"); } //Включение и установки прогресса if (barOnEvent != null) barOnEvent(this, new BarEventArgs(inData.Count, 0x40)); //Установка стартового адреса записи writeAsc[8] = (byte)startAddress; //Основной цикл записи for (int i = 0; i < inData.Count; i += 0x40) { List<byte> writeData = new List<byte>(); writeData.AddRange(writeAsc); writeData.AddRange(inData.GetRange(i, 0x40).ToArray()); byte checkSum = (byte)writeData.Sum(n => (int)n); writeData.Add(checkSum); tempList.Clear(); //Внутренний цикл записи, 5 попыток записи блока for (int j = 0; j <= 5; j++) { if (ReadBlock(11, writeData.ToArray(), tempList)) break; if (j == 5) { throw new CommonException("Устройство не отвечает!", "Ошибка записи"); } } //Изменение адресов в запросе записи writeAsc[7] = (byte)((writeAsc[7] + 0x40) & 0xff); if (writeAsc[7] == 0) writeAsc[8] = (byte)((writeAsc[8] + 1) & 0xff); //Шаг прогресса if (barStepEvent != null) barStepEvent(this, EventArgs.Empty); } //Сброс массива данных tempList.Clear(); //Проверочное чтение из устройства if (!CheckRead(tempList, inData.Count, startAddress)) { throw new CommonException("Устройство не отвечает!", "Ошибка чтения"); } //Проверочное сравнение данных if (!CompareData(inData, tempList)) { throw new CommonException("Ошибка при записи! Повторите!", "Ошибка проверки"); } //Сообщение об успешном завершении операции if (correctWriteEvent != null) correctWriteEvent(this, new BarEventArgs("Запись прошла успешно!")); } catch (Exception ex) { //Переменные для исключения string Message = ex.Message; string Caption = string.Empty; //Получение типа исключения if (ex is CommonException) { Caption = ((CommonException)ex).Caption; } else { Caption = "Ошибка!"; } //Обработка ошибок с аргументами исключения if (exceptionEvent != null) exceptionEvent(this, new ExceptionEventArgs(Message, Caption)); } finally { port.Close(); } } //Метод проверочного сравнения после записи private bool CompareData(List<byte> inData, List<byte> tempData) { int fileSize = (inData[14] * 0x100 + inData[15]) * 2; for (int i = 0; i < fileSize; i++) { if (inData[i] != tempData[i]) return false; } return true; } //Метод проверочного чтения private bool CheckRead(List<byte> tempList, int size, int startAddress) { //Запрос на чтение byte[] readData = { 0xa5, 0xee, 0x02, 0x95, 0x3f, 0x05, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00 }; //Внутренний массив для считанных данных List<byte> temp = new List<byte>(); //Коррекция адресов readData[9] = 0xff; readData[8] = readData[10] = (byte)startAddress; ////Включение и установки прогресса if (barOnEvent != null) { barOnEvent(this, new BarEventArgs(size, 0x100)); } //Цикл чтения данных for (int j = 0; j < size; j += 0x100) { //Коррекция адресов и КС readData[11] = 0; readData[11] = (byte)readData.Sum(n => n); //Чтение блока if (!ReadBlock(264, readData, temp)) { return false; } //Шаг прогресса if (barStepEvent != null) { barStepEvent(this, EventArgs.Empty); } //Фильтрация temp.RemoveRange(0, 7); temp.RemoveAt(temp.Count - 1); tempList.AddRange(temp); //Коррекция адресов readData[8] = readData[10] = (byte)(readData[8] + 1); } //Отключение прогресса if (barOffEvent != null) { barOffEvent(this, EventArgs.Empty); } return true; } //Общий метод стирания с выбором метода от типа МК private bool CommonErase(int mcuType, int eraseSize=0) { if (mcuType == 0x00) { if (EraseMotorola(eraseSize)) return true; } else { if (EraseRenesas()) return true; } return false; } //Стирание МК Ренесас private bool EraseRenesas() { byte[] errErase = { 0xa5, 0xee, 0x02, 0x9d, 0x5f, 0x02, 0x90, 0x02, 0x25 }; byte[] dataErase = { 0xa5, 0xee, 0x02, 0x95, 0x4e, 0x03, 0x90, 0x00, 0x10, 0x1b }; byte[] statErase = { 0xa5, 0xee, 0x02, 0x95, 0x53, 0x02, 0x90, 0x53, 0x62 }; List<byte> temp = new List<byte>(); //Сброс ошибок if (!ReadBlock(1, errErase, temp)) return false; //Стирание данных if (!ReadBlock(11, dataErase, temp)) return false; //Сброс статистики if (!ReadBlock(11, statErase, temp)) return false; return true; } //Стирание МК Моторола(с коррекцией размера) private bool EraseMotorola(int configLength) { byte[] errErase = { 0xa5, 0xee, 0x02, 0x9d, 0x5f, 0x02, 0x90, 0x02, 0x25 }; byte[] dataErase = { 0xa5, 0xee, 0x02, 0x95, 0x4e, 0x03, 0x90, 0x00, 0x1a, 0x25 }; byte[] statErase = { 0xa5, 0xee, 0x02, 0x95, 0x53, 0x02, 0x90, 0x53, 0x62 }; List<byte> temp = new List<byte>(); int eraseSize=0; //Определение размера стирания eraseSize = GetEraseLength(configLength); //Сброс ошибок if(!ReadBlock(1,errErase,temp)) return false; temp.Clear(); //Цикл стирания for (int i = 0; i < eraseSize / 512; i++) { //Внутренний цикл, 5 попыток стирания for (int j = 0; j <= 5; j++) { if (ReadBlock(11, dataErase, temp)) break; if (j == 5) return false; } //Сброс массива данных temp.Clear(); //Смена адресов в запросе dataErase[8] += 2; dataErase[9] = 0; dataErase[9] = (byte)dataErase.Sum(n => (int)n); } temp.Clear(); if (!ReadBlock(11, statErase, temp)) return false; return true; } //Получение размера стирания(для МК Моторола) private int GetEraseLength(int type) { int eraseSize = 0; switch (type) { case 0x38: eraseSize = 0x1000; break; case 0x48: eraseSize = 0x1400; break; case 0x58: eraseSize = 0x1800; break; case 0x68: eraseSize = 0x1c00; break; case 0x78: eraseSize = 0x2000; break; case 0x98: eraseSize = 0x2800; break; default: eraseSize = 0x2800; break; } return eraseSize; } //Внутренний метод чтения блока private bool ReadBlock(int count, byte[] sendData, List<byte> readData) { //Сброс данных readData.Clear(); //Индекс ожидания int index = 0; //Массив для чтения блока byte[] temp = new byte[count]; //Запись данных в порт port.Write(sendData, 0, sendData.Length); //Сброс буфера порта port.DiscardInBuffer(); //Ожидание заполнения буфера порта while (port.BytesToRead != count) { Thread.Sleep(100); index++; //При отсутствии данных за 10 попыток-возврат false if (index == 10 && port.BytesToRead < count) return false; } //Чтение блока данных port.Read(temp, 0, count); //Копирование в выходной массив readData.AddRange(temp); return true; } //Свойство типа МК public string MCUType { get { switch (mcuType) { case 0x00: return "Motorola GB60/GB60A"; case 0x51: return "Renesas HD64H36079"; default: return "Тип не определен"; } } } //Свойство версии ПО public string SWVersion { get { return swVersion.Replace("-", "."); } } //Свойство максимального размера конфигурации public string ConfigSize { get { switch (configSize) { case 0x38: return "4 кB"; case 0x48: return "5 kB"; case 0x58: return "6 kB"; case 0x68: return "7 kB"; case 0x78: return "8 kB"; case 0x98: return "10 kB"; case 0x05: return "16 kB"; default: return "Размер не определен"; } } } //Свойство типа устройства public string ModuleType { get { switch (moduleType) { case 0x0128: return "DW DEA 600/DEA 601"; case 0x0129: return "DW DEA 602"; case 0x0228: return "WM Arcadia"; case 0x0229: return "WM Arcadia-2"; case 0x0300: return "CK HOT 2005"; case 0x0328: return "CK HOT 2005"; case 0x0020: return "CL CL 2005/CL 2008"; case 0x002D: return "CL Artica"; default: return "Тип не определен"; } } } } //Класс для аргумента событий public class BarEventArgs : EventArgs { public readonly int barSize; //Размер прогресса public readonly int barStep; //Шаг прогресса public readonly string Message; //Сообщение //Цепочка конструкторов public BarEventArgs() :this(0,0,"") { } public BarEventArgs(string mes) : this(0, 0, mes) { } public BarEventArgs(int size, int step) : this(size, step, "") { } private BarEventArgs(int size, int step, string mes) { barSize = size; barStep = step; Message = mes; } } //Класс аргумента события для исключения public class ExceptionEventArgs : EventArgs { public readonly string Message; public readonly string Caption; public ExceptionEventArgs(string msg,string cap) { Message = msg; Caption = cap; } } //Класс исключения public class CommonException : ApplicationException { public readonly string Caption; public CommonException(string msg, string cap) : base(msg) { Caption = cap; } } }
ИИ поможет Вам:
- решить любую задачу по программированию
- объяснить код
- расставить комментарии в коде
- и т.д