// ===================================================================================
// Project: TinyNeoController - NeoPixel Controller based on ATtiny13A
// Version: v1.0
// Year: 2021
// Author: Stefan Wagner
// Github: https://github.com/wagiminator
// EasyEDA: https://easyeda.com/wagiminator
// License: http://creativecommons.org/licenses/by-sa/3.0/
// ===================================================================================
//
// Description:
// ------------
// An ATtiny13 is more than sufficient to control almost any number of NeoPixels
// via an IR remote. The NeoController was originally developed as a tester for
// 800kHz NeoPixel strings. Since there was still so much flash left in the
// ATtiny13, an IR receiver was integrated so that some parameters can be
// controlled with an IR remote control. In this way, it is also suitable as a
// simple and cheap remote-controlled control unit for NeoPixels. Due to its
// small size, it can be soldered directly to the LED strip without any problems.
// The power supply via a USB-C connection enables currents of up to 3A. There is
// still more than a third of the flash memory left for additional ideas.
//
// References:
// -----------
// The Neopixel implementation is based on NeoCandle.
// https://github.com/wagiminator/ATtiny85-TinyCandle
//
// The IR receiver implementation (NEC protocol) is based on TinyDecoder
// https://github.com/wagiminator/ATtiny13-TinyDecoder
//
// Wiring:
// -------
// +-\/-+
// --- RST ADC0 PB5 1|° |8 Vcc
// NEOPIXELS ------- ADC3 PB3 2| |7 PB2 ADC1 --------
// IR RECEIVER ------- ADC2 PB4 3| |6 PB1 AIN1 OC0B ---
// GND 4| |5 PB0 AIN0 OC0A ---
// +----+
//
// Compilation Settings:
// ---------------------
// Controller: ATtiny13A
// Core: MicroCore (https://github.com/MCUdude/MicroCore)
// Clockspeed: 9.6 MHz internal
// BOD: BOD disabled
// Timing: Micros disabled
//
// Leave the rest on default settings. Don't forget to "Burn bootloader"!
// No Arduino core functions or libraries are used. Use the makefile if
// you want to compile without Arduino IDE.
//
// Fuse settings: -U lfuse:w:0x3a:m -U hfuse:w:0xff:m
// ===================================================================================
// Libraries and Definitions
// ===================================================================================
// Libraries
#include <avr/io.h> // for GPIO
#include <avr/sleep.h> // for sleep functions
#include <avr/interrupt.h> // for interrupts
#include <util/delay.h> // for delays
#include <stdlib.h> // For rand()
// Pin definitions
#define NEO_PIN PB3 // Pin for neopixels
// NeoPixel parameter
#define NEO_GRB // type of pixel: NEO_GRB, NEO_RGB or NEO_RGBW
#define NEO_PIXELS 150 // number of pixels in the string (max 255)
uint8_t pixel_data[NEO_PIXELS * 3]; // 3 bytes per pixel (RGB)
// Global variables
uint8_t NEO_brightness = 0; // 0..2
// ===================================================================================
// Neopixel Implementation for 9.6 MHz MCU Clock and 800 kHz Pixels
// ===================================================================================
// NeoPixel parameter and macros
#define NEO_init() DDRB |= (1<<NEO_PIN) // set pixel DATA pin as output
#define NEO_latch() _delay_us(281) // delay to show shifted colors
// Send a byte to the pixels string
void NEO_sendByte(uint8_t byte) { // CLK comment
for(uint8_t bit=8; bit; bit--) asm volatile( // 3 8 bits, MSB first
"sbi %[port], %[pin] \n\t" // 2 DATA HIGH
"sbrs %[byte], 7 \n\t" // 1-2 if "1"-bit skip next instruction
"cbi %[port], %[pin] \n\t" // 2 "0"-bit: DATA LOW after 3 cycles
"rjmp .+0 \n\t" // 2 delay 2 cycles
"add %[byte], %[byte] \n\t" // 1 byte <<= 1
"cbi %[port], %[pin] \n\t" // 2 "1"-bit: DATA LOW after 7 cycles
::
[port] "I" (_SFR_IO_ADDR(PORTB)),
[pin] "I" (NEO_PIN),
[byte] "r" (byte)
);
}
// Write color to a single pixel
void NEO_writeColor(uint8_t r, uint8_t g, uint8_t b) {
#if defined (NEO_GRB)
NEO_sendByte(g); NEO_sendByte(r); NEO_sendByte(b);
#elif defined (NEO_RGB)
NEO_sendByte(r); NEO_sendByte(g); NEO_sendByte(b);
#elif defined (NEO_RGBW)
NEO_sendByte(r); NEO_sendByte(g); NEO_sendByte(b); NEO_sendByte(0);
#else
#error Wrong or missing NeoPixel type definition!
#endif
}
// Switch off all pixels
void NEO_clear(void) {
for(uint8_t i = NEO_PIXELS; i; i--) NEO_writeColor(0, 0, 0);
}
// Write hue value (0..191) to a single pixel
void NEO_writeHue(uint8_t hue) {
uint8_t phase = hue >> 6;
uint8_t step = (hue & 63) << NEO_brightness;
uint8_t nstep = (63 << NEO_brightness) - step;
switch(phase) {
case 0: NEO_writeColor(nstep, step, 0); break;
case 1: NEO_writeColor( 0, nstep, step); break;
case 2: NEO_writeColor( step, 0, nstep); break;
default: break;
}
}
// ===================================================================================
// Standby Implementation
// ===================================================================================
// Go to standby mode
void standby(void) {
NEO_clear(); // turn off NeoPixels
while(1) {
GIFR |= (1<<PCIF); // clear any outstanding interrupts
sei(); // enable interrupts
sleep_mode(); // sleep until IR interrupt
cli(); // disable interrupts
}
}
// Pin change interrupt service routine
EMPTY_INTERRUPT(PCINT0_vect); // just wake up from sleep
// ===================================================================================
// Main Function
// ===================================================================================
int main(void) {
// Local variables
uint8_t start = 0;
uint8_t speed = 3;
uint8_t dense = 2;
// Disable unused peripherals and prepare sleep mode to save power
ACSR = (1<<ACD); // disable analog comperator
PRR = (1<<PRADC); // shut down ADC
GIMSK |= (1<<PCIE); // enable pin change interrupts
set_sleep_mode(SLEEP_MODE_PWR_DOWN); // set sleep mode to power down
// Setup
NEO_init(); // init Neopixels
// Loop
while(1) {
// Set random color for each pixel
for(uint8_t i = 0; i < NEO_PIXELS; i++) {
// Generate random RGB values (range 0-255)
pixel_data[i * 3] = rand() %100+ 156; // Random Red value
pixel_data[i * 3 + 1] = rand() % 200; // Random Green value
pixel_data[i * 3 + 2] = rand() % 15; // Random Blue value
}
// Send all pixel data to the NeoPixels
for(uint8_t i = 0; i < NEO_PIXELS; i++) {
NEO_writeColor(pixel_data[i * 3], pixel_data[i * 3 + 1], pixel_data[i * 3 + 2]);
}
// Latch the data to the NeoPixels
NEO_latch(); // Update all the NeoPixels with the color data
//delay(50); // Small delay (50 milliseconds)
}
}
tiny:PB5
tiny:PB3
tiny:PB4
tiny:GND
tiny:PB0
tiny:PB1
tiny:PB2
tiny:VCC
ring1:GND
ring1:VCC
ring1:DIN
ring1:DOUT