193 lines
5.8 KiB
C
193 lines
5.8 KiB
C
|
/**
|
||
|
* 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 <avr/io.h>
|
||
|
#include <avr/interrupt.h>
|
||
|
#include <avr/wdt.h>
|
||
|
#include <stdint.h>
|
||
|
#include <util/delay.h>
|
||
|
|
||
|
#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;
|
||
|
}
|