Creating an Arcade gaming console from scratch (AVR / C & C++)

Go To Last Post
100 posts / 0 new

Pages

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

No, all fine. All you're doing is helping! 

 

Worth mentioning that I'll be using 3 matrix instead of 4 (23 pins vs 28 pins). Doing this because I still want to connect a joystick (or push buttons) + I have to have a blinking LED as a requirement to this project. If I use 4 matrixs, I'll only have 1 or 2 free pins.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Considering what was suggested before (and hoping I didn't make any errors), this is the general schematic of the whole circuit:

 

 

I'm still missing connecting the required LED (for blinking purposes) and the push buttons / joystick in order to control the menus / ingame movements. Besides that, I think that everything else required is already connected.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If you are using 3 matrices, then that's 15 "C" pins, you can connect using just 2 ports. Trust me, it will make life easier later. I suggest you use PA and PB.

 

edit: that's because you image data can then be an array of 8 uint16_t integers (each represents one line), and you can just copy that to PA and PB without much processing.

Last Edited: Sat. Nov 23, 2019 - 04:50 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

But if I connect all of them to portA I would "lose" my ADC (I'm not sure if I need it for the joystick but still). As for portB, I'll be using the USBasp in order to transfer the code into the ATMega. Can I still use those ports for circuitry? (Kind of a dumb question but to be honest my first time using usbasp was barely 2 months ago).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok. What kind of joystick do you want to use? I will admit my knowledge of joystics is zero.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I haven't decided yet (if I'm going for the simple push buttons or the joystick). Nonetheless, I'm not at home so I can't really link the one I saw. If, i.e., I would choose to go with push buttons (up, down, left, right), wouldn't need the adc right?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

"Dare to be naïve." - Buckminster Fuller

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Alright. As for the other pins (MOSI, MISO, etc..) can I still use them for circuitry (given that I'll need them in order to connect my usbasp)? I've seen projects where these pins have nothing connected (which I'm not sure if it's because of this issue or if it was a simple coincidence) while others use them for circuitry (though most of them don't even show how they programmed the micro).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Jonaas18 wrote:
As for the other pins (MOSI, MISO, etc..) can I still use them for circuitry (given that I'll need them in order to connect my usbasp)?
Yes

ATmega32A Datasheet

[page 269]

27.8 SPI Serial Downloading

Both the Flash and EEPROM memory arrays can be programmed using the serial SPI bus while RESET is pulled to GND. The serial interface consists of pins SCK, MOSI (input), and MISO (output). ...

"Dare to be naïve." - Buckminster Fuller

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Having connected everything on the same port (using PA and PB for the LED Matrix columns), I now have the following circuit:

 

 

I also connected 4 push buttons in order to control the game (though the usage of a joystick could still be a possibility later on). Anyways, will build the circuit on 2 breadboards and proceed to code the led matrix and the game.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

After building the circuit, I started coding the basic informations (void init, etc. etc.). I wrote a basic loop code in order to check if everything was alright but something seems off:

 

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

void inic(void) {
    DDRA = 0xFF;    // PORTA defined as outputs
    DDRB = 0xF7;    // PORTB defined as outputs excluding PB3
    DDRC = 0xFF;    // PORTC defined as outputs
}

int main(void) {
    PORTA = (1<<PA0);
    PORTC = (0<<PC0);
}

In my mind, what this is supposed to do is turn on the top left corner LED of the 1st matrix (I also used another matrix and connected PIN13 to the VCC and PIN9 to the Ground - this is the procedure to turn on the top left corner led - and it worked). Is something wrong with my circuit or am I coding this wrong? 

 

I might be dumb by thinking that saying PC0 = 0 is the same as connecting it to the ground (and therefore, creating a positive voltage drop between the anode and the cathode, which would turn the led on).

 

 

EDIT1: Also, I was having issues understading what the COM pin meant on the ULN chip. I read the datasheet and understood that it the pin was supposed to be connected to the Voltage Supply and, therefore, I connected it to the VCC pin (5V). When sending the code from the AVR Studio software to the breadboard, some leds - 3rd matix only -  turn on (very softly) and then nothing happens, like explained above.

Last Edited: Sun. Dec 8, 2019 - 06:50 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0


Jonaas18 wrote:
I also used another matrix and connected PIN13 to the VCC and PIN9 to the Ground

I hope you didn't connect it directly without the current limiting resistor. You risk burning the LED if you connect it directly as described.

 

Jonaas18 wrote:
PC0 = 0 is the same as connecting it to the ground

PC0=0 is connecting PC0 to GND. However, the ULN chip is a kind of inverter, to connect the output of the ULN to GND, you need to connect the ULN input PCx to VDD, that is, PCx=1.

In fact you can see it described in the datasheet with the inverter symbol:

 

Jonaas18 wrote:
I was having issues understading what the COM pin meant on the ULN chip. I read the datasheet and understood that it the pin was supposed to be connected to the Voltage Supply

The COM pin is a return path for current when driving inductive loads (via internal diodes), so you only need to connect it when driving such loads, like motors. For LEDs, you can leave COM unconnected.

Quoting from the datasheet:

8.4.2 Resistive Load Drive

(...)The COM pin can be left floating for these applications.

 

Finally, you initialization function doesn't execute automatically. You need to call it from main():

int main(void) {
    inic();
    PORTA = (1<<PA0);
    PORTC = (0<<PC0);
}

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I hope you didn't connect it directly without the current limiting resistor. You risk burning the LED if you connect it directly as described.

No worries, I used a 2.2k resistor in order to avoid that!

 

PC0=0 is connecting PC0 to GND. However, the ULN chip is a kind of inverter, to connect the output of the ULN to GND, you need to connect the ULN input PCx to VDD, that is, PCx=1.

 

 I didn't understand. I was right about coding PC0 = 0 in order to force the cathode to 0 and the anode to 1 (therefore, turning the led on). However, I didn't understand what you mean by connecting the ULN Input to VDD. Does this mean that I should code PC0 = 1 AS WELL AS PA0 = 1 in order to have a voltage drop (which only makes sense if, as you said, the value inverts itself to 0). Sorry if I'm beeing really dumb.

 

The COM pin is a return path for current when driving inductive loads (via internal diodes), so you only need to connect it when driving such loads, like motors. For LEDs, you can leave COM unconnected.

Alright. I read somewhere that connecting the pin was optional but I didn't know / notice that it wasn't required for these kind of applications. Thanks! 

 

Finally, you initialization function doesn't execute automatically. You need to call it from main():

 My bad, that's a dumb mistake lol. Thanks for spotting it man!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Jonaas18 wrote:
Does this mean that I should code PC0 = 1 AS WELL AS PA0 = 1 in order to have a voltage drop

Yes.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Alright...  So, given this, in order to have a voltage drop between the anode and the cathode, both pins should be set to 1 and to turn the respective led off, the cathode should be set to 0 right? Will try it tomorrow.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Update:

 

After dicussing the project with my professor and messing around with my circuit multiple times, I built the last version of my project (or at least I think so). It is supposed to run the game "Pong" in a 8x8 Led Matrix, using an ATMega32 as well as 2x 7 Segment Displays (with the help of a Shift Register and 2x 74LS47N chips).

 

Besides the components mentioned above, I also used 1x ULN2803 and 1x UDN2981A (I haven't tested the code yet since I can't buy this component, it either costs 15€ or it doesn't exist in a local market). Thought about using individual PNP transistors in order to imitate the UDN functioning but have no idea which ones to choose. I'm also having doubts on whether or not my Shift Registor connections are alright (I used an already made project to guide me through the coding / circuitry but seems weird connecting VCC / GND to other pins rather than the VCC / GND pins of the chip (lol). Would love some insight if someone cares to explain as I've never messed with shift registors (though I learnt some theory about how they work some years ago).

 

I will also post my code, even though I haven't commented everything (it's on my native language as well). If needed, I have no problem translating the comments. The code has 4 warnings while compiling, given the fact that some variables are declared / initialized but not used. I have no idea if this is ok to happen as I wrote my code according to the already made project I talked earlier. Nothing bad was reported so I figured that everything was up and running. Will read through the whole code and try to comment / understand as much as I can in order to explain if anyone has any doubt (meaning, not understanding why the use of this or that). The compiler also reported an error "_builtin_avr_delay_cycles expects a compile time integer constant”. I browsed online and found out that writing the line "#define __DELAY_BACKWARD_COMPATIBLE__" fixed this, no idea why.

 

Followed this project for circuitry + coding of the game PONG.

Feedback would be appreciated! Cheers!

 

#define __DELAY_BACKWARD_COMPATIBLE__
#include <avr/io.h>
#include <stdlib.h>
#define F_CPU 8000000UL                                                        // Oscilador interno a correr a 8MHz.
#include <util/delay.h>

#define nCol 8                                                                // Número de colunas = 8.
#define periodo 10                                                            // periodo = 10 ms.
#define DCycle 32                                                            // Respetivo ao funcionamento do buzzer.
#define CLK 0                                                                // Ligação ao ShiftRegister via PB0.
#define S_IN 1                                                                // Ligação ao ShiftRegister via PB1.
#define LATCH 2                                                                // Ligação ao ShiftRegister via PB2.
#define _B_TIME 200


//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------//
uint8_t column[nCol];
uint8_t l[12]={0,0,1,2,4,8,16,32,64,128,0,0};                                // Linhas para mapeamento.
uint8_t sw,sw1,sw2,score1,score2;                                            // Variáveis para controlo de scores, etc.
uint8_t PSPEED=10, BSPEED=20;                                                // Speed do jogador = 10.        Speed da bola = 20.
uint8_t go;

struct coord {
    int8_t x;
    int8_t y;
    
    int8_t old_x;
    int8_t old_y;
    
    int8_t speed_x;
    int8_t speed_y;
} coord;


//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------//
void inic(void);
void display(uint8_t *column);                                                // Demora "x" milisegundos (periodo) a realizar. uint8_t = unsigned integer of length 8 bits.

// Lê o estado de um botão (1=pressionado; 0=não pressionado).
uint8_t readButton(uint8_t index);                                            // Index: Define qual o botão a ler (Jogador 1: 1,2        Jogador 2: 3,4).

void start_buzzer();                                                        // Ativa o buzzer de forma a reproduzir som (só termina quando stop_buzzer() for chamada).
void stop_buzzer();                                                            // Termina a reprodução de som.
void single_beep(uint16_t d_ms);                                            // Faz o buzzer tocar durante "d_ms" (milisegundos).

void shift_data(uint8_t data);                                                // Utilizado à frente na função update_score();
void update_score(uint8_t score_p1, uint8_t score_p2);                        // Atualiza os scores de cada jogador (Jogador 1: p1    Jogador 2: p2).


//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------//
void inic_matrix(uint8_t *column) {
    uint8_t i;
    for (i=0; i<nCol; i++)
    column[i] = 0xFF;                                                        // Liga todos os leds da matriz.
}

void clear() {
    uint8_t i;
    for (i=0; i<nCol; i++) {
        column[i] = 0;                                                        // Desliga todos os leds da matriz.
    }
}

void long_display() {
    uint8_t i;
    for (i=0; i<50; i++) {
        display(column);
    }
}

void inicBall() {
    int8_t v[2]={-1,1};
    // int8_t v2[3]={-1,0,1};
    
    coord.x=3+rand()%2;
    coord.y=3+rand()%2;
    coord.old_x=coord.x;
    coord.old_y=coord.y;
    coord.speed_x=v[rand()%2];
    coord.speed_y=0;
    
    column[0]=column[7]=0x18;
}

void move_ball() {
    // uint8_t i;
    
    coord.old_x=coord.x;
    coord.x=coord.x + coord.speed_x;
    coord.old_y=coord.y;
    coord.y=coord.y + coord.speed_y;
    
    /* Codificação da colisão da bola na parede superior / inferior */
    
    if(coord.y==7 && coord.speed_y==1)
        coord.speed_y=-1;
    if(coord.y==0 && coord.speed_y==-1)
        coord.speed_y=1;
    
    sw1=0;
    sw2=0;
    
    /* Colisão com o jogador da direita */
    
    if(coord.x==6) {
        
        if((l[2+coord.y]+l[2+coord.y-1])==column[7]) {
            coord.speed_x=-1;
            if(coord.speed_y!=1)
            coord.speed_y++;
            
            sw2=1;
        }
        
        if ((l[2+coord.y+1]+l[2+coord.y])==column[7]) {
            coord.speed_x=-1;
            if(coord.speed_y!=-1)
            coord.speed_y--;
            
            sw2=1;
        }
        
        if (((l[2+coord.y+1]+l[2+coord.y+2])==column[7]) && (coord.speed_y==1)) {
            coord.speed_x=-1;
            coord.speed_y=-1;
            
            sw2=1;
        }
        
        if (((l[2+coord.y-1]+l[2+coord.y-2])==column[7]) && (coord.speed_y==-1)) {
            coord.speed_x=-1;
            coord.speed_y=1;
            
            sw2=1;
        }
    }
    
    /* Marcação de ponto - Jogador 1 */
    
    if (sw2==0 && coord.x==6) {
        score1++;
        go=1;
        update_score(score1,score2);
        
        column[7] = 255;
        column[coord.x]=l[2+coord.y];
        
        for (sw=0; sw<100; sw++)
        display(column);
        
        inicBall();
    }
    
    /* Colisão com o jogador da esquerda */
    
    if(coord.x==1) {
        
        if((l[2+coord.y]+l[2+coord.y-1])==column[0]) {
            coord.speed_x=1;
            if(coord.speed_y!=1)
            coord.speed_y++;
            
            sw1=1;
        }
        

        if ((l[2+coord.y+1]+l[2+coord.y])==column[0]) {
            coord.speed_x=1;
            if(coord.speed_y!=-1)
            coord.speed_y--;
            
            sw1=1;
        }
        

        if (((l[2+coord.y+1]+l[2+coord.y+2])==column[0]) && (coord.speed_y==1)) {
            coord.speed_x=1;
            coord.speed_y=-1;
            
            sw1=1;
        }

        if (((l[2+coord.y-1]+l[2+coord.y-2])==column[0]) && (coord.speed_y==-1)) {
            coord.speed_x=1;
            coord.speed_y=1;
            
            sw1=1;
        }
    }
    
    /* Marcação de ponto - Jogador 2 */
    
    if (sw1==0 && coord.x==1) {
        score2++;
        go=1;
        update_score(score1, score2);

        column[0]=255;
        column[coord.x]=l[2+coord.y];
        
        for (sw=0;sw<100;sw++)
        display(column);
        
        inicBall();
    }
    
    /* Condições para cantos do chão ??????? */
    
    if (coord.x==1 && coord.y==0 && coord.speed_y==-1)
    coord.speed_y=1;

    if (coord.x==6 && coord.y==0 && coord.speed_y==-1)
    coord.speed_y=1;

    if (coord.x==1 && coord.y==7 && coord.speed_y==1)
    coord.speed_y=-1;

    if (coord.x==6 && coord.y==7 && coord.speed_y==1)
    coord.speed_y=-1;
    
    /* Bola */
    
    column[coord.x]=l[2+coord.y];
}

void game() {
    uint8_t i;
    clear();
    
    column[0] = 0x18;
    column[7] = 0x18;
    sw1=0; sw2=0;
    score1 = 0; score2 = 0;

    uint8_t bot1 = 0, bot2 = 0, bot3 = 0, bot4 = 0;
    uint8_t counter = 0;
    uint8_t counter2 = 0;
    uint8_t c1 = column[0], c2 = column[7];

    inicBall();

    for(;;) {
        
        /* Leitura de botões */
        
        bot1 = readButton(1);
        bot2 = readButton(2);
        bot3 = readButton(3);
        bot4 = readButton(4);
        
        if(counter>PSPEED) {
            if (bot1) {
                if((column[7] << 1) <=192)
                c2 = column[7] = column[7] << 1;
            }

            if (bot2) {
                if((column[7] >> 1) >= 3)
                c2 = column[7] = column[7] >> 1;
            }

            if (bot3) {
                if((column[0] << 1) <=192)
                c1 = column[0] = column[0] << 1;
            }

            if (bot4) {
                if((column[0] >> 1) >= 3)
                c1 = column[0] = column[0] >> 1;
            }
            
            counter=0;
        }
        
        counter++;
        
        /* Atualização do score */
        
        if(score1>9 || score2>9) {
            score1=0;
            score2=0;
            update_score(score1,score2);
        }
        
        for(i=1; i<nCol-1; i++)
        column[i]=0;
        
        /* Movimento da bola */
        
        if(counter2>BSPEED) {
            move_ball();
            counter2=0;
        }
        counter2++;
        
        display(column);
    }
}

int main(void) {
    uint8_t column[nCol];
    
    inic();
    inic_matrix(column);
    update_score(0, 0);
    
    for(;;) {                                                                // Loop infinito.
        display(column);                                                    // Demora 10ms a executar.
    }
    
    return 0;
}

void inic(void) {
    DDRA = 0xFF;                                                            // Inicializa todos os pins do portA a saídas (leds da matriz).
    PORTA = 0x00;
    
    DDRD = 0xFF;                                                            // Inicializa todos os pins do portD a saídas (leds da matriz).
    PORTD = 0x00;
    
    DDRB &= 0xF0;                                                            // Inicializa os pinos 4,5,6 e 7 do portB a entradas (push buttons para jogar).
    DDRB |= 0x0F;                                                            // Inicializa os pinos 0,1,2 e 3 do portB a saídas (ShiftRegister: 0 a 2    Buzzer: 3).
    
    TCCR0 |= (1<<WGM01) | (1<<WGM00) | (1<<COM01) | (0<<CS02) | (1<<CS01) | (0<<CS00);// Fast PWM, Clear 0C0 on compare match (non-inv), precaler a 8.
    TCCR0 &= ~((1<<COM01) | (1<<COM00));                                    // Desativa o buzzer manualmente, para já.
}

void display(uint8_t *column) {
    uint8_t i;
    uint8_t sequencia;
    
    sequencia = 0x01;
    
    for(i=0;i<nCol;i++) {
        PORTA = sequencia;
        PORTD = column[i];
        
        _delay_ms(periodo / nCol);
        sequencia = sequencia << 1;
    }
}

uint8_t readButton(uint8_t index) {
    if (index < 1 || index > 4)                                                // Se o valor de index for menor que 1 ou maior que 4, return 0 visto que não estamos no intervalo definido
    return 0;                                                                // anteriormente (Botões: 1, 2, 3 e 4).
    
    return !(PINB & (0x10 << (index -1)));
}

void start_buzzer() {
    TCCR0 |= (1<<COM01);                                                    // Ativa buzzer.
    OCR0 = DCycle;                                                            // Define o sinal aplicado ao buzzer: V = Dcycle / (256*5)
}

void stop_buzzer() {
    TCCR0 &= ~((1<<COM01) | (1<<COM00));                                    // Desativa buzzer.
    OCR0 = 0;                                                                // Define o sinal aplicado ao buzzer. Apesar de V = 0, continua a haver um sinal pequeno de tensão aplicado ao buzzer.
}

void single_beep(uint16_t d_ms) {
    start_buzzer();
    _delay_ms(d_ms);
    stop_buzzer();
}

void shift_data(uint8_t data) {
    uint8_t i;
    uint8_t bit;
    
    PORTB &= ~(1<<LATCH);                                                    // Desativa a saída LATCH do ShiftRegister
    
    for(i=0;i<8;i++) {
        PORTB &= ~(1<<S_IN);                                                // Desativa S_IN.
        PORTB &= ~(1<<CLK);                                                    // Desativa CLK.
        
        if (data & (1<<i))
        bit = 0x01;
        else
        bit = 0x00;
        
        PORTB |= (bit << S_IN);
        PORTB |= (1 << CLK);
    }
    
    PORTB &= ~(1<<CLK);                                                        // Desativa CLK.
    PORTB |= (1<<LATCH);                                                    // Ativa a saída LATCH do ShiftRegister.
}

void update_score(uint8_t score_p1, uint8_t score_p2) {
    shift_data((score_p2 & 0x0F) | ((score_p1 & 0x0F) << 4));
}

 

Attachment(s): 

Last Edited: Mon. Dec 23, 2019 - 12:03 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Jonaas18 wrote:
1x UDN2981A (I haven't tested the code yet since I can't buy this component, it either costs 15€ or it doesn't exist in a local market)

See if you can get one of these instead: https://www.businesswire.com/new...

This TBD62783A is an interesting part from Toshiba, an 8 channel P-MOSFET array. So it should be able to replace the UDN2981A and in fact it should be a lot better.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

El Tangas wrote:

Jonaas18 wrote:
1x UDN2981A (I haven't tested the code yet since I can't buy this component, it either costs 15€ or it doesn't exist in a local market)

See if you can get one of these instead: https://www.businesswire.com/new...

This TBD62783A is an interesting part from Toshiba, an 8 channel P-MOSFET array. So it should be able to replace the UDN2981A and in fact it should be a lot better.

 

Sadly there's no reference to that part in local markets. Could only find one on Mouser's website but it would cost 20€ to buy a 0.5€ piece. Will not using the chip cause any problems? Since this is a proof of concept, I only need to test the game for 5-10 minutes in order to get a grade (could talk about future implementations - like the use of the said chip - in my report, afterwards, which would somewhat backup the fact that I researched and tried to solve issues regarding circuitry, etc.).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Jonaas18 wrote:
Will not using the chip cause any problems?

So long as you keep within the current limits specified in the datasheet, you should be fine. Besides, if the game is pong, most LEDs will be off most of the time.

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 

Got the components today and built the circuit like shown below. Sadly nothing happens. My guess is the code written is wrong since I was very careful while placing the components on the breadboard. Any idea what could be wrong (code is posted on some past replies)?

 

(The connections on the SN74HC595N / SN74LS47N seem a bit dodgy as well since I'm not connecting any VCC or GND, idk if I have to tbh).

 

edit1: Worth noticing that I'm not using the UDN2981 chip for the reasons stated before. Instead, I'm connecting the matrix's rows directly to the ATMega32.

 

 

Last Edited: Sat. Dec 28, 2019 - 07:44 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Nobody is interested in looking at a monolithic wall of code like you have in #67. Actually that is sure to scare people away.

That's why I advised you to separate your code in modules that can be debugged separately.

 

To start, please build a code that enables all the LEDs in col 1.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm sorry, I posted everything in the same code in order to be more brief in my explanation. You're totally right anyways.

 

I started by testing if the matrix was ok. For this, I connected my VCC to PA0 (column 1) via a resistor. Afterwards, I connected all row pins to the ground. The column lighted up properly.

 

I then connected the led matrix columns to the resistors (similar to how it is connected in reply #61) and the matrix rows to the ULN chip (ATMega pins connected to the input pins of the ULN chip, the outputs connected directly to the matrix rows).

 

I then proceeded to write this code:

 

#include <avr/io.h>
#include <stdlib.h>
#define F_CPU 8000000UL                                                        // 8Mhz Internal Oscillator
#include <util/delay.h>
#include <stdio.h>

void inic (void) {
	DDRA = 0xFF;							       // Defined as outputs.

	DDRD = 0xFF;							       // Defined as outputs.
	PORTD = 0xFF;				// Setting all pins of PORTD to High (1). Since it is connected to the ULN chip, it will invert the signal, setting it to 0 (setting all rows to GND).

}

int main (void) {
	inic();
	PORTA |= (1<<PORTA0);						       // Setting Pin0 of PORTA to High (1). Basically, column 1 is connected to VCC.
}

Everything went well.

Last Edited: Sat. Dec 28, 2019 - 09:29 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok. I meant for you to do the other way around, that is, only 1 of the ULN outputs to be on (that is, only one of PORTD high), while all of PORTA are high. It has to be done this way, because the ULN has the larger current capacity, so 1 ULN pin can handle the current for 8 MCU pins, not the other way around (you can burn the MCU that way).

 

Then, to test all LEDs, cycle the PORTD bit that is on, i.e. first D0 is on, then D1, etc. waiting 1s or so so that you can observe all the LEDs.

 

Then, to test display memory, create some array with 8 bytes, and initialize the array with some pattern. When the active ULN bit is 0 (D0=1), copy array[0] to PORTA, when ULN bit is 1 (D1=1) copy array[1] to PORTA and so on. Do an infinite cycle. You should see the pattern being displayed in the LED matrix.

 

edit: Please post a pic of the matrix when it's working, I like pics.

Last Edited: Sun. Dec 29, 2019 - 12:45 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok. I meant for you to do the other way around, that is, only 1 of the ULN outputs to be on (that is, only one of PORTD high), while all of PORTA are high. It has to be done this way, because the ULN has the larger current capacity, so 1 ULN pin can handle the current for 8 MCU pins, not the other way around (you can burn the MCU that way).

Will get right on it. If I understood correctly, you're advising me to connect the columns, that is, PORTAx to the ULN output and PORTD (rows) directly to the MCU? (What I did was the opposite, I connected the columns to the MCU through the resistors and the rows to the ULN chip).

 

Then, to test all LEDs, cycle the PORTD bit that is on, i.e. first D0 is on, then D1, etc. waiting 1s or so so that you can observe all the LEDs.

 

Then, to test display memory, create some array with 8 bytes, and initialize the array with some pattern. When the active ULN bit is 0 (D0=1), copy array[0] to PORTA, when ULN bit is 1 (D1=1) copy array[1] to PORTA and so on. Do an infinite cycle. You should see the pattern being displayed in the LED matrix.

Got it, will do! I'll post a video when I'm done.

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Jonaas18 wrote:
Will get right on it. If I understood correctly, you're advising me to connect the columns, that is, PORTAx to the ULN output and PORTD (rows) directly to the MCU? (What I did was the opposite, I connected the columns to the MCU through the resistors and the rows to the ULN chip).

 

It doesn't matter what you call columns or rows or where they are connected. What matters is: Only one ULN pin can be active at a time.

 

So if the ULN is connected to PORTA, only one bit of PORTA can be high. If the ULN is connected to PORTD, only 1 bit of PORTD can be 1.

The port that is not connected to the ULN needs resistors in each pin.

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok. I meant for you to do the other way around, that is, only 1 of the ULN outputs to be on (that is, only one of PORTD high), while all of PORTA are high. It has to be done this way, because the ULN has the larger current capacity, so 1 ULN pin can handle the current for 8 MCU pins, not the other way around (you can burn the MCU that way).

 

Then, to test all LEDs, cycle the PORTD bit that is on, i.e. first D0 is on, then D1, etc. waiting 1s or so so that you can observe all the LEDs.

Given this code, I obtained the results shown in video 1:

void inic (void) {
	DDRA = 0xFF;						// Defined as outputs.
	PORTA = 0xFF;						// Set to HIGH value in order to obtain a voltage drop (anode-cathode).

	DDRD = 0xFF;						// Defined as outputs.

}

int main (void) {
	inic();
	while (1) {
		PORTD = (1<<PORTD0);
		_delay_ms(1000);
		PORTD = (1<<PORTD1);
		_delay_ms(1000);
		PORTD = (1<<PORTD2);
		_delay_ms(1000);
		PORTD = (1<<PORTD3);
		_delay_ms(1000);
		PORTD = (1<<PORTD4);
		_delay_ms(1000);
		PORTD = (1<<PORTD5);
		_delay_ms(1000);
		PORTD = (1<<PORTD6);
		_delay_ms(1000);
		PORTD = (1<<PORTD7);
		_delay_ms(1000);
	}
}

 

Then, to test display memory, create some array with 8 bytes, and initialize the array with some pattern. When the active ULN bit is 0 (D0=1), copy array[0] to PORTA, when ULN bit is 1 (D1=1) copy array[1] to PORTA and so on. Do an infinite cycle. You should see the pattern being displayed in the LED matrix.

void inic (void) {
	DDRA = 0xFF;						// Defined as outputs.
	
	DDRD = 0xFF;						// Defined as outputs.

}

int main (void) {
	inic();	
	int array1[8] = {194,0,255,3,128,255,0,96};
	
	while (1) {
		PORTD = (1<<PORTD0);
		PORTA = array1[0];
		_delay_ms(1000);
		PORTD = (1<<PORTD1);
		PORTA = array1[1];
		_delay_ms(1000);
		PORTD = (1<<PORTD2);
		PORTA = array1[2];
		_delay_ms(1000);
		PORTD = (1<<PORTD3);
		PORTA = array1[3];
		_delay_ms(1000);
		PORTD = (1<<PORTD4);
		PORTA = array1[4];
		_delay_ms(1000);
		PORTD = (1<<PORTD5);
		PORTA = array1[5];
		_delay_ms(1000);
		PORTD = (1<<PORTD6);
		PORTA = array1[6];
		_delay_ms(1000);
		PORTD = (1<<PORTD7);
		PORTA = array1[7];
		_delay_ms(1000);
	}
}

Included another video (2) as well!

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Cool. Now if you speed it up, that is, remove the 1s delay, the display will change so fast that it will look like a static display, showing your pattern.

 

I'm sure you learned in class about interrupts and interrupt service routines (ISRs). The next step, is setting up a timer so that it generates an interrupt at about every millisecond or so.

Each time the interrupt fires, the ISR will move the display to the next column. That is, it will copy the data from the next position in the array and display it, like you are doing in the while loop of the second program.

 

To be accessible from the ISR, the array (and all other variables used by the ISR) will need to be global, and also have the "volatile" attribute.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Cool. Now if you speed it up, that is, remove the 1s delay, the display will change so fast that it will look like a static display, showing your pattern.

The display kinda gets messed up when the delay functions are removed. The leds I want on are on, though the ones next to it light up slightly. I'll attach an image. I guess this is normal, from what I've learned, but could be wrong.

 

I'm sure you learned in class about interrupts and interrupt service routines (ISRs). The next step, is setting up a timer so that it generates an interrupt at about every millisecond or so.

Each time the interrupt fires, the ISR will move the display to the next column. That is, it will copy the data from the next position in the array and display it, like you are doing in the while loop of the second program.

Yes. I already used my ATMega's Timer/Counter2  in order to have a blinking led (required for the project). Will code the next interrupt like advised using the Timer/Counter0. Should I write everything present in the while loop inside the interrupt function (i.e., PORTD = (1<<PORTD0); PORTA = array1[0]; ........) or only the array / PORTA lines?

 

 

Edit1: Though I've learned ISRs (and used them already in this project, like said above), I'm unsure about which mode to use. For the blinking led I used the CTC mode. Besides that, I've only used the Fast Pwm mode (on college and outside of it) in order to control brightness / fans / etc. With this, I suppose CTC mode is the way to go (?)

Attachment(s): 

Last Edited: Sun. Dec 29, 2019 - 06:41 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Jonaas18 wrote:
The display kinda gets messed up when the delay functions are removed.

That's probably because the ULN can't respond so fast. Try to set the delay to 1ms instead of removing it completely.

 

 

Jonaas18 wrote:
Should I write everything present in the while loop inside the interrupt function

No. Set up a volatile global variable with the current position on the array, for example "uint8_t index". Then the ISR just does one line at a time i. e. PORTD = (1<<index); PORTA = array1

; nothing more, and then increments "index" to the next time the ISR is called. Also add if (index == 8) index = 0; in the ISR to make it cycle again to the beginning.

 

Jonaas18 wrote:
I suppose CTC mode is the way to go (?)

It doesn't really matter what mode you use, as long as a periodic interrupt gets generated.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

That's probably because the ULN can't respond so fast. Try to set the delay to 1ms instead of removing it completely.

U were right. With a 1ms delay inbetween the tasks it shows the pattern properly!

 

No. Set up a volatile global variable with the current position on the array, for example "uint8_t index". Then the ISR just does one line at a time i. e. PORTD = (1<<index); PORTA = array1

; nothing more, and then increments "index" to the next time the ISR is called. Also add if (index == 8) index = 0; in the ISR to make it cycle again to the beginning.

Got it. Coded an exactly 1ms interrupt. The matrix displays the array properly! Pic attached.

#include <avr/io.h>
#include <stdlib.h>
#include <stdio.h>
#include <avr/interrupt.h>
#include <stdint.h>

#define LED 1
#define F_CPU 8000000UL                 // 8Mhz Internal Oscillator.
#include <util/delay.h>

volatile unsigned char count = 30;		// In order to have a blinking led with 1s timer.
volatile unsigned char led;				// Verifies if LED is turned ON or OFF.
volatile unsigned char pos = 0;
volatile unsigned int array1[8] = {194,0,255,3,128,255,0,96};

void inic (void) {
	DDRA = 0xFF;						// Defined as outputs.
	DDRD = 0xFF;						// Defined as outputs.
	DDRC = 0x01;						// PC0 defined as output (blinking led).
	DDRB = 0x0F;						// First 4 pins defined as outputs (buzzer and shift register) and last 4 pins defined as inputs (switches).

	TCCR0 |= (1<<WGM01)|(0<<COM01)|(0<<COM00)|(1<<CS01);						// CTC Mode with normal port operation. T = [8/8MHz] * (OCR0 + 1).
	OCR0 = 99;

	TCCR2 |= (1<<WGM21)|(0<<COM21)|(0<<COM20)|(1<<CS22)|(1<<CS21)|(1<<CS20);	// CTC Mode with normal port operation. T = [1024/8MHz] * (OCR2 + 1).
	OCR2 = 255;

	TIMSK |= (1<<OCIE0)|(1<<OCIE2);				// Timer/Counter Compare Match interrupt enabled.

	sei();
}

ISR(TIMER0_COMP_vect) {

	if (pos == 8)									// Means it has displayed the 8 values of the array.
		pos = 0;									// Resets value so it can loop the code again.
	else {
		PORTD = (1<<pos);
		PORTA = array1[pos];
		pos++;
	}
}

ISR(TIMER2_COMP_vect) {											// Led blink interruption

	count--;													// Counts 500ms
	led = (PORTC & LED);										// Blinking LED

	if (count==0 && led==0) {									// Count = 15 by default. 32.7ms * 15 = 491ms. Half a second on, Half a second off. Comes here if led is turned off
		PORTC |= LED;											// LED ON
		count = 30;												// Reset counter
	}

	if (count==0 && led==1) {									// Comes here if led is turned on
		PORTC &= ~LED;											// LED OFF
		count = 30;												// Resets counter
	}
}

int main (void) {
	inic();

	while (1) {
	}
}

 

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Good work.

 

The LCD ISR is running at 10kHz, right? So 10kHz/8 gives 1250Hz screen refresh rate, you can slow it down much more if you want to. As you know, PC monitors refresh at 60-100Hz which is enough to fool our eyes into seeing a solid image.

 

Anyway, I just wanted to show you how to offload work to ISRs working behind the scenes, so that you leave main() uncluttered for what really matters, which is the game code.

The game code can just access display memory (that is, "array1") and the ISR will automatically display whatever is there.

 

Now you can offload button detection to an ISR also, that then sets some variables depending on the button states, if it was pressed or released, etc. and handles debouncing behind the scenes.

The game code just reads these variables and reacts accordingly.

 

Next step, maybe build a test program that just moves a dot around the screen, controlled by the buttons, and you will be well on your way to complete the project.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Now you can offload button detection to an ISR also, that then sets some variables depending on the button states, if it was pressed or released, etc. and handles debouncing behind the scenes.

The game code just reads these variables and reacts accordingly.

 I was trying to make a simple code to test a button press but it's doing the opposite.

 

DDRB = 0x0F;						// First 4 pins defined as outputs (buzzer and shift register) and last 4 pins defined as inputs (switches).

int main (void) {
	inic();

	while (1) {
		if (PINB & (1<<PINB4)) {			// Switch pressed.
			PORTD = 0xFF;
			PORTA = 0xFF;
		}
		else {
			PORTD = 0x00;
		}
	}
}

In my mind, when the button placed on PB4 is pressed, the matrix would light up. Otherwise, it wouldn't (PORTD = 0x00) but it's doing the exact opposite.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

How is your button wired?

from port pin to VCC with pull down resistor to gnd (port reads 0 until button is pressed then 1) or..

 

from port pin to gnd with pull up resistor to vcc (port reads 1 until button pressed then 0)?

 

Jim

 

Click Link: Get Free Stock: Retire early! PM for strategy

share.robinhood.com/jamesc3274
get $5 free gold/silver https://www.onegold.com/join/713...

 

 

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

How is your button wired?

The button was broken (somehow). I replaced it with another one and it's working properly. I'm connecting it by wiring one side of the button to VCC and the other one (which connects to the MCU port) to GND through a 1k resistor.

 

 

Now you can offload button detection to an ISR also, that then sets some variables depending on the button states, if it was pressed or released, etc. and handles debouncing behind the scenes.

The game code just reads these variables and reacts accordingly.

 

Next step, maybe build a test program that just moves a dot around the screen, controlled by the buttons, and you will be well on your way to complete the project.

 I built a code, which I think is correct, though the program isn't working properly. My guess is that due to the fact that the ISR has a 1ms delay inbetween repetitions, pressing a button once equals to pressing a button a thousand times (given the fact that I can't press the button in less than 1 ms). Tried to troubleshoot it by placing some code lines on the main function etc. etc. but nothing changed:

 

volatile unsigned int array1[8] = {1,2,4,8,16,32,64,128};
volatile unsigned char posy = 4;

void inic (void) {
	DDRA = 0xFF;						// Defined as outputs.
	DDRD = 0xFF;						// Defined as outputs.
	DDRC = 0x01;						// PC0 defined as output (blinking led).
	DDRB = 0x0F;						// First 4 pins defined as outputs (buzzer and shift register) and last 4 pins defined as inputs (switches).

	TCCR0 |= (1<<WGM01)|(0<<COM01)|(0<<COM00)|(1<<CS01);						// CTC Mode with normal port operation. T = [8/8MHz] * (OCR0 + 1).
	OCR0 = 99;

	TIMSK |= (1<<OCIE0)|(1<<OCIE2);					// Timer/Counter Compare Match interrupt enabled.

	sei();
}

ISR(TIMER0_COMP_vect) {

		if (PINB & (1<<PINB4)) {				// Switch 1 (up) pressed.
			posy++;
			if (posy > 8)
				posy = 8;
			PORTA = array1[posy];
		}

		if (PINB & (1<<PINB5)) {				// Switch 2 (down) pressed.
			posy--;
			if (posy < 0)
				posy = 0;
			PORTA = array1[posy];
		}
}

int main (void) {
	inic();

	PORTD = (1<<3);
	PORTA = array1[posy];

	while (1) {

	}
}

 

Edit1: I'm stupid.. I should have created a new interrupt in order to read button presses right? Or a function, since I suppose it's easier.

Last Edited: Mon. Dec 30, 2019 - 08:00 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It's very common that you don't want an action taking place continuously while a button is pressed. Normally, you just want the action to take place when the button changes state, that is, when it is pressed or when it is released.

 

One way you can do this, is if the ISR uses some variable to remember the previous state of the button, and if it changes, then that means the button was pressed (or released).

In that case, the ISR sets some flag that indicates the button was pressed, for example.

 

Then, the main program can detect that flag and take whatever action is needed. When the action is complete, you clear the flag, so that the action happens only once per button press.

 

A good test program for buttons is to toggle a LED each time a button is pressed (or released according to preference).

 

edit: of course in your game maybe you really want a continuous action, in that case you don't need a new ISR or you soon will run out of timers. One way is you can use a counter, so for example, even if the ISR runs 1000 times per second, you can count to 200 or so so that the action only happens 5 times per second.

Last Edited: Mon. Dec 30, 2019 - 08:23 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Oh right. Totally forgot about interrupt flags. In order to do so, should I create an external interrupt in order to read button state, etc.?

 

While waiting for a reply, I decided to look at my code and figure out what was wrong. I managed to fix it and it's working properly now with the help of a function readButton();. Don't know if this is ok to use or if the ISR is a must.

 

#define F_CPU 8000000UL                 // 8Mhz Internal Oscillator.
#include <util/delay.h>

volatile unsigned int array1[8] = {1,2,4,8,16,32,64,128};
volatile signed char posy = 4;
volatile unsigned int btn1 = 0;
volatile unsigned int btn2 = 0;

void inic (void) {
	DDRB = 0x0F;						// First 4 pins defined as outputs (buzzer and shift register) and last 4 pins defined as inputs (switches).

	TCCR0 |= (1<<WGM01)|(0<<COM01)|(0<<COM00)|(1<<CS01);						// CTC Mode with normal port operation. T = [8/8MHz] * (OCR0 + 1).
	OCR0 = 99;
	TIMSK |= (1<<OCIE0)|(1<<OCIE2);					// Timer/Counter Compare Match interrupt enabled.

	sei();
}

ISR(TIMER0_COMP_vect) {

	if (btn1 == 1) {
		if (posy > 7)
			posy = 7;
		PORTA = array1[posy];
		btn1 = 0;
	}

	if (btn2 == 1) {
		if (posy < 0)
			posy = 0;
		PORTA = array1[posy];
		btn2 = 0;
	}
}

void readButton (void) {
	if (PINB & (1<<PINB4)) {			// Switch 1 (up) pressed.
		btn1 = 1;
		posy++;
	}

	if (PINB & (1<<PINB5)) {			// Switch 2 (down) pressed.
		btn2 = 1;
		posy--;
	}
}

int main (void) {
	inic();

	PORTD = (1<<3);
	PORTA = array1[posy];

	while (1) {
		_delay_ms(200);
		readButton();
	}
}

 

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You haven't quite understood the point of this exercise.

 

You need to use this as the ISR (from post #81):

 

ISR(TIMER0_COMP_vect) {

	if (pos == 8)									// Means it has displayed the 8 values of the array.
		pos = 0;									// Resets value so it can loop the code again.
	else {
		PORTD = (1<<pos);
		PORTA = array1[pos];
		pos++;
	}
}

 

Then, move the dot by writing to the array. That is, the array starts as {4,0,0,0,0,0,0,0} for example. This will set one dot. Then you can move the dot by making the array {0,4,0,0,0,0,0,0}, {0,0,4,0,0,0,0,0} etc. (movement in one axis) or {8,0,0,0,0,0,0,0}, {16,0,0,0,0,0,0,0}, {32,0,0,0,0,0,0,0} etc. (movement in the other axis).

 

Do it without buttons first, just make the dot go one side to the other, then back continuously.

Last Edited: Mon. Dec 30, 2019 - 09:16 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Oh alright. My bad I wasn't understanding, sorry.

 

Thing is, If I'm going to use that ISR (1ms interrupt), the matrix just stays the same (the refresh time is so High that I can't see the ball moving). If I change the duration (prescaler from 8 -> 1024, OCR0 from 99->255) I can now see the ball moving in the required axis.

 

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok. I guess I have to demonstrate with code since words are not working. Here, see if this works, it's just a small change from your code:

 

#include <avr/io.h>
#include <stdlib.h>
#include <stdio.h>
#include <avr/interrupt.h>
#include <stdint.h>

#define LED 1
#define F_CPU 8000000UL                 // 8Mhz Internal Oscillator.
#include <util/delay.h>

volatile unsigned char count = 30;		// In order to have a blinking led with 1s timer.
volatile unsigned char led;				// Verifies if LED is turned ON or OFF.
volatile unsigned char pos = 0;
volatile unsigned int array1[8] = {0,0,0,0,0,0,0,0};

void inic (void) {
    DDRA = 0xFF;						// Defined as outputs.
    DDRD = 0xFF;						// Defined as outputs.
    DDRC = 0x01;						// PC0 defined as output (blinking led).
    DDRB = 0x0F;						// First 4 pins defined as outputs (buzzer and shift register) and last 4 pins defined as inputs (switches).

    TCCR0 |= (1<<WGM01)|(0<<COM01)|(0<<COM00)|(1<<CS01);						// CTC Mode with normal port operation. T = [8/8MHz] * (OCR0 + 1).
    OCR0 = 99;

    TCCR2 |= (1<<WGM21)|(0<<COM21)|(0<<COM20)|(1<<CS22)|(1<<CS21)|(1<<CS20);	// CTC Mode with normal port operation. T = [1024/8MHz] * (OCR2 + 1).
    OCR2 = 255;

    TIMSK |= (1<<OCIE0)|(1<<OCIE2);				// Timer/Counter Compare Match interrupt enabled.

    sei();
}

ISR(TIMER0_COMP_vect) {

    if (pos == 8)									// Means it has displayed the 8 values of the array.
        pos = 0;									// Resets value so it can loop the code again.
    else {
        PORTD = (1<<pos);
        PORTA = array1[pos];
        pos++;
    }
}

ISR(TIMER2_COMP_vect) {											// Led blink interruption

    count--;													// Counts 500ms
    led = (PORTC & LED);										// Blinking LED

    if (count==0 && led==0) {									// Count = 15 by default. 32.7ms * 15 = 491ms. Half a second on, Half a second off. Comes here if led is turned off
        PORTC |= LED;											// LED ON
        count = 30;												// Reset counter
    }

    if (count==0 && led==1) {									// Comes here if led is turned on
        PORTC &= ~LED;											// LED OFF
        count = 30;												// Resets counter
    }
}

int main (void) {
    inic();

    int8_t index1 = 0;
    int8_t dir = 1;
    while (1) {
        array1[index1] = 4;   //display dot
        _delay_ms(200);
        array1[index1] = 0;   //clear dot
        if (index1 == 0)
            dir = 1;
        if (index1 == 7)
            dir = -1;
        index1 += dir;
    }
}

 

Last Edited: Mon. Dec 30, 2019 - 11:13 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok. I guess I have to demonstrate with code since words are not working. Here, see if this works, it's just a small change from your code:

Oh... This simple lol. I'm sorry... Makes sense. While I'm changing the array

values on the main function, the ISR is displaying the array with the PORTA = xxx function. Got it. Will tweak the code so I can interact with switches! 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Jonaas18 wrote:

While I'm changing the array values on the main function, the ISR is displaying the array

 

Exactly. The ISR does it's work in the background and you only need to focus on manipulating the array. This memory that is rendered automatically to a display is called a frame buffer.

In this case, the frame buffer is 8 bytes, that is, 8x8 = 64 bits. Each bit corresponds to a point on the LED matrix.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Alright, nice to know since I'll have to talk about it on my presentation! One doubt:

 

Should I code the buttons (i.e., sw1 equals to the ball moving right, sw2 equals to the ball moving left, sw3 moving up, sw4 moving down) on another function or on main?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I tweaked the code a bit and managed to make a simple code in which, by pressing a switch (PINB4), the ball would move back and forth: 

 

#include <avr/io.h>
#include <stdlib.h>
#include <stdio.h>
#include <avr/interrupt.h>
#include <stdint.h>
#include <math.h>

#define LED 1
#define F_CPU 8000000UL                 // 8Mhz Internal Oscillator.
#include <util/delay.h>

volatile unsigned char count = 30;        // In order to have a blinking led with 1s timer.
volatile unsigned char led;                // Verifies if LED is turned ON or OFF.
volatile unsigned char pos = 0;
volatile unsigned int array1[8] = {0,0,0,0,0,0,0,0};
volatile unsigned int array2[8] = {0,0,0,0,0,0,0,0};
volatile unsigned int btn1 = 0;
volatile unsigned int btn2 = 0;
volatile unsigned int btn3 = 0;
volatile unsigned int btn4 = 0;
volatile unsigned int index1 = 0;
volatile signed int dir = 1;

void inic (void) {
    DDRA = 0xFF;                        // Defined as outputs.
    DDRD = 0xFF;                        // Defined as outputs.
    DDRC = 0x01;                        // PC0 defined as output (blinking led).
    DDRB = 0x0F;                        // First 4 pins defined as outputs (buzzer and shift register) and last 4 pins defined as inputs (switches).

    TCCR0 |= (1<<WGM01)|(0<<COM01)|(0<<COM00)|(1<<CS01);                        // CTC Mode with normal port operation. T = [8/8MHz] * (OCR0 + 1).
    OCR0 = 99;

    TCCR2 |= (1<<WGM21)|(0<<COM21)|(0<<COM20)|(1<<CS22)|(1<<CS21)|(1<<CS20);    // CTC Mode with normal port operation. T = [1024/8MHz] * (OCR2 + 1).
    OCR2 = 255;

    TIMSK |= (1<<OCIE0)|(1<<OCIE2);                // Timer/Counter Compare Match interrupt enabled.

    sei();
}

ISR(TIMER0_COMP_vect) {

    if (pos == 8)                                    // Means it has displayed the 8 values of the array.
        pos = 0;                                    // Resets value so it can loop the code again.
    else {
        PORTD = (1<<pos);
        if (btn1 == 1) {
            PORTA = array1[pos];
            pos++;
        }
        if (btn2 == 1)
            PORTA = array1[pos];
    }
}

ISR(TIMER2_COMP_vect) {                                            // Led blink interruption

    count--;                                                    // Counts 500ms
    led = (PORTC & LED);                                        // Blinking LED

    if (count==0 && led==0) {                                    // Count = 15 by default. 32.7ms * 15 = 491ms. Half a second on, Half a second off. Comes here if led is turned off
        PORTC |= LED;                                            // LED ON
        count = 30;                                                // Reset counter
    }

    if (count==0 && led==1) {                                    // Comes here if led is turned on
        PORTC &= ~LED;                                            // LED OFF
        count = 30;                                                // Resets counter
    }
}

void readButton (void) {
    
    if (PINB & (1<<PINB4))             // RIGHT pressed.
    btn1 = 1;


    if (PINB & (1<<PINB5))            // UP pressed.
    btn2 = 1;
}

void move (void) {
    
    int8_t x = 1;
    
    if (btn1 == 1) {
        array1[index1] = x;
        _delay_ms(100);
        array1[index1] = 0;
            
        if (index1 == 0)
            dir = 1;
        if (index1 == 7)
            dir = -1;
        index1+= dir;
    }
    
    if (btn2 == 1) {
        x = 2*x;
        array1[index1] = x;
        
        if(x == 128)
            x = 1;
    }
}
int main (void) {
    

    inic();

    while (1) {
        _delay_ms(200);
        readButton();
        move();        
    }
}

I was trying to create the code for another button (i.e., instead of changing directions in one axis, it changes directions in the other one). It's not working properly and I can't seem to figure out how to troubleshoot it. It has to be something wrong with the ISR / move() code.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Here we go again...

 

ISR(TIMER0_COMP_vect) {

    if (pos == 8)                                    // Means it has displayed the 8 values of the array.
        pos = 0;                                    // Resets value so it can loop the code again.
    else {
        PORTD = (1<<pos);
        if (btn1 == 1) {
            PORTA = array1[pos];
            pos++;
        }
        if (btn2 == 1)
            PORTA = array1[pos];
    }
}

Man! Keep things separate! This ISR should just handle the display, not react to buttons! Do you think in you PC, the keyboard is connected directly to your graphics card? That your graphics card reacts to keyboard presses directly?

No, of course not. That would be absurd, right?

Everything goes through the CPU. In your case, everything has to go through main().

So just keep the ISR like this

ISR(TIMER0_COMP_vect) {

	if (pos == 8)									// Means it has displayed the 8 values of the array.
		pos = 0;									// Resets value so it can loop the code again.
	else {
		PORTD = (1<<pos);
		PORTA = array1[pos];
		pos++;
	}
}

Forever! OK? It's working, doing it's one and only job, so don't touch it any more, ever. Unless you want to optimize it or something.

 

Buttons should also have an ISR, I think the LED ISR should also handle buttons.

If a button is pressed for a certain time, then the button ISR sets a flag.  You achieve this by setting a counter for each button, similar to the counter you are using to blink the LED. The counter increases for as long as the button is pressed and when it reaches a value that corresponds to, let's say, 200ms, a button action is generated (that is, you write 1 to btn1 or btn2 or whatever) and the counter is reset to zero. If the button is released, the counter is also reset to zero.

 

The main() function detects this by waiting in a loop until any of btn1/2/3/4 becomes 1.

Then it takes the proper action (i.e. if btn1 then action1, if btn2 then action2, etc.), that is moving the ball by writing new values to the frame buffer. And then it writes 0 to the button flag that was processed, so that it can wait again for it to become 1.

 

I strongly recommend that you write functions set_pixel(x,y) and clear_pixel(x,y). These functions shall take (x,y) coordinates on the LED matrix, and set or clear the corresponding point by manipulating the frame buffer.

Last Edited: Tue. Dec 31, 2019 - 01:21 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

My mind is blowing right now. I understand what you mean but I'm struggling hard to make anything out of it. I have no idea how to code set_pixel / clear_pixel while still make them interact with the ISR function.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Jonaas18 wrote:
while still make them interact with the ISR function.

 

Look again at the code in #90. You tested it? It works, I hope. You should see a point moving back and forth. So how is this happening, if the ISR function is unchanged?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You should see a point moving back and forth.

 Yes, exactly.

 

So how is this happening, if the ISR function is unchanged?

While the ISR function is only reading the array1 values (via the counter pos) and sending them to PORTA / PORTD, I'm changing the array1 values in the main function with the help of the lines: 

 

array1[index1] = 4;                     //display dot
        _delay_ms(200);
        array1[index1] = 0;             //clear dot        
index1 += dir;
        

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0


Exactly, that's why you never need to change the ISR, only the array.

 

The screen is mapped like

 

 

So, if you want to set a point (x,y) you can do array[y] |= 1 << x; and if you want to clear a point, you can do array[y] &= ~(1 << x); but it's better to have this code in functions that are called when needed.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So, if you want to set a point (x,y) you can do array[y] |= 1 << x; and if you want to clear a point, you can do array[y] &= ~(1 << x); but it's better to have this code in functions that are called when needed.

 I think I got it. I will try to code both set() and clear() functions according to your explanation! 

Pages