summaryrefslogtreecommitdiff
path: root/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'main.c')
-rw-r--r--main.c231
1 files changed, 220 insertions, 11 deletions
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 <avr/io.h>
-#include <avr/delay.h>
+#include <avr/interrupt.h>
+#include <avr/wdt.h>
+#include <avr/eeprom.h>
+#include <util/delay.h>
+#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;
}