Страниц: 1
В связи с тем, что компьютерные платы становятся всё дешевле и дешевле (AMD Geode в типоразмере 2.5" стоит нынче 160$), такие платы довольно удобно использовать во многих приложениях реального времени, например, для управления чем-либо, обработки данных и т.п.. Вот тут и всплывает проблема "дальней" связи с такой платой. Наиболее простое решение - реализация интерфейса RS-485.
Вот тут и всплывает давно забытый досовский "дзен", например, работа с COM-портом. Компьютер, как правило, не имеет встроенного интерфейса RS-485, а имеет интерфейс RS-232. Дальность связи по интерфейсу RS-232 редко превышает 10м, а для перехода от интерфейса RS-232 к RS-485 используют переходные адаптеры.
Интерфейс RS-232 предназначен для работы в дуплексном режиме, т.е. может одновременно принимать и передавать данные. Но полный дуплекс редко где используется, имеет смысл "затачивать" программу под полудуплекс, т.е. сразу делать работу по порту совместимой с протоколом RS-485: в этом протоколе передача и приём не могут идти одновременно, т.к. в протоколе RS-485 приём и передача идут по одним и тем же проводам. Ну и как дополнительный аргумент, говорящий в пользу такого подхода - по дуплексному интерфейсу RS-232 полудуплексный софт для RS-485 всегда будет работать ![]()
Вобщем, функции ввода-вывода программы должны быть настроены на работу через интерфейс RS-232, но с учётом RS-485: когда ведём передачу - приёма быть не должно.
Канал RS-485 настроен на приём, и переключается на передачу только в момент появления данных на выходе TxD на COM-порте. Есть автоматические переключатели, которые реализуют ф-цию переключения приём-передача по наличию данных на выводе TxD COM-порта (например, IP-CON), но большинство простых адаптеров используют дополнительный вывод COM-порта для переключения направления приём-передача. Обычно, это вывод RTS - этот вывод участвует в синхронной передаче данных, однако, некоторые поделки успешно используют вывод DTR. Чтобы не было промашки с выводом адаптера, лучше "дёргать" при передаче оба вывода: и RTS, и DTR. При передаче состояние RTS=DTR=0.
Работу c COM-портом под ДОС целесообразно вести без прерываний: производительность обычно избыточная, от пропускания приёма байта спасает кэш в контроллере COM-порта. При такой организации легко реализуются тайминги обмена и обработка ошибок, не требуется сложная привязка к контроллеру прерываний, которые сейчас, мягко говоря, могут несколько отличаться от того, что было 10 лет назад (ага, можно подумать, что каскадное включение 8259 вырублено навечно
)
Пусть есть глобальные переменные: uart_base - базовый адрес порта, uart_rate - скорость в бодах. Ниже приводится ф-ция для инициализации COM-порта. Весь код написан для WATCOM C под экстендер DOS4G.
BOOL link_init(void)
{
BOOL bRet=FALSE;
WORD divider;
divider=(uart_rate)?115200/uart_rate:0xFFFF;
outp(uart_base+3,0x80); // переводим в режим установки скорости
if ((inp(uart_base+3)&0x80)==0) goto lini_exit; // выход, если нет порта
outp(uart_base+0,(BYTE)(divider&0xFF)); // мл.байт делителя
if (inp(uart_base+0)!=((BYTE)(divider&0xFF))) goto lini_exit; // выход, если нет порта
outp(uart_base+1,(BYTE)(divider>>8)); // ст.байт делителя
if (inp(uart_base+1)!=((BYTE)(divider>>8))) goto lini_exit; // выход, если нет порта
outp(uart_base+3,0x03); // управление линией, протокол 8N1
if ((inp(uart_base+3)&0x80)) goto lini_exit; // выход, если нет порта
outp(uart_base+1,0x00); // прерывания выключены
outp(uart_base+4,0x03); //485: DTR==1, RTS=1
outp(uart_base+2,0x07); // сброс FIFO
bRet=TRUE;
lini_exit:
return bRet;
}Ф-ция link_init возвращает TRUE в случае успешной инициализации порта. В принципе, эту ф-цию можно использовать для поиска порта в адресном пространстве PC: в uart_base записываем номер базового порта (3F8h, 2F8h, 3E8h, 2E8h) и вызываем ф-цию link_init. Если ф-ция вернула TRUE - порт есть, FALSE - порта нет.
Для реализации таймаутов удобно использовать таймер системы: достаточно просто читать его текущее значение. В отличие от Windows и Linux, шаг таймера под ДОС грубее - всего 55млС, но для большинства применений этого шага вполне достаточно. Вот ф-ция чтения кол-ва тиков таймера от начала старта системы GetTickCount.
DWORD GetTickCount(void)
{
return *((PDWORD)0x46C);
}Ниже приводится ф-ция приёма байта с учётом таймаута.
BOOL link_rxbyte(PBYTE val)
{
DWORD stoptick;
outp(uart_base+4,0x03); //485: DTR==1, RTS==1
stoptick=GetTickCount()+TIMEOUT_RX;
while ((inp(uart_base+5)&0x01)==0)
if (GetTickCount()>=stoptick) return FALSE;
*val=inp(uart_base+0);
return TRUE;
}Если байт принят, ф-ция возвращает TRUE.
Ф-ция передачи байта, если байт успешно передан, то возвращает TRUE.
BOOL link_txbyte(BYTE val)
{
DWORD stoptick;
stoptick=GetTickCount()+TIMEOUT_TX;
while ((inp(uart_base+5)&0x20)==0) // ожидание освобождения буфера передачи
if (GetTickCount()>=stoptick) return FALSE;
outp(uart_base+4,0x00); //485: DTR==0, RTS==0
outp(uart_base+0,val); // вывод байта
// ожидание конца передачи (для RS485)
stoptick=GetTickCount()+TIMEOUT_TX; //485: установка таймаута ожидания конца передачи
while ((inp(uart_base+5)&0x60)!=0x60) //485: ожидание конца передачи
if (GetTickCount()>=stoptick) return FALSE; //485: выход по таймауту
outp(uart_base+4,0x03); //485: DTR==1, RTS==1
outp(uart_base+2,0x07); //485: убиваем эхо, сбросом FIFO
return TRUE;
}Вот собственно и всё. Всё просто ![]()
Неактивен
Страниц: 1