ANTICHAT.XYZ    VIDEO.ANTICHAT.XYZ    НОВЫЕ СООБЩЕНИЯ    ФОРУМ  
Баннер 1   Баннер 2
Antichat снова доступен.
Форум Antichat (Античат) возвращается и снова открыт для пользователей. Здесь обсуждаются безопасность, программирование, технологии и многое другое. Сообщество снова собирается вместе.
Новый адрес: forum.antichat.xyz
Вернуться   Форум АНТИЧАТ > ИНФО > Статьи > Чужие Статьи
   
 
 
Опции темы Поиск в этой теме Опции просмотра

  #2  
Старый 07.11.2007, 14:15
Аватар для FraiDex
FraiDex
Участник форума
Регистрация: 16.06.2006
Сообщений: 179
Провел на форуме:
515368

Репутация: 135
Отправить сообщение для FraiDex с помощью ICQ
По умолчанию

Внутренний регистр состояния модема (БА+6):
(только чтение)


Благодаря этому регистру драйвер может отслеживать изменения состояния модема (или другого устройства). С помощью него можно определить сигналы на соответствующих линиях, а также наличие изменения состояния линий с момента последнего обращения к регистру.

бит 0 Изменение на линии CTS
бит 1 Изменение на линии DSR
бит 2 Изменение на линии RI
бит 3 Изменение на линии DCD
бит 4 Активный сигнал на линии CTS
бит 5 Активный сигнал на линии DSR
бит 6 Активный сигнал на линии RI
бит 7 Активный сигнал на линии DCD

Перед первым использованием адаптера асинхронной последовательной связи, совместимого с 8250, нам необходимо произвести его инициализацию, указав ему следующие параметры: частоту передачи, количество информационных битов (ради которых мы всё это и затеяли), количество стоп-битов и наличие бита чётности в кадре, а для более продвинутых контроллеров (16550 UART) можно также определить размер буфера FIFO и разрешить/запретить DMA. Эту опреацию проводит BIOS при запуске компьютера, но когда мы захотим изменить параметры по умолчанию, нам придётся переинициализировать адаптер ещё раз. Детально это выглядит так:
0) Контроль ошибок
1) Устанавливаем регистр управления линией (БА+3) следующим образом (см. выше):
биты 0,1 - длина слова
бит 2 - число стоп-битов
биты 3,4,5 - способ контроля по чётности
бит 6 - 0=обычное функционирование, 1=сигнал BREAK
бит 7 - 0=режим передачи/приёма данных, 1=режим выбора делителя частоты
Во время инициализации бит 7 устанавливаем в еденицу, в бите 6 всегда выбираем первое.
2) Пишем в БА+0 и в БА+1 младший и старший байты делителя частоты. Делитель частоты - это число, получаемое делением магического числа 1 843 200 на желаемую скорость, помноженную на 16. Магическое число - это частота внутреннего источника синхронизации в 1843200 Гц. Делитель частоты, равный еденице соответствует максимальной частоте в 115 200 бит/с, т.к. 1843200/(115200*16) = 1.
3) Устанавливаем бит 7 регистра БА+3 в нуль. Если пропустить этот пункт, то программа не сможет передавать/принимать данные через интерфейс и разрешать/запрещать прерывания, т.к. будет считать эти данные делителем частоты.
4) При необходимости можно также включить режим буферизации (FIFO) и прямого доступа к памяти (DMA) для микросхемы 16550 UART путём изменения регистра БА+2

Код:
/*
* 8250.c v0.1
* RS-232 I/O Driver.
* Copyright (c) Pashix, 2005-06, mailto: pashix(at)pochta.ru
*/

#define MAX_RS232_IF 5 // Максимальное количество адаптеров RS-232

#define _ADAPTER_FREQ 1843200L // Частота источника синхронизации, константа
#define _16550_UART_FIFO // Этот дефайн я использую для включения/выключения буферизации

unsigned long RS232_base_ports [MAX_RS232_IF]; // В этом массиве мы будем хранить базовые адреса для каждого интерфейса

/* Использование такого массиа позволит нам указать базовый адрес только один раз, в функции RS232_init(),
а в остальных функциях указывать лишь номер адаптера. Просто так удобнее =) */

char RS232_init(unsigned char num, unsigned short base_port, unsigned char parity,
unsigned char stop_bits, unsigned char word_length, unsigned long baud_rate)

// Функция инициализациии RS-232

// num - номер интерфейса (от 0 до MAX_RS232_IF), base_port - БА, parity - контроль по чётности
// stop_bits - количество стоп-битов, word_length - длина слова, baud_rate - желаемая скорость передачи/приёма

{
char format_byte;
union
{
int baud_divisor;
char bytes[2];
}word;

// Контроль ошибок:
if ((num > MAX_RS232_IF-1)||(word_length > 3)||(stop_bits > 1)||(parity > 7)||(!baud_rate)) return RS232_INVALID_PARAM;

RS232_base_ports[num] = base_port; // Запомнили БА для данного интерфейса, он нам пригодится в других функциях

format_byte = 0×3F & ((word_length)|(stop_bits << 2)|(parity << 3));
word.baud_divisor = (_ADAPTER_FREQ / baud_rate) >> 4;

outportb(base_port+3, format_byte | 0×80); // (Шаг 1)
outportb(base_port+0, word.bytes[0]); // (Шаг 2)
outportb(base_port+1, word.bytes[1]);
outportb(base_port+3, format_byte); // (Шаг 3)

#ifdef _16550_UART_FIFO // Если мы разрешили FIFO для микросхемы 16550 UART
outportb(base_port+2, 0xC7); // Включаем режим FIFO и устанавливаем размер буфера в 16 байт (Шаг 4)
#endif

return RS232_NOERROR;
}
Опишем, заодно, функцию проверки инициализации RS-232. Эта функция поможет установить, был ли инициализирован контроллер:
Код:
char isRS232_init(unsigned char num)
{ // Проверка инициализации адаптера (была ли использована функция RS232_init()?)
return (RS232_base_ports[num] != 0)?RS232_NOERROR:RS232_NOT_INIT;
}
После этого данные, записанные в регистр БА+0 будут передаваться через интерфейс к устройству, а данные, прочитанные из этого регистра будут приниматься из интерфейса RS-232. Но не всё так просто! Не стоит забывать, что при работе с каким-либо устройством нам сначала нужно его инициализировать (попросту говоря, включить и подготовить), а также про то, что перед любой передачей/приёмом данных нам нужно дожидаться аппаратного подтверждения готовности.

Для того, что бы проверить готовность интерфейса передать данные драйверу на обработку и готовность устройства получить данные от драйвера через интерфейс RS-232 необходимо прочитать биты готовности из соответствующих портов ввода/вывода (адаптер должен быть инициализирован функцией RS232_init() перед этим). Если нужная функция возвращает значение RS232_NOERROR - можно передавать следующий байт
Код:
char RS232_receive_ready(unsigned char num)
{ // Функция возвращает признак готовности приёмника (проверяет бит 0 в БА+5)
return (inportb(RS232_base_ports[num]+5) & 0×01)?RS232_NOERROR:RS232_NOT_READY;
}

char RS232_send_ready(unsigned char num)
{ // Функция возвращает признак готовности передатчика (проверяет бит 4 в БА+6)
return (inportb(RS232_base_ports[num]+6) & 0×10)?RS232_NOERROR:RS232_NOT_READY;
}
А теперь - самые важные функции: функции отправки и получения байта в/из адаптера RS-232. Обращаю внимание на то, что в них отсутствует проверка правильности инициализации. RS232_receivebyte() и RS232_sendbyte() используют массив RS232_base_ports[], в котором хранятся базовые адреса для инициализированных функцией RS232_init() адаптеров.
Код:
char RS232_receivebyte(unsigned char num)
{ // Чтение байта из интерфейса
return inportb(RS232_base_ports[num]);
}

void RS232_sendbyte(unsigned char num, char byte)
{ // Отправка байта в интерфейс
outportb(RS232_base_ports[num], byte);
}
В принципе, написанных функций уже будет достаточно для обмена информацией по линиям интерфейса RS-232, но перед тем, как мы начнём работу с устройством (например, с модемом или с мышкой), не плохо бы было произвести его инициализацию, да и просто проконтролировать его работоспособность. С точки зрения программирования бывает 2 типа устройств для RS-232: умные и не очень =). Различие в том, что умные устройства (например, модемы) могут аппаратно поддтверждать своё включение и готовность, выставляя сигналы на соответствующих линиях (см. таблицу “сигналы интерфейса RS-232″). Не очень умные (например, мышки) - не могут.

Для инициализации не очень умного устройства можно воспользоваться схемой:

1) Выставляем сигнал на линиях DTR и RTS

2) Если надо, выставляем сигнал на линии OUT2 для разрешения генерации IRQ

Схема инициализации умного, такого как модем, устройства выглядит несколько сложнее:

1) Выставляем сигнал на линии DTR

2) Ждём немного

3) Проверяем сигнал на линии DSR: если он отсутствует - модем не отвечает (или его просто нет), тогда выходим с ошибкой 1

4) Выставляем сигнал на линии DTR и RTS одновременно

5) Ждём немного

6) Если модем не ответил сигналом на линии CTS - питание включено, но он не готов к обмену информацией, тогда выходим с ошибкой 2

7) Если надо, выставляем сигнал на линии OUT2

Исходя из этих двух алгоритмов, напишем ещё четыре функции инициализации и деинициализации различных устройств, подключаемых к RS232 (хотя к самому микроконтроллеру 8250 они не относятся, они необходимы для выставления и проверки соответствующих сигналов на линиях):
Код:
char RS232_modem_on(unsigned char num)
{ // Инициализация модема
char mcr;
unsigned long wait;

if(num>MAX_RS232_IF-1) return RS232_INVALID_PARAM;

mcr = inportb(RS232_base_ports[num]+4);
outportb(RS232_base_ports[num]+4, mcr | 0×01); // Выставляем DTR (Data Terminal Ready)

wait = _WAIT;
while(wait–); // Задержка

if(!(inportb(RS232_base_ports[num]+6) & 0×20))
{ // Если сигнал DSR (Data Set Ready) не поступил - модем не отвечает
outportb(RS232_base_ports[num]+4, mcr); // Восстанавливаем регистр
return RS232_NO_MODEM;
}

outportb(RS232_base_ports[num]+4, mcr | 0×03); // Выставляем DTR и RTS (Request To Send)

wait = _WAIT;
while(wait–); // Задержка

if(!(inportb(RS232_base_ports[num]+6) & 0×10))
{ // Если нет сигнала CTS (Clear To Send) - модем есть, но не готов
outportb(RS232_base_ports[num]+4, mcr); // Восстанавливаем регистр
return RS232_MODEM_NOT_READY;
}

outportb(RS232_base_ports[num]+4, mcr | 0×0B); // Выставляем сигналы DTR, RTS и OUT2

// Очищаем все возможные события аппаратного прерывания:
inportb(RS232_base_ports[num]+0);
inportb(RS232_base_ports[num]+5);
inportb(RS232_base_ports[num]+6);

return RS232_NOERROR;
}

void RS232_modem_off(unsigned char num)
{ // Деинициализация (выключение) модема
RS232_device_off(num); // Вызываем функцию RS232_device_off(), описанную ниже
}

char RS232_device_on(unsigned char num)
{ // Инициализация устройства (из разряда не очень умных =) )
char mcr;

if(num>MAX_RS232_IF-1) return RS232_INVALID_PARAM;

mcr = inportb(RS232_base_ports[num]+4);
outportb(RS232_base_ports[num]+4, mcr | 0×03); // Выставляем DTR и RTS

return RS232_NOERROR;
}

void RS232_device_off(unsigned char num)
{ // Деинициализация устройства (подходит и для модемов)
if(num>MAX_RS232_IF-1) return;

outportb(RS232_base_ports[num]+4, 0×00); // Очищаем все биты управления

// Очищаем все возможные события аппаратного прерывания:
inportb(RS232_base_ports[num]+0);
inportb(RS232_base_ports[num]+5);
inportb(RS232_base_ports[num]+6);
}
_WAIT - это некоторое время задержки между тем, как модем (или другое “умное” устройство) обрабатывает наш сигнал и отвечает на него. Если значение _WAIT слишком мало, устройство может не успеть ответить на наш запрос, и функция вернёт ошибку. Если слишком велико - будет ощутимая пауза при работе драйвера, что также не допустимо. Задавать его рекомендую в дефайне в начале программы таким образом: #define _WAIT 0×0FF. При инициализации другого устройства (не модема), задержки не требуются, поскольку ответа в любом случае не будет.

В некоторых случаях (особенно, если мы используем обработчик аппаратного прерывания) нам будет необходимо контролировать ошибки передачи данных по интерфейсу. Для этого воспользуемся достаточно примитивными функциями обнаружения ошибки кадрирования RS232_framing_error() и ошибки паритета, то есть контроля по чётности/нечётности RS232_parity_error():
Код:
char RS232_framing_error(unsigned char num)
{ // Определяем наличие ошибки кадрирования
return (inportb(RS232_base_ports[num]+5) & 0×08)?RS232_FRAMING_ERROR:RS232_NOERROR;
}

char RS232_parity_error(unsigned char num)
{ // Определяем наличие ошибки паритета
return (inportb(RS232_base_ports[num]+5) & 0×04)?RS232_PARITY_ERROR:RS232_NOERROR;
}
Следующая функция также является очень интересной для программиста. С технической стороны, она всего лишь определяет наличие сигнала RI на линии интерфейса. При подключении к адаптеру RS-232 модема, сигнал будет генерироваться последним в случае звонка на телефонной линии. Это позволит нам программно среагировать на входящий звонок (например, снять трубку и установить соединение с удалённым модемом).
Код:
char RS232_modem_ring(unsigned char num)
{
if(num>MAX_RS232_IF-1) return RS232_INVALID_PARAM;

return (inportb(RS232_base_ports[num]+6) & 0×40)?RS232_NOERROR:RS232_MODEM_NO_RING;
}
Ну и последнее (уже предчувствую радостный вздох читателя =) ). При написании драйвера многозадачной операционной системы нужно учитывать то, что вряд ли у нас в распоряжении будет много процессорного времени, и наш драйвер не должен постоянно (в цикле) опрашивать контроллер RS-232. Например, при попытке отправки/получения байта некой программой через наш драйвер, последний, вместо того, что бы “подвесив” всю систему ожидать готовности интерфейса, должен будет активизироваться только когда контроллер сам сообщит ему о готовности. Для этого мы будем пользоваться обработчиком аппаратного прерывания, который сможет автоматически активизировать драйвер по изменении состояния интерфейса.

Говоря человеческим языком, мы повесим свой обработчик на аппаратное прерывание. Про то, как это делается, можно прочитать в соответствующей документации по программированию. А в этой статье я расскажу про то, как заставить контроллер генерировать такое прерывание (IRQ). Итак, функция, устанавливающая события, при которых сгенерируется IRQ:
void RS232_IRQ_init(unsigned char num, unsigned char can_receive, unsigned char can_send,
unsigned char line_status_change, unsigned char modem_status_change)

{ // Устанавливаем события для генерации IRQ
outportb(RS232_base_ports[num]+1,
(can_receive?1:0)|(can_send?2:0)|(line_status_chan ge?4:0)|(modem_status_change?8:0));
/*
can_receive=1 - генерировать IRQ в случае готовности получения данных контроллером
can_send=1 - генерировать IRQ в случае готовности отправления данных контроллером
line_status_change=1 - генерировать IRQ в случае изменения состояния линии
modem_status_change=1 - генерировать IRQ в случае изменения состояния модема

Если эти параметры равны нулю - прерывание не будет генерироваться по этим событиям.
Если установить все параметры (кроме номера интерфейса num) в ноль - IRQ контроллером не сгенерируется.
*/
}

Вот, собственно, и всё, что можно сказать про программирование драйвера RS-232. В заключении можно отметить то, что практически все функции возвращают коды ошибок (либо значение RS232_NOERROR, что свидетельствует об отсутствии ошибки), про которые мы не сказали ни слова. Опишем наши коды завершения в отдельнов файле rs232err.h, который подключим к основному модулю с помощью дерективы #include “rs232err.h”:
Код:
/*
* rs232err.h
* Copyright (c) Pashix, 2005-06, mailto: pashix(at)pochta.ru
*/

#ifndef __RS232ERR_H
#define __RS232ERR_H

#define RS232_NOERROR 0
#define RS232_INVALID_PARAM -1
#define RS232_NOT_INIT -2
#define RS232_NOT_READY -3
#define RS232_NO_MODEM -4
#define RS232_MODEM_NOT_READY -5
#define RS232_MODEM_NO_RING -6
#define RS232_FRAMING_ERROR -7
#define RS232_PARITY_ERROR -8

#endif
В своей версии драйвера я ещё добавил дополнительную функцию, возвращающую строку с описанием ошибки в удобочитаемой форме.

Заметим, также, что всё взаимодействие с контроллером 8250 происходит через порты ввода/вывода с помощью функций inportb() и outportb(). Эти функции также должны быть описаны в драйвере (описание этих функций на встроенном ассемблере дано с учётом компиляции в GCC и не подходит для Borland C v3 и др.):
Код:
void outportb(unsigned short port, unsigned char value)
{ // Запись в порт ввода/вывода
asm(”outb %b0,%w1″::”a”(value), “d”(port));
}

unsigned char inportb(unsigned short port)
{ // Чтение из порта ввода/вывода
unsigned char value;
asm(”inb %w1, %b0″:”=a”(value): “d”(port));
return value;
}
Если Вы хотите использовать аналогичные функции при компиляции в Borland C, замените данный исходный текст на текст, с использованием ключевого слова _asm или используйте библиотечный функции. Про то, как это сделать прочитайте в документации по _asm, inportb и outportb во встроенной справочной состеме. В целях оптимизации этого исходного текста по скорости вместо описания функций можно использовать директиву #define, за счёт которой в программе не будет не нужных переходов. Самостоятельно =)

Copyright (c) Pashix, 2005-06, pashix(at)pochta.ru
 
Ответить с цитированием
 



Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Ссылки на сайты и книги по C#, Программирование на C# NeuRonix С/С++, C#, Delphi, .NET, Asm 8 11.12.2009 11:02
Программирование в Symbian MafiaBoy3 С/С++, C#, Delphi, .NET, Asm 12 23.04.2007 22:40
Безопасное программирование на Php ОТЕЦ PHP, PERL, MySQL, JavaScript 2 28.09.2006 00:20
Программирование, как оно есть... Mertvii-Listopad Статьи 0 20.03.2006 01:22



Здесь присутствуют: 1 (пользователей: 0 , гостей: 1)
 


Быстрый переход




ANTICHAT.XYZ