//definizione dei pin
#define RS PB4
#define E PB3
#define D4 PD4
#define D5 PD5
#define D6 PD6
#define D7 PD7
//definisco una variabile volatile per lo stato della bocca del pac-man
volatile bool bocca = true;
//definisco una variabile volatile per contare il tempo in millisecondi
volatile uint32_t counter = 0;
//definisco l'array che contiene gli indirizzi della prima colonna del display
const uint8_t primaColonna[4] = { 0, 64, 20, 84 };
//definisco le variabili che conterrano il valore digitale del joystick
uint16_t x = 0;
uint16_t y = 0;
//definisco le variabili che indicano le coordinate sullo schermo
uint8_t posX = 1;
uint8_t posY = 1;
//definisco una variabile booleana per la routine dell'ADC
volatile bool leggiX = true;
//funzione per mandare un comando al display
void lcd_cmd(byte cmd) {
PORTD = (cmd & 0xF0);
PORTB &= ~(1<<RS);
PORTB |= (1<<E);
delay_us(1);
PORTB &= ~(1<<E);
delay_us(1);
PORTD = ( (cmd<<4) & 0xF0 );
PORTB &= ~(1<<RS);
PORTB |= (1<<E);
delay_us(1);
PORTB &= ~(1<<E);
delay_us(1);
}
//funzione per mandare dei dati al display
void lcd_data(byte data) {
PORTD = (data & 0xF0); //maschero i 4 bit meno significativi
PORTB |= (1<<RS);
PORTB |= (1<<E);
delay_us(1);
PORTB &= ~(1<<E);
delay_us(1);
PORTD = ( (data<<4) & 0xF0 );
PORTB |= (1<<RS);
PORTB |= (1<<E);
delay_us(1);
PORTB &= ~(1<<E);
delay_us(1);
}
//funzione che permette di creare un carattere personalizzato
void lcd_create_char(byte location, byte charmap[]) {
location &= 0x7;
lcd_cmd(0x40 | (location << 3));
for (int i = 0; i < 8; i++) {
lcd_data(charmap[i]);
}
}
//funzione per posizionare il cursore sul display tramite delle coordinate x e y
void go_to(uint8_t x, uint8_t y) {
lcd_cmd( 0x80 + primaColonna[y-1] + (x-1));
}
//funzione di delay in millisecondi utilizzando il timer0
void delay_ms(uint32_t milli) {
uint32_t target = counter + milli;
while ( counter < target) {
// Attendi fino al raggiungimento del tempo target
}
}
//funzione di delay in microsecondi utilizando il timer0
void delay_us(uint32_t micro) {
uint32_t millisec = micro / 1000;
uint32_t remainingMicro = micro % 1000;
delay_ms(millisec);
if (remainingMicro > 0) {
uint32_t start = 0;
while (start < remainingMicro) {
// Ciclo busy-wait vuoto
start++;
asm volatile ("nop");
}
}
}
//definisco le varie icone di pacman tramite degli array di byte
uint8_t pacmanHorz[8] = {
0b00000,
0b00000,
0b01110,
0b11011,
0b11111,
0b01110,
0b00000,
0b00000
};
uint8_t pacmanRight[8] = {
0b00000,
0b00000,
0b01110,
0b11011,
0b11100,
0b01110,
0b00000,
0b00000
};
uint8_t pacmanLeft[8] = {
0b00000,
0b00000,
0b01110,
0b11011,
0b00111,
0b01110,
0b00000,
0b00000
};
uint8_t pacmanVert[8] = {
0b00000,
0b00000,
0b01100,
0b11110,
0b10110,
0b11110,
0b01100,
0b00000
};
uint8_t pacmanUp[8] = {
0b00000,
0b00000,
0b01000,
0b11010,
0b10110,
0b11110,
0b01100,
0b00000
};
uint8_t pacmanDown[8] = {
0b00000,
0b00000,
0b01110,
0b11110,
0b10110,
0b11010,
0b01000,
0b00000
};
void setup() {
// Disabilita gli interrupt
cli();
// Configura l'ADC
ADMUX = (1 << REFS0);
ADCSRA = (1 << ADEN) | (1 << ADIE) | (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0);
ADCSRA |= (1 << ADSC);
//configuro il timer1 per l'animazione della bocca
TCCR1A = 0;
TCCR1B = 0;
TCCR1B |= B00000100;
TCCR1B |= (1<<WGM12);
TIMSK1 |= B00000010;
OCR1A = 31250;
// Configura il timer0 in modalità CTC per la funzione delay
TCCR0A = (1 << WGM01);
TCCR0B = (1 << CS01) | (1 << CS00);
OCR0A = 250;
TIMSK0 = (1 << OCIE0A);
// Abilita gli interrupt
sei();
//imposto i pin del display in output
DDRB |= (1<<RS) | (1<<E);
DDRD |= (1<<D4) | (1<<D5) | (1<<D6) | (1<<D7);
// Inizializzo il display
lcd_cmd(0b00110011); //invia tre volte per impostare l'LCD in modalità 4 bit
delay_us(50);
lcd_cmd(0b00110010);
delay_us(50);
lcd_cmd(0b00101000); //function set: 4 bit, 2 linee, 5x8 punti
delay_us(50);
lcd_cmd(0b00001100); //display on, cursore off, blink off
delay_us(50);
lcd_cmd(0b00000110); //entry mode set: incrementa, nessun shift
delay_us(50);
lcd_cmd(0b00000001); //clear display
delay_ms(2);
//creo i caratteri personalizzati, caricandoli nella CGRAM
lcd_create_char(0, pacmanHorz);
lcd_create_char(1, pacmanRight);
lcd_create_char(2, pacmanLeft);
lcd_create_char(3, pacmanVert);
lcd_create_char(4, pacmanUp);
lcd_create_char(5, pacmanDown);
//per comodità, setto il cursore in alto a sinistra nel display
go_to(1,1);
}
//definisco i vari comandi per lo spostamento
void loop() {
// Verso sinistra
if (x == 1023 && y == 512) {
//imposto il cursore nella posizione precedente e metto lo spazio
go_to(posX, posY);
lcd_data(' ');
//controllo se ho superato il limite dello schermo
if (posX == 1) {
posX = 20;
} else {
posX--;
}
//imposto il cursore nella nuova posizione e faccio apparire l'icona a schermo
go_to(posX, posY);
lcd_data(bocca ? 0 : 2);
//delay per velocità di 4 caselle al secondo
delay_ms(250);
}
// Verso destra
else if (x == 0 && y == 512) {
go_to(posX, posY);
lcd_data(' ');
if (posX == 20) {
posX = 1;
} else {
posX++;
}
go_to(posX, posY);
lcd_data(bocca ? 0 : 1);
delay_ms(250);
}
// Verso l'alto
else if (x == 512 && y == 1023) {
go_to(posX, posY);
lcd_data(' ');
if (posY == 1) {
posY = 4;
} else {
posY--;
}
go_to(posX, posY);
lcd_data(bocca ? 3 : 4);
delay_ms(1000);
}
// Verso il basso
else if (x == 512 && y == 0) {
go_to(posX, posY);
lcd_data(' ');
if (posY == 4) {
posY = 1;
} else {
posY++;
}
go_to(posX, posY);
lcd_data(bocca ? 3 : 5);
delay_ms(1000);
}
//posizione neutra, quando il joystick, e quindi il pac-man, è fermo
else if (x == 512 && y == 512) {
go_to(posX, posY);
lcd_data(bocca ? 0 : 1);
}
}
//routine per aumentare il counter che conta i millisecondi
ISR(TIMER0_COMPA_vect) {
counter++;
}
//routine per animare la bocca, e cambiare icona ogni 0,5 secondi
ISR(TIMER1_COMPA_vect) {
bocca = !bocca;
}
//routine per leggere i valori analogici del joystick e convertirli in digitale
ISR(ADC_vect) {
if (leggiX) {
x = ADC; // Legge il valore dell'asse X
ADMUX = (1 << REFS0) | (1 << MUX0); // Imposta il prossimo canale su A1 (asse Y)
}
else {
y = ADC; // Legge il valore dell'asse Y
ADMUX = (1 << REFS0); // Imposta il prossimo canale su A0 (asse X)
}
leggiX = !leggiX; // Alterna la variabile per la prossima lettura
ADCSRA |= (1 << ADSC);// avvio una nuova conversione
}