Added polling-based version (handles fast SPI)

This commit is contained in:
Phillip Burgess 2013-07-29 22:08:23 -07:00
parent f7cae8f582
commit 4fec699f2d
3 changed files with 585 additions and 0 deletions

500
alt.c Normal file
View 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
View 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
View 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_