Отправить файл прошивки устройству через 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;
        }
    }
}

ИИ поможет Вам:


  • решить любую задачу по программированию
  • объяснить код
  • расставить комментарии в коде
  • и т.д
Попробуйте бесплатно

Оцени полезность:

11   голосов , оценка 4 из 5
Похожие ответы