ΠΡ ΡΠΎΠ·Π΄Π°Π΄ΠΈΠΌ Π³ΠΎΠ»ΠΎΠ³ΡΠ°ΡΠΈΡΠ΅ΡΠΊΠΈΠΉ Π΄ΠΈΡΠΏΠ»Π΅ΠΉ Ρ ΠΏΠΎΠΌΠΎΡΡΡ Π±ΡΡΡΡΠΎΠ³ΠΎ Π²ΡΠ°ΡΠ΅Π½ΠΈΡ 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,
// ... (ΠΏΠΎΠ»Π½Π°Ρ ΡΠ°Π±Π»ΠΈΡΠ°)
};