MHVLib  20111011
An efficiency oriented runtime library for AVR microcontrollers
A:/eclipse/mhvlib-Vusb-Keyboard/MHV_VusbKeyboard.cpp
Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2011, Make, Hack, Void Inc
00003  * All rights reserved.
00004  *
00005  *  License: GNU GPL v2 (see mhvlib-Vusb-Keyboard/vusb/License.txt)
00006  *
00007  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
00008  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00009  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00010  * DISCLAIMED. IN NO EVENT SHALL MAKE, HACK, VOID BE LIABLE FOR ANY
00011  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00012  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00013  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
00014  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00015  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00016  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00017  */
00018 
00019 extern "C" {
00020         #include <vusb/usbdrv.h>
00021 }
00022 
00023 #include <MHV_VusbKeyboard.h>
00024 #include <avr/pgmspace.h>
00025 #include <util/delay.h>
00026 
00027 #define MHV_OSCCAL_EEPROM_ADDRESS       0
00028 
00029 /* We use a simplifed keyboard report descriptor which does not support the
00030  * boot protocol. We don't allow setting status LEDs and but we do allow
00031  * simultaneous key presses.
00032  * The report descriptor has been created with usb.org's "HID Descriptor Tool"
00033  * which can be downloaded from http://www.usb.org/developers/hidpage/.
00034  * Redundant entries (such as LOGICAL_MINIMUM and USAGE_PAGE) have been omitted
00035  * for the second INPUT item.
00036  */
00037 PROGMEM const char usbHidReportDescriptor[USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH] = { /* USB report descriptor */
00038   0x05, 0x01,   // USAGE_PAGE (Generic Desktop)
00039   0x09, 0x06,   // USAGE (Keyboard)
00040   0xa1, 0x01,   // COLLECTION (Application)
00041   0x05, 0x07,   //   USAGE_PAGE (Keyboard)
00042   0x19, 0xe0,   //   USAGE_MINIMUM (Keyboard LeftControl)
00043   0x29, 0xe7,   //   USAGE_MAXIMUM (Keyboard Right GUI)
00044   0x15, 0x00,   //   LOGICAL_MINIMUM (0)
00045   0x25, 0x01,   //   LOGICAL_MAXIMUM (1)
00046   0x75, 0x01,   //   REPORT_SIZE (1)
00047   0x95, 0x08,   //   REPORT_COUNT (8)
00048   0x81, 0x02,   //   INPUT (Data,Var,Abs)
00049   0x95, 0x01,   //   REPORT_COUNT (1)
00050   0x75, 0x08,   //   REPORT_SIZE (8)
00051   0x25, 0x65,   //   LOGICAL_MAXIMUM (101)
00052   0x19, 0x00,   //   USAGE_MINIMUM (Reserved (no event indicated))
00053   0x29, 0x65,   //   USAGE_MAXIMUM (Keyboard Application)
00054   0x81, 0x00,   //   INPUT (Data,Ary,Abs)
00055   0xc0          // END_COLLECTION
00056 };
00057 
00058 /* This code has been borrowed from Sparkfun's AVR Stick firmware
00059  * See: http://www.sparkfun.com/products/9147
00060  */
00061 #if USB_CFG_HAVE_MEASURE_FRAME_LENGTH
00062 #include <avr/eeprom.h>
00063 /* ------------------------------------------------------------------------- */
00064 /* ------------------------ Oscillator Calibration ------------------------- */
00065 /* ------------------------------------------------------------------------- */
00066 
00067 /* Calibrate the RC oscillator to 8.25 MHz. The core clock of 16.5 MHz is
00068  * derived from the 66 MHz peripheral clock by dividing. Our timing reference
00069  * is the Start Of Frame signal (a single SE0 bit) available immediately after
00070  * a USB RESET. We first do a binary search for the OSCCAL value and then
00071  * optimize this value with a neighboorhod search.
00072  * This algorithm may also be used to calibrate the RC oscillator directly to
00073  * 12 MHz (no PLL involved, can therefore be used on almost ALL AVRs), but this
00074  * is wide outside the spec for the OSCCAL value and the required precision for
00075  * the 12 MHz clock! Use the RC oscillator calibrated to 12 MHz for
00076  * experimental purposes only!
00077  */
00078 static void calibrateOscillator(void)
00079 {
00080 uchar       step = 128;
00081 uchar       trialValue = 0, optimumValue;
00082 int         x, optimumDev, targetValue = (unsigned)(1499 * (double)F_CPU / 10.5e6 + 0.5);
00083 
00084     /* do a binary search: */
00085     do{
00086         OSCCAL = trialValue + step;
00087         x = usbMeasureFrameLength();    /* proportional to current real frequency */
00088         if(x < targetValue)             /* frequency still too low */
00089             trialValue += step;
00090         step >>= 1;
00091     }while(step > 0);
00092     /* We have a precision of +/- 1 for optimum OSCCAL here */
00093     /* now do a neighborhood search for optimum value */
00094     optimumValue = trialValue;
00095     optimumDev = x; /* this is certainly far away from optimum */
00096     for(OSCCAL = trialValue - 1; OSCCAL <= trialValue + 1; OSCCAL++){
00097         x = usbMeasureFrameLength() - targetValue;
00098         if(x < 0)
00099             x = -x;
00100         if(x < optimumDev){
00101             optimumDev = x;
00102             optimumValue = OSCCAL;
00103         }
00104     }
00105     OSCCAL = optimumValue;
00106 }
00107 /*
00108 Note: This calibration algorithm may try OSCCAL values of up to 192 even if
00109 the optimum value is far below 192. It may therefore exceed the allowed clock
00110 frequency of the CPU in low voltage designs!
00111 You may replace this search algorithm with any other algorithm you like if
00112 you have additional constraints such as a maximum CPU clock.
00113 For version 5.x RC oscillators (those with a split range of 2x128 steps, e.g.
00114 ATTiny25, ATTiny45, ATTiny85), it may be useful to search for the optimum in
00115 both regions.
00116 */
00117 
00118 void    usbEventResetReady(void)
00119 {
00120     calibrateOscillator();
00121         uchar calibrationValue = eeprom_read_byte(MHV_OSCCAL_EEPROM_ADDRESS);
00122         if (calibrationValue != OSCCAL) {
00123                 eeprom_write_byte(MHV_OSCCAL_EEPROM_ADDRESS, OSCCAL);
00124         }
00125 }
00126 #endif
00127 
00128 static unsigned char    mhv_vusbReportBuffer[2];        /* buffer for HID reports */
00129 static unsigned char    mhv_vusbIdleRate = 1;           /* in 4 ms units */
00130 
00131 unsigned char usbFunctionSetup(uchar data[8]) {
00132         usbRequest_t *rq = (usbRequest_t *) ((void *) data);
00133 
00134         usbMsgPtr = mhv_vusbReportBuffer;
00135         if ((rq->bmRequestType & USBRQ_TYPE_MASK) == USBRQ_TYPE_CLASS) { /* class request type */
00136                 if (rq->bRequest == USBRQ_HID_GET_REPORT) { /* wValue: ReportType (highbyte), ReportID (lowbyte) */
00137                         /* we only have one report type, so don't look at wValue */
00138                         return sizeof(mhv_vusbReportBuffer);
00139                 } else if (rq->bRequest == USBRQ_HID_GET_IDLE) {
00140                         usbMsgPtr = &mhv_vusbIdleRate;
00141                         return 1;
00142                 } else if (rq->bRequest == USBRQ_HID_SET_IDLE) {
00143                         mhv_vusbIdleRate = rq->wValue.bytes[1];
00144                 }
00145         } else {
00146                 /* no vendor specific requests implemented */
00147         }
00148         return 0;
00149 }
00150 
00151 
00158 MHV_VusbKeyboard::MHV_VusbKeyboard(MHV_RTC *rtc) {
00159         _rtc = rtc;
00160 
00161 #if USB_CFG_HAVE_MEASURE_FRAME_LENGTH
00162         uchar calibrationValue;
00163 
00164         calibrationValue = eeprom_read_byte(MHV_OSCCAL_EEPROM_ADDRESS); /* calibration value from last time */
00165         if (calibrationValue != 0xff) {
00166                 OSCCAL = calibrationValue;
00167         }
00168 #endif
00169 
00170         mhv_setInput(mhv_make_pin(USB_CFG_IOPORTNAME, USB_CFG_DPLUS_BIT));
00171         mhv_setInput(mhv_make_pin(USB_CFG_IOPORTNAME, USB_CFG_DMINUS_BIT));
00172 
00173         usbDeviceDisconnect();
00174     for(uint8_t i=0;i<20;i++){  /* 300 ms disconnect */
00175         _delay_ms(15);
00176     }
00177         usbDeviceConnect();
00178 
00179         usbInit();
00180 
00181         MHV_ALARM newAlarm;
00182 
00183         _rtc->current(&(newAlarm.when));
00184         mhv_timestampIncrement(&(newAlarm.when), 0, 5);
00185         newAlarm.repeat.milliseconds = 5;
00186         newAlarm.repeat.timestamp = 0;
00187         newAlarm.listener = this;
00188 
00189         _rtc->addAlarm(&newAlarm);
00190 }
00191 
00199 void MHV_VusbKeyboard::keyStroke(MHV_VUSB_KEYBOARD_KEY key, uint8_t modifiers) {
00200         keyDown(key, modifiers);
00201         keysUp(0);
00202 }
00203 
00210 void MHV_VusbKeyboard::keyStroke(MHV_VUSB_KEYBOARD_KEY key) {
00211         return keyStroke(key, 0);
00212 }
00213 
00219 void MHV_VusbKeyboard::keyDown(MHV_VUSB_KEYBOARD_KEY key, uint8_t modifiers) {
00220         mhv_vusbReportBuffer[0] = modifiers;
00221         mhv_vusbReportBuffer[1] = key;
00222 
00223         while (!usbInterruptIsReady()) {}
00224         usbSetInterrupt(mhv_vusbReportBuffer, sizeof(mhv_vusbReportBuffer));
00225 }
00226 
00231 void MHV_VusbKeyboard::keysUp(uint8_t modifiers) {
00232         while (!usbInterruptIsReady()) {}
00233 
00234         mhv_vusbReportBuffer[0] = modifiers;
00235         mhv_vusbReportBuffer[1] = 0;
00236         usbSetInterrupt(mhv_vusbReportBuffer, sizeof(mhv_vusbReportBuffer));
00237 }
00238 
00242 void MHV_VusbKeyboard::keysUp() {
00243         keysUp(0);
00244 }
00245 
00249 void MHV_VusbKeyboard::alarm(MHV_ALARM *alarm) {
00250         usbPoll();
00251 }