#include "wiring_private.h" // utili funzioni sbi e cbi
#define F_CPU 16000000UL //Definisce la frequenza della CPU come 16 MHz
#include <avr/io.h> //Include definizioni di input/output specifiche per i microcontrollori AVR.
#include <avr/interrupt.h> // Include funzioni per introdurre ritardi nel codice.
#define DATA_PIN PB0 //Definisce il pin per l'invio dei dati ai LED
#define BUTTON_PIN PB1 //Definisce il pin per il pulsante
#define NPIX 32 //Numero di LED nella matrice
#define NBIT 24 //numero di bit per ogni colore (RGB)
#define RSPACE 50 //spazio di riposo tra gli aggiornamenti della matrice LED in msec
// Colori predefiniti (8 colori RGB)
uint8_t colors[8][3] = {
{255, 0, 0}, // Rosso
{0, 255, 0}, // Verde
{0, 0, 255}, // Blu
{255, 255, 0}, // Giallo
{0, 255, 255}, // Ciano
{255, 0, 255}, // Magenta
{255, 255, 255}, // Bianco
{127, 127, 127} // Grigio
};
volatile uint8_t currentColor = 0; // Indice del colore attuale
volatile uint8_t posX = 0, posY = 0; // Posizione del LED nella matrice.
volatile uint8_t sendState = 0;
volatile uint8_t currentByte;
volatile uint8_t bitIndex = 0;
volatile uint8_t byteIndex = 0;
volatile uint8_t colorIndex = 0;
volatile uint8_t sendNext = 0;
// Funzione per inviare un singolo bit utilizzando il Timer
void sendBit(uint8_t bit) {
if (bit) {
PORTB |= (1 << DATA_PIN);
OCR1A = 11; // 0.7us a 16MHz con prescaler 8
} else {
PORTB |= (1 << DATA_PIN);
OCR1A = 5; // 0.35us a 16MHz con prescaler 8
}
sendState = 1;
TIMSK1 |= (1 << OCIE1A); // Abilita l'interrupt di confronto
}
ISR(TIMER1_COMPA_vect) {
if (sendState == 1) {
PORTB &= ~(1 << DATA_PIN);
if (currentByte & (1 << (7 - bitIndex))) {
OCR1A = 10; // 0.6us per bit 1
} else {
OCR1A = 13; // 0.8us per bit 0
}
sendState = 0;
bitIndex++;
if (bitIndex >= 8) {
bitIndex = 0;
byteIndex++;
if (byteIndex >= 3) {
byteIndex = 0;
TIMSK1 &= ~(1 << OCIE1A); // Disabilita l'interrupt di confronto
sendNext = 1;
}
}
} else {
if (byteIndex == 0) currentByte = colors[colorIndex][0]; // Verde
else if (byteIndex == 1) currentByte = colors[colorIndex][1]; // Rosso
else currentByte = colors[colorIndex][2]; // Blu
sendBit(currentByte & (1 << (7 - bitIndex)));
}
}
void initTimer() {
TCCR1A = 0; // Normale operazione
TCCR1B = (1 << WGM12) | (1 << CS11); // CTC mode, prescaler 8
TCNT1 = 0; // Inizia da 0
OCR1A = 0; // Imposta il valore di confronto iniziale
}
// Invia i valori di colore RGB a un LED.
void sendColor(uint8_t r, uint8_t g, uint8_t b) {
colorIndex = 0;
sendBit(g);
while (!sendNext); // Attendi fine invio colore
sendNext = 0;
colorIndex = 1;
sendBit(r);
while (!sendNext); // Attendi fine invio colore
sendNext = 0;
colorIndex = 2;
sendBit(b);
while (!sendNext); // Attendi fine invio colore
sendNext = 0;
}
// Aggiorna lo stato della matrice di LED.
void updateLEDs() {
for (uint8_t y = 0; y < 4; y++) {
for (uint8_t x = 0; x < 8; x++) {
if (x == posX && y == posY) {
sendColor(colors[currentColor][0], colors[currentColor][1], colors[currentColor][2]);
} else {
sendColor(0, 0, 0);
}
}
}
updateLEDs();
}
// Configura l'ADC. Imposta il riferimento di tensione a AVcc e abilita l'ADC con un prescaler di 128.
void initADC() {
ADMUX |= (1 << REFS0); // Vref = AVcc
ADCSRA |= (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0); // Prescaler 128
ADCSRA |= (1 << ADEN); // Abilita ADC
}
// Legge il valore ADC da un canale specificato.
uint16_t readADC(uint8_t channel) {
ADMUX = (ADMUX & 0xF0) | (channel & 0x0F);
ADCSRA |= (1 << ADSC); // Inizia conversione
while (ADCSRA & (1 << ADSC)); // Attendi fine conversione
return ADC;
}
// Legge i valori del joystick e aggiorna la posizione (posX, posY) del LED.
void readJoystick() {
uint16_t xVal = readADC(0);
uint16_t yVal = readADC(1);
uint8_t moved = 0;
// Mappa il valore del joystick per muovere il LED
if (xVal < 200 && posX > 0) {
posX--;
moved = 1;
}
if (xVal > 800 && posX < 7) {
posX++;
moved = 1;
}
if (yVal < 200 && posY > 0) {
posY--;
moved = 1;
}
if (yVal > 800 && posY < 3) {
posY++;
moved = 1;
}
// Cambia colore solo se il joystick si è mosso
if (moved) {
currentColor = (currentColor + 1) % 8;
}
}
// Configura i pin I/O. Imposta DATA_PIN come output e BUTTON_PIN come input con pull-up.
void initIO() {
DDRB |= (1 << DATA_PIN);
DDRB &= ~(1 << BUTTON_PIN);
PORTB |= (1 << BUTTON_PIN);
}
// Funzione principale. Inizializza I/O e ADC, quindi entra in un loop infinito.
int main(void) {
initIO();
initADC();
initTimer();
sei(); // Abilita gli interrupt globali
while (1) {
readJoystick();
if (!(PINB & (1 << BUTTON_PIN))) { // Se il pulsante è premuto
currentColor = (currentColor + 1) % 8;
}
updateLEDs();
}
}