noobs_nerd_notifier/firmware/main.c

193 lines
5.8 KiB
C
Raw Normal View History

/**
* 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;
}