SWUSB.LBX Examples |
Examples /
HIDKeyboardWhat is it?HID keyboard does not require drivers. Sub TypeMessage(message as string) Types a message over the keyboard. In the example, pressing button 0 types "Hello" to the PC. Sub TypeKey(key as byte) Types a single key at a time over the keyboard. In the example, pressing button 1 types "A" to the PC. Function Ascii2usage(ascii As Byte) As Word Converts an ASCII value to a HID keyboard usage code. keyboard.bas'USB Keyboard Emulator Example 'ollopa 2009-2012 ' 'This project emulates a standard 105 key USB keyboard. 'PORTB shows the status of the keyboard LEDs (CAPS lock, NUMLOCK, etc) 'PORTA connects to STK500 buttons. ' BTN0 = send "hello" message. ' BTN1 = send "A" 'Note: Buttons and LEDs are inverted due to STK500's inverted logic (0 = pressed / on) ' 'Test setup is on the STK500 with Vtarget set to 3.6V 'Using an atmega644 and external 12MHz crystal 'Save about 38 bytes of code size $noramclear $hwstack = 30 $swstack = 30 $framesize = 40 '24 bytes reserved rem porta.0 = 1 rem $regfile = "m644def.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 TypeMessage(message as string) 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 '******************************************************************************* '*************************** 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 Portd _usb_pin Alias Pind _usb_ddr Alias Ddrd 'Define the D+ and D- pins. (put D+ on an interrupt pin) Const _usb_dplus = 2 Const _usb_dminus = 3 'Configure the pins as inputs Config Pind.2 = Input Config Pind.3 = Input '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 = &HC0 'Required current in 2mA increments (500mA max) Const _usb_maxpower = 150 '150 * 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 '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 ************************** Config Portb = Output Config Porta = Input portb=&HFF '115200 buad double rate 'UCSR0A.u2x0 = 1 'ubrr0 = 12 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 message = "Hello" 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 if pina.0 = 0 then call TypeMessage(message) end if if PINA.1 = 0 then akey = 65 call TypeKey(akey) end if 'if buttons_current <> buttons_last or idlemode = 0 then 'end if Loop Sub TypeMessage(message as string) Local count as byte Local key as byte Local char as string * 1 for count = 1 to len(message) char = mid(message, count, 1) key = asc(char) call TypeKey(key) next End Sub 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 Word '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 'USB Keyboard usage codes '0 No event (no keys pressed) '1 Keyboard ErrorRollOver '2 Keyboard POSTFail '3 Undefined Keyboard Error 'Standard Keyboard keys '04-29 (a and A) to (z and Z) Letters '30-39 (1 and !) to (0 and )) Numbers '40 Enter/Return (^m) '41 Escape '42 Backspace (^h) '43 Tab '44 Space '45 - and _ '46 = and + '47 [ and { '48 ] and } '49 \ and | '50 Non-US # and ~ or \ and | '51 ; and : '52 ' and " '53 ` and ~ '54 , and < '55 . and > '56 / and ? '57 CAPS LOCK '58-69 F1 - F12 '70 PRINT SCREEN '71 SCROLL LOCK '72 Pause '73 Insert '74 Home '75 PageUp '76 Delete '77 End '78 PageDown '79 Right Arrow '80 Left Arrow '81 Down Arrow '82 Up Arrow 'Standard Keypad keys (ten-key) '83 Keypad Numlock '84 Keypad / '85 Keypad * '86 Keypad - '87 Keypad + '88 Keypad ENTER '89-98 Keypad 1-0 '99 Keypad . and DEL Select Case Ascii Case "a" To "z": Result = Ascii - 93 Case "A" To "Z": Result = Ascii - 61 Result = Result OR &B00000010_00000000 'left shift modifier Case "1" To "9": Result = Ascii - 19 Case "0": Result = 39 Case " ": Result = 44 Case Else: Result = 0 End Select Ascii2usage = Result End Function |