Автор статьи Семенов Ю.В.
Пример использования модуля USB.
<<< Начало       Окончание >>>
Здесь рассматривается использование
модуля USB совместно с микроконтроллером PIC18F458 фирмы «Microchip» и индикатора
WD-C1602Q-1YLYc фирмы «Wintek». Пример не имеет практической ценности, но необходим
для понимания принципов организации связи по USB со стороны микроконтроллера.
Учебная задача сформулирована следующим
образом:
- На индикатор должен выводиться текст, вводимый из окна редактирования
формы на экране монитора компьютера.
- Этот же текст должен возвращаться устройством по USB в другое окно той же
формы.
- Устройство должно позволять перемещать курсор на индикаторе влево и
вправо, а так же полностью очищать индикатор.
- Максимальная длина текста ограничена шестнадцатью символами, что
соответствует одной строке индикатора.
Задача решается поэтапно. Сначала
на основании документов [5] и [6] разрабатывается схема гипотетического
устройства. Она приведена на рис. 5. В принципе, для микроконтроллеров с малым
числом выводов можно объединить шину данных модуля USB и индикатора, оставив
разделёнными лишь управляющие сигналы обоих
устройств. С целью упрощения
использования модуля, его выводы были перенумерованы в соответствии с разводкой
печатной платы. Таблица соответствия приведена в таблице 2.
№ |
Наименование сигнала |
Поз.обозначение |
1 |
GND – общий |
X15 |
2 |
D0 – данные |
X1 |
3 |
D1 – данные |
X3 |
4 |
D2 – данные |
Х4 |
5 |
D3 – данные |
X5 |
6 |
D4 – данные |
X6 |
7 |
D5 – данные |
X7 |
8 |
D6 – данные |
X8 |
9 |
D7 – данные |
X9 |
10 |
RD# - вход чтения |
X10 |
11 |
WR – вход записи |
X11 |
12 |
TXE# - выход буфера передачи |
X12 |
13 |
RXF# - выход буфера приёма |
X13 |
14 |
GND – общий |
X14 |
Рис.5. Схема включения модуля USB и двухстрочного индикатора совместно с
микроконтроллером
Программа для микроконтроллера PIC18F458.
Предлагаемая программа по всей
вероятности не самая совершенная, но она позволяет решить поставленную выше
задачу со стороны микроконтроллера. При его рассмотрении всё, что касается
индикатора, не обсуждается. Ответы на вопросы по индикатору читатель найдёт в
комментариях к программе. Блоки,
работающие с USB, будут подробно рассмотрены ниже.
;Порт В ;Шина ввода-вывода для индикатора ;Порт С ;RC1 - выход
управления индикатором RS ;RC2 - выход управления индикатором R/W ;RC3 -
выход управления индикатором E ;RC4 - выход управления USB RD# ;RC5 -
выход управления USB WR ;RC6 - вход с контроллера USB TXE# ;RC7 - вход с
контроллера USB RXF# ;Порт D ;Шина ввода-вывода контроллера
USB LIST
P=18F458; #include
; В соответствии со схемой
на рис.5 производится назначение выводов порта С, предназначенных для работы с
модулем USB. RD# четвёртый вывод, WR пятый, TXE# и RXF# шестой и седьмой
соответственно. Выводу WR не удалось присвоить родное обозначение из-за того,
что оно зарезервировано во включаемом файле P18F458.INC для других целей. RD#
equ 0x04 ;выход управления USB RD# WRITE equ 0x05 ;выход управления USB
WR TXE# equ 0x06 ;вход с контроллера USB TXE# RXF# equ 0x07 ;вход с
контроллера USB RXF#
Далее следует текст, касающийся
индикатора.
RS equ 0x01 ;выход управления индикатором RS RW equ 0x02 ;выход управления
индикатором R/W E equ 0x03 ;выход управления индикатором
E CBLOCK
0x000 IndStat ;регистр состояний
индикатора CntSymb ;счётчик
символов Memory
;память IndBuf0
;символ
ENDC ;******************************************************************************
org 0x00 ; goto START
; ;****************************************************************************** ;Таблица
перекодировки русских букв org
0x22 CodTab data
0xa041,0xa142,0x45e0,0xa4a3,0xa6a5,0xa74b,0x484d,0xa84f
data
0x4350,0xa954,0x58aa,0xabe1,0xe2ac,0xaead,0xaf62,0xb1b0
data
0xb261,0xb4b3,0x65e3,0xb7b6,0xb9b8,0xbbba,0xbdbc,0xbe6f
data 0x6370,0x79bf,0x78e4,0xc0e5,0xe6c1,0xc3c2,0xc5c4,0xc7c6 ;инициализация
микро-ЭВМ START bcf INTCON,GIE,0 ;запрет всех
прерываний; clrf PORTA,0
;программирование порта clrf LATA,0 ;А
на ввод; movlw 0x01
; movwf ADCON0,0
; movlw 0x8C
; movwf ADCON1,0
; movlw 0xff ;программирование порта
А movwf TRISA,0 ;на
ввод; clrf PORTB,0
;программирование clrf LATB,0 ;порта
В movlw 0xff
;на movwf TRISB,0
;ввод; clrf PORTC,0
;программирование clrf LATC,0 ;RC6, RC7,
RC0 movlw 0xc1 ;на ввод,
а movwf TRISC,0 ;остальные на
вывод; При начальных установках, сразу
после первой инициализации порта С, выводы WR и RD# переводятся в пассивное
состояние. bsf PORTC,WRITE,0
;WR=1; bsf PORTC,RD#,0
;RD#=1; После этих команд на указанных
выводах появится высокий уровень, что переведёт шину данных модуля USB в третье
состояние. Далее следует текст, касающийся индикатора и инициализации отдельных
узлов микроконтроллера. bsf PORTC,RS
;RS=1; bsf PORTC,RW
;RW=1; bcf PORTC,E
;E=0; clrf PORTD,0
; clrf LATD,0
; movlw 0x07
; movwf CMCON,0
; Шина данных модуля USB, то есть порт D,
при начальной инициализации программируется на
ввод. movlw 0xff
;программирование movwf TRISD,0 ;порта D
на ввод; clrf PORTE,0
;программирование clrf LATE,0 ;порта
Е movlw 0x03 ;на
ввод; movwf TRISE,0
; movlw 0xc0
; movwf T0CON,0
; ;****************************************************************************** ;индикатор
clrf TBLPTRU,0 ;очистка указателя clrf
TBLPTRH,0 ;двнных в памяти программ;
movlw 0x9e ;задержка 40 мс; movwf
TMR1H,0 ; movlw 0x57
; movwf TMR1L,0
; movlw 0xb1
; movwf T1CON,0
; bcf PIR1,TMR1IF ; M1 btfsc
PIR1,TMR1IF ; bra M2
; bra M1 ; M2 clrf IndStat,0 ;регистр
состояния индикатора=начало; Begin movlw 0x0d ;если(!регистр состояния
индикатора==вывод первой строки|| xorwf
IndStat,w,0 ; !регистр состояния индикатора==|| вывод второй
строки) bz M16 ; переход к
переключателю movlw 0x11 ;
xorwf IndStat,w,0
; bnz M15
; В этом месте производится чтение из
буфера приёма модуля USB. В начале опрашивается вывод RXF#. Если на нём высокий
уровень, то буфер пуст и чтение не производится. M16 btfsc PORTC,RXF#,0
;если(RXF#) переход к концу цикла; bra
M12 ; В случае, когда в буфере приёма
есть хотя бы один символ, вывод RXF# имеет низкий уровень, и выполняются
действия в соответствии с временной диаграммой, представленной на
рис.3. bcf PORTC,RD#,0 ;сброс
RD# После этой команды уровень на выводе
RD# становится низким, что приводит к тому, что на выводах порта D,
запрограммированного на ввод, через время Т3 появятся
данные. movff PORTD,IndBuf0
;символ=портD; Затем эти данные попадают
с выводов порта D в приёмный буфер из одного байта в данном
случае. bsf PORTC,RD#,0 ;установка
RD#; Эта команда переводит вывод RD# в
состояние с высоким уровнем, что означает окончание цикла чтения данных из
буфера приёма модуля USB. Далее следует обработка полученных данных в
соответствии с поставленной
задачей.
movf IndBuf0,w,0 ;если(символ==0х80)
xorlw 0x80 ; { bnz M18
; movff IndStat,Memory ; память=текущее
состояние индикатора; movlw 0x12 ;
регистр состояния индикатора=сдвиг курсора
влево; movwf IndStat,0 ; переход к
переключателю; bra M15 ; }; M18 movf
IndBuf0,w,0 ;если(символ==0х81) xorlw
0x81 ; { bnz M181 ;
movff IndStat,Memory ; память=текущее
состояние индикатора; movlw 0x14 ;
регистр состояния индикатора=сдвиг курсора
вправо; movwf IndStat,0 ; переход к
переключателю; bra M15 ; }; M181 movf
IndBuf0,w,0 ;если(символ==0х82) xorlw
0x82 ; { bnz M182
; movlw 0x06 ; регистр состояния
индикатора=очистка дисплея; movwf
IndStat,0 ; переход к переключателю; bra
M15 ; }; Аналогично осуществляется и
запись данных в буфер передачи модуля USB. Перед циклом записи необходимо
проверить, свободен ли буфер записи модуля USB. Если буфер занят целиком и в нём
нет ни одного свободного байта, то запись откладывается до его освобождения. В
этом случае на выводе TXE# будет присутствовать высокий уровень. M182 btfsc
PORTC,TXE#,0 ;если(TXE#) переход к концу
цикла; bra M12
; Если же в буфере записи есть хотя бы
один свободный байт, то есть вывод TXE# имеет низкий уровень, тогда начинается
цикл записи в соответствии с
рис.4.
clrf TRISD,0 ;портD->вывод; nop
; nop
; Порт D программируется на вывод. Затем
следуют две холостые команды, которые дают время для полного затухания
переходных процессов на выводах порта D, связанных с изменением его
конфигурации. movff IndBuf0,PORTD
;портD=символ; После этого, данные
передаются из буфера, состоящего из одного байта, на выводы порта D.
bcf PORTC,WRITE,0 ;сброс
WR; bsf PORTC,WRITE,0 ;установка
WR; Две предыдущие команды формируют
строб записи, то есть переводят вывод WR сначала в низкий, а затем в высокий
уровень. На этом цикл записи данных в модуль USB можно считать
завершённым. movlw 0xff
;портD->ввод; movwf TRISD,0
; Однако после операции записи порт D
необходимо перевести в состояние с высоким импедансом, что и делается с помощью
двух предыдущих команд. После них порт D будет запрограммирован на ввод.
Далее следует текст, не имеющий
отношения к модулю USB. Этот текст снабжён комментариями, и желающие могут
разобраться по нему с работой индикатора. M19 movf IndBuf0,w,0 ;
xorlw 0xa8
; bz M20
; movf IndBuf0,w,0
; xorlw 0xb8
; bz M21
; bra M22 ; M20 movlw 0xa2
;перекодировка символа "ё"; movwf
IndBuf0,0 ; bra M15 ;переход к
переключателю; M21 movlw 0xb5 ; movwf
IndBuf0,0 ;перекодировка символа "Ё";
bra M15 ;переход к переключателю; M22 movlw 0xc0 ;если
(символ==русский) subwf IndBuf0,w,0
;{ bc M23
; bra M15 ; M23 addlw CodTab
;перекодировка символа; movwf TBLPTRL,0
; tblrd *
; movff TABLAT,IndBuf0 ;}; M15 movf
PCL,w,0 ;переключатель (регистр состояния
индикатора) rlncf IndStat,w,0
; addwf PCL,f,0
; bra IndGo0 ;случай
0:{FunctionSet();выход;} bra IndGo1
;случай 1:{задержка 37 мкс();выход;}
bra IndGo0 ;случай 2:{Function Set();выход;}
bra IndGo1 ;случай 3:{задержка 37
мкс()выход;} bra IndGo2 ;случай
4:{проверка флага занятости();выход;}
bra IndGo3 ;случай 5:{DisplayON/OFF();выход;}
bra IndGo2 ;случай 6:{проверка флага
занятости();выход;} bra IndGo4 ;случай
7:{ClearDispley();выход;} bra IndGo2
;случай 8:{проверка флага занятости();выход;}
bra IndGo5 ;случай
9:{EntryModeSet();выход;} bra IndGo2
;случай A:{проверка флага занятости();выход;}
bra IndGo6 ;случай B:{начало первой
строки();выход;} bra IndGo2 ;случай
C:{проверка флага занятости();выход;}
bra IndGo7 ;случай D:{вывод символа первой строки();выход;}
bra IndGo2 ;случай E:{проверка флага
занятости();выход;} bra IndGo8 ;случай
F:{начало второй строки();выход;} bra
IndGo2 ;случай 10:{проверка флага занятости();выход;}
bra IndGo9 ;случай 11:{вывод символа
второй строки();выход;} bra IndGo2
;случай 12:{проверка флага занятости();выход;}
bra IndGo10 ;случай 13:{сдвиг курсора
влево();выход;} bra IndGo2 ;случай
14:{проверка флага занятости();выход;}
bra IndGo11 ;случай 15:{сдвиг курсора
вправо();выход;} ;-------------------------------------------------------------------------------- ;FunctionSet() ;{ IndGo0
movlw 0x38 ;
clrf TRISB,0
;портВ->вывод; bcf PORTC,RS ;сброс
RS; bcf PORTC,RW ;сброс
RW; bsf PORTC,E ;установка
Е; movwf PORTB,0 ;портВ=8 бит,2 строки,
5Х8 точек bcf PORTC,E ;сброс
Е; movlw 0xFF
;портВ->ввод; movwf TRISB,0
; movlw 0x8e
; movwf TMR1L,0 ;загрузка таймера
задержки movlw 0xff
; movwf TMR1H,0
; movlw 0xb1
; movwf T1CON,0 ;и его
запуск bcf PIR1,TMR1IF,0
; incf IndStat,f,0 ;регистр состояния
индикатора++ bra M12
; ;} ;-------------------------------------------------------------------------------- ;задержка
37 мкс() ;{ IndGo1 btfss PIR1,TMR1IF,0 ;если (TMR1IF
переполнен) bra M12
;{ incf IndStat,f,0 ;регистр состояния
индикатора++; bcf PIR1,TMR1IF,0
; bra M12
;}; ;} ;-------------------------------------------------------------------------------- ;проверка
флага занятости() ;{ IndGo2 movlw 0xFF
; movwf TRISB,0
;портВ->ввод; bcf PORTC,RS ;сброс
RS; bsf PORTC,RW ;установка
RW; bsf PORTC,E ;установка
E; movf PORTB,w,0 ;флаг занятости
индикатора=портВ[бит7]; bcf PORTB,E
;сброс E; andlw 0x80 ;если (флаг
занятости индикатора) bz CHKOK
; bra M12 ;{остаться в состоянии
проверки флага занятости;} CHKOK incf IndStat,f,0 ;иначе
bra M12 ;{перейти к следующему
состоянию;}; ;} ;-------------------------------------------------------------------------------- ;Display
ON/OFF()/*дисплей вкл, курсор вкл, мерцание вкл ;{ IndGo3 movlw 0x0F
;Execute(0x0f); ;} ;-------------------------------------------------------------------------------- ;Execute(int
W) ;{ Execute clrf TRISB,0
;портВ->вывод; bcf PORTC,RS ;сброс
RS; bcf PORTC,RW ;сброс
RW; bsf PORTC,E ;установка
Е; movwf PORTB,0
;портВ=W; bcf PORTC,E ;сброс
Е; movlw 0xFF
;портВ->ввод; movwf TRISB,0
; incf IndStat,f,0 ;регистр состояния
индикатора++; bra M12
; ;} ;-------------------------------------------------------------------------------- ;ClearDisplay() ;{ IndGo4
movlw 0x01 ; bra Execute
;Execute(0x01); ;} ;-------------------------------------------------------------------------------- ;EntryModeSet() ;{ IndGo5
movlw 0x06 ; bra Execute
;Execute(0x06); ;} ;-------------------------------------------------------------------------------- ;начало
первой строки() ;{ IndGo6 movlw 0x10 ;счётчик
символов=16; movwf CntSymb,0 ;
movlw 0x80
;Execute(0x80); bra Execute
; ;} ;-------------------------------------------------------------------------------- ;вывод
символа первой строки() ;{ IndGo7 clrf TRISB,0
;портВ->вывод; bsf PORTC,RS,0
;установка RS; bcf PORTC,RW,0 ;сброс
RW; bsf PORTC,E,0 ;установка
E; movff IndBuf0,PORTB
;портВ=символ; bcf PORTC,E,0 ;сброс
Е; movlw 0xff
;портВ->ввод; movwf TRISB,0
; dcfsnz CntSymb,f,0 ;счётчик символов
-- bra IncStat ;если (счётчик
символов==0) decf IndStat,f,0 ;{регистр
состояния индикатора--;} bra M12
;иначе IncStat incf IndStat,f,0 ;{регистр состояния
индикатора++;} bra M12
; ;} ;-------------------------------------------------------------------------------- ;начало
второй строки() ;{ IndGo8 movlw 0x10
; movwf CntSymb,0 ;счётчик
символов=16; movlw 0xc0
; bra Execute
;Execute(0xc0); ;} ;-------------------------------------------------------------------------------- ;вывод
символа второй строки() ;{ IndGo9 clrf TRISB,0
;портВ->вывод; bsf PORTC,RS
;установка RS; bcf PORTC,RW ;сброс
RW; bsf PORTC,E,0 ;установка
E; movff IndBuf0,PORTB
;портВ=символ; bcf PORTC,E,0 ;сброс
Е; movlw 0xff
;портВ->ввод; movwf TRISB,0
; dcfsnz CntSymb,f,0 ;счётчик символов
-- bra MovStat ;если (счётчик
символов==0) decf IndStat,f,0 ;{регистр
состояния индикатора--;} bra M12
;иначе MovStat movlw 0x0a ;{регистр состояния индикатора=начало первой
строки;} movwf IndStat,0
; bra M12
; ;} ;-------------------------------------------------------------------------------- ;сдвиг
курсора влево() ;{ IndGo10 movff Memory,IndStat
; movf CntSymb,w,0
; xorlw 0x10 ;если (курсор==начало
строки) bnz M30
; movf IndStat,w,0
; xorlw 0x0d ; если (регистр состояния
индикатора==вывод первой строки) bnz M31
; { movlw 0x01 ;
movwf CntSymb,0
; movlw 0x11 ; регистр состояния
индикатора=вывод второй строки; movwf
IndStat,0 ; movlw 0xcf ; позиция
курсора=конец второй строки; bra M32 ;
} M31 movlw 0x01 ; иначе movwf
CntSymb,0 ; { movlw 0x0d ; регистр
состояния индикатора=вывод первой
строки; movwf IndStat,0 ; позиция
курсора=конец первой строки; movlw 0x8f
; }; bra M32 ;иначе M30 incf
CntSymb,f,0 ;{ movlw 0x10 ;сдвиг курсора
влево; M32 decf IndStat,f,0 ;регистр состояния индикатора=проверка флага
занятости; decf IndStat,f,0
;}; bra Execute ; ;}
;-------------------------------------------------------------------------------- ;сдвиг
курсора вправо() ;{ IndGo11 movff Memory,IndStat ;сдвиг курсора
вправо() movf CntSymb,w,0
;{ xorlw 0x01 ;если (курсор==конец
строки) bnz M33
; movf IndStat,w,0 ; если (регистр
состояния индикатора==вывод первой
строки) xorlw 0x0d ;
{ bnz M34
; movlw 0x10
; movwf CntSymb,0 ;
movlw 0x11 ; регистр состояния
индикатора=вывод второй строки; movwf
IndStat,0 ; movlw 0xc0 ; позиция
курсора=начало второй строки; bra M35 ;
} M34 movlw 0x10 ; иначе movwf
CntSymb,0 ; { movlw 0x0d ; регистр
состояния индикатора=вывод первой
строки; movwf IndStat,0 ; позиция
курсора=начало первой строки; movlw 0x80
; }; bra M35 ;иначе M33 decf
CntSymb,f,0 ;{ movlw 0x14 ;сдвиг курсора
вправо; M35 decf IndStat,f,0 ;регистр состояния индикатора=проверка флага
занятости; decf IndStat,f,0
;}; bra Execute
; ;} ;-------------------------------------------------------------------------------- ;конец
цикла M12 goto Begin ;переход к началу
цикла end
Как видно из приведённого примера, работа
с модулем USB со стороны микроконтроллера проста и прозрачна. В связи с большой
скоростью, на которой работает модуль USB, в циклах записи и чтения не требуются
задержки, характерные при работе с медленными устройствами.
Для проверки примера, рассмотренного выше,
необходимо скопировать текст программы в среду разработки, поддерживающую
микроконтроллер PIC18F458 или подобный ему. Этой средой может быть, например,
MPLAB 5.70, которым пользовался автор. Дальнейшие действия стандартны. После
получения файла типа HEX его тем или иным способом загружают в микроконтроллер.
Его, в свою очередь, помещают в устройство, изготовленное по схеме на рис.5 и
соединяют с компьютером кабелем USB. Если
всё сделано правильно, то на экране монитора появится сообщение об обнаружении
нового устройства. Тому, что необходимо сделать после этого, будет посвящён
следующий раздел.
Литература.
5. www.microchip.com/download/lit/pline/picmicro/families/18fxx8/41159c.pdf 6.
6.
www.wintek.com.tw/spec/WD-C1602Q-6YLYc.pdf
|