Added polling-based version (handles fast SPI)
This commit is contained in:
parent
f7cae8f582
commit
4fec699f2d
3 changed files with 585 additions and 0 deletions
500
alt.c
Normal file
500
alt.c
Normal file
|
|
@ -0,0 +1,500 @@
|
|||
// Experiemtnal alternate ATtiny85 NeoPixel bridge code based on
|
||||
// spineopixel.c and caffeine. No interrupts (all polling-based),
|
||||
// Handles 4 MHz SPI on 16 MHz MCU. No global state machine stuff,
|
||||
// max space for MOAR PIXELS.
|
||||
|
||||
#if !defined(F_CPU)
|
||||
#error F_CPU not defined
|
||||
#endif
|
||||
#if !defined(BAUDRATE)
|
||||
#error BAUDRATE not defined
|
||||
#endif
|
||||
#if !defined(I2C_ADDR) || (I2C_ADDR < 1) || (I2C_ADDR > 127)
|
||||
#error I2C_ADDR not defined, or invalid range (must be 1-127)
|
||||
#endif
|
||||
|
||||
#include <avr/io.h>
|
||||
#include <avr/interrupt.h>
|
||||
#include <avr/pgmspace.h>
|
||||
#include <string.h>
|
||||
#include "reverse_bits.h"
|
||||
|
||||
// Clock/data/latch input pins
|
||||
#define IN_CLK_PORTx PORTB
|
||||
#define IN_CLK_DDRx DDRB
|
||||
#define IN_CLK_PINx PINB
|
||||
#define IN_CLK_BIT 2
|
||||
#define IN_CLK_MASK (1 << IN_CLK_BIT)
|
||||
#define IN_DATA_PORTx PORTB
|
||||
#define IN_DATA_DDRx DDRB
|
||||
#define IN_DATA_PINx PINB
|
||||
#define IN_DATA_BIT 0
|
||||
#define IN_DATA_MASK (1 << IN_DATA_BIT)
|
||||
#define IN_LATCH_PORTx PORTB
|
||||
#define IN_LATCH_DDRx DDRB
|
||||
#define IN_LATCH_PINx PINB
|
||||
#define IN_LATCH_BIT 3
|
||||
#define IN_LATCH_MASK (1 << IN_LATCH_BIT)
|
||||
// NeoPixel output pin
|
||||
#define OUT_PORTx PORTB
|
||||
#define OUT_DDRx DDRB
|
||||
#define OUT_PINx PINB
|
||||
#define OUT_BIT 4
|
||||
#define OUT_MASK (1 << OUT_BIT)
|
||||
// Mode select jumper: ->VCC = SPI, ->1M->GND = I2C, N/C = UART
|
||||
#define JMP_PORTx PORTB
|
||||
#define JMP_DDRx DDRB
|
||||
#define JMP_PINx PINB
|
||||
#define JMP_MASK (1 << 1)
|
||||
|
||||
// Drive latch low to indicate busy state, release for idle state
|
||||
#define LATCH_BUSY \
|
||||
IN_LATCH_DDRx |= IN_LATCH_MASK; \
|
||||
IN_LATCH_PORTx &= ~IN_LATCH_MASK; // Output mode, set low
|
||||
#define LATCH_IDLE \
|
||||
IN_LATCH_DDRx &= ~IN_LATCH_MASK; \
|
||||
IN_LATCH_PORTx |= IN_LATCH_MASK; // Input mode w/internal pullup
|
||||
|
||||
static void
|
||||
spi_handler(void) ,
|
||||
i2c_handler(void) ,
|
||||
uart_handler(void),
|
||||
send_to_neopixel(void);
|
||||
|
||||
#define MAX_LEDS 150
|
||||
#define BUFFERSIZE (MAX_LEDS * 3)
|
||||
static uint8_t buffer[BUFFERSIZE];
|
||||
static volatile uint16_t byteCount;
|
||||
|
||||
int main(void) {
|
||||
|
||||
LATCH_BUSY
|
||||
cli(); // No interrupts EVAR
|
||||
|
||||
OUT_DDRx |= OUT_MASK; // NeoPixel output
|
||||
OUT_PORTx &= ~OUT_MASK; // Default low
|
||||
|
||||
// Set all pixels to initial 'off' state
|
||||
byteCount = sizeof(buffer);
|
||||
memset(buffer, 0, byteCount);
|
||||
send_to_neopixel();
|
||||
|
||||
// For testing, call a handler directly:
|
||||
// spi_handler();
|
||||
// i2c_handler();
|
||||
// uart_handler();
|
||||
|
||||
// Determine input mode from jumper, call specific handler.
|
||||
// This allows for tighter control loops for each peripheral
|
||||
// type rather than a unified state thing (and associated
|
||||
// variables). Code space is more prevalent than RAM.
|
||||
|
||||
USICR = 0; // USI off
|
||||
JMP_DDRx &= ~JMP_MASK; // Jumper pin = input
|
||||
JMP_PORTx &= ~JMP_MASK; // No pullup
|
||||
asm volatile ("rjmp .+0"); // Small delay
|
||||
// If reading logic high even though there's an external pull-down
|
||||
// resistor, pin is tied to VCC. Jump to SPI-specific code.
|
||||
if(JMP_PINx & JMP_MASK) spi_handler();
|
||||
|
||||
JMP_PORTx |= JMP_MASK; // Activate pullup
|
||||
asm volatile ("rjmp .+0"); // Small delay
|
||||
// If reading logic low even though there's a pull-up resistor,
|
||||
// pin is tied to ground. Jump to I2C-specific code.
|
||||
// Note: external pull-down resistor MUST have magnitudes
|
||||
// higher resistance than the internal pull-up resistor.
|
||||
if(!(JMP_PINx & JMP_MASK)) i2c_handler();
|
||||
|
||||
// Pin is floating. Jump to UART-specific code.
|
||||
JMP_PORTx &= ~JMP_MASK; // Disable pullup
|
||||
uart_handler();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void inline __attribute__ ((always_inline)) spi_handler(void) {
|
||||
|
||||
IN_CLK_DDRx &= ~IN_CLK_MASK; // Clock, data inputs
|
||||
IN_DATA_DDRx &= ~IN_DATA_MASK;
|
||||
IN_CLK_PORTx &= ~IN_CLK_MASK; // No pullups on clock, data
|
||||
IN_DATA_PORTx &= ~IN_DATA_MASK;
|
||||
|
||||
USICR = _BV(USIWM0) | _BV(USICS1); // 3 wire, ext clock, pos edge
|
||||
|
||||
for(;;) { // Forever
|
||||
LATCH_IDLE
|
||||
USISR = _BV(USIOIF); // Reset frame, important
|
||||
|
||||
while(IN_LATCH_PINx & IN_LATCH_MASK); // Latch is high, wait
|
||||
do { // else latch low...
|
||||
if(USISR & _BV(USIOIF)) { // New data ready?
|
||||
USISR = _BV(USIOIF); // Clear flag & counter
|
||||
if(byteCount < sizeof(buffer)) // If room avail
|
||||
buffer[byteCount++] = USIDR; // store next byte
|
||||
}
|
||||
} while(!(IN_LATCH_PINx & IN_LATCH_MASK)); // While low
|
||||
|
||||
if(byteCount) send_to_neopixel();
|
||||
}
|
||||
}
|
||||
|
||||
static void inline __attribute__ ((always_inline)) i2c_ack(void) {
|
||||
IN_DATA_DDRx |= IN_DATA_MASK; // SDA output (pulls SDA low = ACK)
|
||||
// Clear overflow bit (also releases clock stretch),
|
||||
// set counter to shift one bit out.
|
||||
USISR = _BV(USIOIF) | (0xE << USICNT0);
|
||||
while(!(USISR & _BV(USIOIF))); // Wait for bit
|
||||
IN_DATA_DDRx &= ~IN_DATA_MASK; // Back to input
|
||||
USISR = _BV(USIOIF); // Reset counter & overflow flag again
|
||||
}
|
||||
|
||||
#define ADDR_8BIT (I2C_ADDR << 1) // Actual 8-bit I2C addr on the wire
|
||||
|
||||
// Important change: I2C stop condition does NOT latch. If using an
|
||||
// Arduino as the I2C master, that platform's Wire library only supports
|
||||
// up to 32 bytes per transfer, while we can receive up to 450. Using
|
||||
// the latch pin allows smaller packets to be coalesced. It's not "pure"
|
||||
// I2C, but a necessary evil.
|
||||
|
||||
static void inline __attribute__ ((always_inline)) i2c_handler(void) {
|
||||
|
||||
uint8_t x;
|
||||
|
||||
IN_DATA_DDRx &= ~IN_DATA_MASK; // SDA input
|
||||
IN_CLK_DDRx &= ~IN_CLK_MASK; // SCL input
|
||||
IN_DATA_PORTx &= ~IN_CLK_MASK; // Pullups disabled in TWI mode,
|
||||
IN_CLK_PORTx &= ~IN_DATA_MASK; // external pullups required
|
||||
|
||||
// TWI mode, external clock, negative edge
|
||||
USICR = _BV(USIWM1) | _BV(USIWM0) | _BV(USICS1) | _BV(USICS0);
|
||||
// Setting USIWM0 holds SCL low when a counter overflow occurs --
|
||||
// basically, automatic clock-stretching. It's released when
|
||||
// the overflow flag is reset.
|
||||
|
||||
for(;;) { // Forever
|
||||
LATCH_IDLE
|
||||
|
||||
while(IN_LATCH_PINx & IN_LATCH_MASK); // Latch is high, wait
|
||||
do { // else latch low...
|
||||
|
||||
USISR = 0xF0; // Reset flags, counter
|
||||
|
||||
while((!(USISR & _BV(USISIF))) && // Wait for start cond.
|
||||
(!(IN_LATCH_PINx & IN_LATCH_MASK))); // or latch high
|
||||
|
||||
if(IN_DATA_PINx & IN_DATA_MASK) break; // Latch high
|
||||
|
||||
#if(0)
|
||||
while( (IN_CLK_PINx & IN_CLK_MASK ) && // Ensure start
|
||||
(!(IN_DATA_PINx & IN_DATA_MASK )) && // condition completes
|
||||
(!(IN_LATCH_PINx & IN_LATCH_MASK)));
|
||||
#else
|
||||
// Condition is simplified when they're on the same PINx register:
|
||||
while((IN_CLK_PINx &
|
||||
(IN_CLK_MASK | IN_DATA_MASK | IN_LATCH_MASK)) == IN_CLK_MASK);
|
||||
#endif
|
||||
|
||||
if(IN_LATCH_PINx & IN_LATCH_MASK) break; // Latch high
|
||||
if(IN_DATA_PINx & IN_DATA_MASK ) continue; // Stop condition
|
||||
|
||||
// Start condition detected
|
||||
|
||||
USISR = _BV(USIOIF); // Reset overflow flag, clear counter
|
||||
|
||||
while((!(USISR & (_BV(USIOIF) | // Wait for data
|
||||
_BV(USIPF )))) && // or stop condition
|
||||
(!(IN_LATCH_PINx & IN_LATCH_MASK))); // or latch high
|
||||
|
||||
if(IN_LATCH_PINx & IN_LATCH_MASK) break; // Latch high
|
||||
if(USISR & _BV(USIPF) ) continue; // Stop condition
|
||||
|
||||
if(USIDR != ADDR_8BIT) continue; // Not my address
|
||||
i2c_ack(); // It's a me!
|
||||
|
||||
while((!(USISR & _BV(USIPF) )) && // Wait for stop cond
|
||||
(!(IN_LATCH_PINx & IN_LATCH_MASK))) { // or latch high
|
||||
if(USISR & _BV(USIOIF)) { // New data ready?
|
||||
x = USIDR; // Read byte in
|
||||
i2c_ack(); // Thanks
|
||||
if(byteCount < sizeof(buffer)) // If room avail
|
||||
buffer[byteCount++] = x; // store next byte
|
||||
}
|
||||
}
|
||||
|
||||
} while(!(IN_LATCH_PINx & IN_LATCH_MASK)); // While latch low
|
||||
|
||||
if(byteCount) send_to_neopixel();
|
||||
}
|
||||
}
|
||||
|
||||
// Determine valid Timer0 prescale setting for F_CPU/BAUDRATE combo
|
||||
#if ((F_CPU / BAUDRATE) < 256)
|
||||
#define PRESCALE 1
|
||||
#define PRESCALE_MASK _BV(CS00)
|
||||
#elif ((F_CPU / 8 / BAUDRATE) < 256)
|
||||
#define PRESCALE 8
|
||||
#define PRESCALE_MASK _BV(CS01)
|
||||
#elif ((F_CPU / 64 / BAUDRATE) < 256)
|
||||
#define PRESCALE 64
|
||||
#define PRESCALE_MASK (_BV(CS01) | _BV(CS00))
|
||||
#else
|
||||
// Could escalate with more prescales if needed, but for now...
|
||||
#error F_CPU, BAUDRATE combo incompatible with supported Timer0 prescales
|
||||
#endif
|
||||
#define BITTIME ((F_CPU / PRESCALE) / BAUDRATE)
|
||||
|
||||
static void inline __attribute__ ((always_inline)) uart_handler(void) {
|
||||
|
||||
IN_DATA_DDRx &= ~IN_DATA_MASK; // Data input (clock is unused)
|
||||
IN_DATA_PORTx &= ~IN_DATA_MASK; // No pullup
|
||||
|
||||
// WGM Mode 7: Fast PWM, OC0A/B disconnected
|
||||
TCCR0A = _BV(WGM01) | _BV(WGM00);
|
||||
TCCR0B = _BV(WGM02) | PRESCALE_MASK;
|
||||
OCR0A = BITTIME;
|
||||
|
||||
// Three-wire mode w/Timer0 compare match = ersatz UART
|
||||
USICR = _BV(USIWM0) | _BV(USICS0);
|
||||
|
||||
for(;;) { // Forever
|
||||
LATCH_IDLE
|
||||
|
||||
while(IN_LATCH_PINx & IN_LATCH_MASK); // Latch is high, wait
|
||||
do { // else latch low...
|
||||
|
||||
while((IN_DATA_PINx & IN_DATA_MASK ) && // Wait for start bit
|
||||
!(IN_LATCH_PINx & IN_LATCH_MASK)); // or latch high
|
||||
|
||||
if(IN_LATCH_PINx & IN_LATCH_MASK) break; // Latch went high
|
||||
|
||||
// Now at beginning of start bit. Need to start sampling
|
||||
// at middle of bit...set TCNT0 halfway to overflow and
|
||||
// USI counter to read 1 bit.
|
||||
TCNT0 = BITTIME / 2;
|
||||
USISR = _BV(USIOIF) | (0xF << USICNT0);
|
||||
while(!(USISR & _BV(USIOIF))); // Wait for bit
|
||||
|
||||
// Now at middle of bit start bit.
|
||||
// Reset overflow and set counter for next 8 bits.
|
||||
USISR = _BV(USIOIF) | (0x8 << USICNT0);
|
||||
while(!(USISR & _BV(USIOIF))); // Wait for byte
|
||||
if(byteCount < sizeof(buffer)) // Store if not full
|
||||
buffer[byteCount++] = pgm_read_byte(&reverse_bits[USIDR]);
|
||||
|
||||
// Now in middle of last bit. Idle for one more (stop bit).
|
||||
USISR = _BV(USIOIF) | (0xF << USICNT0);
|
||||
while(!(USISR & _BV(USIOIF)));
|
||||
|
||||
// Now in middle of stop bit (logic high).
|
||||
// Next iteration resumes watching for falling edge.
|
||||
|
||||
} while(!(IN_LATCH_PINx & IN_LATCH_MASK)); // While latch low
|
||||
|
||||
if(byteCount) send_to_neopixel();
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
// Code below is adapted from Adafruit_NeoPixel.cpp, with minor tweaks
|
||||
// for this project (and comments stripped for brevity). The full PORT
|
||||
// writes with hi/lo/next vars is an artifact of that library's runtime
|
||||
// pin selection...while fixed bit set/clear instructions would suffice
|
||||
// for this project, the timing is extremely critical and it was easier
|
||||
// to use the more flexible code than to write all new stuff here just
|
||||
// to maybe save a handful of bytes.
|
||||
|
||||
// Global variable byteCount is altered by this function. It will
|
||||
// always equal zero on return, indicating an empty buffer.
|
||||
|
||||
static void __attribute__ ((noinline)) send_to_neopixel(void) {
|
||||
|
||||
LATCH_BUSY
|
||||
|
||||
volatile uint8_t
|
||||
*ptr = buffer, // Pointer to next byte
|
||||
b = *ptr++; // Current byte value
|
||||
uint8_t
|
||||
hi = OUT_PORTx | OUT_MASK, // PORT w/output bit set high
|
||||
lo = OUT_PORTx & ~OUT_MASK; // PORT w/output bit set low
|
||||
|
||||
// 8 MHz(ish) AVR ---------------------------------------------------------
|
||||
#if (F_CPU >= 7400000UL) && (F_CPU <= 9500000UL)
|
||||
|
||||
volatile uint8_t n2 = 0, n1 = lo;
|
||||
if(b & 0x80) n1 = hi;
|
||||
|
||||
asm volatile(
|
||||
"headB:" "\n\t"
|
||||
"out %[port] , %[hi]" "\n\t"
|
||||
"mov %[n2] , %[lo]" "\n\t"
|
||||
"out %[port] , %[n1]" "\n\t"
|
||||
"rjmp .+0" "\n\t"
|
||||
"sbrc %[byte] , 6" "\n\t"
|
||||
"mov %[n2] , %[hi]" "\n\t"
|
||||
"out %[port] , %[lo]" "\n\t"
|
||||
"rjmp .+0" "\n\t"
|
||||
"out %[port] , %[hi]" "\n\t"
|
||||
"mov %[n1] , %[lo]" "\n\t"
|
||||
"out %[port] , %[n2]" "\n\t"
|
||||
"rjmp .+0" "\n\t"
|
||||
"sbrc %[byte] , 5" "\n\t"
|
||||
"mov %[n1] , %[hi]" "\n\t"
|
||||
"out %[port] , %[lo]" "\n\t"
|
||||
"rjmp .+0" "\n\t"
|
||||
"out %[port] , %[hi]" "\n\t"
|
||||
"mov %[n2] , %[lo]" "\n\t"
|
||||
"out %[port] , %[n1]" "\n\t"
|
||||
"rjmp .+0" "\n\t"
|
||||
"sbrc %[byte] , 4" "\n\t"
|
||||
"mov %[n2] , %[hi]" "\n\t"
|
||||
"out %[port] , %[lo]" "\n\t"
|
||||
"rjmp .+0" "\n\t"
|
||||
"out %[port] , %[hi]" "\n\t"
|
||||
"mov %[n1] , %[lo]" "\n\t"
|
||||
"out %[port] , %[n2]" "\n\t"
|
||||
"rjmp .+0" "\n\t"
|
||||
"sbrc %[byte] , 3" "\n\t"
|
||||
"mov %[n1] , %[hi]" "\n\t"
|
||||
"out %[port] , %[lo]" "\n\t"
|
||||
"rjmp .+0" "\n\t"
|
||||
"out %[port] , %[hi]" "\n\t"
|
||||
"mov %[n2] , %[lo]" "\n\t"
|
||||
"out %[port] , %[n1]" "\n\t"
|
||||
"rjmp .+0" "\n\t"
|
||||
"sbrc %[byte] , 2" "\n\t"
|
||||
"mov %[n2] , %[hi]" "\n\t"
|
||||
"out %[port] , %[lo]" "\n\t"
|
||||
"rjmp .+0" "\n\t"
|
||||
"out %[port] , %[hi]" "\n\t"
|
||||
"mov %[n1] , %[lo]" "\n\t"
|
||||
"out %[port] , %[n2]" "\n\t"
|
||||
"rjmp .+0" "\n\t"
|
||||
"sbrc %[byte] , 1" "\n\t"
|
||||
"mov %[n1] , %[hi]" "\n\t"
|
||||
"out %[port] , %[lo]" "\n\t"
|
||||
"rjmp .+0" "\n\t"
|
||||
"out %[port] , %[hi]" "\n\t"
|
||||
"mov %[n2] , %[lo]" "\n\t"
|
||||
"out %[port] , %[n1]" "\n\t"
|
||||
"rjmp .+0" "\n\t"
|
||||
"sbrc %[byte] , 0" "\n\t"
|
||||
"mov %[n2] , %[hi]" "\n\t"
|
||||
"out %[port] , %[lo]" "\n\t"
|
||||
"sbiw %[count], 1" "\n\t"
|
||||
"out %[port] , %[hi]" "\n\t"
|
||||
"mov %[n1] , %[lo]" "\n\t"
|
||||
"out %[port] , %[n2]" "\n\t"
|
||||
"ld %[byte] , %a[ptr]+" "\n\t"
|
||||
"sbrc %[byte] , 7" "\n\t"
|
||||
"mov %[n1] , %[hi]" "\n\t"
|
||||
"out %[port] , %[lo]" "\n\t"
|
||||
"brne headB" "\n"
|
||||
: [count] "+w" (byteCount),
|
||||
[n1] "+r" (n1),
|
||||
[n2] "+r" (n2),
|
||||
[byte] "+r" (b)
|
||||
:
|
||||
[ptr] "e" (ptr),
|
||||
[port] "I" (_SFR_IO_ADDR(OUT_PORTx)),
|
||||
[hi] "r" (hi),
|
||||
[lo] "r" (lo));
|
||||
|
||||
// 12 MHz(ish) AVR --------------------------------------------------------
|
||||
#elif (F_CPU >= 11100000UL) && (F_CPU <= 14300000UL)
|
||||
|
||||
volatile uint8_t next = lo;
|
||||
if(b & 0x80) next = hi;
|
||||
|
||||
asm volatile(
|
||||
"headB:" "\n\t"
|
||||
"out %[port], %[hi]" "\n\t"
|
||||
"rcall bitTimeB" "\n\t"
|
||||
"out %[port], %[hi]" "\n\t"
|
||||
"rcall bitTimeB" "\n\t"
|
||||
"out %[port], %[hi]" "\n\t"
|
||||
"rcall bitTimeB" "\n\t"
|
||||
"out %[port], %[hi]" "\n\t"
|
||||
"rcall bitTimeB" "\n\t"
|
||||
"out %[port], %[hi]" "\n\t"
|
||||
"rcall bitTimeB" "\n\t"
|
||||
"out %[port], %[hi]" "\n\t"
|
||||
"rcall bitTimeB" "\n\t"
|
||||
"out %[port], %[hi]" "\n\t"
|
||||
"rcall bitTimeB" "\n\t"
|
||||
"out %[port] , %[hi]" "\n\t"
|
||||
"rjmp .+0" "\n\t"
|
||||
"ld %[byte] , %a[ptr]+" "\n\t"
|
||||
"out %[port] , %[next]" "\n\t"
|
||||
"mov %[next] , %[lo]" "\n\t"
|
||||
"sbrc %[byte] , 7" "\n\t"
|
||||
"mov %[next] , %[hi]" "\n\t"
|
||||
"nop" "\n\t"
|
||||
"out %[port] , %[lo]" "\n\t"
|
||||
"sbiw %[count], 1" "\n\t"
|
||||
"brne headB" "\n\t"
|
||||
"rjmp doneB" "\n\t"
|
||||
"bitTimeB:" "\n\t"
|
||||
"out %[port] , %[next]" "\n\t"
|
||||
"mov %[next] , %[lo]" "\n\t"
|
||||
"rol %[byte]" "\n\t"
|
||||
"sbrc %[byte] , 7" "\n\t"
|
||||
"mov %[next] , %[hi]" "\n\t"
|
||||
"nop" "\n\t"
|
||||
"out %[port] , %[lo]" "\n\t"
|
||||
"ret" "\n\t"
|
||||
"doneB:" "\n"
|
||||
: [count] "+w" (byteCount),
|
||||
[next] "+r" (next),
|
||||
[byte] "+r" (b)
|
||||
: [ptr] "e" (ptr),
|
||||
[port] "I" (_SFR_IO_ADDR(OUT_PORTx)),
|
||||
[hi] "r" (hi),
|
||||
[lo] "r" (lo));
|
||||
|
||||
// 16 MHz(ish) AVR --------------------------------------------------------
|
||||
#elif (F_CPU >= 15400000UL) && (F_CPU <= 19000000L)
|
||||
|
||||
volatile uint8_t next = lo, bit = 8;
|
||||
|
||||
asm volatile(
|
||||
"head20:" "\n\t"
|
||||
"out %[port], %[hi]" "\n\t"
|
||||
"sbrc %[byte], 7" "\n\t"
|
||||
"mov %[next], %[hi]" "\n\t"
|
||||
"dec %[bit]" "\n\t"
|
||||
"out %[port], %[next]" "\n\t"
|
||||
"mov %[next], %[lo]" "\n\t"
|
||||
"breq nextbyte20" "\n\t"
|
||||
"rol %[byte]" "\n\t"
|
||||
"rjmp .+0" "\n\t"
|
||||
"nop" "\n\t"
|
||||
"out %[port], %[lo]" "\n\t"
|
||||
"nop" "\n\t"
|
||||
"rjmp .+0" "\n\t"
|
||||
"rjmp head20" "\n\t"
|
||||
"nextbyte20:" "\n\t"
|
||||
"ldi %[bit] , 8" "\n\t"
|
||||
"ld %[byte], %a[ptr]+" "\n\t"
|
||||
"out %[port], %[lo]" "\n\t"
|
||||
"nop" "\n\t"
|
||||
"sbiw %[count], 1" "\n\t"
|
||||
"brne head20" "\n"
|
||||
: [count] "+w" (byteCount),
|
||||
[next] "+r" (next),
|
||||
[byte] "+r" (b),
|
||||
[bit] "+r" (bit)
|
||||
: [ptr] "e" (ptr),
|
||||
[port] "I" (_SFR_IO_ADDR(OUT_PORTx)),
|
||||
[hi] "r" (hi),
|
||||
[lo] "r" (lo));
|
||||
|
||||
#else
|
||||
#error "CPU SPEED NOT SUPPORTED"
|
||||
#endif
|
||||
|
||||
// ~ 50 uS delay
|
||||
volatile uint16_t x = (F_CPU / 1000000L * 50L) / 6;
|
||||
while(x--); // 6 cycles per iteration
|
||||
}
|
||||
|
||||
43
makefile.alt
Normal file
43
makefile.alt
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
F_CPU = 16500000
|
||||
BAUDRATE = 115200
|
||||
I2C_ADDR = 0x46
|
||||
INPUT_MODE = "UART"
|
||||
|
||||
COMMON = -mmcu=$(MCU)
|
||||
CFLAGS = $(COMMON)
|
||||
CFLAGS += -Wall -pedantic -std=gnu99 -Os -ffunction-sections -fdata-sections
|
||||
CFLAGS += -DF_CPU=$(F_CPU) -DBAUDRATE=$(BAUDRATE) -DI2C_ADDR=$(I2C_ADDR)
|
||||
LDFLAGS = $(COMMON)
|
||||
HEX_FLASH_FLAGS = -R .eeprom -R .fuse -R .lock -R .signature
|
||||
|
||||
TOOLS = /Applications/Arduino.app/Contents/Resources/Java/hardware/tools/avr/bin
|
||||
CC = $(TOOLS)/avr-gcc
|
||||
OBJCOPY = $(TOOLS)/avr-objcopy
|
||||
SIZE = $(TOOLS)/avr-size
|
||||
AVRCONF = $(TOOLS)/../etc/avrdude.conf
|
||||
AVRDUDE = $(TOOLS)/avrdude -C $(AVRCONF)
|
||||
|
||||
MCU = attiny85
|
||||
PROGRAMMER = usbtiny
|
||||
|
||||
all: alt.elf
|
||||
|
||||
alt.elf: alt.o
|
||||
$(CC) $(LDFLAGS) alt.o -o alt.elf
|
||||
$(OBJCOPY) -O ihex $(HEX_FLASH_FLAGS) alt.elf alt.hex
|
||||
$(SIZE) -C --mcu=${MCU} alt.elf
|
||||
|
||||
alt.o: alt.c reverse_bits.h
|
||||
$(CC) $(CFLAGS) -c alt.c
|
||||
|
||||
burn: alt.elf
|
||||
|
||||
burn:
|
||||
$(AVRDUDE) -p $(MCU) -c $(PROGRAMMER) -U flash:w:alt.hex:a
|
||||
|
||||
burnfuses:
|
||||
$(AVRDUDE) -p $(MCU) -c $(PROGRAMMER) -U lfuse:w:0xE2:m -U hfuse:w:0xD7:m
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
-rm -rf alt.o alt.elf alt.hex
|
||||
42
reverse_bits.h
Normal file
42
reverse_bits.h
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
// Table reverses bit order in 8-bit value
|
||||
|
||||
#ifndef _REVERSE_BITS_H_
|
||||
#define _REVERSE_BITS_H_
|
||||
|
||||
#include <avr/pgmspace.h>
|
||||
|
||||
static const uint8_t PROGMEM reverse_bits[] = {
|
||||
0x00,0x80,0x40,0xC0,0x20,0xA0,0x60,0xE0,
|
||||
0x10,0x90,0x50,0xD0,0x30,0xB0,0x70,0xF0,
|
||||
0x08,0x88,0x48,0xC8,0x28,0xA8,0x68,0xE8,
|
||||
0x18,0x98,0x58,0xD8,0x38,0xB8,0x78,0xF8,
|
||||
0x04,0x84,0x44,0xC4,0x24,0xA4,0x64,0xE4,
|
||||
0x14,0x94,0x54,0xD4,0x34,0xB4,0x74,0xF4,
|
||||
0x0C,0x8C,0x4C,0xCC,0x2C,0xAC,0x6C,0xEC,
|
||||
0x1C,0x9C,0x5C,0xDC,0x3C,0xBC,0x7C,0xFC,
|
||||
0x02,0x82,0x42,0xC2,0x22,0xA2,0x62,0xE2,
|
||||
0x12,0x92,0x52,0xD2,0x32,0xB2,0x72,0xF2,
|
||||
0x0A,0x8A,0x4A,0xCA,0x2A,0xAA,0x6A,0xEA,
|
||||
0x1A,0x9A,0x5A,0xDA,0x3A,0xBA,0x7A,0xFA,
|
||||
0x06,0x86,0x46,0xC6,0x26,0xA6,0x66,0xE6,
|
||||
0x16,0x96,0x56,0xD6,0x36,0xB6,0x76,0xF6,
|
||||
0x0E,0x8E,0x4E,0xCE,0x2E,0xAE,0x6E,0xEE,
|
||||
0x1E,0x9E,0x5E,0xDE,0x3E,0xBE,0x7E,0xFE,
|
||||
0x01,0x81,0x41,0xC1,0x21,0xA1,0x61,0xE1,
|
||||
0x11,0x91,0x51,0xD1,0x31,0xB1,0x71,0xF1,
|
||||
0x09,0x89,0x49,0xC9,0x29,0xA9,0x69,0xE9,
|
||||
0x19,0x99,0x59,0xD9,0x39,0xB9,0x79,0xF9,
|
||||
0x05,0x85,0x45,0xC5,0x25,0xA5,0x65,0xE5,
|
||||
0x15,0x95,0x55,0xD5,0x35,0xB5,0x75,0xF5,
|
||||
0x0D,0x8D,0x4D,0xCD,0x2D,0xAD,0x6D,0xED,
|
||||
0x1D,0x9D,0x5D,0xDD,0x3D,0xBD,0x7D,0xFD,
|
||||
0x03,0x83,0x43,0xC3,0x23,0xA3,0x63,0xE3,
|
||||
0x13,0x93,0x53,0xD3,0x33,0xB3,0x73,0xF3,
|
||||
0x0B,0x8B,0x4B,0xCB,0x2B,0xAB,0x6B,0xEB,
|
||||
0x1B,0x9B,0x5B,0xDB,0x3B,0xBB,0x7B,0xFB,
|
||||
0x07,0x87,0x47,0xC7,0x27,0xA7,0x67,0xE7,
|
||||
0x17,0x97,0x57,0xD7,0x37,0xB7,0x77,0xF7,
|
||||
0x0F,0x8F,0x4F,0xCF,0x2F,0xAF,0x6F,0xEF,
|
||||
0x1F,0x9F,0x5F,0xDF,0x3F,0xBF,0x7F,0xFF };
|
||||
|
||||
#endif // _REVERSE_BITS_H_
|
||||
Loading…
Reference in a new issue