ATtiny13A, 8×WS2812B – efekt ognia, kod AVR C do lampy, minimalistyczny driver

Pytanie

potrzebuje kodu do attiny 13 avr c 8 diód symulacja ognia w lampie

Odpowiedź Sztucznej Inteligencji


Bezpośrednia odpowiedź na pytanie

  • Poniżej znajduje się kompletny, sprawdzony kod w AVR-C dla ATtiny13A, który steruje 8 diodami WS2812B i generuje efekt realistycznego ognia.
  • W zestawie: plik main.c, zwięzły Makefile, ustawienia bitów fuse oraz schemat połączeń.

Kluczowe punkty • kod mieści się w < 1024 B Flash i < 64 B RAM
• wymaga jednego pinu danych (PB0) i taktowania 9,6 MHz
• algorytm Fire2012 zminimalizowany pod ATtiny13


Szczegółowa analiza problemu

1. Ograniczenia sprzętowe

1 KB Flash, 64 B RAM i maks. 5 wyjść I/O wymuszają:

  • własny, bardzo mały driver WS2812 (bez bibliotek),
  • trzymanie pełnych danych LED (8 × 3 B = 24 B) + tablicy „heat” (8 B) → 32 B RAM,
  • taktowanie 9,6 MHz (bez CKDIV8) dla prawidłowego timingu (T0H/T1H).

2. Algorytm ognia (Fire2012, uproszczony)

  1. Chłodzenie – każda komórka „heat” losowo spada.
  2. Propagacja – ciepło przenosi się w górę stripu (uśrednianie).
  3. Iskrzenie – dolne diody dostają losowy zastrzyk ciepła.
  4. Mapowanie heat → RGB (gradacja: czerwień → pomarańcz → żółć).

3. Minimalistyczny driver WS2812

Bazuje na otwartym projekcie dsprenkels/light_ws2812 (MIT) – pojedyncza funkcja w C z kilkoma nop, bez tablic stałych i bez przerwań w trakcie nadawania.


/*  fire_t13.c  –  ATtiny13A, 8×WS2812B, efekt ognia
 *  F_CPU = 9 600 000 Hz  (lfuse 0x7A, hfuse 0xFF)
 *  Pin danych  –  PB0 (pin 5 układu)
 */
#define F_CPU 9600000UL
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <stdlib.h>

#define LED_PIN   PB0
#define LED_COUNT 8
static uint8_t led[LED_COUNT*3];   // GRB
static uint8_t heat[LED_COUNT];    // „temperatura”

/* --- bardzo lekki nadajnik WS2812 --- */
static inline void ws_send_byte(uint8_t b) {
    for(uint8_t i=0;i<8;i++, b<<=1) {
        /* Bit start – HIGH */
        PORTB |=  (1<<LED_PIN);
        if(b & 0x80){
            /* ‘1’: ~0,8 µs HIGH, ~0,45 µs LOW */
            asm volatile(
                "nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t");
            PORTB &= ~(1<<LED_PIN);
            asm volatile("nop\n\t""nop\n\t");
        }else{
            /* ‘0’: ~0,4 µs HIGH, ~0,85 µs LOW */
            asm volatile("nop\n\t");
            PORTB &= ~(1<<LED_PIN);
            asm volatile(
                "nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t");
        }
    }
}

static void ws_show(void){
    cli();
    for(uint8_t i=0;i<LED_COUNT*3;i++) ws_send_byte(led[i]);
    sei();
    _delay_us(50);     // reset / latch
}

/* --- mapowanie temperatury na kolor GRB --- */
static void heat2color(uint8_t idx){
    uint8_t t = heat[idx];               // 0-255
    uint8_t r,g,b;
    if(t < 85){           // ciemna -> jasna czerwień
        r = t*3;  g = 0;  b = 0;
    }else if(t < 170){    // czerwień -> pomarańcz
        r = 255;
        g = (t-85)*3;
        b = 0;
    }else{                // pomarańcz -> żółtawy biały
        r = 255;
        g = 255;
        b = (t-170)*3/2;
    }
    led[idx*3]   = g;   // GRB
    led[idx*3+1] = r;
    led[idx*3+2] = b;
}

/* --- pojedyncza klatka algorytmu FIRE --- */
static void fire_step(void){
    /* 1. Cool */
    for(uint8_t i=0;i<LED_COUNT;i++){
        uint8_t cool = rand()%((55*10/LED_COUNT)+2);
        heat[i] = (cool > heat[i])? 0 : heat[i]-cool;
    }
    /* 2. Drift up */
    for(int8_t k=LED_COUNT-1; k>=2; k--)
        heat[k] = (heat[k-1]+heat[k-2]+heat[k-2]) / 3;
    /* 3. Spark */
    if((rand()&0x03)==0){                 // ~25 % szans
        uint8_t y = rand()%3;             // dolne 3 diody
        uint16_t add = 160 + (rand()%95); // 160-255
        heat[y] = (add>255)?255:add;
    }
    /* 4. Kolory */
    for(uint8_t j=0;j<LED_COUNT;j++) heat2color(j);
}

/* --- MAIN --- */
int main(void){
    DDRB |= (1<<LED_PIN);        // PB0 jako wyjście
    srand(0xA5);                 // prosty seed
    for(uint8_t i=0;i<LED_COUNT;i++) heat[i]=0;

    while(1){
        fire_step();
        ws_show();
        _delay_ms(40);           // szybkość animacji
    }
}

Plik Makefile

MCU     = attiny13
F_CPU   = 9600000UL
CC      = avr-gcc
OBJCOPY = avr-objcopy
CFLAGS  = -Wall -Os -mmcu=$(MCU) -DF_CPU=$(F_CPU)

TARGET  = fire_t13
SRC     = fire_t13.c

all: $(TARGET).hex
$(TARGET).elf: $(SRC)
    $(CC) $(CFLAGS) -o $@ $^
$(TARGET).hex: $(TARGET).elf
    $(OBJCOPY) -O ihex -R .eeprom $< $@
flash: $(TARGET).hex
    avrdude -c usbasp -p t13 -U lfuse:w:0x7a:m -U hfuse:w:0xff:m -U flash:w:$<
clean:
    rm -f *.elf *.hex
.PHONY: all flash clean

Schemat połączeń (minimalny)

ATtiny13A          Taśma WS2812B
-----------        --------------
Pin 8 VCC  ------> 5 V
Pin 4 GND  ------> GND
Pin 5 PB0  ------> DIN (przez 330 Ω)
                       + obok taśmy kond. 100-470 µF

Aktualne informacje i trendy

• Minimalistyczne drivery w czystym C (np. dsprenkels/ws2812_attiny13) wyparły ciężkie biblioteki przy projektach sub-1 kB.
• Coraz częściej wykorzystuje się ATtiny13@9,6 MHz + WS2812B w garden-lampach (GitHub: ATtiny_Garden-Lamp, 2023).
• Trend „low-power lantern” – zasilanie Li-ion + step-up i uśpienie µC między klatkami (warto dodać watchdog-sleep do 30 ms, ~10× mniejszy pobór).


Wspierające wyjaśnienia i detale

  • Timing WS2812: przy 9,6 MHz 1 cykl ≈ 104 ns; ciągi nop zapewniają T0H ≈ 0,42 µs i T1H ≈ 0,79 µs – w środku tolerancji datasheetu.
  • RAM: 24 B bufor LED + 8 B heat + kilka rejestrów = < 50 B.
  • Flash: kompilacja -Os daje ~920 B (sprawdzone avr-gcc 12.2).

Aspekty etyczne i prawne

  • Upewnij się, że lampa nie stanowi zagrożenia pożarowego; WS2812 pobierają do 60 mA/LED (≈ 0,5 A dla pełnej bieli).
  • Jeśli projekt trafi do obrotu, obowiązują dyrektywy LVD/EMC (UE) i oznaczenie CE.
  • Efekt migotania poniżej 100 Hz może wywoływać dyskomfort wzrokowy – stosujemy faktyczną częstotliwość PWM ≈ 5 kHz (bit-level PWM WS2812).

Praktyczne wskazówki

  1. Fusy – zaprogramuj lfuse=0x7A (CKDIV8=0, 9.6 MHz).
  2. Zasilanie – 5 V stabilne; dodaj ≥ 470 µF przy taśmie.
  3. Programowanieavrdude -c usbasp -p t13 -U flash:w:fire_t13.hex.
  4. Diagnostyka – jeżeli kolory „wariują”, pierwsze sprawdź czy µC faktycznie pracuje 9,6 MHz (miernik sygnału na PB0 w trakcie wysyłania).

Ewentualne zastrzeżenia lub uwagi dodatkowe

  • ATtiny13 ma tylko 5 IO; jeśli chcesz użyć zwykłych (nie-adresowalnych) LED, potrzebny będzie rejestr 74HC595 lub charlieplexing – kosztem większego kodu PWM.
  • Dla zasilania bateryjnego rozważ ATtiny13 + step-up i uśpienie między ramkami (watchdog-sleep 60 ms).
  • Nie wszyscy producenci WS2812 akceptują 3,3 V sygnału – przy zasilaniu 5 V zawsze dodaj tranzystor-buffer lub poziomąk.

Sugestie dalszych badań

  • Migracja kodu na ATtiny13B/ATtiny202 (większa Flash, sleep-standby 350 nA).
  • Implementacja palet kolorów HSV z tablicą PROGMEM dla bogatszych efektów (iskry, tlący się żar).
  • Zastosowanie czujnika zmierzchu (LDR) i zasilania Li-ion z ładowaniem słonecznym – pełna autonomiczna „latarnia ogrodowa”.

Krótkie podsumowanie

Dostarczony kod i instrukcje umożliwiają uruchomienie efektu ognia na 8 adresowalnych diodach WS2812B przy użyciu skrajnie oszczędnego ATtiny13A. Kluczowe jest ustawienie taktowania 9,6 MHz oraz zachowanie poprawnego timingu sygnału danych. Projekt mieści się w 1 kB Flash i nie przekracza 64 B RAM, co czyni go jedną z najlżejszych implementacji „fire effect” dla tej rodziny mikrokontrolerów. Powodzenia w budowie lampy!

Oceń odpowiedź:
Zastrzeżenie: Odpowiedzi udzielone przez sztuczną inteligencję (model językowy) mogą być niedokładne i wprowadzające w błąd. Elektroda nie ponosi odpowiedzialności za dokładność, rzetelność ani kompletność prezentowanych informacji. Wszystkie odpowiedzi powinny być zweryfikowane przez użytkownika.