Принцип последовательного ввода-вывода можно применить не только по отношению к клавиатуре, но и для других потребностей. Рассмотрим возможность подключения к контроллеру регистра сдвига, для реализации возможности параллельного обмена данными контроллера с внешним устройством. Схема подключения регистра приведена на рис. 1.
В табл. 1 приведена таблица истинности работы регистра в разных режимах работы. При программировании следует учесть, что в процессе установки уровней на управляющих входах /CS и W/R, уровень на входе синхросигнала CLK должен быть высоким. Состояние адресного входа C/D для режима работы регистра безразлично, поэтому в таблице не указано.
Режим работы регистра | /CS | W/R | /Z1 | S0 | S1 | D0-D7 |
Сдвиг вправо | H | H | L | H | L | data |
Запись во внешнее устройство | L | H | L | L | L | data |
Чтение из внешнего устройства | L | L | H | H | H | Z-состояние |
Подпрограмма для сдвига байта в регистре приведена на рис. 2. Написана она в ассемблерном варианте, т.к., естественно, скорость работы ассемблерного варианта примерно на порядок превышает скорость варианта на Си. При тактовой частоте контроллера 20 МГц скорость приема/передачи данных равна примерно 80 кбит.
#define BYTE unsigned char BYTE ShiftByte(BYTE); // прием/выдача байта в сдвиговый регистр ... // ------------------------------------------------ // BYTE ShiftByte(BYTE) // ------------------------------------------------ // Прием/передача байта в регистр сдвига // ------------------------------------------------ // P24 - /MCS2 - J2.28 - (O10) - Выход последовательных данных // P25 - /MCS3 - J2.29 - (O11) - Выход синхроимпульсов сдвига данных // P19 - RTS1/PCS3 - J2.25 - (IO8) - Вход последовательных данных // P26 - UZI - J2.30 - (O12) - Выход сигнала чтения/записи // P29 - /CLKDIV2 - J2.31 - (O13) - Выход сигнала выбора внешнего устройства // ------------------------------------------------ // 1. выводим 8 бит данных в последовательный регистр, одновременно считывая // из него последовательные данные // // 2. перед началом выходы: синхроимпульса (P25), чтения/записи (P26), выбора // внешнего устройства (P29) должны быть установлены в высокое состояние (H). // BYTE ShiftByte(BYTE d) { BYTE b; asm mov dx, 0xff7a // загружаем в DX адрес порта (16-32) линий вв/выв. // устанавливаем высокие уровни на выходах управления asm in ax, dx // Не забываем, что сигналы на выходах должны быть инвертированы!!! asm and ax, 0xd9ff asm out dx, ax // готовим данные для цикла чтения asm mov bl, d asm mov bh, 0 asm mov cx, 8 // цикл для приема/передачи 8 бит данных asm cycle: asm in ax, dx asm mov es, ax // считываем бит входных данных asm and ax, 0x0008 // считываем бит входных данных asm jz d0 asm clc // устанавливаем флаг переноса если бит данных 1 asm jmp d1 asm d0: asm stc // сбрасываем бит переноса если бит данных 0 asm d1: asm rcl bh, 1 // сдвигаем перенос в регистр asm mov ax, es // востанавливаем AX asm or ax, 0x0200 // установим LOW на линии CLK при подготовке бита данных // устанавливаем бит выходных данных // с учетом инвертора на выходе порта контроллера, выходной // бит выдается в обратной полярности к оригинальному!!! asm rcl bl, 1 // сдвигаем влево со старшего разряда asm jc m0 asm or ax, 0x0100 // если бит данных 0 asm jmp m1 asm m0: asm and ax, 0xfef7 // если бит данных 1 asm m1: asm out dx, ax // формируем передний фронт CLK для сдвига данных в регистре asm and ax, 0xfdf7 asm out dx, ax asm loop cycle asm mov b, bh return b; }Рис. 2.
ShiftByte — подпрограмма приема/передачи последовательных данных через регистр сдвига.
Конечно было бы правильнее, при сдвиге данных переводить выходы регистра в третье состояние, а сдвигаемый бит брать с отдельного выхода восьмого разряда регистра. Однако есть предположение, что входы/выходы сдвигаемых бит можно будет использовать для работы с другими устройствами, поэтому может оказаться удобнее иметь возможность перевести выход регистра, подключенный ко входу сдвигаемых данных DI, в третье состояние, тем самым освободив этот вход контроллера для работы с другими последовательными устройствами.
Подпрограмма записи данных из внешнего устройства в регистр (операция чтения ReadByte) приведена на рис. 3. Нужно учитывать, что после этой операции байт записывается только в регистр, а для его считывания контроллером нужно выполнить функцию ShiftByte. По этой причине функция не возвращает значения, а передается ей только адрес регистра внешнего устройства.
void ReadByte(char); // чтение байта из внешнего устр. в регистр. ... // // ------------------------------------------------ // void ReadByte(char addr) // addr - адрес регистра внешнего устройства (0 или 1) // ------------------------------------------------ // чтение байта из внешнего устройства в регистр сдвига // ------------------------------------------------ // P24 - /MCS2 - J2.28 - (O10) - Выход последовательных данных // P25 - /MCS3 - J2.29 - (O11) - Выход синхроимпульсов сдвига данных // P19 - RTS1/PCS3 - J2.25 - (IO8) - Вход последовательных данных // P26 - UZI - J2.30 - (O12) - Выход сигнала чтения/записи // P29 - /CLKDIV2 - J2.31 - (O13) - Выход сигнала выбора внешнего устройства // T7 - J2.32 - (O14) - Выход выбора регистра внешнего устройства // ------------------------------------------------ // 1. перед началом выходы: синхроимпульса (P25), чтения/записи (P26), выбора // внешнего устройства (P29) должны быть установлены в высокое состояние (H). // // 2. параметр addr указывает на адрес опрашиваемого регистра внешнего // устройства. В данном варианте можно опрашивать устройство, имеющее // ТОЛЬКО ДВА РЕГИСТРА, поэтому параметр addr может быть только 0 или 1. // // 3. следует учесть, что считанный байт находится в регистре, и для его // считывания контроллером следует использовать функцию ShiftByte // void ReadByte(char addr) { // устанавливаем адрес регистра внешнего устройства // не забываем о наличии инвертора на выходе контроллера!!!! asm mov dx, 0x0007 asm mov al, addr asm xor al, 0x1 // инвертируем адрес регистра asm out dx, al // устанавливаем сигнал чтения на шине P26 (LOW), тем самым переводя // выходы регистра в третье состояние asm mov dx, 0xff7a asm in ax, dx asm or ax, 0x0400 asm out dx, ax // устанавливаем сигналы выбора устройства (P29) и синхроимпульса (P25) // в состояние LOW, подготавливая регистр и внешнее устройство к чтению asm in ax, dx asm or ax, 0x2200 asm out dx, ax // формируем передний фронт синхроимпульса (P25) для записи в регистр asm and ax, 0xfdff asm out dx, ax // перед завершением функции устанавливаем высокие уровни для всех // управляющих сигналов asm in ax, dx // Не забываем, что сигналы на выходах должны быть инвертированы!!! asm and ax, 0xd9ff asm out dx, ax }
Рис. 3.
ReadByte — подпрограмма чтения байта из внешнего устройства в регистр сдвига.
Подпрограмма записи байта из регистра сдвига во внешнее устройство (WriteByte) приведена на рис. 4. Так же, как и при работе с ReadByte следует учесть, что перед обращением к данной функции необходимо с помощью ShiftByte "сдвинуть" записываемый байт из контроллера в регистр. По этой причине (как и при работе с ReadByte) функции передается только адрес регистра внешнего устройства.
void WriteByte(char); // запись байта во внешнее устр. из регистр. ... // ------------------------------------------------ // void WriteByte(char addr) // addr - адрес регистра внешнего устройства (0 или 1) // ------------------------------------------------ // запись байта во внешнее устр. из регистра сдвига // ------------------------------------------------ // P24 - /MCS2 - J2.28 - (O10) - Выход последовательных данных // P25 - /MCS3 - J2.29 - (O11) - Выход синхроимпульсов сдвига данных // P19 - RTS1/PCS3 - J2.25 - (IO8) - Вход последовательных данных // P26 - UZI - J2.30 - (O12) - Выход сигнала чтения/записи // P29 - /CLKDIV2 - J2.31 - (O13) - Выход сигнала выбора внешнего устройства // T7 - J2.32 - (O14) - Выход выбора регистра внешнего устройства // ------------------------------------------------ // 1. перед началом выходы: синхроимпульса (P25), чтения/записи (P26), выбора // внешнего устройства (P29) должны быть установлены в высокое состояние (H). // // 2. параметр addr указывает на адрес опрашиваемого регистра внешнего // устройства. В данном варианте можно опрашивать устройство, имеющее // ТОЛЬКО ДВА РЕГИСТРА, поэтому параметр addr может быть только 0 или 1. // // 3. следует учесть, что записываемый байт должен находиться в регистре, и для его // занесения в регистр следует использовать функцию ShiftByte // void WriteByte(char addr) { // устанавливаем адрес регистра внешнего устройства // не забываем о наличии инвертора на выходе контроллера!!!! asm mov dx, 0x0007 asm mov al, addr asm xor al, 0x1 // инвертируем адрес регистра asm out dx, al // устанавливаем сигнал записи на шине P26 (HIGH) asm mov dx, 0xff7a asm in ax, dx asm out dx, ax // устанавливаем сигналы выбора устройства (P29) и синхроимпульса (P25) // в состояние LOW, подготавливая регистр и внешнее устройство к записи asm in ax, dx asm or ax, 0x2200 asm out dx, ax // формируем передний фронт синхроимпульса (P25) для чтения из регистра asm and ax, 0xfdff asm out dx, ax // перед завершением функции устанавливаем высокие уровни для всех // управляющих сигналов asm in ax, dx // Не забываем, что сигналы на выходах должны быть инвертированы!!! asm and ax, 0xd9ff asm out dx, ax }
Рис. 4.
WriteByte — подпрограмма записи байта из регистра сдвига во внешнее устройство.
Таким образом, у нас имеется набор функций, потребных для работы с внешним устройством. Остается применить их на практике, и посмотреть на результат. Для этого возьмем, например, дисплей на основе контроллера T6963C и посмотрим, что с ним можно сделать.
В заключение нужно добавить, что данная конструкция может работать с любым контроллером, который имеет 6 свободных линий ввода/вывода, следует лишь соответствующим образом изменить команды управления портами в соответствии с логикой работы соответствующего контроллера.