|
MHVLib
20111227
An efficiency oriented runtime library for AVR microcontrollers
|
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