From ef0613fc6a5eef89df1cff5c695e1ccf844359f9 Mon Sep 17 00:00:00 2001 From: Florian Jung Date: Thu, 20 Mar 2014 17:44:06 +0100 Subject: initial experiments --- main.c | 231 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 220 insertions(+), 11 deletions(-) (limited to 'main.c') diff --git a/main.c b/main.c index dea03c1..72f9b1a 100644 --- a/main.c +++ b/main.c @@ -1,25 +1,234 @@ +/** + * Project: AVR ATtiny USB Tutorial at http://codeandlife.com/ + * Author: Joonas Pihlajamaa, joonas.pihlajamaa@iki.fi + * Base on V-USB example code by Christian Starkjohann + * Copyright: (c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH + * License: GNU GPL v3 (see License.txt) + */ #include -#include +#include +#include +#include +#include +#include "usbdrv/usbdrv.h" -int main (void) +// ************************ +// *** USB HID ROUTINES *** +// ************************ + +// From Frank Zhao's USB Business Card project +// http://www.frank-zhao.com/cache/usbbusinesscard_details.php +PROGMEM const char + usbHidReportDescriptor[USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH] = { + 0x05, 0x01, // USAGE_PAGE (Generic Desktop) + 0x09, 0x06, // USAGE (Keyboard) + 0xa1, 0x01, // COLLECTION (Application) + 0x75, 0x01, // REPORT_SIZE (1) + 0x95, 0x08, // REPORT_COUNT (8) + 0x05, 0x07, // USAGE_PAGE (Keyboard)(Key Codes) + 0x19, 0xe0, // USAGE_MINIMUM (Keyboard LeftControl)(224) + 0x29, 0xe7, // USAGE_MAXIMUM (Keyboard Right GUI)(231) + 0x15, 0x00, // LOGICAL_MINIMUM (0) + 0x25, 0x01, // LOGICAL_MAXIMUM (1) + 0x81, 0x02, // INPUT (Data,Var,Abs) ; Modifier byte + 0x95, 0x01, // REPORT_COUNT (1) + 0x75, 0x08, // REPORT_SIZE (8) + 0x81, 0x03, // INPUT (Cnst,Var,Abs) ; Reserved byte + 0x95, 0x05, // REPORT_COUNT (5) + 0x75, 0x01, // REPORT_SIZE (1) + 0x05, 0x08, // USAGE_PAGE (LEDs) + 0x19, 0x01, // USAGE_MINIMUM (Num Lock) + 0x29, 0x05, // USAGE_MAXIMUM (Kana) + 0x91, 0x02, // OUTPUT (Data,Var,Abs) ; LED report + 0x95, 0x01, // REPORT_COUNT (1) + 0x75, 0x03, // REPORT_SIZE (3) + 0x91, 0x03, // OUTPUT (Cnst,Var,Abs) ; LED report padding + 0x95, 0x06, // REPORT_COUNT (6) + 0x75, 0x08, // REPORT_SIZE (8) + 0x15, 0x00, // LOGICAL_MINIMUM (0) + 0x25, 0x65, // LOGICAL_MAXIMUM (101) + 0x05, 0x07, // USAGE_PAGE (Keyboard)(Key Codes) + 0x19, 0x00, // USAGE_MINIMUM (Reserved (no event indicated))(0) + 0x29, 0x65, // USAGE_MAXIMUM (Keyboard Application)(101) + 0x81, 0x00, // INPUT (Data,Ary,Abs) + 0xc0 // END_COLLECTION +}; + +typedef struct { - DDRC=0x00; - PORTC=0x00; + uint8_t modifier; + uint8_t reserved; + uint8_t keycode[6]; +} keyboard_report_t; - DDRB=0xFF; +static keyboard_report_t keyboard_report; // sent to PC +volatile static uchar LED_state = 0xff; // received from PC +static uchar idleRate; // repeat rate for keyboards +#define LED_BLUE (1<<5) +#define LED_RED (1<<4) +#define LED_GREEN (1<<3) +usbMsgLen_t usbFunctionSetup(uchar data[8]) +{ + usbRequest_t *rq = (void *) data; - while(1) + if ((rq->bmRequestType & USBRQ_TYPE_MASK) == USBRQ_TYPE_CLASS) { - PORTB|=1; - _delay_ms(500); - PORTB&= ~1; - _delay_ms(500); + switch (rq->bRequest) + { + case USBRQ_HID_GET_REPORT: // send "no keys pressed" if asked here + // wValue: ReportType (highbyte), ReportID (lowbyte) + usbMsgPtr = (void *) &keyboard_report; // we only have this one + keyboard_report.modifier = 0; + keyboard_report.keycode[0] = 0; + return sizeof(keyboard_report); + case USBRQ_HID_SET_REPORT: // if wLength == 1, should be LED state + return (rq->wLength.word == 1) ? USB_NO_MSG : 0; + + case USBRQ_HID_GET_IDLE: // send idle rate to PC as required by spec + usbMsgPtr = &idleRate; + return 1; + + case USBRQ_HID_SET_IDLE: // save idle rate as required by spec + idleRate = rq->wValue.bytes[1]; + return 0; + } } + return 0; // by default don't return any data +} + +usbMsgLen_t usbFunctionWrite(uint8_t * data, uchar len) +{ + if (data[0] == LED_state) + return 1; + else + LED_state = data[0]; + + return 1; // Data read, not expecting more +} + +// Now only supports letters 'a' to 'z' and 0 (NULL) to clear buttons +void buildReport(int n) +{ + keyboard_report.modifier = 0; + + if (n > 0 && n <= 9) + keyboard_report.keycode[0] = 29+n; + else if (n == 0) + keyboard_report.keycode[0] = 39; + else if (n == -2) + keyboard_report.keycode[0] = 40; // enter + else if (n == -1) + keyboard_report.keycode[0] = 0; +} + +#define STATE_WAIT 0 +#define STATE_SEND_KEY 1 +#define STATE_RELEASE_KEY 2 + +void jump_to_bootloader(void) +{ + cli(); + wdt_enable(WDTO_15MS); + while (1); +} + + +int main() +{ + uchar i, button_release_counter = 0, state = STATE_WAIT; + + DDRC = 0x38; // LEDs as output + PORTC |= LED_BLUE | LED_RED | LED_GREEN; + DDRD &= ~0xF3; // connector ports as input + DDRB &= ~0x3C; + PORTD &= ~0xF3; // disable pullups for unused ports + PORTB &= ~0x0C; + PORTB |= 0x30; // enable pullups for PB4 and 5 + + cli(); + + for (i = 0; i < sizeof(keyboard_report); i++) // clear report initially + ((uchar *) & keyboard_report)[i] = 0; + + wdt_enable(WDTO_1S); // enable 1s watchdog timer + + usbInit(); + + usbDeviceDisconnect(); // enforce re-enumeration + for (i = 0; i < 250; i++) + { // wait 500 ms + wdt_reset(); // keep the watchdog happy + _delay_ms(10); + } + usbDeviceConnect(); + + sei(); // Enable interrupts after re-enumeration + uint32_t j; + + for (uint32_t i = 0; i < 300000; i++) + { + wdt_reset(); // keep the watchdog happy + usbPoll(); + + if (i % 5000 == 0) + { // button pressed (PB1 at ground voltage) + PORTC ^= LED_GREEN; + // also check if some time has elapsed since last button press + if (state == STATE_WAIT && button_release_counter == 255) + state = STATE_SEND_KEY; + + button_release_counter = 0; // now button needs to be released a while until retrigger + } + + if (button_release_counter < 255) + button_release_counter++; // increase release counter + + // characters are sent when messageState == STATE_SEND and after receiving + // the initial LED state from PC (good way to wait until device is recognized) + + if (state == STATE_WAIT) + j++; + + if (PINB & 0x20) + PORTC &= ~LED_RED; + else + PORTC |= LED_RED; + + if (PINB & 0x10) + PORTC |= LED_GREEN; + else + PORTC &= ~LED_GREEN; + + if (usbInterruptIsReady() && state != STATE_WAIT && LED_state != 0xff) + { + switch (state) + { + case STATE_SEND_KEY: + buildReport(2); + state = STATE_RELEASE_KEY; // release next + PORTC &= ~LED_BLUE; + break; + + case STATE_RELEASE_KEY: + buildReport(-1); + state = STATE_WAIT; // go back to waiting + PORTC |= LED_BLUE; + break; + + default: + state = STATE_WAIT; // should not happen + } + + // start sending + usbSetInterrupt((void *) &keyboard_report, sizeof(keyboard_report)); + } + } - return 0; // never reached + jump_to_bootloader(); + return 0; } -- cgit v1.2.1