MHVLib  20111109
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 
00144 MHV_VusbConsole::MHV_VusbConsole(MHV_RingBuffer *txBuffer, MHV_RTC *rtc) : MHV_Device_TX(txBuffer) {
00145 
00146         _rtc = rtc;
00147 
00148 #if USB_CFG_HAVE_MEASURE_FRAME_LENGTH
00149         uchar calibrationValue;
00150 
00151         calibrationValue = eeprom_read_byte(MHV_OSCCAL_EEPROM_ADDRESS); /* calibration value from last time */
00152         if (calibrationValue != 0xff) {
00153                 OSCCAL = calibrationValue;
00154         }
00155 #endif
00156 
00157         mhv_setInput(mhv_make_pin(USB_CFG_IOPORTNAME, USB_CFG_DPLUS_BIT));
00158         mhv_setInput(mhv_make_pin(USB_CFG_IOPORTNAME, USB_CFG_DMINUS_BIT));
00159 
00160         usbDeviceDisconnect();
00161     for(uint8_t i=0;i<20;i++){  /* 300 ms disconnect */
00162         _delay_ms(15);
00163     }
00164         usbDeviceConnect();
00165 
00166         usbInit();
00167 
00168         MHV_ALARM newAlarm;
00169 
00170         _rtc->current(&(newAlarm.when));
00171         mhv_timestampIncrement(&(newAlarm.when), 0, 5);
00172         newAlarm.repeat.milliseconds = 5;
00173         newAlarm.repeat.timestamp = 0;
00174         newAlarm.listener = this;
00175 
00176         _rtc->addAlarm(&newAlarm);
00177 }
00178 
00183 void MHV_VusbConsole::alarm(MHV_ALARM *alarm) {
00184         usbPoll();
00185 
00186         if (usbInterruptIsReady()) {
00187                 int c;
00188                 unsigned char buf[MAX_TX_SIZE];
00189                 uint8_t bufSize;
00190 
00191                 for (bufSize = 0; bufSize < MAX_TX_SIZE; bufSize++) {
00192                         c = nextCharacter();
00193                         if (-1 == c) {
00194                                 break;
00195                         }
00196                         buf[bufSize] = (unsigned char)c;
00197                 }
00198 
00199                 if (bufSize) {
00200                         for (; bufSize < MAX_TX_SIZE; bufSize++) {
00201                                 buf[bufSize] = '\0';
00202                         }
00203                         usbSetInterrupt(buf, bufSize);
00204                 }
00205         }
00206 }
00207 
00212 void MHV_VusbConsole::runTxBuffers() {
00213 }
00214