/** * Project: AVR ATtiny USB Tutorial at http://codeandlife.com/ * Author: Joonas Pihlajamaa, joonas.pihlajamaa@iki.fi * Inspired by V-USB example code by Christian Starkjohann * Copyright: (C) 2012 by Joonas Pihlajamaa * License: GNU GPL v3 (see License.txt) */ #include #include #include #include #include #include "usbdrv.h" #define USB_DATA_OUT 2 #define USB_DATA_IN 4 #define BUFER_SIZE 8 static uchar replyBuf[BUFER_SIZE]; static uchar dataReceived = 0; static uchar dataLength = 0; static uchar updated = 0; // this gets called when custom control message is received USB_PUBLIC uchar usbFunctionSetup(uchar data[8]) { usbRequest_t *rq = (void *)data; // cast data to correct type switch(rq->bRequest) { // custom command is in the bRequest field case USB_DATA_OUT: // send data to PC usbMsgPtr = replyBuf; return sizeof(replyBuf); case USB_DATA_IN: // receive data from PC dataLength = (uchar)rq->wLength.word; dataReceived = 0; if(dataLength > sizeof(replyBuf)) // limit to buffer size dataLength = sizeof(replyBuf); return USB_NO_MSG; // usbFunctionWrite will be called now } return 0; // should not get here } USB_PUBLIC uchar usbFunctionWrite(uchar *data, uchar len) { uchar i; for(i = 0; dataReceived < dataLength && i < len; i++, dataReceived++) { replyBuf[dataReceived] = data[i]; } if (dataReceived == dataLength) { updated = 1; } return (dataReceived == dataLength); // 1 if we received it all, 0 if not } #define abs(x) ((x) > 0 ? (x) : (-x)) // Called by V-USB after device reset void hadUsbReset() { int frameLength, targetLength = (unsigned)(1499 * (double)F_CPU / 10.5e6 + 0.5); int bestDeviation = 9999; uchar trialCal, bestCal = 0, step, region; // do a binary search in regions 0-127 and 128-255 to get optimum OSCCAL for(region = 0; region <= 1; region++) { frameLength = 0; trialCal = (region == 0) ? 0 : 128; for(step = 64; step > 0; step >>= 1) { if(frameLength < targetLength) // true for initial iteration trialCal += step; // frequency too low else trialCal -= step; // frequency too high OSCCAL = trialCal; frameLength = usbMeasureFrameLength(); if(abs(frameLength-targetLength) < bestDeviation) { bestCal = trialCal; // new optimum found bestDeviation = abs(frameLength -targetLength); } } } OSCCAL = bestCal; } #define LED_PIN PB1 #define LED_PORT B #define CONCAT(a, b) a ## b #define CONCAT_EXP(a, b) CONCAT(a, b) #define LED_PORTREG CONCAT_EXP(PORT,LED_PORT) #define LED_DDRREG CONCAT_EXP(DDR,LED_PORT) #define LED_BIT_VAL (1 << PB1) void LED_init() { LED_PORTREG &= ~LED_BIT_VAL; LED_DDRREG |= LED_BIT_VAL; } void LED_reset() { LED_PORTREG &= ~LED_BIT_VAL; _delay_us(40); } void __attribute__((always_inline)) inline LED_byte(uint8_t byte) { volatile uint8_t counter = 8; asm volatile ( "1: \n\t" "sbi %1, 0x01 \n\t" // 2 CLK (high) "rjmp .+0 \n\t" // 2 CLK "rol %2 \n\t" // 1 CLK "brcs 2f \n\t" // 2 CLK // highest bit not set => go low "cbi %1, 0x01 \n\t" // 2 CLK (low) "rjmp .+0 \n\t" // 2 CLK "rjmp .+0 \n\t" // 2 CLK "rjmp .+0 \n\t" // 2 CLK "rjmp .+0 \n\t" // 2 CLK "rjmp .+0 \n\t" // 2 CLK "rjmp .+0 \n\t" // 2 CLK "rjmp .+0 \n\t" // 2 CLK "nop \n\t" // 1 CLK "rjmp 3f \n\t" // 2 CLK "2: \n\t" // highest bit set => stay high "rjmp .+0 \n\t" // 2 CLK "rjmp .+0 \n\t" // 2 CLK "rjmp .+0 \n\t" // 2 CLK "rjmp .+0 \n\t" // 2 CLK "rjmp .+0 \n\t" // 2 CLK "rjmp .+0 \n\t" // 2 CLK "rjmp .+0 \n\t" // 2 CLK "nop \n\t" // 1 CLK "cbi %1, 0x01 \n\t" // 2 CLK (low) "3: \n\t" "nop \n\t" // 1 CLK "dec %0 \n\t" // 1 CLK "brne 1b \n\t" // 1/2 CLK // code after 8 bits : : "r" (counter), "I" (_SFR_IO_ADDR(PORTB)), "r" (byte) : ); } void LED_color(uchar r, uchar g, uchar b) { LED_byte(r); LED_byte(g); LED_byte(b); LED_reset(); } int main() { uchar i; LED_init(); LED_color(255,0,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(2); } usbDeviceConnect(); sei(); // Enable interrupts after re-enumeration while(1) { wdt_reset(); // keep the watchdog happy usbPoll(); if (updated == 1) { cli(); LED_color(replyBuf[0], replyBuf[1], replyBuf[2]); sei(); replyBuf[3] = 'O'; replyBuf[4] = 'K'; replyBuf[5] = '!'; updated = 0; } } return 0; }