Как преобразовать доступ к порту ввода/вывода Turbo Pascal для Win32 в C#.Net

У меня есть счетчик монет (SC350/360), который подключается к компьютеру через интерфейс RS232C. У меня есть техническая документация, в которой описаны протоколы связи, а также включена работающая программа на Паскале для управления машиной. Я скопировал код Pascal и протестировал его на Turbo Pascal, используя DosBox, для Windows 7 64 бит, и код успешно компилируется. Чего я хотел добиться сейчас, так это преобразовать эти паскаль-коды в С#.Net, но мне было трудно преобразовать несколько строк в С#, так как у меня нет большого опыта в программировании последовательного порта.

Это был код на Паскале для инициализации связи с машиной. (Установите скорость передачи данных на 9600, 8 бит, без четности, 1 стоповый бит)

uses crt;
const
{ COM1: RS232 port address }
RXTX = $3F8; { $2F8 if COM2: is used }
ACK = 6;
NAK = 21;
ESC = 27;
var
dummy,
checkSum : integer;
key : char;
protocol : integer;

    var i : integer;
begin
i := 1843200 div 9600 div 16;
port[RXTX + 3] := $80;
port[RXTX + 1] := hi(i);
port[RXTX]:= lo(i);
port[RXTX + 3] := 3;
port[RXTX + 4] := $A;
while odd(port[RXTX + 5]) do
begin
dummy := port[RXTX];
delay(10);
end;
end; { InitComm }

Соответствующий С# для приведенного выше кода, который я придумал, был; (Пожалуйста, поправьте меня, если я ошибаюсь)

SerialPort port=new SerialPort("COM1",9600,Parity.None,8,StopBits.One);

Но я не мог понять, как преобразовать остальные паскаль-процедуры. Некоторые из этих процедур, с которыми у меня были трудности:

procedure Tx(data : integer);
{ Transmit a character on serial channel }
begin
while port[RXTX + 5] and $20 = 0 do;
port[RXTX] := data and $FF;
end; { Tx }

function RxWait : integer;
{ Waits for a character from serial channel }
begin
while not odd(port[RXTX + 5]) do;
RxWait := port[RXTX];
end; { RxWait }

procedure Tx2(data : integer);
{ Transmit a char on serial channel + Calculate check sum }
begin
Tx(data);
checkSum := (checkSum + data) and $FF;
end; { Tx2 }

Не могли бы вы, ребята, помочь мне, как преобразовать эти коды Паскаля в эквивалентный С#? Я знаю, что могу писать в порт, используя метод «port.Write», но это не может точно соответствовать коду Turbo Pascal с массивом портов (например, port[RXTX + 3] := $80;). Я не знаю, что такое индекс массива портов «RXTX+3». ' относится к С#.

Я был бы очень признателен, если бы вы могли помочь мне в этом, и я надеюсь, что научусь преобразовывать остальные коды Паскаля самостоятельно. :)

Я написал следующий эквивалентный код C# для программы на паскале, используя помощь хороших людей. Пожалуйста, поправьте меня, если я допустил ошибку в своем коде.

            public void Tx(int data)
            {
                if (!port.IsOpen)
                    port.Open();
                port.Write(new byte[] { (byte)(data & 0xFF) }, 0, 1);
                port.Close();
            }
            /// <summary>
            /// Wait for a character from serial channel
            /// </summary>
            /// <returns></returns>
            public int RxWait()
            {
                if (!port.IsOpen)
                    port.Open();
                int readByte = port.ReadByte();
                port.Close();
                return readByte;
            }
            /// <summary>
            /// Transmit a char on serial channel + Calculate check sum
            /// </summary>
            /// <param name="data"></param>
            public void Tx2(int data)
            {
                Tx(data);
                checkSum = (checkSum + data) & 0xFF;
            }

Кстати вот протокол, описанный в документации устройства.

Computer SC 350/360
–––––––> ESC (message start)
–––––––> Command
<––––––> Data (direction depends on command)
<––––––> Check sum (direction depends on command)
<––––––– Receipt:
- ACK (if check sum is correct) or
- NAK (if check sum is incorrect)

И, кроме того, здесь я предоставил остальную часть кода с примером отправки команды для подсчета количества монет.

 /// <summary>
    /// Transmit command (no data) on serial channel
    /// </summary>
        /// <param name="c1"></param>
        /// <param name="c2"></param>
        /// <param name="sendCheckSum"></param>
        public void TxCommand(char c1, char c2, bool sendCheckSum)
        {
            Tx(ESC);
            checkSum = 0;
            Tx2((int)c1);
            Tx2((int)c2);
            if (sendCheckSum)
            {
                Tx2(checkSum);
                dummy = RxWait();
            }
        }
        /// <summary>
        /// Read n bytes from serial channel
        /// </summary>
        /// <param name="n"></param>
        /// <returns></returns>
        public double ReadNumber(int n)
        {
            double number;
            int i;
            number = checkSum = 0;
            for (i = 0; i < n; i++)
                number = number * 256 + RxWait();
            dummy = RxWait();
            return number;
        }

        /// <summary>
        /// Read the number of Coins counted
        /// </summary>
        /// <returns>Number of Coins</returns>
        public double ReadCountReg()
        {
            TxCommand('R', 'C', false);
            double coinsCounted = ReadNumber(4);
            dummy = RxWait();
            return coinsCounted;
        }

отправить команду на подсчет монет;

double coinsCounted = ReadCountReg();
 Console.WriteLine(Math.Round(coinsCounted, 0) + " coins counted");

Протокол чтения регистра счетчика:

Computer SC 350/360
–––––––> ESC
–––––––> “R”
–––––––> “C”
<––––––– CountRegister (CR)
<––––––– (CR ^ FF00,000016 + CR ^ FF,000016 + CR ^ FF0016 +
CR ^ FF16) ^ FF16
<––––––– ACK

person Eliyah    schedule 01.02.2014    source источник


Ответы (2)


Здесь делается прямой доступ к аппаратному порту ПК (в частности, COM1). Для тех, кому интересно, что происходит, базовый порт + 5 — это регистр состояния линии UART 8250 и его совместимых по выводам преемников, таких как 16450 и 16550. См. здесь для получения более подробной информации о внутренней работе последовательных портов в классическом стиле ПК.

Не уверен, что вы когда-нибудь заставите это работать должным образом в Windows (и одно можно сказать наверняка, это никогда не будет работать, например, с ключами последовательного порта, подключенными через USB), это код DOS, который частично зависит от того, чтобы быть довольно близким к аппаратное обеспечение, иметь полный контроль над синхронизацией (DOS была однозадачной) и прекрасно знать, какое оборудование ожидать. В большинстве случаев должна быть возможность полагаться на средства, которые предлагает Windows (и, в вашем случае, .Net framework). То, что вы показываете выше, предназначено для отправки байтов (вы можете использовать Write для этого). Часть контрольной суммы должна быть довольно тривиальной для воспроизведения.

.Net предлагает API SerialPort, должна быть возможность использовать этот API и покончить с этим пережитком старых добрых дней DOS.

person fvu    schedule 01.02.2014
comment
Спасибо за ответ. Сначала я пытался скомпилировать код Pascal с помощью FreePascal, но он не поддерживает массив портов и модуль go32 для win32. Если бы я мог найти способ манипулировать портами из FreePascal любым возможным способом, я бы скомпилировал его в файл dll, а затем экспортировал бы его для использования из C#.NET с помощью вызова платформы. Есть ли способ добиться этого? - person Eliyah; 02.02.2014
comment
И мой другой вопрос: будет ли моя скомпилированная программа Turbo Pascal (код Pascal с массивом портов) работать, если я запущу и протестирую ее на ПК с Windows 7, подключив ее к машине для подсчета монет? Как я упоминал ранее, мне удалось скомпилировать его в Turbo Pascal с помощью DosBox. - person Eliyah; 02.02.2014
comment
@Eliyah нет, не будет, поскольку у него не будет прямого доступа к порту ввода-вывода в Dosbox. - person fvu; 02.02.2014
comment
Итак, как я могу выполнить задачу в коде паскаля с помощью С#? Можете ли вы предоставить мне фрагмент кода C #, который реализует ту же задачу этих процедур? - person Eliyah; 02.02.2014
comment
@Eliyah ссылается на ответ Ханса Пассанта, он уже дает список эквивалентов. Что касается контрольной суммы, вам придется проанализировать, как она используется, и подражать этому поведению. - person fvu; 02.02.2014
comment
большое спасибо. Я отредактировал свой исходный вопрос, написав эквивалентный код С# для программы на паскале. Пожалуйста, поправьте меня, если я ошибаюсь - person Eliyah; 02.02.2014
comment
@Eliyah, за исключением постоянного открытия / закрытия порта, это выглядит хорошо. Вы можете оставить порт открытым, пока работает ваша программа, за исключением потери времени, закрытие порта также может привести к трудным для отладки проблемам, таким как потерянные символы, и закрытие может повлиять на управляющие сигналы порта, что может нарушить Устройство. - person fvu; 02.02.2014
comment
Я ценю ваше руководство. Так где именно и когда я должен закрыть порт? - person Eliyah; 03.02.2014
comment
@Eliyah где-то ближе к концу программы, возможно, непосредственно перед выходом - это действительно немного зависит от того, как будет организовано приложение, если, например, у вас есть меню или другое действие пользователя, которое открывает порт, может быть полезно также предлагают близкий вариант. В противном случае просто откройте порт при запуске программы и закройте его при завершении работы. - person fvu; 03.02.2014
comment
Спасибо за ответ. Я просто подключал машину с помощью кабеля RS232 к ПК и тестировал программу, которую написал. Он выдает ошибку тайм-аута (›250 мс) при чтении данных с использованием «port.ReadByte()» в методе RxWait. В чем может быть проблема? Я не мог уйти далеко от этого. - person Eliyah; 03.02.2014
comment
@Eliyah см. документацию, SerialPort выдаст это исключение, если тайм-аут, настроенный для порта, будет превышен, но ничего не будет получено. У вас есть два варианта: установить тайм-аут на InfiniteTimeout или изменить свою логику так, чтобы она зацикливалась до тех пор, пока выдается исключение. Преимущество установки не бесконечного тайм-аута может заключаться в том, что вы можете избежать зависания вашей программы в случае проблем со связью, а недостатком является то, что для зацикливания исключения потребуется некоторая дополнительная логика. - person fvu; 03.02.2014
comment
Я уже пробовал это с бесконечным тайм-аутом, но я только что заметил, что «port.BytesToRead» всегда равен 0, когда я проверял его в методе RxWait. Я также пытался проверить, срабатывает ли событие DataReceived, и оно также не срабатывает. Я не могу понять, что здесь происходит. - person Eliyah; 03.02.2014
comment
Не зная протокола сложно сказать. Возможно, вы можете установить последовательный сниффер, подобно этому one, чтобы увидеть, воспроизводит ли ваша программа поток данных, ожидаемый устройством. - person fvu; 03.02.2014
comment
Я загрузил сниффер, и информация о последовательном устройстве отображается с очередью Max.TX и очередью Max.RX, установленными на None. Я обновил свой вопрос с подробным описанием протоколов и примером. Не могли бы вы взглянуть на это и дать мне руку здесь? Благодарность - person Eliyah; 03.02.2014
comment
Не могли бы вы добавить информацию, которую вы видите в снифере? Идет ли на самом деле какой-то обмен данными? - person fvu; 03.02.2014
comment
Максимум. Очередь TX Нет Макс. Очередь приема Нет Макс. Настраиваемая скорость передачи Возможности RS232 провайдера DTR/DSR, RTS/CTS, RLSD, проверка четности, XON/XOFF, настраиваемый XON/XOFF, общее время ожидания, интервал времени ожидания Устанавливаемые параметры Четность, бод, биты данных, стоповые биты, квитирование , проверка четности, скорость передачи RLSD 75, 110, 134,5, 150, 300, 600, 1200, 1800, 2400, 4800, 7200, 9600, 14400, 19200, 38400, 56K, 115200, 57600, настраиваемые данные 7, 8 Стоповые биты 1, 1,5, 2 Четность Нет, Нечетный, Четный, Метка, Пробел Текущая очередь Tx недоступна Текущая очередь Rx 4096 байт (не могли бы вы добавить меня в скайп и обсудить?) - person Eliyah; 03.02.2014
comment
Спасибо за вашу помощь, теперь я могу запускать и останавливать машину с помощью программы С#. Я добавил «Thread.Sleep(100)» перед записью байтов (метод Tx) и чтением байтов (метод RxWait), и это сработало. :) Но не работает отправка команды на чтение подсчитанных монет, которая показана в моем вопросе внизу. Метод «ReadNumber» вычисляет «число = число * 256 + RxWait()» в цикле, как вы можете видеть. «RxWait» считывает 0 байт при отладке. Не могли бы вы взглянуть на методы «ReadNumber» и «ReadCountReg» и помочь мне с этим? Это вопросы, над которыми я застрял прямо сейчас. - person Eliyah; 03.02.2014
comment
Только что перепроверил исходный код, похоже, они включают RTS (через порт + 4), а SerialPort по умолчанию - нет. См. здесь. - предлагаю RtsEnable = true сразу после открытия порта. - person fvu; 03.02.2014
comment
Я уже сделал это, но я написал это до открытия порта. Должен ли я снова установить значение true после открытия порта? Кстати, теперь я могу запускать автомат, останавливать его, переносить подсчитанные монеты в память и сбрасывать счетчик в памяти. Что мне осталось, так это чтение монет на счетчике и подсчет монет в памяти, а также несколько других с такой же функциональностью. Я заметил, что эти процедуры получают статус ложной контрольной суммы, в отличие от других. Я обновил свой вопрос, который показывает, как я реализовал метод счетчика монет и его протокол, если у вас есть время, чтобы взглянуть. Большое спасибо за это - person Eliyah; 03.02.2014
comment
Почти уверен, что команду RTS следует выдавать только на открытом порту. Что касается логики контрольной суммы, трудно сказать, поскольку ваш вопрос не был обновлен процедурой rx, которая отслеживает контрольную сумму. Мне также трудно интерпретировать описание команды чтения счетчика (CR ^ FF00,000016 + CR ^ FF,000016 + CR ^ FF0016 + CR ^ FF16) ^ FF16) - и я думаю, что ACK должен возвращаться к устройству, а не от устройства, как кажется, предлагает ваш рисунок. - person fvu; 03.02.2014
comment
Контрольная сумма вычисляется в методе под названием «Tx2», который вы найдете в коде C#, который я реализовал в своем вопросе. Я думаю, что мне следует обновить свой вопрос, указав весь исходный код паскаля. - person Eliyah; 03.02.2014
comment
Возможно, было бы лучше начать с нового вопроса, эта вещь немного растянулась за пределы своего первоначального объема и, следовательно, стала менее заметной. Теперь, поскольку у меня нет документации по протоколу, я просто предполагаю, но вы уверены, что контрольные суммы используются только при отправке на устройство, а не для защиты данных, отправленных вам? Это было бы очень-очень странно... - person fvu; 03.02.2014
comment
Я думаю ты прав. Я мог бы начать новый вопрос, и вы могли бы следовать за мной там. Я чувствую, что почти готов решить эту проблему. И чтобы ответить на ваш вопрос, пример программы на паскале, приведенный в технической документации, говорит, что для простоты не используются строки рукопожатия и не реализована контрольная сумма. Как я уже отмечал ранее в своих сообщениях, у меня нет большого опыта в области программирования нижнего уровня, поэтому я точно не знаю побочного эффекта этого. - person Eliyah; 03.02.2014
comment
Что ж, это будет не первый раз, когда какой-то технический документ утверждает, что образец не реализует контрольные суммы для простоты, тогда как на самом деле реализация контрольной суммы устройства просто ошибочна :) Рукопожатие - хорошо, но это не значит, что вы не Вам не нужно постоянно включать RTS, чтобы показать свое согласие на получение. - person fvu; 03.02.2014
comment
Я открыл еще один вопрос с заголовком «Как интерпретировать связь интерфейса R232C, написанную с использованием Pascal для C # .Net». Я был бы очень признателен, если бы вы нашли время последовать за мной туда. Спасибо за вашу помощь. :) - person Eliyah; 03.02.2014
comment
Привет фву. Извините, что мне пришлось снова прийти сюда. Я получил следующий результат при прослушивании последовательного порта при выполнении команды «ReadCountReg». (1Б 52 43 .РК). Перед «.RC» есть большое пространство. Это информация, которую я получил из вкладки «запись» сниффера. Однако в разделе «Чтение» ничего не прослушивается. может в этом проблема? - person Eliyah; 04.02.2014
comment
давайте продолжим это обсуждение в чате - person fvu; 04.02.2014

Фу. Регистры 8250 UART задокументированы здесь. Прямой доступ к регистрам UART в Windows не поддерживается. Ваша инициализация SerialPort верна. Имейте в виду, что маловероятно, что на вашей машине есть порт COM1, если только у вас нет реального оборудования. Эмуляторы USB, как правило, выбирают более высокие номера портов. Используйте SerialPort.GetPortNames(), чтобы посмотреть.

Tx() ожидает пустой бит состояния передатчика. Просто замените на SerialPort.Write(), чтобы записать один байт, он уже блокируется, если буфер передачи заполнен. Этого не будет.

RxWait() ожидает бит состояния готовности приемника. Просто замените на SerialPort.ReadByte().

Tx2() — это просто вспомогательная процедура для обновления простой контрольной суммы. Просто добавьте отправляемый байт в переменную checksum.

person Hans Passant    schedule 01.02.2014
comment
Я понимаю. Когда вы сказали «просто добавьте байт, который вы отправляете, в переменную контрольной суммы», это правильно?; - person Eliyah; 01.02.2014
comment
Это верно. Предположительно, вам нужно будет отправить это значение контрольной суммы в качестве последнего байта сообщения. И установите его обратно на 0, прежде чем вы начнете отправлять следующее сообщение. Во всяком случае, обычно так это работает. - person Hans Passant; 01.02.2014
comment
Спасибо за ответ. Сначала я пытался скомпилировать код Pascal с помощью FreePascal, но он не поддерживает массив портов и модуль go32 для win32. Если бы я мог найти способ манипулировать портами из FreePascal любым возможным способом, я бы скомпилировал его в файл dll, а затем экспортировал бы его для использования из C#.NET с помощью вызова платформы. Есть ли способ добиться этого? - person Eliyah; 02.02.2014
comment
Спасибо за вашу помощь. Я написал эквивалентный код С# для кода паскаля и обновил свой предыдущий вопрос. Пожалуйста, с уважением, поправьте меня, если я ошибаюсь, так как у меня нет большого опыта в этом. Большое спасибо. - person Eliyah; 02.02.2014