🎨 Π§Π°ΡΡ‚ΡŒ 40: ГолографичСский дисплСй Π½Π° основС LED-ΠΌΠ°Ρ‚Ρ€ΠΈΡ† (3D-визуализация)

ΠœΡ‹ создадим Π³ΠΎΠ»ΠΎΠ³Ρ€Π°Ρ„ичСский дисплСй Ρ ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ быстрого вращСния LED-ΠΌΠ°Ρ‚Ρ€ΠΈΡ†Ρ‹, ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡ эффСкт POV (Persistens of Vision).

3D-голографичСский ΠΏΡ€ΠΎΠ΅ΠΊΡ‚ΠΎΡ€:

cpp

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>

#define LED_COUNT 64
#define ANGLE_STEPS 64
#define FRAME_BUFFER_SIZE (LED_COUNT * ANGLE_STEPS / 8)

// Π’Ρ€Π΅Ρ…ΠΌΠ΅Ρ€Π½Ρ‹ΠΉ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ (сфСра)
const int8_t sphere[LED_COUNT][3] PROGMEM = {
    // ... ΠΊΠΎΠΎΡ€Π΄ΠΈΠ½Π°Ρ‚Ρ‹ Ρ‚ΠΎΡ‡Π΅ΠΊ Π½Π° сфСрС (x, y, z)
};

// Π‘ΡƒΡ„Π΅Ρ€ ΠΊΠ°Π΄Ρ€Π° (битовая ΠΊΠ°Ρ€Ρ‚Π°)
uint8_t frameBuffer[FRAME_BUFFER_SIZE];

// Π’Π°ΠΉΠΌΠ΅Ρ€ для управлСния Π²Ρ€Π°Ρ‰Π΅Π½ΠΈΠ΅ΠΌ
volatile uint16_t angle = 0;

ISR(TIMER1_COMPA_vect) {
    angle = (angle + 1) % 360;
}

// 3D-проСкция Π½Π° 2D (ΠΌΠ°Ρ‚Ρ€ΠΈΡ‡Π½ΠΎΠ΅ ΠΏΡ€Π΅ΠΎΠ±Ρ€Π°Π·ΠΎΠ²Π°Π½ΠΈΠ΅)
void project3D(int8_t x, int8_t y, int8_t z, uint16_t angle, uint8_t* screenX, uint8_t* screenY) {
    // ΠœΠ°Ρ‚Ρ€ΠΈΡ†Π° вращСния Π²ΠΎΠΊΡ€ΡƒΠ³ оси Y
    int16_t cosA = pgm_read_word(&cosTable[angle]);
    int16_t sinA = pgm_read_word(&sinTable[angle]);
    
    // Π’Ρ€Π°Ρ‰Π΅Π½ΠΈΠ΅
    int16_t rx = (x * cosA - z * sinA) / 256;
    int16_t rz = (x * sinA + z * cosA) / 256;
    
    // ΠŸΠ΅Ρ€ΡΠΏΠ΅ΠΊΡ‚ΠΈΠ²Π½Π°Ρ проСкция
    int16_t fov = 128;
    int16_t scale = fov / (rz + fov + 1);
    
    *screenX = 32 + (rx * scale) / 32;
    *screenY = 32 + (y * scale) / 32;
}

// ΠžΡ‚Ρ€ΠΈΡΠΎΠ²ΠΊΠ° ΠΊΠ°Π΄Ρ€Π° (вызываСтся 1000 Ρ€Π°Π·/сСк)
void renderFrame() {
    // ΠžΡ‡ΠΈΡ‰Π°Π΅ΠΌ Π±ΡƒΡ„Π΅Ρ€
    memset(frameBuffer, 0, sizeof(frameBuffer));
    
    // Для ΠΊΠ°ΠΆΠ΄ΠΎΠΉ Ρ‚ΠΎΡ‡ΠΊΠΈ сфСры
    for (uint8_t i = 0; i < LED_COUNT; i++) {
        int8_t x = pgm_read_byte(&sphere[i][0]);
        int8_t y = pgm_read_byte(&sphere[i][1]);
        int8_t z = pgm_read_byte(&sphere[i][2]);
        
        uint8_t screenX, screenY;
        project3D(x, y, z, angle, &screenX, &screenY);
        
        // УстанавливаСм пиксСль Π² Π±ΡƒΡ„Π΅Ρ€Π΅
        if (screenX < 64 && screenY < 64) {
            uint16_t idx = (screenY * 64 + screenX) / 8;
            uint8_t bit = screenX % 8;
            frameBuffer[idx] |= (1 << bit);
        }
    }
}

// ΠžΡ‚ΠΏΡ€Π°Π²ΠΊΠ° ΠΊΠ°Π΄Ρ€Π° Π½Π° LED-ΠΌΠ°Ρ‚Ρ€ΠΈΡ†Ρƒ (Ρ‡Π΅Ρ€Π΅Π· SPI)
void sendFrame() {
    digitalWrite(SS, LOW);
    for (uint16_t i = 0; i < FRAME_BUFFER_SIZE; i++) {
        SPI.transfer(frameBuffer[i]);
    }
    digitalWrite(SS, HIGH);
}

void setup() {
    // Настройка SPI для LED-ΠΌΠ°Ρ‚Ρ€ΠΈΡ†Ρ‹ MAX7219
    SPI.begin();
    SPI.setClockDivider(SPI_CLOCK_DIV2);
    
    // Настройка Ρ‚Π°ΠΉΠΌΠ΅Ρ€Π° для ΠΏΡ€Π΅Ρ€Ρ‹Π²Π°Π½ΠΈΠΉ (1000 Π“Ρ†)
    TCCR1A = 0;
    TCCR1B = (1 << WGM12) | (1 << CS10);
    OCR1A = 15999;  // 1 ΠΊΠ“Ρ† ΠΏΡ€ΠΈ 16 ΠœΠ“Ρ†
    TIMSK1 = (1 << OCIE1A);
    
    sei();
}

void loop() {
    static uint16_t frameCounter = 0;
    
    // Π Π΅Π½Π΄Π΅Ρ€ΠΈΠΌ ΠΊΠ°ΠΆΠ΄Ρ‹ΠΉ 10-ΠΉ ΠΊΠ°Π΄Ρ€ (экономия CPU)
    if (frameCounter % 10 == 0) {
        renderFrame();
    }
    frameCounter++;
    
    sendFrame();
    delayMicroseconds(100);
}

Π’Π°Π±Π»ΠΈΡ†Ρ‹ синуса ΠΈ косинуса (Π² PROGMEM):

cpp

const int16_t sinTable[360] PROGMEM = {
    0, 4, 9, 13, 18, 22, 27, 31, 36, 40, 45, 49, 53, 58, 62, 67,
    71, 75, 80, 84, 88, 93, 97, 101, 105, 109, 114, 118, 122, 126, 130, 134,
    // ... (полная Ρ‚Π°Π±Π»ΠΈΡ†Π° Π½Π° 360 градусов)
};

const int16_t cosTable[360] PROGMEM = {
    256, 255, 255, 254, 253, 252, 251, 250, 249, 248, 246, 245, 243, 242, 240, 238,
    // ... (полная Ρ‚Π°Π±Π»ΠΈΡ†Π°)
};

Π’Π°ΠΌ Ρ‚Π°ΠΊΠΆΠ΅ ΠΌΠΎΠΆΠ΅Ρ‚ ΠΏΠΎΠ½Ρ€Π°Π²ΠΈΡ‚ΡŒΡΡ

Π”ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ ΠΊΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΉ

Π’Π°Ρˆ адрСс email Π½Π΅ Π±ΡƒΠ΄Π΅Ρ‚ ΠΎΠΏΡƒΠ±Π»ΠΈΠΊΠΎΠ²Π°Π½. ΠžΠ±ΡΠ·Π°Ρ‚Π΅Π»ΡŒΠ½Ρ‹Π΅ поля ΠΏΠΎΠΌΠ΅Ρ‡Π΅Π½Ρ‹ *