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.
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.
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.
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).
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?
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).
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). ...
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.
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.
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);
}
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 itselfto 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!
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.
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.
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.
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.).
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.
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.
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.
}
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.
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.
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.
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);
}
}
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.
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 (?)
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.
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) {
}
}
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.
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.
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.
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.
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();
}
}
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.
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.
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;
}
}
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!
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.
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?
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.
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.
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.
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?
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:
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.
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!
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.
- Log in or register to post comments
TopConsidering 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.
- Log in or register to post comments
TopIf 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.
- Log in or register to post comments
TopBut 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).
- Log in or register to post comments
TopOk. What kind of joystick do you want to use? I will admit my knowledge of joystics is zero.
- Log in or register to post comments
TopI 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?
- Log in or register to post comments
TopShared Use of SPI Programming Lines | AVR® Microcontroller Hardware Design Considerations
"Dare to be naïve." - Buckminster Fuller
- Log in or register to post comments
TopAlright. 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).
- Log in or register to post comments
Top"Dare to be naïve." - Buckminster Fuller
- Log in or register to post comments
TopHaving 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.
- Log in or register to post comments
TopAfter 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:
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.
- Log in or register to post comments
TopI hope you didn't connect it directly without the current limiting resistor. You risk burning the LED if you connect it directly as described.
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:
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:
Finally, you initialization function doesn't execute automatically. You need to call it from main():
- Log in or register to post comments
TopNo worries, I used a 2.2k resistor in order to avoid that!
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.
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!
My bad, that's a dumb mistake lol. Thanks for spotting it man!
- Log in or register to post comments
TopYes.
- Log in or register to post comments
TopAlright... 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.
- Log in or register to post comments
TopUpdate:
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!
Attachment(s):
- Log in or register to post comments
TopSee 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.
- Log in or register to post comments
TopSadly 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.).
- Log in or register to post comments
TopSo 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.
- Log in or register to post comments
TopGot 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.
- Log in or register to post comments
TopNobody 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.
- Log in or register to post comments
TopI'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:
Everything went well.
- Log in or register to post comments
TopOk. 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.
- Log in or register to post comments
TopWill 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).
Got it, will do! I'll post a video when I'm done.
- Log in or register to post comments
TopIt 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.
- Log in or register to post comments
TopGiven this code, I obtained the results shown in video 1:
Included another video (2) as well!
Attachment(s):
- Log in or register to post comments
TopCool. 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.
- Log in or register to post comments
TopThe 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.
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):
- Log in or register to post comments
TopThat's probably because the ULN can't respond so fast. Try to set the delay to 1ms instead of removing it completely.
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.
It doesn't really matter what mode you use, as long as a periodic interrupt gets generated.
- Log in or register to post comments
TopU were right. With a 1ms delay inbetween the tasks it shows the pattern properly!
Got it. Coded an exactly 1ms interrupt. The matrix displays the array properly! Pic attached.
Attachment(s):
- Log in or register to post comments
TopGood 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.
- Log in or register to post comments
TopI was trying to make a simple code to test a button press but it's doing the opposite.
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.
- Log in or register to post comments
TopHow 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
(Possum Lodge oath) Quando omni flunkus, moritati.
"I thought growing old would take longer"
- Log in or register to post comments
TopThe 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.
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:
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.
- Log in or register to post comments
TopIt'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.
- Log in or register to post comments
TopOh 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.
Attachment(s):
- Log in or register to post comments
TopYou haven't quite understood the point of this exercise.
You need to use this as the ISR (from post #81):
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.
- Log in or register to post comments
TopOh 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.
- Log in or register to post comments
TopOk. 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:
- Log in or register to post comments
TopOh... This simple lol. I'm sorry... Makes sense. While I'm changing the array
- Log in or register to post comments
TopExactly. 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.
- Log in or register to post comments
TopAlright, 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?
- Log in or register to post comments
TopI 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:
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.
- Log in or register to post comments
TopHere we go again...
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
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.
- Log in or register to post comments
TopMy 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.
- Log in or register to post comments
TopLook 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?
- Log in or register to post comments
TopYes, exactly.
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:
- Log in or register to post comments
TopExactly, 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.
- Log in or register to post comments
TopI think I got it. I will try to code both set() and clear() functions according to your explanation!
- Log in or register to post comments
TopPages