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

Go To Last Post
100 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello everyone. Long time lurker here! I'm on my senior year at college and, in one subject, I have to create a working prototype of something (literelly anything, going from a capsule dispensor, to a AGV, to a working solar panel circuit, etc.). I got really interested in some projects (since it's basically the reason why I applied to this degree in the first place) and decided to go with a gaming console (my second choice was the AGV - Automated Guided Vehicle - but I figured that it would be more expensive and harder to understand - by this I mean I don't really have a clear idea of where to start or what to do - than the console).

 

With this, a gaming console is something I'm really hyped to build. I made a quick research in order to see if someone had already thought about this and found lots of information on this topic (my first step in this project is to write a research paper aka State of the Art and talk about similar projects and working prototypes). 

 

Apart from that, my biggest "issue" is creating the project itself (not to mention the creation of PCB's and the working prototype). For my research paper, I'll write about a gaming console named MakerBuino by CircuitMess since this whole project is inspired on that. 

From what I could tell, everything is open source since the company itself promotes a DIY approach from students (aswell as offering a purchasable console through their website). With this, I'm not going to copy everything and simply build it together. My idea is to code everything myself (I have no idea whether that's possible or not) and inspire my working prototype design on the MakerBuino one (I loved it!).

 

I would like to note that in order to build a MakerBuino, the company provides the PCB schematics as well as the console bootloader. I can't use the bootloader since it's something I didn't code. My idea would be to create a simple program (i.e. starting with a switch/case menu to alternate between games that, later on, I would code [I REALLY don't think creating games like Snake / Space Invaders / Tetris is something hard given that there are LOTS of examples in the internet but I could be massively wrong]). 

After having the code ready, my idea would be to sketch my PCB's according to the MakerBuino ones (after understanding its' circuit and usage of components) and create the console itself. If what I said earlier (about beeing simple to code a switch/case menu with games) is true, I guess that my biggest issue (coding wise) is to make all this work in a monitor (it can be a computer monitor, a small LCD display, etc... MakerBuino uses a Nokia 5110 LCD). 

 

I would also like to say that even though this my last college year, I'm by no means an expert in programming / creating circuits. I've never messed with PCB's nor created a working prototype like this. I mainly learn C at college though C++ isn't really that much different. I retained some HTML programming skills as well. Important to state that this whole project must run / use an ATMega (must also have a blinking LED - which is easily coded).

 

Would love some insight on how to start a big project like this and I'm all ears to ideas / suggestions about this or the above mentioned projects.

 


Note: Besides MakerBuino, I've also found a series of videos on how to create a gaming console (it only has one game from what I can see) on youtube. Part 1 link is here. There's a total of 4 parts. I have no idea if this the right approach / a different one than the one I talked about earlier. Worth nothing this was coded on C++.


 

Edit1:  I'm due to deliver this project on the first week of January. Besides that, when I say "gaming console" I'm talking about an Arcade Console to play simple games like Snake or Tetris. Nothing particularly pretty or in need of 3D graphics.

Last Edited: Sat. Oct 12, 2019 - 09:58 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Welcome to the Forum.

 

What a great project.

 

I'll share a few thoughts, I'm sure others will pitch in.

 

1)  Make it doable within the timeframe and experience level you have.

2)  A Nokia 5110 is a great little display, and is easily interfaced to a Mega or Xmega.  There are plenty of libraries for writing to it, or you can write your own driver, which is in and of itself a reasonable undertaking.

3)  I would suggest you focus on making a one game console at the moment.  (See #1, above).

The micro has a limited amount of memory, it isn't a PC or an ARM.  So if you were to have multiple games one would need a method to load them off an SD card, (or whatever), and that is an entirely  new level of complexity if you don't have a lot of micro projects already under your belt.  "Loading" a new game is itself open to several different approaches, and again some would not be trivial.

4)  Once you have your project, single game version, up and running, then if you have additional time you can write a second game.  Then think about loading it on a second board, or think about project Version #2, and how to approach multiple games.

5)  Hardware wise you will need your GLCD and user interface buttons, (up/down/left/right/fire, etc.).

One can use individual push button switches, or one can use mini-"joystick" push button switches, (five function, directional and push).  Don't mess with trying to interface to a USB driven controller, that is itself another non-trivial task.

6)  Pick a really big (pins and memory) micro.  Interfacing to the GLCD (Nokia 5110), multiple PB Switches, and a piezo beeper, and an LED or two or three, and whatever else gets added, will be much easier when you are not I/O pin constrained.  Likewise, you wants lots of memory.

 

7)  You might consider using an Arduino for your base hardware platform, if allowable.  You can still program it in any language you wish.  If you decide to go with your own circuit design and PCB, (both good learning experiences), you can certainly post your schematic here for lots of input before you get a board made.

 

8)  Good luck with your project!

 

  JC

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

Hi there. Thanks a lot for quick and helpful response!

 

1)  Make it doable within the timeframe and experience level you have.

3)  I would suggest you focus on making a one game console at the moment.  (See #1, above).

 Will do! Is starting with a menu and coding a game (in C) the right approach (like explained in the OP)?

5)  Hardware wise you will need your GLCD and user interface buttons, (up/down/left/right/fire, etc.).

One can use individual push button switches, or one can use mini-"joystick" push button switches, (five function, directional and push).

Already searched ebay / gearbest / amazon and found lots of components for pennies. My idea was to use individual PB switches rather than the joystick. Simpler (for me) to understand.

6) Pick a really big (pins and memory) micro.

The micro has a limited amount of memory, it isn't a PC or an ARM

Any suggestions? I've only worked with versions 88, 128 and 328 of the ATMega component. Don't recall every datasheet but I have the idea that the 328 is faster (clock speed) and has more memory than the others. Nonetheless, there are thousands of versions that I don't know anything about.

  a piezo beeper

I forgot to add this since that would elevate the project into a new level. It's a really great idea though I'm not sure how to play audio with a C code (I guess that's something easily explained in 1 or 2 youtube videos).

7)  You might consider using an Arduino for your base hardware platform, if allowable.

I own an Arduino but I don't understand what you meant by this (probably translation issues). If you're talking about using the ARDUINO chip instead of the ATMega one, I can't since Arduino has already developed multiple functions that I can't use (i.e., in arduino I can read an analog value with a simple function, while in ATMega we don't do that that way). (At least, by talking to my teacher, this is what I understood... Arduino is allowable ONLY if you code the functions yourself).

If you're talking about the board itself, I also have the board with all its' pins (Arduino UNO).

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

This is an ambitious project, but if you break it down and tackle each part it is doable. 
I would use an Arduino mega, as it gives you plenty of flash and ram, you will need it for the graphics!

find a display shield and your hardware is mostly complete, just add your buttons and speaker/ buzzer.
although you can not use Arduino libs for your final project, there is no problem using it to test your hardware and connections, this will remove much work and troubleshooting, as any problems will be in your code.
As suggested, pick one game, keep it simple, you can always expand if time permits.

come back here if you get stuck.

good luck

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

jona_ wrote:
I guess that my biggest issue (coding wise) is to make all this work in a monitor (it can be a computer monitor, ... Important to state that this whole project must run / use an ATMega ...
May you consider XMEGA?

XMega cranks out NTSC color and digital stereo sound! | AVR Freaks by AtomicZombie

jona_ wrote:
I've never messed with PCB's ...
Some universities have some form of PCB fab; likewise, Makerspaces and some city libraries.

jona_ wrote:
... nor created a working prototype like this.
The first prototype is the first PCBA; its precursor is a proof-of-concept (PoC, iow a bench test)

PoC is off-the-shelf parts patched together; PoC is one way to do risk reduction.

jona_ wrote:
I mainly learn C at college ...
Excellent and thank you!

jona_ wrote:
... though C++ isn't really that much different.
Turing completeness is a characteristic of part of C++.

AVR metaprogramming (C++, article) | AVR Freaks

 


Learning C and C++ can begin in secondary education via a C/C++ interpreter, course work, and a course plan.

UC Davis Center for Integrated Computing and STEM Education

UC - University of California

 

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

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

Hi! Thanks for the quick reply.

find a display shield

I searched for shields on the internet like you mentioned but there are many models and they're all different. Mind linking a basic one or telling me what should I keep an eye on to differentiate each of them? 

I would use an Arduino mega

although you can not use Arduino libs for your final project, there is no problem using it to test your hardware and connections, this will remove much work and troubleshooting

What's the difference between using an ATMega on a breadboard or, in this example, an Arduino Mega? What connections are you talking about?

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

May you consider XMEGA?

From what I know, XMEGA's are more complete ports/pin wise. Besides that, is there a lot of difference programming ATMegas and XMEGAS? In terms of programming ports and creating loops / interrupts / messing with timers.

 

Some universities have some form of PCB fab; likewise, Makerspaces and some city libraries

Exactly. Mine builds costum PCB's with the schematics we need. I've also seen some online stores (mainly one named JLCPCB, not sure what's the difference between ordering from them or ordering in my local college though).

The first prototype is the first PCBA; its precursor is a proof-of-concept (PoC, iow a bench test)

 

PoC is off-the-shelf parts patched together; PoC is one way to do risk reduction

Alright this is great! Thanks for specifying the different stages of the project. Will help me a lot with scheduling and knowing what to do within the time frame. It's also great since I'll need to make an article on the final prototype and I can now call the different prototypes by their actual name!

 

Thank you! 

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

I can’t recommend a display as I don’t know your gaming system specs!!

As for the diff between proven hardware ( Arduino Mega) and a bare chip on a bread board, night and day.

what is your degree of study?   hardware EE, or software CS?  Are you developing your hw design skills, or software dev skills?  If the latter, then using proven hw, ie Arduino mega with a compatible display shield gives you a working platform to start with, if the former, then study the proven design of the Arduino such as a Nano as the basis to start your journey.

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

I can’t recommend a display as I don’t know your gaming system specs!!

Right. Dumb question, sorry.

 

  what is your degree of study?

 I'm studying Electrotechnical Engineering which I'm not entirely sure that it's the same as EE. Nonetheless, it's something similar. Besides that, the course / degree is more oriented towards software rather than hardware. It's a mix of both given that I have to create both programs AND physical circuits. Perhaps that's the reason why I don't know in depth characteristic about certain things.

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

Suggest you discuss your wants with you professor, they can guide you to resources you will need for your project. Remember the ram size on an AVR is limited so a graphics display will be small, you can’t do 1024 x 4096 w/ 1M colors, do some Google  searching for similar projects, see what they used, download it and take it apart to see how they did it to get ideas on how your project can work, research can be fun!

 

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...

 

 

 

 

Last Edited: Sat. Oct 12, 2019 - 09:16 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Will do. Thanks a lot for your suggestions. I'll update later on this month but you've helped me massively. Cheers laugh

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

jona_ wrote:
From what I know, XMEGA's are more complete ports/pin wise.
... and compute and memory and power (bounds : compute, memory, I/O, power)

XMEGA :

  • 32MHz CPU frequency (vs 20MHz)
  • 32KB local RAM (XMEGA384C3, XMEGA384D3 vs 16KB in mega1284P)
  • 24b addressing in program and data spaces
  • DMA (most XMEGA)
  • PicoPower is shared with P, PA, and PB megaAVR

jona_ wrote:
... is there a lot of difference programming ATMegas and XMEGAS?
Yes

jona_ wrote:
... not sure what's the difference between ordering from them or ordering in my local college though).
Regional PCB fabs typically have discounts for students.

A university's PCB fab may be zero price or very low price for students, faculty, and staff.

 


AN_8075 AVR1000: Getting Started Writing C-code for XMEGA via ATxmega384C3 - 8-bit AVR Microcontrollers

 

Universities have access to zero price IC (die fab, die packaging)

A university student can design and test a zero price IC.

MOSIS Integrated Circuit Fabrication Service | University Support Program

 

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

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

Alright. I'll discuss with my teacher whether or not using XMEGA's is a good idea given that, if programming is a lot different, I might not have the time to finish everything / will take longer to write my code since it won't be that simple.

 

Thank you for the suggestions. I'll update later on this month!

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

Gaming might not be the best thing to jump right into...depending on graphics...are you a graphics programmer? There's a lot to generating real-time graphics, also the processing memory needs, speed, working with displays in general & the game itself add up to quite an effort.  Do you need 3D effects? This would all be a tough job, even if you already had a lot of programming ,experience, display experience, etc.  This is not to scare you off, just realize that if you are creating your OWN software, then it is much tougher than making a burglar alarm, or automatic bird feeder. How much real time to you have to spend?  You may regret when you only have 2 days left & little to show.

 

Rule of thumb: Do something you think will take 2 weeks "if all goes well", then double that...if it is something you've never done even once, double it again!

 

With this, a gaming console is something I'm really hyped to build.  

That certainly is good for getting started 

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

Last Edited: Sat. Oct 12, 2019 - 09:57 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I should have said this in the main post, will edit it now. 

 

I'm due to deliver this project on the first week of January. Besides that, when I say "gaming console" I'm talking about an Arcade Console to play simple games like Snake or Tetris. Nothing particularly pretty or in need of 3D graphics,

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

jona_ wrote:
I'm due to deliver this project on the first week of January.

Yeah, this doesn't leave much time. I'd go for pong or snake, they are the simplest games I know.

Pong would be the simplest, I think. The controls would just be 2 potentiometers and one or two buttons, the display just needs a function to draw rectangles.

 

Regarding the display, apart from the Nokia 5110 there are also 128x64 graphic LCDs that are more or less standard. They have a parallel interface which is faster, maybe useful for games that need a fluid display refresh.

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

Not to knock pong, but maybe time to get creative .....make an automated can crusher...people will like that!  Or an arm-wrestling machine (basic motor-torque control) they can compete against.  If someone comes up and shows me a pong game, I might do a lot of yawning, if I didn't appreciate all the effort it takes to implement.  

 

 

 

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

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

apart from the Nokia 5110 there are also 128x64 graphic LCDs

Keep it simple.

 

You're making a class project, to show what you have learned, and that you can apply it.

 

You aren't trying to sell a million of them.

 

The 5110 is a great little display, certainly adequate for a simple Snake or Pong game.

Yet it won't take up too much memory for a memory mapped array.

Remember you can write to the Nokia 5110, but you can't read back from it.

So one would generally keep a "copy" of the display in memory, update that, and then have a routine that writes the memory array to the GLCD.

 

Think small, and accomplish the task.

 

You can come back another day and do version 2 with upgraded features.

 

JC 

 

Edit: Typo

Last Edited: Sun. Oct 13, 2019 - 12:25 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Will search and compare both displays. Thanks

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

make an automated can crusher.

 I really don't know whether or not a car crusher would be accepted as a project. Besides, I have no idea how it works or where to start :P. The arm wrestling machine looks fun. While I agree with you that making a Pong / Snake console is something common and not very breath taking, arm wrestling might be a little tough to do at this moment (according to my skills), no?

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

That's my plan. Showing the game to the LCD might be the last thing I'm gonna do since coding everything else might take a little while. It will all end up in the same place anyways. I'm already bookmarking some other projects / threads displayed in other avrfreaks sub-forums so I can backup my work!

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

Hey once again.

 

After having finished my SOA work I'm now focusing my time on building the project itself. I found lots of projects but decided to only follow "study" from what was done already 3 or 4 projects (1234). 

 

I'm yet to decide what ATMega to choose but I'm leaning towards the ATMega32 version (will also use an external oscilator given that most of the projects require either 8 or 16Mhz speed because of the display).

 

Besides that, I'm having trouble actually starting the project. My idea is to start by interfacing a display with my ATMega32. Then again, I'm still unsure whether to choose an LCD - 16x2 - or a LED Matrix - something bigger. Since the game is Snake (for now, only 1 game), a 16x2 LCD display is probably a bad choice given that I would only have 2 rows / 16 columns to move (while talking to my teacher, he told me that we could code EACH pixel inside 1 character - i.e., in a 16x2 LCD with 50 pixels p/ character, I could code each pixel of each character - therefore having more resolution. The problem is that it seems way too hard and I really don't understand it well / not intuitive for me).

Given this, a X * Y led matrix will probably be our main choice (we're thinking about having something like 16x8).

 

My problem right now is how to actually code the matrix (and which one to choose as there are SO SO SO SO SO MANY and every project has a different kind of matrix with different sizes or manufacturers). Every project makes use of pre built libraries (not official ones but rather some created by somebody). I can't simply use them as I need to code them by myself (I can't just present something made by a guy across the world and explain it since it's something I should code - even though it's something I've never done).

 

I just feel lost on how to start this or what to do. Everything seems so hard to implement and the more I search the more confused it seems (since there are thousands of projects with thousands of atmega / displays / games / size of displays combinations).

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


Jonaas18 wrote:
I found lots of projects but decided to only follow "study" from what was done already 3 or 4 projects (1234). 

Jonaas18 wrote:
Given this, a X * Y led matrix will probably be our main choice (we're thinking about having something like 16x8).

 

Ok, so basically you are going for project number 2 on your list. I think it's a good choice, because using a LED matrix you will have total control over the display, that is, you need to write everything.

Learning about multiplexing, display memory and stuff like that is important. For this, you should buy LED matrices that do not come with a controller, this way you will need to implement the control stuff yourself using the ATMega.

 

Instead of a 16x8 display, it will be cheaper for sure to get 2x 8x8 LED matrices.

You will need a MCU with plenty of pins, so I guess a 40 pin DIP like the Mega32 is ok. If you want to be adventurous and try out the new generation of MCUs, the Mega4809 also comes in a 40 pin DIP version. But in this case I guess you should stick to the mega32, there is much more info.

 

Soon, you will discover that the MCU can't handle the necessary current for the display. The chip you will need is the ULN2803A.

 

Your display setup would be something like this, but with 16 columns instead of 5 (adapted from https://www.mikrocontroller.net/...):

 

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

Here you will drive each row , one -by-one

Soon, you will discover that the MCU can't handle the necessary current for the display. The chip you will need is the ULN2803A.

Careful with wording.....where do you thing the current comes from? (why its... the AVR). At least using the ULNxxxx cuts the return current (so chip heating cut in half).  A single AVR pin would't like the total return current from 5 leds (especially if they are old school 15ma types), so the ULN really helps out.

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

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

Ok, so basically you are going for project number 2 on your list. I think it's a good choice, because using a LED matrix you will have total control over the display, that is, you need to write everything.

Learning about multiplexing, display memory and stuff like that is important. For this, you should buy LED matrices that do not come with a controller, this way you will need to implement the control stuff yourself using the ATMega.

 

Alright. Most of the LED Matrix I see in local stores don't come with any datasheet attached so I can't really write which model I'm planning on buying. Anyways, here are some links: 1 2 3. Those don't come with any controller I suppose, so any of them should work (?).

 

Instead of a 16x8 display, it will be cheaper for sure to get 2x 8x8 LED matrices.

We thought about this aswell. What drove us away was the fact that we thought it would be harder to code 2x8 matrix instead of a 1x16 one (given that, in the game Snake i.e., we would need to constantly send data to BOTH the matrix). Nonetheless, if that's your suggestion, I will happily take it!

 

  You will need a MCU with plenty of pins, so I guess a 40 pin DIP like the Mega32 is ok. If you want to be adventurous and try out the new generation of MCUs, the Mega4809 also comes in a 40 pin DIP version. But in this case I guess you should stick to the mega32, there is much more info.

We noticed that (the fact that we needed lots of pins), that's why we didn't choose our default ATMega328p and instead went with the 32 version.

 

  Soon, you will discover that the MCU can't handle the necessary current for the display. The chip you will need is the ULN2803A

Will search about it tomorrow. Had no idea about that! 

 

About the image, how do I connect 16 data lines to the ATMega32 (given that I don't have those much pins)?

 

Also, I asked my teacher about a protocol named SPI (in order to connect the display to the ATMega, many projects referenced the use of this protocol). What he told me was that I could use the SPI library if I know what the code does (since I need to explain and present the work to my class, I will need to explain each line in case someone has any doubt). With this, he also told me that he would not take this part of the code into account when giving us our grade (it basically counts for nothing). I'm still unsure if that's bad or if it doesn't matter much. Any way of getting around that (from what I read / heard, creating an SPI protocol by myself isn't something THAT easy).

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


Jonaas18 wrote:
Anyways, here are some links: 1 2 3. Those don't come with any controller I suppose, so any of them should work (?).

 

Option 1 is charlieplexed, this is a somewhat complex scheme so you better avoid it. Option 3 is RGB, again, not the best choice for you. I'd go with option 2, which is pretty standard.

 

 

Jonaas18 wrote:
About the image, how do I connect 16 data lines to the ATMega32 (given that I don't have those much pins)?

 

What do you mean?  I think there are enough pins. You need 16 + 8 pins total for display control. There are still 8 more left for other things, like control buttons and UART interface if needed. If you load a bootloader on the chip, the UART can also be used to upload firmware.

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

What do you mean?  I think there are enough pins. You need 16 + 8 pins total for display control. There are still 8 more left for other things, like control buttons and UART interface if needed. If you load a bootloader on the chip, the UART can also be used to upload firmware.

I thought that I could only connect my display to the ADC pins on the ATMega (every project I've seen connects it this way so I thought that it was needed). With this, can I simply connect the 24 pins needed in any ATMega Pin I want? (besides the ones in which I'll place my external oscilator, vcc, aref, etc,etc)

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


Jonaas18 wrote:
Since the game is Snake (for now, only 1 game), a 16x2 LCD display is probably a bad choice
The most famous implementation of "snake" ever was almost undoubtedly:

 

 

 

You scan still get those Nokia displays and they are fairly easy to interface to AVRs:

 

https://www.dharmanitech.com/2008/09/nokia-3310-lcd-interfacing-with-atmega8.html

https://learn.adafruit.com/nokia-5110-3310-monochrome-lcd

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

 

Jonaas18 wrote:
With this, can I simply connect the 24 pins needed in any ATMega Pin I want?

Well, not exactly, you need to respect the current limits indicated in the datasheet, since the AVR is sourcing all the current. The current then sinks through the ULN chip, which can handle lots of current, so that shouldn't be a problem.

 

For example, if each LED needs 10mA, you may need a maximum of 160mA (only one row is on at any moment in the multiplexing scheme). This is within the total limit (200mA), but you need to source half from port A and the other half from one of ports B, C or D to be within the 100mA port group limit.

 

edit: in practice, there will be problems with voltage drops on the AVR and ULN pins possibly causing uneven LED brightness. These are real world problems that I think you, as a student, need to experience yourself, that's how humans learn best. Analysing these kind of things will also help to beef up your report later and maybe get a better gradewink.

Last Edited: Wed. Nov 6, 2019 - 10:38 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Just to noted that the Nokia 3310 display is 84x84 pixels. It looks like in the game there is a bit of a border so maybe call the active area 80x80 then the snake/etc are about 4 pixels wide so reduce that by /4 and the active area is 20x20. I do wonder if 16x8 (done as LED) will be enough "pixels" ?

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

I've seen multiple projects using the Nokia 5510 display (I think one of them was linked in my previous response) but I kinda forgot it as I didn't find any datasheets / thought the LED Matrix was easier to code. It can also be a solution though!

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

For example, if each LED needs 10mA, you may need a maximum of 160mA (only one row is on at any moment in the multiplexing scheme). This is within the total limit (200mA), but you need to source half from port A and the other half from one of ports B, C or D to be within the 100mA port group limit.

 Got it, that makes sense. I'll have to pay attention to both the micro and the LCD datasheet and figure that out before actually building the IRL project. Thanks a lot for the simple explanation!

 

edit: in practice, there will be problems with voltage drops on the AVR and ULN pins possibly causing uneven LED brightness. These are real world problems that I think you, as a student, need to experience yourself, that's how humans learn best. Analysing these kind of things will also help to beef up your report later and maybe get a better gradewink.

Agreed. Will take that into consideration while building the project and making the actual work afterwards.

 

Thanks a lot! 

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

Just to noted that the Nokia 3310 display is 84x84 pixels. It looks like in the game there is a bit of a border so maybe call the active area 80x80 then the snake/etc are about 4 pixels wide so reduce that by /4 and the active area is 20x20. I do wonder if 16x8 (done as LED) will be enough "pixels" ?

 Not sure whether or not it's enough. The idea (if I choose to go with the LED matrix) will be to implement a 16x16 matrix (2* 8x8 LED Matrix) or one 16x16. Otherwise, like others suggested, I could use a nokia 5510 (or another model like you mentioned) and figure that out then.

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

Worst part about this kindof project is when you're finished and proudly show it to somebody, and they say, "so," or "lousy graphics."

The largest known prime number: 282589933-1

Without adult supervision.

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

Update:

 

After buying my microcontroller (ATMega32) and some other components I was missing, I'm now at this stage (Image 1):

 

  • Created a simple voltage regulator circuit (since I have a 12V battery, I'm using a LM7805 voltage regulator in order to supply 5V to my micro).
  • Added a 10Mhz crystal in order to get more accurate time measurements. This means that TimerCounters / Interrupts will be more precise.

 

With this, I still haven't connected my LED Matrix. I'm going to use 4x(5*8) Led Matrix (TA16-11GWA by Kingbright). Read the datasheet and understood what was needed in order for each LED to work. 

 

Given this, my idea is to connect all 4x14 pins (there are 14 pins in each Led Matrix) to my ATMega32 (with the help of 4x[14*1k] resistors in order to protect the matrix). I would then set each pin to a high level (no LED is turned on given that there's no voltage drop between the anode and the cathode of the LED). With this, whenever I want to turn a specific LED on, I would simply need to set the respective cathode pin to 0 (low). I'm still brainstorming on how to do this but my idea is to create a function which recieves, by parameters, the number of the line and the column of that specific LED. With this, I would know which led is it and set the pins in order to turn it on / off.

 

Is something like this a good approach or am I on a constant stupidity loop?

This seems really confusing and I'm not even thinking on how to build the game yet.. Just interfacing the matrix lol.

 

One question as well:

 

Given that I'll be using an external 10 Mhz crystal, do I need to turn off my micro's internal oscillator / mess with its' fuses?

 

EDIT1: Thinking about it, I can't even connect 4*14 pins to my ATMega32. Am I beeing stupid or is it really impossible to connect 4 matrix to this ATMega (given the number of pins avaliable)? Can stick with just 2 led matrix if needed.

Attachment(s): 

Last Edited: Tue. Nov 19, 2019 - 04:46 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

An Atmega32 in 2019? You may find a (pin compatible) ATmega324P is a "nicer" chip to work with - moer bells and whistles which were added in the many years since mega32.

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

Bough this micro because of the number of pins tbh. I really don't know much about each micro besides the basic features like memory, speed, pins, etc...

 

Would the 324P be a massive improvement over this? I have some on school (can rent them basically). Sad that I bought this one online though 

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

The 324P has some nice features. Of particular interest if you have any plans to make this battery powered is the P in 324P. That means "Picopower" and indicates the chip has added goodies to aid in lowering power consumption making it perfect for battery use. It is a 32 pin DIP like the mega32 is.

 

But if you already have the 32 then I guess just persevere with that - if, however you later "grow out of it" (that can happen in things like games machines!) then you could still switch over the 324P and what's particularly nice about it is that there's also 644P and 1284P "above" it. So if you run out of flash (or perhaps more likely RAM) then you would have the chance to trade up to 644P or the monster 1284P which, until recently, was the only AVR8 with 16K of SRAM.

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

Got it. Will take that into account and if needed, I'll make the switch. Beeing fully compatible is a big plus in this scenario. Thanks for the suggestion!

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


Jonaas18 wrote:
EDIT1: Thinking about it, I can't even connect 4*14 pins to my ATMega32. Am I beeing stupid or is it really impossible to connect 4 matrix to this ATMega (given the number of pins avaliable)? Can stick with just 2 led matrix if needed.

 

You need to understand how multiplexing works, maybe it would be a good idea to ask help from your teacher/supervisor about that.

So, if you look at that datasheet, actually, all the "Rx" pins of the 4 LED matrices may be connected together (that is, all the "R1" have a single line, all the "R2" another line, etc) so you just need 8 MCU pins for that (via the ULN2803A don't forget it!), plus 20 MCU pins to connect the "C" pins on the matrices (via current limiting resistors!).

 

 

edit: it is said, a picture is worth a thousand words, so:

Last Edited: Wed. Nov 20, 2019 - 11:26 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 

El Tangas wrote:
You need to understand how multiplexing works ... a picture is worth a thousand words

Here's one I prepared earlier:

 

 

 

http://www.8052mcu.com/forum/read/142043

 

EDIT

 

now with animation

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
Last Edited: Wed. Nov 20, 2019 - 11:49 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Oh alright. That makes sense. I had one subject in which we talked about multiplexing and such but to be honest, it was one of my least favourite subject (I was also struggling to get a decent grade at it) and I haven't used any of that for 2 years now.

Anyways, I understood what you meant and the picture helped a lot.

I will design the circuit with the 4 matrix + the ULN and build it IRL and then proceed to code.

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

When I first studied micros, I had an atmega board with a 7 segment display just like that. I controlled all 4 digits and I never knew how it was wired/connected on the inside. I guess I now know.

Thanks for clearing it up and helping me understand how multiplexing / interfacing these kind of displays works.

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

Jonaas18 wrote:
I never knew how it was wired/connected on the inside.
How could you have written software to drive it without knowing the layout? Or are you saying it just came with some pre-made display_7seg(...) or similar that you just called without knowing what was going on inside?

 

The key thing about multiplexing (esp if not current limiting) is often "never stop" ! If you don't keep hopping from one to the next and only illuminating each for a short time (essentially "PWM") then you can stop with one fixed  pattern that sucks huge current and lets the smoke dragon out of its cave !

Last Edited: Wed. Nov 20, 2019 - 01:56 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

How could you have written software to drive it without knowing the layout? Or are you saying it just came with some pre-made display_7seg(...) or similar that you just called without knowing what was going on inside?

When I had that subject, everything was already built. Everything was connected inside so all we had to do was code the pins. I attatched an image so you could understand what I was dealing with. The teacher would ask us to make a led blink or things like that so we would only have to worry about coding. Wiring, building and interfacing the ATMega + the components was already done.

 

To be honest, it was hard at first because I had never coded anything like that but I would have prefered to know how to wire a micro with other components (i.e., the 7 segment display, a buzzer, etc.) since it would have helped me massively at the moment. Nonetheless, I try my best to study and understand it alone.

 

The key thing about multiplexing (esp if not current limiting) is often "never stop" ! If you don't keep hopping from one to the next and only illuminating each for a short time (essentially "PWM") then you can stop with one fixed  pattern that sucks huge current and lets the smoke dragon out of its cave !

Will pay attention while doing the routines / using the interruptions. Thank you! 

Attachment(s): 

Last Edited: Wed. Nov 20, 2019 - 02:08 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Jonaas18 wrote:
(...) and then proceed to code

 

Remember that the game logic and the hardware control are 2 separate things, and should be separated in code (in different files). The "Snake" game consists just of reading inputs and drawing and erasing snake segments and "snake food" as needed. In this case, they are just LED dots, but you should write the game logic separately as if you don't know what is the underlying hardware.

 

The game should only use functions like draw_segment(), draw_ food() or something general like that, so that it can be plugged in to a variety of hardware.

The hardware part should provide functions like set_pixel(), clear_pixel(), get_screen_x_size() and stuff like that, while handling all the screen related stuff in a more or less invisible way (to the game logic section), so that it could be use by other application instead of the "Snake" game.

 

Then, all that is left is writing the "middle", that is, implementing for example draw_ food() which in this case is just set_pixel().

 

You should get used to writing code like this, because of maintainability and code reuse.

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

That means, for example, creating a LED.c file in order to interface the matrix with the atmega and such + a Snake.C in which I code the actual game itself, right? 

 

I was planning on starting by creating the LED.c file, that means, interfacing the display and the micro.

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

Jonaas18 wrote:
it was hard at first because I had never coded anything like that

That is always true of learning any new skill!

 

By definition, if it's a new skill - then you haven't done it before!

 

This is why it's important to start with the basics, lay good foundations, practice, etc to build up your skills - rather than just leaping into some over-ambitious project.

 

 

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Jonaas18 wrote:
I was planning on starting by creating the LED.c file, that means, interfacing the display and the micro.

 

Sure. That's the obvious starting point and it's fine. I was just pointing out that you could start at either end. For example, if this was a team, each person could be working on a different section.

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

El Tangas wrote:

So, if you look at that datasheet, actually, all the "Rx" pins of the 4 LED matrices may be connected together (that is, all the "R1" have a single line, all the "R2" another line, etc) so you just need 8 MCU pins for that (via the ULN2803A don't forget it!), plus 20 MCU pins to connect the "C" pins on the matrices (via current limiting resistors!).

 

 

edit: it is said, a picture is worth a thousand words, so:

 

I was designing my schematic (in order to build the circuit properly IRL afterwards) and I got stuck. When you suggest connecting the 8 Rx pins (matrix rows) from the LED Matrix to the ULN chip and then to the MCU, do you mean:

 

  • Connecting 8 MCU pins to 8 ULN2803 inputs (1B, 2B, 3B, 4B, 5B, 6B, 7B, 8B); then connect 8 ULN2803 outputs (1C, 2C, 3C, 4C, 5C, 6C, 7C, 8C) to the 8 Led Matrix's pins.
  • Connecting 8 MCU pins to 8 ULN2803 outputs (1C, 2C, 3C, 4C, 5C, 6C, 7C, 8C); then connect 8 ULN2803 inputs (1B, 2B, 3B, 4B, 5B, 6B, 7B, 8B) to the 8 Led Matrix's pins.

 

In my head, it makes sense to connect the MCU pins to the ULN chip (in order to amplify and stabilize the current) and only then connect it to the matrix. At least that's what I understood when you first suggested the use of the chip (~a month ago). However, by the image you sent, it says "To driver chip, then to MCU", which makes it seem like I would connect the matrix pins on the input pins of the ULN chip and only then connect it to the MCU. 

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

Jonaas18 wrote:
Connecting 8 MCU pins to 8 ULN2803 inputs (1B, 2B, 3B, 4B, 5B, 6B, 7B, 8B); then connect 8 ULN2803 outputs (1C, 2C, 3C, 4C, 5C, 6C, 7C, 8C) to the 8 Led Matrix's pins.

 

This one, of course. Sorry if I mislead you.

  • 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!