MHVLib  20111227
An efficiency oriented runtime library for AVR microcontrollers
A:/eclipse/mhvlib-Vusb-Console/MHV_VusbConsole.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-Console/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_VusbConsole.h>
00024 #include <avr/pgmspace.h>
00025 #include <util/delay.h>
00026 
00027 #define MHV_OSCCAL_EEPROM_ADDRESS       1022
00028 
00029 #define MAX_TX_SIZE             8
00030 
00031 // Taken from usb_debug_only.c:
00032 PROGMEM const char usbHidReportDescriptor[USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH] = { /* USB report descriptor */
00033                 0x06, 0x31, 0xFF,               // Usage Page 0xFF31 (vendor defined)
00034                 0x09, 0x74,                             // Usage 0x74
00035                 0xA1, 0x53,                             // Collection 0x53
00036                 0x75, 0x08,                             // report size = 8 bits
00037                 0x15, 0x00,                             // logical minimum = 0
00038                 0x26, 0xFF, 0x00,               // logical maximum = 255
00039                 0x95, MAX_TX_SIZE,              // report count
00040                 0x09, 0x75,                             // usage
00041                 0x81, 0x02,                             // Input (array)
00042                 0xC0                                    // end collection
00043 };
00044 
00045 /* This code has been borrowed from Sparkfun's AVR Stick firmware
00046  * See: http://www.sparkfun.com/products/9147
00047  */
00048 #if USB_CFG_HAVE_MEASURE_FRAME_LENGTH
00049 #include <avr/eeprom.h>
00050 /* ------------------------------------------------------------------------- */
00051 /* ------------------------ Oscillator Calibration ------------------------- */
00052 /* ------------------------------------------------------------------------- */
00053 
00054 /* Calibrate the RC oscillator to 8.25 MHz. The core clock of 16.5 MHz is
00055  * derived from the 66 MHz peripheral clock by dividing. Our timing reference
00056  * is the Start Of Frame signal (a single SE0 bit) available immediately after
00057  * a USB RESET. We first do a binary search for the OSCCAL value and then
00058  * optimize this value with a neighboorhod search.
00059  * This algorithm may also be used to calibrate the RC oscillator directly to
00060  * 12 MHz (no PLL involved, can therefore be used on almost ALL AVRs), but this
00061  * is wide outside the spec for the OSCCAL value and the required precision for
00062  * the 12 MHz clock! Use the RC oscillator calibrated to 12 MHz for
00063  * experimental purposes only!
00064  */
00065 static void calibrateOscillator(void)
00066 {
00067 uchar       step = 128;
00068 uchar       trialValue = 0, optimumValue;
00069 int         x, optimumDev, targetValue = (unsigned)(1499 * (double)F_CPU / 10.5e6 + 0.5);
00070 
00071     /* do a binary search: */
00072     do{
00073         OSCCAL = trialValue + step;
00074         x = usbMeasureFrameLength();    /* proportional to current real frequency */
00075         if(x < targetValue)             /* frequency still too low */
00076             trialValue += step;
00077         step >>= 1;
00078     }while(step > 0);
00079     /* We have a precision of +/- 1 for optimum OSCCAL here */
00080     /* now do a neighborhood search for optimum value */
00081     optimumValue = trialValue;
00082     optimumDev = x; /* this is certainly far away from optimum */
00083     for(OSCCAL = trialValue - 1; OSCCAL <= trialValue + 1; OSCCAL++){
00084         x = usbMeasureFrameLength() - targetValue;
00085         if(x < 0)
00086             x = -x;
00087         if(x < optimumDev){
00088             optimumDev = x;
00089             optimumValue = OSCCAL;
00090         }
00091     }
00092     OSCCAL = optimumValue;
00093 }
00094 /*
00095 Note: This calibration algorithm may try OSCCAL values of up to 192 even if
00096 the optimum value is far below 192. It may therefore exceed the allowed clock
00097 frequency of the CPU in low voltage designs!
00098 You may replace this search algorithm with any other algorithm you like if
00099 you have additional constraints such as a maximum CPU clock.
00100 For version 5.x RC oscillators (those with a split range of 2x128 steps, e.g.
00101 ATTiny25, ATTiny45, ATTiny85), it may be useful to search for the optimum in
00102 both regions.
00103 */
00104 
00105 void    usbEventResetReady(void)
00106 {
00107     calibrateOscillator();
00108         uchar calibrationValue = eeprom_read_byte(MHV_OSCCAL_EEPROM_ADDRESS);
00109         if (calibrationValue != OSCCAL) {
00110                 eeprom_write_byte(MHV_OSCCAL_EEPROM_ADDRESS, OSCCAL);
00111         }
00112 }
00113 #endif
00114 
00115 static unsigned char    mhv_vusbIdleRate = 1;           /* in 4 ms units */
00116 
00117 unsigned char usbFunctionSetup(uchar data[8]) {
00118         usbRequest_t *rq = (usbRequest_t *) ((void *) data);
00119 
00120         usbMsgPtr = NULL;
00121         if ((rq->bmRequestType & USBRQ_TYPE_MASK) == USBRQ_TYPE_CLASS) { /* class request type */
00122                 if (rq->bRequest == USBRQ_HID_GET_REPORT) { /* wValue: ReportType (highbyte), ReportID (lowbyte) */
00123                         return 0;
00124                 } else if (rq->bRequest == USBRQ_HID_GET_IDLE) {
00125                         usbMsgPtr = &mhv_vusbIdleRate;
00126                         return 1;
00127                 } else if (rq->bRequest == USBRQ_HID_SET_IDLE) {
00128                         mhv_vusbIdleRate = rq->wValue.bytes[1];
00129                 }
00130         } else {
00131                 /* no vendor specific requests implemented */
00132         }
00133         return 0;
00134 }
00135 
00136 
00148 MHV_VusbConsole::MHV_VusbConsole(MHV_RingBuffer &txBuffer, MHV_RTC &rtc) :
00149                                 MHV_Device_TX(txBuffer),
00150                                 _rtc(rtc) {
00151 #if USB_CFG_HAVE_MEASURE_FRAME_LENGTH
00152         uchar calibrationValue;
00153 
00154         calibrationValue = eeprom_read_byte(MHV_OSCCAL_EEPROM_ADDRESS); /* calibration value from last time */
00155         if (calibrationValue != 0xff) {
00156                 OSCCAL = calibrationValue;
00157         }
00158 #endif
00159 
00160         mhv_setInput(MHV_MAKE_PIN(USB_CFG_IOPORTNAME, USB_CFG_DPLUS_BIT));
00161         mhv_setInput(MHV_MAKE_PIN(USB_CFG_IOPORTNAME, USB_CFG_DMINUS_BIT));
00162 
00163         usbDeviceDisconnect();
00164     for(uint8_t i=0;i<20;i++){  /* 300 ms disconnect */
00165         _delay_ms(15);
00166     }
00167         usbDeviceConnect();
00168 
00169         usbInit();
00170 
00171         MHV_ALARM newAlarm;
00172 
00173         _rtc.current(&(newAlarm.when));
00174         mhv_timestampIncrement(&(newAlarm.when), 0, 5);
00175         newAlarm.repeat.milliseconds = 5;
00176         newAlarm.repeat.timestamp = 0;
00177         newAlarm.listener = this;
00178 
00179         _rtc.addAlarm(&newAlarm);
00180 }
00181 
00186 void MHV_VusbConsole::alarm(const MHV_ALARM &alarm) {
00187         usbPoll();
00188 
00189         if (usbInterruptIsReady()) {
00190                 int c;
00191                 unsigned char buf[MAX_TX_SIZE];
00192                 uint8_t bufSize;
00193 
00194                 for (bufSize = 0; bufSize < MAX_TX_SIZE; bufSize++) {
00195                         c = nextCharacter();
00196                         if (-1 == c) {
00197                                 break;
00198                         }
00199                         buf[bufSize] = (unsigned char)c;
00200                 }
00201 
00202                 if (bufSize) {
00203                         for (; bufSize < MAX_TX_SIZE; bufSize++) {
00204                                 buf[bufSize] = '\0';
00205                         }
00206                         usbSetInterrupt(buf, bufSize);
00207                 }
00208         }
00209 }
00210 
00215 void MHV_VusbConsole::runTxBuffers() {
00216 }
00217