USBASP / ISP Programmer as V-USB
USBASP / ISP Programmer as V-USB
I have ordered 2 USBASP sticks.
This is the schematic:
Can I use this device as V-USB device?
Can you help me to Implement a 4 Button HID Keyboard?
Ripper121
This is the schematic:
Can I use this device as V-USB device?
Can you help me to Implement a 4 Button HID Keyboard?
Ripper121
Re: USBASP / ISP Programmer as V-USB
Yes, it can be used for V-USB (and in fact the USBasp firmware itself uses V-USB). You can easily reprogram the second one with the first. It would be great for a four-button keyboard, as it has at least four I/O pins on the end connector: PB2, PB3, PB4, and PB5:
Once you get them I'd be glad to help out, as I love using USBasp sticks as inexpensive V-USB development boards.
Code: Select all
------
MOSI PB3 | 1 2 | +5V
N/C | 3 4 | PD1 TXD (via 1K resistor)
/RES PB2 5 6 | PD0 RXD
SCK PB5 | 7 8 | GND
MISO PB4 | 9 10 | GND
------
Male end on programmer
Once you get them I'd be glad to help out, as I love using USBasp sticks as inexpensive V-USB development boards.
Re: USBASP / ISP Programmer as V-USB
Yes my way of thinking. Cheapest hardware i found was 1,72€.
And this version has 5 (6 when i use the reset also) free pins and with 6pins = 6 bit there are 63 Keys possible.
If you can test it with a USBASP stick can you send me pleas the code?
I can begin to develope and test i hope next week when i recive the parts.
my Mail is: Ripper121(at)gmail.com for future contact
And this version has 5 (6 when i use the reset also) free pins and with 6pins = 6 bit there are 63 Keys possible.
If you can test it with a USBASP stick can you send me pleas the code?
I can begin to develope and test i hope next week when i recive the parts.
my Mail is: Ripper121(at)gmail.com for future contact
Re: USBASP / ISP Programmer as V-USB
I modified HIDKeys to work with the usbasp and four switches connected to PB2-PB5 and took pictures of the steps. In the pictures I show how an inexpensive set of "dupont" male-male cables makes it nice to use with a breadboard.
My steps: connect one programmer to another, bridge JP2 on the one being programmed (not connected to USB). Connect other to USB. make flash to put HIDKeys on. Disconnect reprogrammed USBasp. Connect switches (one between PB2 and GND, next PB3 and GND, etc.). Verify wiring. Connect to PC. Type ABCD.
Oh and once all this is working and you're comfortable with it, you can put the USBaspLoader bootloader on the device and be able to reflash it without having to connect the programmer.
My steps: connect one programmer to another, bridge JP2 on the one being programmed (not connected to USB). Connect other to USB. make flash to put HIDKeys on. Disconnect reprogrammed USBasp. Connect switches (one between PB2 and GND, next PB3 and GND, etc.). Verify wiring. Connect to PC. Type ABCD.
Oh and once all this is working and you're comfortable with it, you can put the USBaspLoader bootloader on the device and be able to reflash it without having to connect the programmer.
Re: USBASP / ISP Programmer as V-USB
With what for software do you upload it?
Do you have change any Fusebits or settings to flash it?
Can you write me pleas a mail?
Is this the 12MHz quarz on the stick and a Atmega8 L?
Ripper121[at]googlemail.com
I need exactly what you have done. But it should work with 8 keys.
Do you have change any Fusebits or settings to flash it?
Can you write me pleas a mail?
Is this the 12MHz quarz on the stick and a Atmega8 L?
Ripper121[at]googlemail.com
I need exactly what you have done. But it should work with 8 keys.
Re: USBASP / ISP Programmer as V-USB
I use avrdude to flash it. I didn't change any fusebits since naturally USBasp needs the same settings as almost any V-USB program would. It is a 12MHz quartz crystal with atmega8A. That is' not an atmega8L shouldn't matter for this use.
Oh, change in requirements to double the number of keys I see. For double the number, a scanning line would probably be best. I can add that to the code as I'm experimenting with it further today.
I'll contact you in e-mail once you get your boards and have something definite to work with. I'm sharing the rest here in the forum so others can benefit too.
Can you help me to Implement a 4 Button HID Keyboard?
But it should work with 8 keys.
Oh, change in requirements to double the number of keys I see. For double the number, a scanning line would probably be best. I can add that to the code as I'm experimenting with it further today.
I'll contact you in e-mail once you get your boards and have something definite to work with. I'm sharing the rest here in the forum so others can benefit too.
Re: USBASP / ISP Programmer as V-USB
Ok big thanks.
Can't wait to test it.
Can't wait to test it.
Re: USBASP / ISP Programmer as V-USB
This is my schematic.
This is my source:
Download
But it dont work for me, i cant read out the Key Matrix .
This is my source:
Download
Code: Select all
/* Name: main.c
* Project: HID-Test
* Author: Christian Starkjohann
* Creation Date: 2006-02-02
* Tabsize: 4
* Copyright: (c) 2006 by OBJECTIVE DEVELOPMENT Software GmbH
* License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt)
* This Revision: $Id$
*/
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <avr/wdt.h>
#include "usbdrv.h"
#include "oddebug.h"
/* ----------------------- hardware I/O abstraction ------------------------ */
/* pin assignments:
PB0 Key 1
PB1 Key 2
PB2 Key 3
PB3 Key 4
PB4 Key 5
PB5 Key 6
PC0 Key 7
PC1 Key 8
PC2 Key 9
PC3 Key 10
PC4 Key 11
PC5 Key 12
PD0 USB-
PD1 debug tx
PD2 USB+ (int0)
PD3 Key 13
PD4 Key 14
PD5 Key 15
PD6 Key 16
PD7 Key 17
*/
uint8_t keyscan( void )
{
DDRB &= ~( (1<<PB4) ); // PB4 als Eingaenge
PORTB |= ( (1<<PB4) ); // PB4 Pull-Up einschalten
DDRB |= (1 << PB5); // Ausgang
PORTB &= ~(1 << PB5); //Low
if (!(PIND & (1<<PIND1))){
if (!(PINB & (1<<PINB4))) {
return 1;
}
}
if (!(PIND & (1<<PIND0))) {
if (!(PINB & (1<<PINB4))) {
return 2;
}
}
if (!(PINB & (1<<PINB2))) {
if (!(PINB & (1<<PINB4))) {
return 3;
}
}
if (!(PINB & (1<<PINB3))) {
if (!(PINB & (1<<PINB4))) {
return 4;
}
}
DDRB &= ~( (1<<PB5) ); // PB5 als Eingaenge
PORTB |= ( (1<<PB5) ); // PB5 Pull-Up einschalten
DDRB |= (1 << PB4); // Ausgang
PORTB &= ~(1 << PB4); //Low
if (!(PIND & (1<<PIND1))) {
if (!(PINB & (1<<PINB5))) {
return 5;
}
}
if (!(PIND & (1<<PIND0))) {
if (!(PINB & (1<<PINB5))) {
return 6;
}
}
if (!(PINB & (1<<PINB2))) {
if (!(PINB & (1<<PINB5))) {
return 7;
}
}
if (!(PINB & (1<<PINB3))) {
if (!(PINB & (1<<PINB5))) {
return 8;
}
}
return 0;
}
static void hardwareInit(void)
{
DDRC |= (1 << PC0); // Ausgang
PORTC &= ~(1 << PC0); //PB1 High
DDRC |= (1 << PC1); // Ausgang
PORTC &= ~(1 << PC1); //PB1 High
DDRB &= ~( (1<<PB2) ); // PB2 als Eingaenge
PORTB |= ( (1<<PB2) ); // PB2 Pull-Up einschalten
DDRB &= ~( (1<<PB3) ); // PB3 als Eingaenge
PORTB |= ( (1<<PB3) ); // PB3 Pull-Up einschalten
DDRD &= ~( (1<<PD0) ); // PD0 als Eingaenge
PORTD |= ( (1<<PD0) ); // PD0 Pull-Up einschalten
DDRD &= ~( (1<<PD1) ); // PD1 als Eingaenge
PORTD |= ( (1<<PD1) ); // PD1 Pull-Up einschalten
/* configure timer 0 for a rate of 12M/(1024 * 256) = 45.78 Hz (~22ms) */
TCCR0 = 5; /* timer 0 prescaler: 1024 */
}
/* ------------------------------------------------------------------------- */
#define NUM_KEYS 8
/* ------------------------------------------------------------------------- */
/* ----------------------------- USB interface ----------------------------- */
/* ------------------------------------------------------------------------- */
static uchar reportBuffer[2]; /* buffer for HID reports */
static uchar idleRate; /* in 4 ms units */
const PROGMEM char usbHidReportDescriptor[35] = { /* USB report descriptor */
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x06, // USAGE (Keyboard)
0xa1, 0x01, // COLLECTION (Application)
0x05, 0x07, // USAGE_PAGE (Keyboard)
0x19, 0xe0, // USAGE_MINIMUM (Keyboard LeftControl)
0x29, 0xe7, // USAGE_MAXIMUM (Keyboard Right GUI)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x75, 0x01, // REPORT_SIZE (1)
0x95, 0x08, // REPORT_COUNT (8)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x95, 0x01, // REPORT_COUNT (1)
0x75, 0x08, // REPORT_SIZE (8)
0x25, 0x65, // LOGICAL_MAXIMUM (101)
0x19, 0x00, // USAGE_MINIMUM (Reserved (no event indicated))
0x29, 0x65, // USAGE_MAXIMUM (Keyboard Application)
0x81, 0x00, // INPUT (Data,Ary,Abs)
0xc0 // END_COLLECTION
};
/* We use a simplifed keyboard report descriptor which does not support the
* boot protocol. We don't allow setting status LEDs and we only allow one
* simultaneous key press (except modifiers). We can therefore use short
* 2 byte input reports.
* The report descriptor has been created with usb.org's "HID Descriptor Tool"
* which can be downloaded from http://www.usb.org/developers/hidpage/.
* Redundant entries (such as LOGICAL_MINIMUM and USAGE_PAGE) have been omitted
* for the second INPUT item.
*/
/* Keyboard usage values, see usb.org's HID-usage-tables document, chapter
* 10 Keyboard/Keypad Page for more codes.
*/
#define MOD_CONTROL_LEFT (1<<0)
#define MOD_SHIFT_LEFT (1<<1)
#define MOD_ALT_LEFT (1<<2)
#define MOD_GUI_LEFT (1<<3)
#define MOD_CONTROL_RIGHT (1<<4)
#define MOD_SHIFT_RIGHT (1<<5)
#define MOD_ALT_RIGHT (1<<6)
#define MOD_GUI_RIGHT (1<<7)
#define KEY_A 4
#define KEY_B 5
#define KEY_C 6
#define KEY_D 7
#define KEY_E 8
#define KEY_F 9
#define KEY_G 10
#define KEY_H 11
#define KEY_I 12
#define KEY_J 13
#define KEY_K 14
#define KEY_L 15
#define KEY_M 16
#define KEY_N 17
#define KEY_O 18
#define KEY_P 19
#define KEY_Q 20
#define KEY_R 21
#define KEY_S 22
#define KEY_T 23
#define KEY_U 24
#define KEY_V 25
#define KEY_W 26
#define KEY_X 27
#define KEY_Y 28
#define KEY_Z 29
#define KEY_1 30
#define KEY_2 31
#define KEY_3 32
#define KEY_4 33
#define KEY_5 34
#define KEY_6 35
#define KEY_7 36
#define KEY_8 37
#define KEY_9 38
#define KEY_0 39
#define KEY_F1 58
#define KEY_F2 59
#define KEY_F3 60
#define KEY_F4 61
#define KEY_F5 62
#define KEY_F6 63
#define KEY_F7 64
#define KEY_F8 65
#define KEY_F9 66
#define KEY_F10 67
#define KEY_F11 68
#define KEY_F12 69
static const uchar keyReport[NUM_KEYS + 1][2] PROGMEM = {
/* none */ {0, 0}, /* no key pressed */
/* 1 */ {MOD_SHIFT_LEFT, KEY_A},
/* 2 */ {MOD_SHIFT_LEFT, KEY_B},
/* 3 */ {MOD_SHIFT_LEFT, KEY_C},
/* 4 */ {MOD_SHIFT_LEFT, KEY_D},
/* 5 */ {MOD_SHIFT_LEFT, KEY_E},
/* 6 */ {MOD_SHIFT_LEFT, KEY_F},
/* 7 */ {MOD_SHIFT_LEFT, KEY_G},
/* 8 */ {MOD_SHIFT_LEFT, KEY_H},
};
static void buildReport(uchar key)
{
/* This (not so elegant) cast saves us 10 bytes of program memory */
*(int *)reportBuffer = pgm_read_word(keyReport[key]);
}
uchar usbFunctionSetup(uchar data[8])
{
usbRequest_t *rq = (void *)data;
usbMsgPtr = reportBuffer;
if((rq->bmRequestType & USBRQ_TYPE_MASK) == USBRQ_TYPE_CLASS){ /* class request type */
if(rq->bRequest == USBRQ_HID_GET_REPORT){ /* wValue: ReportType (highbyte), ReportID (lowbyte) */
/* we only have one report type, so don't look at wValue */
buildReport(keyscan());
return sizeof(reportBuffer);
}else if(rq->bRequest == USBRQ_HID_GET_IDLE){
usbMsgPtr = &idleRate;
return 1;
}else if(rq->bRequest == USBRQ_HID_SET_IDLE){
idleRate = rq->wValue.bytes[1];
}
}else{
/* no vendor specific requests implemented */
}
return 0;
}
/* ------------------------------------------------------------------------- */
int main(void)
{
uchar key, lastKey = 0, keyDidChange = 0;
uchar idleCounter = 0;
wdt_enable(WDTO_2S);
hardwareInit();
odDebugInit();
usbInit();
sei();
DBG1(0x00, 0, 0);
for(;;){ /* main event loop */
wdt_reset();
usbPoll();
key = keyscan();
if(lastKey != key){
lastKey = key;
keyDidChange = 1;
}
if(TIFR & (1<<TOV0)){ /* 22 ms timer */
TIFR = 1<<TOV0;
if(idleRate != 0){
if(idleCounter > 4){
idleCounter -= 5; /* 22 ms in units of 4 ms */
}else{
idleCounter = idleRate;
keyDidChange = 1;
}
}
}
if(keyDidChange && usbInterruptIsReady()){
keyDidChange = 0;
/* use last key and not current key status in order to avoid lost
changes in key status. */
buildReport(lastKey);
usbSetInterrupt(reportBuffer, sizeof(reportBuffer));
}
}
return 0;
}
/* ------------------------------------------------------------------------- */
But it dont work for me, i cant read out the Key Matrix .
Re: USBASP / ISP Programmer as V-USB
Three problems fixed and your code works for me. First, you mistakenly used PINxx as the bit number almost everywhere:
The Pxx constants for port bits aren't very useful IMO. I just use the bit number. True, the Pxx constants shield code from Atmel some day putting bit 0 of a port on a different bit in the register, but that seems too remote to guard against.
The next problem is a minor logic error:
You check PB4 for low when you've got it pulled up. I thought you meant to check PB5, but that would be pointless since you know PB5 is low. I'm thinking maybe this is a check for multiple keys pressed (ghosting) so you don't rept a phantom key press. In that case, maybe you wanted if(PINB & (1<<PB4)).
Finally, put a delay after setting the scan lines, to give them time to reach their new levels. This is especially true if you're checking for phantom keys by using the one pulled up, as that will take a little time to reach high level. 200 microseconds works for me:
One final change, as the original code doesn't do any debouncing. Only scan the keys when USB is ready for new updates. This puts an 8ms delay after key changes and avoids most bouncing:
Complete main.c:
Code: Select all
if (!(PIND & (1<<PIND0))) { // error
if (!(PIND & (1<<PD0))) { // correct
if (!(PIND & (1<<0))) { // what I use
The Pxx constants for port bits aren't very useful IMO. I just use the bit number. True, the Pxx constants shield code from Atmel some day putting bit 0 of a port on a different bit in the register, but that seems too remote to guard against.
The next problem is a minor logic error:
Code: Select all
uint8_t keyscan( void )
{
PORTB |= ( (1<<PB4) ); // PB4 Pull-Up einschalten
[...]
PORTB &= ~(1 << PB5); //Low
if (!(PIND & (1<<PD1))){
if (!(PINB & (1<<PB4))) {
return 1;
}
}
You check PB4 for low when you've got it pulled up. I thought you meant to check PB5, but that would be pointless since you know PB5 is low. I'm thinking maybe this is a check for multiple keys pressed (ghosting) so you don't rept a phantom key press. In that case, maybe you wanted if(PINB & (1<<PB4)).
Finally, put a delay after setting the scan lines, to give them time to reach their new levels. This is especially true if you're checking for phantom keys by using the one pulled up, as that will take a little time to reach high level. 200 microseconds works for me:
Code: Select all
uint8_t keyscan( void )
{
PORTB |= ( (1<<PB4) ); // PB4 Pull-Up einschalten
[...]
PORTB &= ~(1 << PB5); //Low
_delay_us( 200 ); // added
if (!(PIND & (1<<PD1))){
if ((PINB & (1<<PB4))) {
return 1;
}
}
One final change, as the original code doesn't do any debouncing. Only scan the keys when USB is ready for new updates. This puts an 8ms delay after key changes and avoids most bouncing:
Code: Select all
for(;;){ /* main event loop */
wdt_reset();
usbPoll();
if(TIFR & (1<<TOV0)){ /* 22 ms timer */
TIFR = 1<<TOV0;
if(idleRate != 0){
if(idleCounter > 4){
idleCounter -= 5; /* 22 ms in units of 4 ms */
}else{
idleCounter = idleRate;
keyDidChange = 1;
}
}
}
if(usbInterruptIsReady()){ // only scan if USB is ready
key = keyscan();
if(lastKey != key){
lastKey = key;
keyDidChange = 1;
}
if(keyDidChange){
keyDidChange = 0;
/* use last key and not current key status in order to avoid lost
changes in key status. */
buildReport(lastKey);
usbSetInterrupt(reportBuffer, sizeof(reportBuffer));
}
}
}
Complete main.c:
Code: Select all
/* Name: main.c
* Project: HID-Test
* Author: Christian Starkjohann
* Creation Date: 2006-02-02
* Tabsize: 4
* Copyright: (c) 2006 by OBJECTIVE DEVELOPMENT Software GmbH
* License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt)
* This Revision: $Id$
*/
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <avr/wdt.h>
#include <util/delay.h>
#include "usbdrv.h"
#include "oddebug.h"
/* ----------------------- hardware I/O abstraction ------------------------ */
/* pin assignments:
PB0 Key 1
PB1 Key 2
PB2 Key 3
PB3 Key 4
PB4 Key 5
PB5 Key 6
PC0 Key 7
PC1 Key 8
PC2 Key 9
PC3 Key 10
PC4 Key 11
PC5 Key 12
PD0 USB-
PD1 debug tx
PD2 USB+ (int0)
PD3 Key 13
PD4 Key 14
PD5 Key 15
PD6 Key 16
PD7 Key 17
*/
uint8_t keyscan( void )
{
DDRB &= ~( (1<<PB4) ); // PB4 als Eingaenge
PORTB |= ( (1<<PB4) ); // PB4 Pull-Up einschalten
DDRB |= (1 << PB5); // Ausgang
PORTB &= ~(1 << PB5); //Low
_delay_us( 200 );
if (!(PIND & (1<<PD1))){
if ((PINB & (1<<PB4))) {
return 1;
}
}
if (!(PIND & (1<<PD0))) {
if ((PINB & (1<<PB4))) {
return 2;
}
}
if (!(PINB & (1<<PB2))) {
if ((PINB & (1<<PB4))) {
return 3;
}
}
if (!(PINB & (1<<PB3))) {
if ((PINB & (1<<PB4))) {
return 4;
}
}
DDRB &= ~( (1<<PB5) ); // PB5 als Eingaenge
PORTB |= ( (1<<PB5) ); // PB5 Pull-Up einschalten
DDRB |= (1 << PB4); // Ausgang
PORTB &= ~(1 << PB4); //Low
_delay_us( 200 );
if (!(PIND & (1<<PD1))) {
if ((PINB & (1<<PB5))) {
return 5;
}
}
if (!(PIND & (1<<PD0))) {
if ((PINB & (1<<PB5))) {
return 6;
}
}
if (!(PINB & (1<<PB2))) {
if ((PINB & (1<<PB5))) {
return 7;
}
}
if (!(PINB & (1<<PB3))) {
if ((PINB & (1<<PB5))) {
return 8;
}
}
return 0;
}
static void hardwareInit(void)
{
DDRC |= (1 << PC0); // Ausgang
PORTC &= ~(1 << PC0); //PB1 High
DDRC |= (1 << PC1); // Ausgang
PORTC &= ~(1 << PC1); //PB1 High
DDRB &= ~( (1<<PB2) ); // PB2 als Eingaenge
PORTB |= ( (1<<PB2) ); // PB2 Pull-Up einschalten
DDRB &= ~( (1<<PB3) ); // PB3 als Eingaenge
PORTB |= ( (1<<PB3) ); // PB3 Pull-Up einschalten
DDRD &= ~( (1<<PD0) ); // PD0 als Eingaenge
PORTD |= ( (1<<PD0) ); // PD0 Pull-Up einschalten
DDRD &= ~( (1<<PD1) ); // PD1 als Eingaenge
PORTD |= ( (1<<PD1) ); // PD1 Pull-Up einschalten
/* configure timer 0 for a rate of 12M/(1024 * 256) = 45.78 Hz (~22ms) */
TCCR0 = 5; /* timer 0 prescaler: 1024 */
}
/* ------------------------------------------------------------------------- */
#define NUM_KEYS 8
/* ------------------------------------------------------------------------- */
/* ----------------------------- USB interface ----------------------------- */
/* ------------------------------------------------------------------------- */
static uchar reportBuffer[2]; /* buffer for HID reports */
static uchar idleRate; /* in 4 ms units */
const PROGMEM char usbHidReportDescriptor[35] = { /* USB report descriptor */
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x06, // USAGE (Keyboard)
0xa1, 0x01, // COLLECTION (Application)
0x05, 0x07, // USAGE_PAGE (Keyboard)
0x19, 0xe0, // USAGE_MINIMUM (Keyboard LeftControl)
0x29, 0xe7, // USAGE_MAXIMUM (Keyboard Right GUI)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x75, 0x01, // REPORT_SIZE (1)
0x95, 0x08, // REPORT_COUNT (8)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x95, 0x01, // REPORT_COUNT (1)
0x75, 0x08, // REPORT_SIZE (8)
0x25, 0x65, // LOGICAL_MAXIMUM (101)
0x19, 0x00, // USAGE_MINIMUM (Reserved (no event indicated))
0x29, 0x65, // USAGE_MAXIMUM (Keyboard Application)
0x81, 0x00, // INPUT (Data,Ary,Abs)
0xc0 // END_COLLECTION
};
/* We use a simplifed keyboard report descriptor which does not support the
* boot protocol. We don't allow setting status LEDs and we only allow one
* simultaneous key press (except modifiers). We can therefore use short
* 2 byte input reports.
* The report descriptor has been created with usb.org's "HID Descriptor Tool"
* which can be downloaded from http://www.usb.org/developers/hidpage/.
* Redundant entries (such as LOGICAL_MINIMUM and USAGE_PAGE) have been omitted
* for the second INPUT item.
*/
/* Keyboard usage values, see usb.org's HID-usage-tables document, chapter
* 10 Keyboard/Keypad Page for more codes.
*/
#define MOD_CONTROL_LEFT (1<<0)
#define MOD_SHIFT_LEFT (1<<1)
#define MOD_ALT_LEFT (1<<2)
#define MOD_GUI_LEFT (1<<3)
#define MOD_CONTROL_RIGHT (1<<4)
#define MOD_SHIFT_RIGHT (1<<5)
#define MOD_ALT_RIGHT (1<<6)
#define MOD_GUI_RIGHT (1<<7)
#define KEY_A 4
#define KEY_B 5
#define KEY_C 6
#define KEY_D 7
#define KEY_E 8
#define KEY_F 9
#define KEY_G 10
#define KEY_H 11
#define KEY_I 12
#define KEY_J 13
#define KEY_K 14
#define KEY_L 15
#define KEY_M 16
#define KEY_N 17
#define KEY_O 18
#define KEY_P 19
#define KEY_Q 20
#define KEY_R 21
#define KEY_S 22
#define KEY_T 23
#define KEY_U 24
#define KEY_V 25
#define KEY_W 26
#define KEY_X 27
#define KEY_Y 28
#define KEY_Z 29
#define KEY_1 30
#define KEY_2 31
#define KEY_3 32
#define KEY_4 33
#define KEY_5 34
#define KEY_6 35
#define KEY_7 36
#define KEY_8 37
#define KEY_9 38
#define KEY_0 39
#define KEY_F1 58
#define KEY_F2 59
#define KEY_F3 60
#define KEY_F4 61
#define KEY_F5 62
#define KEY_F6 63
#define KEY_F7 64
#define KEY_F8 65
#define KEY_F9 66
#define KEY_F10 67
#define KEY_F11 68
#define KEY_F12 69
static const uchar keyReport[NUM_KEYS + 1][2] PROGMEM = {
/* none */ {0, 0}, /* no key pressed */
/* 1 */ {MOD_SHIFT_LEFT, KEY_A},
/* 2 */ {MOD_SHIFT_LEFT, KEY_B},
/* 3 */ {MOD_SHIFT_LEFT, KEY_C},
/* 4 */ {MOD_SHIFT_LEFT, KEY_D},
/* 5 */ {MOD_SHIFT_LEFT, KEY_E},
/* 6 */ {MOD_SHIFT_LEFT, KEY_F},
/* 7 */ {MOD_SHIFT_LEFT, KEY_G},
/* 8 */ {MOD_SHIFT_LEFT, KEY_H},
};
static void buildReport(uchar key)
{
/* This (not so elegant) cast saves us 10 bytes of program memory */
*(int *)reportBuffer = pgm_read_word(keyReport[key]);
}
uchar usbFunctionSetup(uchar data[8])
{
usbRequest_t *rq = (void *)data;
usbMsgPtr = reportBuffer;
if((rq->bmRequestType & USBRQ_TYPE_MASK) == USBRQ_TYPE_CLASS){ /* class request type */
if(rq->bRequest == USBRQ_HID_GET_REPORT){ /* wValue: ReportType (highbyte), ReportID (lowbyte) */
/* we only have one report type, so don't look at wValue */
buildReport(keyscan());
return sizeof(reportBuffer);
}else if(rq->bRequest == USBRQ_HID_GET_IDLE){
usbMsgPtr = &idleRate;
return 1;
}else if(rq->bRequest == USBRQ_HID_SET_IDLE){
idleRate = rq->wValue.bytes[1];
}
}else{
/* no vendor specific requests implemented */
}
return 0;
}
/* ------------------------------------------------------------------------- */
int main(void)
{
uchar key, lastKey = 0, keyDidChange = 0;
uchar idleCounter = 0;
wdt_enable(WDTO_2S);
hardwareInit();
odDebugInit();
usbInit();
sei();
DBG1(0x00, 0, 0);
for(;;){ /* main event loop */
wdt_reset();
usbPoll();
if(TIFR & (1<<TOV0)){ /* 22 ms timer */
TIFR = 1<<TOV0;
if(idleRate != 0){
if(idleCounter > 4){
idleCounter -= 5; /* 22 ms in units of 4 ms */
}else{
idleCounter = idleRate;
keyDidChange = 1;
}
}
}
if(usbInterruptIsReady()){
key = keyscan();
if(lastKey != key){
lastKey = key;
keyDidChange = 1;
}
if(keyDidChange){
keyDidChange = 0;
/* use last key and not current key status in order to avoid lost
changes in key status. */
buildReport(lastKey);
usbSetInterrupt(reportBuffer, sizeof(reportBuffer));
}
}
}
return 0;
}
/* ------------------------------------------------------------------------- */