Inverses Morsekeyboard mit BascomSwUSB          


von Ralf Beesner    
Elektronik-Labor   Projekte   AVR 



In einem früheren Beitrag hatte ich einen BASCOM-Morsedecoder mit einem Serial Keyboard auf Basis der VUSB-Software www.obdev.at/products/vusb/index-de.html kombiniert. Der AtTiny13-Morsedecoder wandelt eingegebene Morsezeichen in ein serielles Signal, dieses wird an das Serial Keyboard (einen AtTiny 2313) weitergeleitet, der als virtuelle USB-Tastatur fungiert und die Zeichen in Tastatureingaben umsetzt.

Allerdings ärgerte mich doch ein wenig, dass die vielen IOs des 20-poligen Attiny2313 brachlagen (der AtTiny2313 kam einzig deshalb ins Spiel, weil er der kleinste AVR mit einem Hardware-UART ist). 

Eigentlich wollte ich immer mal versuchen, den Morsedecoder von BASCOM nach C zu portieren und in die VUSB-Software einzubauen, damit das ganze in einen einzigen AtTiny45 passt.

Mit der BASCOM-SWUSB-Library von Rick Richards (ollopa)- siehe www.sloservers.com/swusb wurde nun eine viel einfachere Lösung möglich: ich habe in seine Keyboard-Beispielimplementation mit recht wenig Aufwand den Morsedecoder "hineingefummelt".


Hardware

Die USB-Anschaltung an den Mikrocontroller ist die gleiche wie bei den VUSB-Projekten. Da die SWUSB-Implementation stets einen Quarz benötigt, hat man bei Verwendung eines AtTiny 45 nur einen Pin frei. Benötigt werden eigentlich zwei, besser sogar vier: Lautsprecher (Mithörton), Morsetaste und zwei Dipschalter für eine grobe Geschwindigkeitsvorwahl und die Umschaltung zwischen manuellem und automatischem Einfügen der Leerzeichen.


Man könnte statt eines AtTiny 45 einen AtTiny44 nehmen, der sechs Pins mehr als der AtTiny 45 hat. Da ich keinen zur Hand hatte, wird (nach einem Vorschlag von Hermann Nieder) der Reset-Pin über einen Trick nutzbar gemacht: wenn der Reset-Pin aktiv ist, kann man ihn zwar nicht als digitalen Eingang verwenden, aber als ADC-Eingang (ADC0). Man muss nur Sorge tragen, dass das angelegte Signal stets größer ist als 1/2 VCC, damit kein Reset ausgelöst wird.




Die beiden DIP-Schalter und die Morsetaste werden daher über einen Spannungsteiler an PB5/ADC0 angeschlossen. Die Widerstände sind so bemessen, dass die Spannung stets höher als 1/2 VCC ist. ADC0 gibt bei offenen Tastern 1023 aus; wird der zweite DIP-Schalter betätigt, sinkt der Wert knapp unter 950, wird der erste DIP-Schalter  betätigt, sinkt er knapp unter 900. Bei Drücken der Morsetatste sinkt er unter 750.



Software

Ollopas Keyboard-Beispiel-Implementation habe ich nur ansatzweise verstanden, aber glücklicherweise erkennt man leicht die Stellen, an denen man sich in den Code mit seiner eigenen Task einhängen kann.

Der USB ist mit seinem relativ hohen Takt (1,5 MHz) sehr zeitkritisch, so dass die Anwendung eigener Interrupts nicht möglich ist. Man kann also in der eigenen Task Eingangspins, ADC-oder Timer-Register und dergleichen nur zyklisch abfragen (pollen).

In Ollopas Beispielimplementation ist vorgesehen, dass die Software einen gleichbleibenden Buchstaben über den USB ausgibt, sobald man einen Pin auf Low zieht. Stattdessen wird nun ADC0 abgefragt, ob der Wert unter 750 gesunken ist. Dann wird in den Morsedecoder verzweigt.

Die Morsezeichen werden zweimal per Lookup-Befehl umcodiert: zunächst werden sie in ASCII-Bytes umgewandelt und anschließend in deutsche Tastaturbelegung.

Mittlere Geschwindigkeit und Leerzeichenmodus werden nur einmal bei Start des Programms (also bei Einstecken des USB-Steckers) festgelegt.

Die Decodersoftware hat zwar einen großen Fangbereich für unsauber gegebene Zeichen und kann daher auch (in gewissem Rahmen) Geschwindigkeitsunterschiede abfangen, aber die mittlere Geschwindigkeit ist lediglich fest auf 60 oder 90 Zeichen einstellbar (DIP-Schalter am 10 kOhm-Widerstand).

Der andere DIP-Schalter legt fest, ob Leerzeichen automatisch durch Zeitablauf oder ein spezielles Morsezeichen generiert werden (letzteres ist mühsamer, aber verhindert, dass bei zögerlicher Gebeweise ungewollte Leerzeichen eingestreut werden).

Als akustische Rückmeldung über den gewählten Modus wird die Tonhöhe des Mithörtons umgeschaltet.

Da es keine Morsezeichen für Return und Leerzeichen gibt, habe ich die zusammengesetzten Zeichen <kn> und <as> eingebaut.


Kompilieren des Codes

Wegen der restriktiven Lizenz für swusb.lbx habe ich dem Sourcecode lediglich das File swusb-includes.bas beigefügt -swusb.lbx ist auf der oben angegebenen Homepage des Projekts herunterzuladen und in den BASCOM-LIB-Ordner zu verschieben.


Fusen des AtTiny

Ein fabrikfrischer Attiny muss für Quarzbetrieb ohne 1:8-Vorteiler gefust werden; Watchdog und Browout sind nicht aktiviert:

Low Fuse = 0xFF   High Fuse = 0xDF


Update

Die Software lässt sich mit neueren BASCOM-Versionen (vermutlich ab Version 2.0.7.3) nicht mehr kompilieren, weil der SWUSB-Autor ein paar Nachlässigkeiten bei der Verwendung von Arrays begangen hat, die von der strikteren Syntaxprüfung der neueren Bascom-Versionen als Fehler gemeldet werden.

Übergangsweise lässt sich das alte Verhalten mit dem Befehl
Config Error = Ignore , 380 = Ignore
wieder herstellen, so dass der Code wieder kompiliert wird.

Die Datei swusb.lbx ist nun beigefügt (der Autor hat inzwischen im Bascom-Forum klargestellt, dass die Datei weiterverteilt werden darf. Sie muss in den LIB-Ordner der Bascom-Installation kopiert werden).

Die aktualisierte Version decodiert nun auch Umlaute.

Update 2

Die Demo-Version von Bascom ist immer noch auf dem Versionsstand 2.0.7.5, mit der zwar das Übersetzen klappt, aber kein funktionierendes HEX-File erzeugt wird. Bei Version 2.0.7.5 muss man zusätzlich den Befehl
$noframeprotect
an den Anfang des Sourcecode eintragen.

Download: swusb-morsekeyboard-software.zip


'USB Keyboard Emulator Example  
'Author: ollopa 2009-2012
'
'This project emulates a standard 105 key USB keyboard.

' Using swusb-includes.bas and swusb.lbx (the latter copied to the bascom plugins folder)
' swusb.lbx is copyrighted by Rick Richard (ollopa) and may only be used only for noncommercial purposes

' modifications by DK5BU:
'
' Morse Keyboard ; morse signals received on Port B.5 are converted into
' keybord characters
'
' - controller changed to Attiny 45
' - omitted LED status, as there are not enough Ports
' - pin usbd_plus and usb_dminus changed to PB.2 and PB.0
' - only one regular port (PB.1) left; used for monitor sound for the keyer
' - PB.5 (reset) ist used as Input for a Morse Keyer
' - to avoid "fusing away" the reset pin, PB.5 is not used as a digital input, but
' as an analog input. The ADC input is usable when pin PB.5 is still used as reset
' pin, but you have to avoid input valuses lower than 1/2 VCC
' - Remapped For German Keyboard Driver
' - Version 3 has 2 Speeds. ADC is not only used for the key, but during initialisation an adc walue
' < 950 switches to 90 digits / min, else it is 60 digits / min.
' - Version 4: adc values < 900 or < 850 during initialisation activate the autospace function
' - the morse decoder software bases mostly on a project of Burkhard Kainka, DK7JD

' Fuse bytes: low fuse = FF high fuse = DF



'$noramclear 'Save about 38 bytes of code size

$hwstack = 30
$swstack = 30
$framesize = 40 '24 bytes reserved



$regfile = "Attiny45.dat"

$crystal = 12000000
'$crystal = 15000000


$eepromhex 'for STK500 programmer


'Include the software USB library
$lib "swusb.lbx"
$external _swusb
$external Crcusb

Declare Sub Usb_reset()
Declare Sub Usb_processsetup(txstate As Byte)
Declare Sub Usb_send(txstate As Byte , Byval Count As Byte)
Declare Sub Usb_senddescriptor(txstate As Byte , Maxlen As Byte)
Declare Sub Typekey(key As Byte)
Declare Function Ascii2usage(ascii As Byte) As Word
Declare Function Crcusb(buffer() As Byte , Count As Byte) As Word
Declare Sub Morse

'*******************************************************************************
'*************************** Begin USB Configuration ***************************
'
'Set the following parameters to match your hardware configuration and USB
'device parameters.

'******************************* USB Connections *******************************

'Define the AVR port that the two USB pins are connected to
_usb_port Alias Portb ' changed by DK5BU
_usb_pin Alias Pinb ' changed by DK5BU
_usb_ddr Alias Ddrb ' changed by DK5BU


'Define the D+ and D- pins. (put D+ on an interrupt pin)
Const _usb_dplus = 2
Const _usb_dminus = 0 ' changed by DK5BU

'Configure the pins as inputs
Config Pinb.2 = Input ' changed by DK5BU
Config Pinb.0 = Input ' changed by DK5BU

'disable pullups
_usb_port._usb_dplus = 0
_usb_port._usb_dminus = 0

'*******************************************************************************
'************************* USB Configuration Constants *************************

'Use EEPROM or FLASH to store USB descriptors
'1 = EEPROM, 0 = FLASH. Storing to EEPROM will reduce code size slightly.
Const _usb_use_eeprom = 0

'Don't wait for sent packets to be ACK'd by the host before marking the
'transmission as complete. This option breaks the USB spec but improves
'throughput with faster polling speeds.
'This may cause reliability issues. Should leave set to 0 to be safe.
Const _usb_assume_ack = 0

' *************************** Device Descriptor *****************************

'USB Vendor ID and Product ID (Assigned by USB-IF)
Const _usb_vid = &HAAAA
Const _usb_pid = &HEF03

'USB Device Release Number (BCD)
Const _usb_devrel = &H0001

'USB Release Spec (BCD)
Const _usb_spec = &H0110

'USB Device Class, subclass, and protocol (assigned by USB-IF).
'&h00 = Class defined by interface. (HID is defined in the interface)
'&hFF = Vendor-defined class (You must write your own PC driver)
'See http://www.usb.org/developers/defined_class for more information
Const _usb_devclass = 0
Const _usb_devsubclass = 0
Const _usb_devprot = 0

'These are _indexes_ to UNICODE string descriptors for the manufacturer,
'product name, and serial number. 0 means there is no descriptor.
Const _usb_imanufacturer = 1
Const _usb_iproduct = 2
Const _usb_iserial = 0

'Number of configurations for this device. Don't change this unless
'you know what you are doing. Ordinarily it should just be 1.
Const _usb_numconfigs = 1

' *************************** Config Descriptor *****************************

'The number of interfaces for this device (Typically 1)
Const _usb_numifaces = 1

'Configuration Number (do not edit)
Const _usb_confignum = 1

'Index of UNICODE string descriptor that describes this config (0 = None)
Const _usb_iconfig = 2

'&H80 = device powered from USB bus.
'&HC0 = self-powered (has a power supply)
Const _usb_powered = &H80

'Required current in 2mA increments (500mA max)
Const _usb_maxpower = 50 '50 * 2mA = 300mA

' ************************** Interface Descriptor ***************************

'Number of interfaces for this device (1 or 2)
Const _usb_ifaces = 1

'Interface number
Const _usb_ifaceaddr = 0

'Alternate index
Const _usb_alternate = 0

'Number of endpoints for this interface (excluding endp 0)
Const _usb_ifaceendpoints = 1

'USB Interface Class, subclass, and protocol (assigned by USB-IF).
'&h00 = RESERVED
'&hFF = Vendor-defined class (You must write your own PC driver)
' Other values are USB interface device class. (such as HID)
'See http://www.usb.org/developers/defined_class for more information
Const _usb_ifclass = 3
Const _usb_ifsubclass = 1 'Boot interface subclass
Const _usb_ifprotocol = 1 'Keyboard

'Index to UNICODE string descriptor for this interface (0 = None)
Const _usb_iiface = 0

' ************************* Optional HID Descriptor *************************
'HID class devices are things like keyboard, mouse, joystick.
'See http://www.usb.org/developers/hidpage/ for the specification,
'tools, and resources.

'Note that for a HID device, the device class, subclass, and protocol
'must be 0. The interface class must be 3 (HID).
'Interface subclass and protocol must be 0 unless you are making a
'keyboard or a mouse that supports the predefined boot protocol.
'See pages 8 and 9 of the HID 1.11 specification PDF.

'Number of HID descriptors (EXCLUDING report and physical)
'If you are not making a HID device, then set this constant to 0
Const _usb_hids = 1

'BCD HID releasenumber. Current spec is 1.11
Const _usb_hid_release = &H0111

'Country code from page 23 of the HID 1.11 specifications.
'Usually the country code is 0 unless you are making a keyboard.
'''Const _usb_hid_country = 0
Const _usb_hid_country = 049

'The number of report and physical descriptors for this HID
'Must be at least 1! All HID devices have at least 1 report descriptor.
Const _usb_hid_numdescriptors = 1

'Use a software tool to create the report descriptor and $INCLUDE it.


' ************************* Endpoint Descriptor(s) **************************

'Endpoint 0 is not included here. These are only for optional
'endpoints.
'Note: HID devices require 1 interrupt IN endpoint

'Address of optional endpoints (Must be > 0. comment-out to not use)
Const _usb_endp2addr = 1
'Const _usb_Endp3Addr = 2

'Valid types are 0 for control or 3 for interrupt
Const _usb_endp2type = 3
Const _usb_endp3type = 0

'Directions are: 0=Out, 1=In. Ignored by control endpoints
Const _usb_endp2direction = 1
Const _usb_endp3direction = 0

'Polling interval (ms) for interrupt endpoints. Ignored by control endpoints
' (Must be at least 10)
Const _usb_endp2interval = 20
Const _usb_endp3interval = 20

'*******************************************************************************
'The includes need to go right here--between the configuration constants above
'and the start of the program below. The includes will automatically calculate
'constants based on the above configuration, dimension required variables, and
'allocate transmit and receive buffers. Nothing inside the includes file needs
'to be modified.
$include "swusb-includes.bas"
'*******************************************************************************

'**************************** USB Interrupt And Init ***************************
'Set all the variables, flags, and sync bits to their initial states
Call Usb_reset()

Const _usb_intf = Intf0
Config Int0 = Rising
On Int0 Usb_isr Nosave
Enable Int0
Enable Interrupts

'*******************************************************************************
'*************************** End Of USB Configuration **************************




'---------------------------------------------




Dim Key As Word
Dim Timepressed As Word
Dim Timenotpressed As Word
Dim Charactercode As Byte
Dim Idling As Word
Dim Spacewait As Byte
Dim W As Byte

Dim Dithalf As Word
Dim Tpconst As Word
Dim Tnpconst As Word
Dim Iconst As Word


Declare Sub Getkey
Declare Sub Getkeytimes
Declare Sub Printdisplay
Declare Sub Getidletime


' Timer 1 is configured for Sound at OC1A / PB1
Config Timer1 = Counter , Prescale = 64 , Compare A = Toggle , Clear Timer = 1


Didr0.1 = 1 ' deactivate digital input
Config Adc = Single , Prescaler = Auto
Start Adc
Ocr1c = 255

Dithalf = 50
Getkey

If Key < 950 Then
Dithalf = 33
Ocr1c = 192
End If

If Key < 900 Then
Dithalf = 50
Spacewait = 1
Ocr1c = 160
End If

If Key < 850 Then
Dithalf = 33
Spacewait = 1
Ocr1c = 128
End If

Tpconst = Dithalf * 3 ' 1.5 * Dit; maximum tinme for a dit
Tnpconst = Dithalf * 4 ' 2 * Dit; minimum time for a dah
Iconst = Dithalf * 4 ' 4 * Dit: minimum time for a blank


'---------------------------------------------


Dim Resetcounter As Word
Dim Idlemode As Byte

Dim Keybd_leds As Byte 'keyboard status LEDs (numlock, capslock, etc.)
Dim Message As String * 10

Dim Akey As Byte



Do
Resetcounter = 0

'Check for reset here
While _usb_pin._usb_dminus = 0
Incr Resetcounter
If Resetcounter = 1000 Then
Call Usb_reset()
End If
Wend

'Check for received data

If _usb_status._usb_rxc = 1 Then

If _usb_status._usb_setup = 1 Then
'Process a setup packet/Control message
Call Usb_processsetup(_usb_tx_status)
'else

End If

'Reset the RXC bit and set the RTR bit (ready to receive a new packet)
_usb_status._usb_rxc = 0
_usb_status._usb_rtr = 1

End If


' ------------------------------------------------

' Main User Loop


Getkey
If Key < 750 Then
Call Morse
Call Typekey(akey)
W = 1
End If
If W = 1 And Spacewait = 1 Then
Getidletime
If Idling > Iconst Then
Akey = 32
Call Typekey(akey)
W = 0
End If
End If

' ------------------------------------------------


' If Buttons_current <> Buttons_last Or Idlemode = 0 Then
' End If
Loop


Sub Typekey(key As Byte)
Local Usage As Word
Usage = Ascii2usage(key)

Do : Loop Until _usb_tx_status2._usb_txc = 1
' Key down
_usb_tx_buffer2(2) = High(usage) 'Modifier keys (shift, ctl, alt, etc)
_usb_tx_buffer2(3) = 0 'Reserved. Always 0
_usb_tx_buffer2(4) = Low(usage) 'key1
_usb_tx_buffer2(5) = 0 'key2
_usb_tx_buffer2(6) = 0 'key3
_usb_tx_buffer2(7) = 0 'key4
_usb_tx_buffer2(8) = 0 'key5
_usb_tx_buffer2(9) = 0 'key6
Call Usb_send(_usb_tx_status2 , 8)

' Key up
Do : Loop Until _usb_tx_status2._usb_txc = 1
_usb_tx_buffer2(2) = 0 'Modifier keys (shift, ctl, alt, etc)
_usb_tx_buffer2(3) = 0 'Reserved. Always 0
_usb_tx_buffer2(4) = 0 'key1
_usb_tx_buffer2(5) = 0 'key2
_usb_tx_buffer2(6) = 0 'key3
_usb_tx_buffer2(7) = 0 'key4
_usb_tx_buffer2(8) = 0 'key5
_usb_tx_buffer2(9) = 0 'key6
Call Usb_send(_usb_tx_status2 , 8)
End Sub

End

'*******************************************************************************
'******************** Descriptors stored in EEPROM or FLASH ********************
' Do not change the order of the descriptors!
'
#if _usb_use_eeprom = 1
$eeprom
#else
$data
#endif

'Device Descriptor
_usb_devicedescriptor:
Data 18 , 18 , _usb_desc_device , _usb_specl , _usb_spech , _usb_devclass
Data _usb_devsubclass , _usb_devprot , 8 , _usb_vidl , _usb_vidh , _usb_pidl
Data _usb_pidh , _usb_devrell , _usb_devrelh , _usb_imanufacturer
Data _usb_iproduct , _usb_iserial , _usb_numconfigs


'Retrieving the configuration descriptor also gets all the interface and
'endpoint descriptors for that configuration. It is not possible to retrieve
'only an interface or only an endpoint descriptor. Consequently, this is a
'large transaction of variable size.
_usb_configdescriptor:
Data _usb_descr_total , 9 , _usb_desc_config , _usb_descr_totall
Data _usb_descr_totalh , _usb_numifaces , _usb_confignum , _usb_iconfig
Data _usb_powered , _usb_maxpower

'_usb_IFaceDescriptor
Data 9 , _usb_desc_iface , _usb_ifaceaddr , _usb_alternate
Data _usb_ifaceendpoints , _usb_ifclass , _usb_ifsubclass , _usb_ifprotocol
Data _usb_iiface

#if _usb_hids > 0
'_usb_HIDDescriptor
Data _usb_hid_descr_len , _usb_desc_hid , _usb_hid_releasel , _usb_hid_releaseh
Data _usb_hid_country , _usb_hid_numdescriptors

'Next follows a list of bType and wLength bytes/words for each report and
'physical descriptor. There must be at least 1 report descriptor. In practice,
'There are usually 0 physical descriptors and only 1 report descriptor.
Data _usb_desc_report
Data 63 , 0
'End of report/physical descriptor list
#endif

#if _usb_endpoints > 1
'_usb_EndpointDescriptor
Data 7 , _usb_desc_endpoint , _usb_endp2attr , _usb_endp2type , 8 , 0
Data _usb_endp2interval
#endif

#if _usb_endpoints > 2
'_usb_EndpointDescriptor
Data 7 , _usb_desc_endpoint , _usb_endp3attr , _usb_endp3type , 8 , 0
Data _usb_endp3interval
#endif

#if _usb_hids > 0
_usb_hid_reportdescriptor:
Data 63
Data &H05 , &H01 ' USAGE_PAGE (Generic Desktop)
Data &H09 , &H06 ' USAGE (Keyboard)
Data &HA1 , &H01 ' COLLECTION (Application)
Data &H05 , &H07 ' USAGE_PAGE (Keyboard)
Data &H19 , &HE0 ' USAGE_MINIMUM (Keyboard LeftControl)
Data &H29 , &HE7 ' USAGE_MAXIMUM (Keyboard Right GUI)
Data &H15 , &H00 ' LOGICAL_MINIMUM (0)
Data &H25 , &H01 ' LOGICAL_MAXIMUM (1)
Data &H75 , &H01 ' REPORT_SIZE (1)
Data &H95 , &H08 ' REPORT_COUNT (8)
Data &H81 , &H02 ' INPUT (Data,Var,Abs)
Data &H95 , &H01 ' REPORT_COUNT (1)
Data &H75 , &H08 ' REPORT_SIZE (8)
Data &H81 , &H03 ' INPUT (Cnst,Var,Abs)
Data &H95 , &H05 ' REPORT_COUNT (5)
Data &H75 , &H01 ' REPORT_SIZE (1)
Data &H05 , &H08 ' USAGE_PAGE (LEDs)
Data &H19 , &H01 ' USAGE_MINIMUM (Num Lock)
Data &H29 , &H05 ' USAGE_MAXIMUM (Kana)
Data &H91 , &H02 ' OUTPUT (Data,Var,Abs)
Data &H95 , &H01 ' REPORT_COUNT (1)
Data &H75 , &H03 ' REPORT_SIZE (3)
Data &H91 , &H03 ' OUTPUT (Cnst,Var,Abs)
Data &H95 , &H06 ' REPORT_COUNT (6)
Data &H75 , &H08 ' REPORT_SIZE (8)
Data &H15 , &H00 ' LOGICAL_MINIMUM (0)
Data &H25 , &H65 ' LOGICAL_MAXIMUM (101)
Data &H05 , &H07 ' USAGE_PAGE (Keyboard)
Data &H19 , &H00 ' USAGE_MINIMUM (Reserved (no event indicated))
Data &H29 , &H65 ' USAGE_MAXIMUM (Keyboard Application)
Data &H81 , &H00 ' INPUT (Data,Ary,Abs)
Data &HC0 ' END_COLLECTION
#endif

'*****************************String descriptors********************************
'Yes, they MUST be written like "t","e","s","t". Doing so pads them with
'0's. If you write it like "test," I promise you it won't work.

'Default language descriptor (index 0)
_usb_langdescriptor:
Data 4 , 4 , _usb_desc_string , 09 , 04 '&h0409 = English

'Manufacturer Descriptor (unicode)
_usb_mandescriptor:
Data 14 , 14 , _usb_desc_string
Data "o" , "l" , "l" , "o" , "p" , "a"

'Product Descriptor (unicode)
_usb_proddescriptor:
Data 46 , 46 , _usb_desc_string
Data "o" , "l" , "l" , "o" , "p" , "a" , "'" , "s" , " " , "k" , "e" , "y" , "b" , "o" , "a" , "r" , "d" , " "
Data "v" , "1" , "." , "0"



'*******************************************************************************



'*******************************************************************************
'******************************** Subroutines **********************************
'*******************************************************************************

Sub Usb_processsetup(txstate As Byte)
Senddescriptor = 0
'Control transfers reset the sync bits like so
Txstate = _usb_setup_sync

'These are the standard device, interface, and endpoint requests that the
'USB spec requires that we support.
Select Case _usb_rx_buffer(2)
'Standard Device Requests
Case &B10000000:
Select Case _usb_rx_buffer(3)
' CASE _usb_REQ_GET_STATUS:
Case _usb_req_get_descriptor:
Select Case _usb_rx_buffer(5)
Case _usb_desc_device:
'Send the device descriptor
#if _usb_use_eeprom = 1
Readeeprom _usb_eepromaddrl , _usb_devicedescriptor
#else
Restore _usb_devicedescriptor
#endif
Senddescriptor = 1
Case _usb_desc_config:
'Send the configuration descriptor
#if _usb_use_eeprom = 1
Readeeprom _usb_eepromaddrl , _usb_configdescriptor
#else
Restore _usb_configdescriptor
#endif
Senddescriptor = 1
Case _usb_desc_string:
Select Case _usb_rx_buffer(4)
Case 0:
'Send the language descriptor
#if _usb_use_eeprom = 1
Readeeprom _usb_eepromaddrl , _usb_langdescriptor
#else
Restore _usb_langdescriptor
#endif
Senddescriptor = 1
Case 1:
'Send the manufacturer descriptor
#if _usb_use_eeprom = 1
Readeeprom _usb_eepromaddrl , _usb_mandescriptor
#else
Restore _usb_mandescriptor
#endif
Senddescriptor = 1
Case 2:
'Send the product descriptor
#if _usb_use_eeprom = 1
Readeeprom _usb_eepromaddrl , _usb_proddescriptor
#else
Restore _usb_proddescriptor
#endif
Senddescriptor = 1
End Select
End Select
' CASE _usb_REQ_GET_CONFIG:
End Select
Case &B00000000:
Select Case _usb_rx_buffer(3)
' CASE _usb_REQ_CLEAR_FEATURE:
' CASE _usb_REQ_SET_FEATURE:
Case _usb_req_set_address:
'USB status reporting for control writes
Call Usb_send(txstate , 0)
While Txstate._usb_txc = 0 : Wend
'We are now addressed.
_usb_deviceid = _usb_rx_buffer(4)
' CASE _usb_REQ_SET_DESCRIPTOR:
Case _usb_req_set_config:
'Have to do status reporting
Call Usb_send(txstate , 0)
End Select
'Standard Interface Requests
Case &B10000001:
Select Case _usb_rx_buffer(3)
' CASE _usb_REQ_GET_STATUS:
' CASE _usb_REQ_GET_IFACE:
Case _usb_req_get_descriptor
'_usb_rx_buffer(4) is the descriptor index and (5) is the type
Select Case _usb_rx_buffer(5)
Case _usb_desc_report:
#if _usb_use_eeprom = 1
Readeeprom _usb_eepromaddrl , _usb_hid_reportdescriptor
#else
Restore _usb_hid_reportdescriptor
#endif
Senddescriptor = 1
' CASE _usb_DESC_PHYSICAL

' CASE _USB_DESC_HID

End Select
End Select
'CASE &B00000001:
'SELECT CASE _usb_rx_buffer(3)
' CASE _usb_REQ_CLEAR_FEATURE:

' CASE _usb_REQ_SET_FEATURE:

' CASE _usb_REQ_SET_IFACE:

'END SELECT
'Standard Endpoint Requests
'CASE &B10000010:
'SELECT CASE _usb_rx_buffer(3)
' CASE _usb_REQ_GET_STATUS:

'END SELECT
'CASE &B00000010:
'SELECT CASE _usb_rx_buffer(3)
' CASE _usb_REQ_CLEAR_FEATURE:

' CASE _usb_REQ_SET_FEATURE:

'END SELECT

'Class specific requests (useful for HID)
Case &B10100001:
'Class specific GET requests
Select Case _usb_rx_buffer(3)
Case _usb_req_get_report:
'CASE _usb_REQ_GET_IDLE:
'CASE _usb_REQ_GET_PROTOCOL:
End Select

Case &B00100001:
'Class specific SET requests
Select Case _usb_rx_buffer(3)
Case _usb_req_set_report:
_usb_status._usb_rxc = 0
_usb_status._usb_rtr = 1
_usb_status2._usb_ignore = 0
'Do status reporting
Call Usb_send(txstate , 0)

'We need to get the second data packet
'Reset the RXC bit and set the RTR bit (ready to receive a new packet)
Do
Loop Until _usb_status._usb_rxc = 1

Keybd_leds = _usb_rx_buffer(2)


'The output report for a keyboard containts a bitmap representing
'the status of the LEDs:
'BIT Description
'0 NUM LOCK
'1 CAPS LOCK
'2 SCROLL LOCK
'3 COMPOSE
'4 KANA
'5-7 CONSTANT/RESERVED
Toggle Keybd_leds
' Portb = Keybd_leds

Case _usb_req_set_idle:
Idlemode = 1
'Do status reporting
Call Usb_send(txstate , 0)
'CASE _usb_REQ_SET_PROTOCOL:
End Select
End Select

If Senddescriptor = 1 Then
Call Usb_senddescriptor(txstate , _usb_rx_buffer(8))
End If

End Sub


Dim Sd_size As Byte
Dim Sd_i As Byte
Dim Sd_j As Byte
Dim Sd_timeout As Word

Sub Usb_senddescriptor(txstate As Byte , Maxlen As Byte)
'Break the descriptor into packets and send to TxState
#if _usb_use_eeprom = 1
'EEPROM access is a little funky. The size has already been fetched
'and stored in _usb_EEPROMADDRL, and the address of the descriptor
'is now in the EEAR register pair.

Sd_size = _usb_eepromaddrl

'Fetch the location of the descriptor and use it as an address pointer
push R24
in R24, EEARL
sts {_USB_EEPROMADDRL}, R24
in R24, eearH
sts {_USB_EEPROMADDRH}, R24
pop R24

#else
Read Sd_size
#endif

If Maxlen < Sd_size Then Sd_size = Maxlen

Sd_i = 2
For Sd_j = 1 To Sd_size
Incr Sd_i
#if _usb_use_eeprom = 1
Incr _usb_eepromaddr
Readeeprom Txstate(sd_i) , _usb_eepromaddr
#else
Read Txstate(sd_i)
#endif

If Sd_i = 10 Or Sd_j = Sd_size Then
Sd_i = Sd_i - 2
Call Usb_send(txstate , Sd_i)
While Txstate._usb_txc = 0
Sd_timeout = 0
'To prevent an infinite loop, check for reset here
While _usb_pin._usb_dminus = 0
Incr Sd_timeout
If Sd_timeout = 1000 Then
Call Usb_reset()
Exit Sub
End If
Wend
Wend
Sd_i = 2
End If
Next
End Sub

Sub Usb_send(txstate As Byte , Byval Count As Byte)

'Calculates and adds the CRC16,adds the DATAx PID,
'and signals to the ISR that the data is ready to be sent.
'
'"Count" is the DATA payload size. Range is 0 to 8. Do not exceed 8!

'Reset all the flags except TxSync and RxSync
Txstate = Txstate And _usb_syncmask

'Calculate the 16-bit CRC
_usb_crc = Crcusb(txstate(3) , Count)

'Bytes to transmit will be PID + DATA payload + CRC16
Count = Count + 3
Txstate = Txstate + Count

Txstate(count) = Low(_usb_crc)
Incr Count
Txstate(count) = High(_usb_crc)


'Add the appropriate DATAx PID
Txstate(2) = _usb_pid_data1
If Txstate._usb_txsync = 0 Then
Txstate(2) = _usb_pid_data0
End If

'The last step is to signal that the packet is Ready To Transmit
Txstate._usb_rtt = 1
Txstate._usb_txc = 0
End Sub

Sub Usb_reset()
'Reset the receive flags
_usb_status._usb_rtr = 1
_usb_status._usb_rxc = 0

'Reset the transmit flags
_usb_tx_status = _usb_endp_init
#if Varexist( "_usb_Endp2Addr")
_usb_tx_status2 = _usb_endp_init
#endif
#if Varexist( "_usb_Endp3Addr")
_usb_tx_status3 = _usb_endp_init
#endif

'Reset the device ID to 0
_usb_deviceid = 0

Idlemode = 0
End Sub




Function Ascii2usage(ascii As Byte) As Word
Local Result As Byte


'Maps common (mostly printable) ASCII characters to USB Keyboard usage codes
'Returns two bytes: keyboard modifier flags and key code

'Modifier bits:
'0 LEFT CTRL
'1 LEFT SHIFT
'2 LEFT ALT
'3 LEFT GUI
'4 RIGHT CTRL
'5 RIGHT SHIFT
'6 RIGHT ALT
'7 RIGHT GUI

' The USB key codes are valid for US keyboards. As the german keyboard driver remaps some of the keys,
' we habe to remap them back using the Data Table "Tabelle:"
' Ascii values < 30 are mapped to ctrl-A to ctrl-Z
' the Ascii Table is 7 bit, but some 8 bit values (German Umlaute) are supported, too

Result = Lookup(ascii , Tabelle )
Ascii2usage = Result


End Function


'----------------------------------------------------------------

Morse:


Charactercode = 1

Do
Getkeytimes
Shift Charactercode , Left , 1
If Timepressed > Tpconst Then
Incr Charactercode
End If
Loop Until Timenotpressed > Tnpconst
Printdisplay



Return


Sub Printdisplay
Restore Morsetable
Akey = Lookup(charactercode , Morsetable)
End Sub




Sub Getkey

Key = Getadc(0)
If Key < 750 Then ' Morsekey pressed
Ddrb.1 = 1
Else
Ddrb.1 = 0
End If

End Sub




Sub Getkeytimes

Timepressed = 0
Timenotpressed = 0

Do
Waitms 1
Incr Timepressed
Getkey
Loop Until Key > 750 ' loop until morsekey released
Do
Waitms 1
Incr Timenotpressed
Getkey
Loop Until Key < 750 Or Timenotpressed > Tnpconst ' loop until morsekey is pressed or pause too long

End Sub




Sub Getidletime

Idling = 0
Do
Getkey
Waitms 1
Incr Idling
Loop Until Key < 750 Or Idling > Iconst ' loop until morsekey pressed

End Sub




Morsetable:
Data 0 ,
Data 32, ' "space" 00000001 ASCII 32
Data 69, ' "E", . , 00000010, ASCII 69
Data 84, ' "T", - , 00000011, ASCII 84
Data 73, ' "I", .. , 00000100, ASCII 73
Data 65, ' "A", .- , 00000101, ASCII 65
Data 78, ' "N", -. , 00000110, ASCII 78
Data 77, ' "M", -- , 00000111, ASCII 77
Data 83, ' "S", ... , 00001000, ASCII 83
Data 85, ' "U", ..- , 00001001, ASCII 85
Data 82, ' "R", .-. , 00001010, ASCII 82
Data 87, ' "W", .-- , 00001011, ASCII 87
Data 68, ' "D", -.. , 00001100, ASCII 68
Data 75, ' "K", -.- , 00001101, ASCII 75
Data 71, ' "G", --. , 00001110, ASCII 71
Data 79, ' "O", --- , 00001111, ASCII 79
Data 72, ' "H", .... , 00010000, ASCII 72
Data 86, ' "V", ...- , 00010001, ASCII 86
Data 70, ' "F", ..-. , 00010010, ASCII 70
Data 0,
Data 76, ' "L", .-.. , 00010100, ASCII 76
Data 0 , '
Data 80, ' "P", .--. , 00010110, ASCII 80
Data 74, ' "J", .--- , 00010111, ASCII 74
Data 66, ' "B", -... , 00011000, ASCII 66
Data 88, ' "X", -..- , 00011001, ASCII 88
Data 67, ' "C", -.-. , 00011010, ASCII 67
Data 89, ' "Y", -.-- , 00011011, ASCII 89
Data 90, ' "Z", --.. , 00011100, ASCII 90
Data 81, ' "Q", --.- , 00011101, ASCII 81
Data 0,
Data 0,
Data 53, ' "5", ..... , 00100000, ASCII 53
Data 52, ' "4", ....- , 00100001, ASCII 52
Data 0,
Data 51, ' "3", ...-- , 00100011, ASCII 51
Data 0,
Data 0,
Data 0,
Data 50, ' "2", ..--- , 00100111, ASCII 50
Data 32, ' "as" .-... , 00101000, ASCII 32
Data 0,
Data 43, ' "+" .-.-. , 00101010 ASCII 43
Data 0,
Data 0,
Data 0,
Data 0,
Data 49, ' "1", .---- , 00101111, ASCII 49
Data 54, ' "6", -.... , 00110000, ASCII 54
Data 61, ' "=" -...- , 00110001, ASCII 61
Data 47, ' "/" -..-. , 00110010, ASCII 47
Data 0,
Data 0,
Data 0,
Data 13, ' "kn" -.--. , 00110110 ASCII 13 (Return)
Data 0,
Data 55, ' "7", --... , 00111000, ASCII 55
Data 0,
Data 0,
Data 0,
Data 56, ' "8", ---.. , 00111100, ASCII 56
Data 0,
Data 57, ' "9", ----. , 00111110, ASCII 57
Data 48, ' "0", ----- , 00111111, ASCII 48
Data 8, ' "......" , 01000000, ASCII 8 (Backspace)
Data 0,
Data 0,
Data 0,
Data 0,
Data 0,
Data 0,
Data 0,
Data 0,
Data 0,
Data 0,
Data 0,
Data 63, ' "?", ..--.. , 01001100, ASCII 63
Data 0,
Data 0,
Data 0,
Data 0,
Data 0,
Data 34 , ' "'" .-..-. , 01010010, ASCII 34
Data 0,
Data 0,
Data 46, ' ".", .-.-.- , 01010101, ASCII 46
Data 0,
Data 0,
Data 0,
Data 0,
Data 0,
Data 0,
Data 0,
Data 0,
Data 0,
Data 0,
Data 0,
Data 45, ' "-", -....- , 01100001, ASCII 45
Data 0,
Data 0,
Data 0,
Data 0,
Data 0,
Data 0,
Data 0,
Data 0,
Data 0,
Data 0,
Data 0,
Data 41, ' ")", -.--.- , 01101101, ASCII 41
Data 0,
Data 0,
Data 0,
Data 0,
Data 0,
Data 44, ' ",", --..-- , 01110011, ASCII 44
Data 0,
Data 0,
Data 0,
Data 0,
Data 58, ' ":", ---... , 01111000, ASCII 58
Data 0,
Data 0,
Data 0,
Data 0,
Data 0
Data 0,
Data 0,
Data 8, ' "......." , 10000000, ASCII 8 (Backspace)




'----------------------------------------





Tabelle:


' 0 - 15 008: backspace 009: tab 011: tab back 013: return
Data 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 42 , 43 , 0 , 171 , 0 , 40 , 0 , 0,


' 16 - 31
Data 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0

' 32 - 47
Data 44 , 158 , 159 , 49 , 161 , 162 , 163 , 177 , 165 , 166 , 176 , 48 , 54 , 56 , 55 , 164

' 48 - 63 starts with 0 - 9
Data 39 , 30 , 31 , 32 , 33 , 34 , 35 , 36 , 37 , 38 , 183 , 182 , 100 , 167 , 228 , 173

' 64 - 79 a - o
Data 0 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 , 13 , 14 , 15 , 16 , 17 , 18,

' 80 - 95 p - z
Data 19 , 20 , 21 , 22 , 23 , 24 , 25 , 26 , 27 , 29 , 28 , 36



Elektronik-Labor   Projekte   AVR