Source

led-strip / led-strip.c

/* 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 <util/delay.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 void fade_pixels(void)
{
	uint16_t i;
	for (i = 0 ; i < NUM_LEDS ; i++)
	{
		uint8_t * const p = pixels[i];
		uint8_t j;
		for (j = 0 ; j < 3 ; j++)
		{
			uint16_t v = (p[j] * (uint16_t) 0) / 4;
			if (v > 0xFF)
				v = 0xFF;
			p[j] = v;
		}
	}
}


#define ROWS 10
#define COLS 15

static void render_char(uint8_t x, char c)
{
	for (uint8_t r = 0 ; r < ROWS ; r++)
	{
		uint8_t bits = font_small.bitmap[c + (r << 7)];
		uint8_t * p = pixels[x + (r * COLS)];

		for (uint8_t i = 0 ; i < 8 ; i++, p += 3, bits >>= 1)
		{
			const uint8_t bit = bits & 1;
			p[0] = bit ? 0xFF : 0;
			p[1] = bit ? 0xFF : 0;
			p[2] = bit ? 0xFF : 0;
		}
	}
}


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);


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();

	LED_ON;

	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();


	uint8_t cycle = 0;
	const uint16_t iter_count = 256;

	while (1)
	{
		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++)
		{
			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);
		}

		cycle++;
	}
}