Source

led-strip / led-strip.c

Full commit
/* LED strip driver
 *
 * Derived from:
 * http://www.pjrc.com/teensy/
 * Copyright (c) 2008, 2010 PJRC.COM, LLC
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#include <avr/io.h>
#include <avr/pgmspace.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <string.h>
#include "usb_debug_only.h"
#include "print.h"

#include "font-small.c"


// Teensy 2.0: LED is active high
#if defined(__AVR_ATmega32U4__) || defined(__AVR_AT90USB1286__)
#define LED_ON		(PORTD |= (1<<6))
#define LED_OFF		(PORTD &= ~(1<<6))

// Teensy 1.0: LED is active low
#else
#define LED_ON	(PORTD &= ~(1<<6))
#define LED_OFF	(PORTD |= (1<<6))
#endif

#define LED_CONFIG	(DDRD |= (1<<6))
#define CPU_PRESCALE(n)	(CLKPR = 0x80, CLKPR = (n))


#define NUM_LEDS ((int16_t) (32 * 5))
#define LED_SCALE 8

static inline void clock(const uint8_t value)
{
	if (value)
		PORTB |= (1 << 1);
	else
		PORTB &= ~(1 << 1);
}


static void send_zero(void)
{
	unsigned i;
	PORTB &= ~(1 << 2);

	for (i = 0 ; i < 8 ; i++)
	{
		clock(1);
		clock(0);
	}
}


static void send_byte(uint8_t v)
{
	unsigned bit;
	for (bit = 0x80 ; bit ; bit >>= 1)
	{
		clock(0);
		if (v & bit)
			PORTB |=  (1 << 2);
		else
			PORTB &= ~(1 << 2);
		clock(1);
	}
}


static void latch(void)
{
	unsigned i;
	const unsigned zeros = 3 * ((NUM_LEDS + 63) / 64);
	for (i = 0 ; i < zeros ; i++)
		send_zero();
}


static uint8_t pixels[NUM_LEDS][3];

static void send_pixels(void)
{
	uint16_t i;
	for (i = 0 ; i < NUM_LEDS ; i++)
	{
		uint8_t * const p = pixels[i];
		uint8_t r = p[0];
		uint8_t g = p[1];
		uint8_t b = p[2];

		send_byte(0x80 | (r >> 1));
		send_byte(0x80 | (g >> 1));
		send_byte(0x80 | (b >> 1));
	}

	latch();
}


static volatile uint8_t do_change;
static volatile uint8_t pattern;


static void fade_pixels(
	uint8_t r,
	uint8_t g,
	uint8_t b
)
{
	uint16_t i;
	for (i = 0 ; i < NUM_LEDS ; i++)
	{
		uint8_t * const p = pixels[i];

		p[0] = (p[0] * (uint16_t) 31 + r) / 32;
		p[1] = (p[1] * (uint16_t) 31 + g) / 32;
		p[2] = (p[2] * (uint16_t) 31 + b) / 32;
	}
}


/******************************
 * Smooth fading between colors.
 */

static const uint8_t colors[][3] =
{
	{ 0x00, 0x00, 0xFF },	// blue
	{ 0xFF, 0xFF, 0xFF },	// white
	{ 0xFF, 0xFF, 0x00 },   // yellow
	{ 0xFF, 0x00, 0x00 },	// red
	{ 0x00, 0x80, 0xFF },	// cyan
	{ 0x00, 0xFF, 0x00 },	// green
	{ 0xFF, 0xFF, 0xFF },	// white
};

static const uint8_t num_colors = sizeof(colors) / sizeof(*colors);


static void pattern_smooth(void)
{
	uint8_t cycle = 0;
	const uint16_t iter_count = 256;

	// total duration: 32 * 256 * 500 ms == 

	for (cycle = 0 ; cycle < 32 ; cycle++)
	{
		const uint8_t * const c0 = colors[(cycle + 0) % num_colors];
		const uint8_t * const c1 = colors[(cycle + 1) % num_colors];
		//const uint8_t * const c2 = colors[(cycle + 2) % num_colors];

		for (uint16_t iter = 0 ; iter < iter_count ; iter++)
		{
			uint16_t i;
			for (i = 0 ; i < NUM_LEDS ; i++)
			{
				uint8_t * const p = pixels[i];
				const uint16_t s0 = iter_count - iter;
				const uint16_t s1 = iter;
				p[0] = (c0[0] * s0 + c1[0] * s1) / iter_count;
				p[1] = (c0[1] * s0 + c1[1] * s1) / iter_count;
				p[2] = (c0[2] * s0 + c1[2] * s1) / iter_count;
			}

			send_pixels();
			_delay_ms(500);

			if (do_change)
				return;
		}
	}
}



/***************************
 * Chase pattern.
 */
struct sprite
{
	int16_t pos;
	int16_t v;
	uint8_t r;
	uint8_t b;
	uint8_t g;
};

static struct sprite sprites[] = {
	{ .v = +1, .r = 0xFF, .b = 0x00, .g = 0x00 },
	{ .v = +2, .r = 0x00, .b = 0xFF, .g = 0x00, .pos = NUM_LEDS*LED_SCALE/2 },
	{ .v = +3, .r = 0xFF, .b = 0xFF, .g = 0xFF },
	{ .v = +5, .r = 0x00, .b = 0xFF, .g = 0xFF },
	{ .v = +7, .r = 0x80, .b = 0x80, .g = 0x10 },
	{ .v = +11, .r = 0x00, .b = 0x00, .g = 0xFF },
	{ .v = +13, .r = 0xFF, .b = 0x00, .g = 0xFF },
};

static const int num_sprites = sizeof(sprites) / sizeof(*sprites);


static void pattern_chase(void)
{
	uint16_t cycle;
	for (cycle = 0 ; cycle < 512 ; cycle++)
	{
		// Compute the next position
		for (int i = 0 ; i < num_sprites ; i++)
		{
			struct sprite * const s = &sprites[i];
			int16_t pos = s->pos + s->v;

			if (pos >= NUM_LEDS * LED_SCALE)
			{
				pos = NUM_LEDS * LED_SCALE - 1;
				s->v = -s->v;
			} else
			if (pos < 0) {
				pos = 0;
				s->v = -s->v;
			}
				
			s->pos = pos;

			uint8_t * const p = pixels[pos / LED_SCALE];
			p[0] = (s->r * (uint16_t) 3 + p[0]) / 4;
			p[1] = (s->g * (uint16_t) 3 + p[1]) / 4;
			p[2] = (s->b * (uint16_t) 3 + p[2]) / 4;

			if (do_change)
				return;
		}

		send_pixels();
		fade_pixels(0, 0, 0);
		_delay_ms(30);
	}
}


/***********************
 * Decay to blue
 */
static void pattern_decay(void)
{
	uint8_t cycle;
	const uint8_t * const c = colors[0];

	for (cycle = 0 ; cycle < 32 ; cycle++)
	{
		fade_pixels(c[0], c[1], c[2]);
		send_pixels();
		_delay_ms(30);
	}
}


/******************
 * Attention getting flash.
 */
static void pattern_flash(void)
{
	uint8_t i;
	for (i = 0 ; i < 8 ; i++)
	{
		memset(pixels, 0, sizeof(pixels));
		send_pixels();
		_delay_ms(250);

		memset(pixels, 0xFF, sizeof(pixels));
		send_pixels();
		_delay_ms(250);
	};
}


typedef void (*pattern_t)(void);

static const pattern_t patterns[] = {
	pattern_flash,
	pattern_decay,
	pattern_chase,
	pattern_decay,
	pattern_smooth,
};

static const uint8_t num_patterns = sizeof(patterns) / sizeof(*patterns);


ISR(USART1_RX_vect)
{
	LED_ON;
	const uint8_t c = UDR1;

	if ('0' <= c && c <= '0' + num_patterns)
	{
		pattern = c - '0';
		do_change = 1;

		print("read: ");
		pchar(c);
		pchar('\n');
	}

	LED_OFF;
}


int main(void)
{
	// set for 16 MHz clock, and make sure the LED is off
	CPU_PRESCALE(0);
	LED_CONFIG;
	LED_OFF;

	// initialize the USB, but don't want for the host to
	// configure.  The first several messages sent will be
	// lost because the PC hasn't configured the USB yet,
	// but we care more about blinking than debug messages!
	usb_init();

	// Enable the serial port interrupts
        // Set the port to 9600 K-baud, 1 start, 8 data, 1 parity
        UBRR1H = 0;
        UBRR1L = 103;
        UCSR1B = (1 << RXEN1) | (1 << RXCIE1);
        UCSR1C = (0 << UCSZ12) | (1 << UCSZ11) | (1 << UCSZ10);


	LED_ON;
	print("started up\n");

	DDRB |= 1 << 2;
	DDRB |= 1 << 1;

	latch();

	uint16_t i;
	for (i = 0 ; i < NUM_LEDS ; i++)
		pixels[i][0] = pixels[i][1] = pixels[i][2] = 0xFF;
	send_pixels();

	pattern = 0;

	while (1)
	{
		patterns[pattern]();

		if (!do_change)
			pattern = (pattern + 1) % num_patterns;

		do_change = 0;
	}
}