Ok, Eric.
I figured that if I do this, I want to show you an example of what I consider to be good code practices.
First, you'll notice there is a lot of code, and much of it is overkill for your little application. But, it is easily extensible for when you want to add features as your skill improves.
The first (and most important!) thing I would like you to remember is this:
Never, never, NEVER, ever hard code time delay loops. I've seen this a lot here, and it is an incredibly bad practice. What's the point of using a 2 MIPs processor (assuming 8Mhz) when you are going to spend 1.9999 MIPs in a tight, closed, timing loop? You could be using those instruction cycles for something useful, like some advanced digital signal processing or perhaps creating a responsive user interface (i.e. press a button, and something happens *immediately*). Since you are a beginner, I urge you to not get into the habit of using loops for timing!
Notice also the code is broken into two files: a .inc (header) file and a .asm (code) file. I put program definitions in the .inc, and code in the .asm. This helps to keep things nice and clean. I try not to hard code constants in the .asm file; instead, I set them as labels (equ) or string substitutions (define) in the .inc file. This is similar to how one would do good code in 'C'.
I also usually have multiple files, one per "device" in my system that contains all the code for the device (i.e. a device driver). In this way, I can reuse the code for other projects that use similar devices.
Pay special attention to the "system clock". Every single project I do has a master system clock based on TMR0. Each time TMR0 rolls over, in interrupt is generated (every 1.024ms in this case). The servicing routine increments the _TIMER register and computes a value in the _TMRCHG register, the bit of which represent bits that have changed when _TIMER was incremented. Later on, you will see this construct is *invaluable* in creating periodic timing signals that your main program can use to drive its execution.
The main program is a simple loop. It does 3 things: it gets the current system time (into the "synchronized" TIMER and TMRCHG registers), it gets the current state of the "keypad" (in your case, only one push button), and processes a "state machine" that drives your LEDs based upon time and pushbutton presses.
I'll let you "discover" how the GETKEY routine works to debounce the pushbutton, as well as how the state machine works (hint: use the simulator and watch windows).
By the way, please notice that the pushbutton is checked every 33ms, regardless of what is going on elsewhere in the program. Therefore, the response, as far as the user is concerned, will be instantaneous.
The code, especially the state machine, could actually be further simplified without departing from my "good practices" principles, but I wanted to try to keep things clear so as not to confuse you too greatly.
The code is written to use the 8mhz intosc. Put both files into a directory, make a project and add 'fourleds.asm' to the project. Set 'case sensitivity' to off and radix to decimal, otherwise you will get build errors.
Be sure to remove the .txt from the end of the file names.
BTW, I wrote this quick, and only simulated it. Hopefully it will work without problems on your hardware.
When you have questions, I'll be happy to explain more. Good luck and have fun!
EDIT: I just notice some of the comments in the state machine are wrong...I copied and paste and didn't update the comments. Sorry.
I figured that if I do this, I want to show you an example of what I consider to be good code practices.
First, you'll notice there is a lot of code, and much of it is overkill for your little application. But, it is easily extensible for when you want to add features as your skill improves.
The first (and most important!) thing I would like you to remember is this:
Never, never, NEVER, ever hard code time delay loops. I've seen this a lot here, and it is an incredibly bad practice. What's the point of using a 2 MIPs processor (assuming 8Mhz) when you are going to spend 1.9999 MIPs in a tight, closed, timing loop? You could be using those instruction cycles for something useful, like some advanced digital signal processing or perhaps creating a responsive user interface (i.e. press a button, and something happens *immediately*). Since you are a beginner, I urge you to not get into the habit of using loops for timing!
Notice also the code is broken into two files: a .inc (header) file and a .asm (code) file. I put program definitions in the .inc, and code in the .asm. This helps to keep things nice and clean. I try not to hard code constants in the .asm file; instead, I set them as labels (equ) or string substitutions (define) in the .inc file. This is similar to how one would do good code in 'C'.
I also usually have multiple files, one per "device" in my system that contains all the code for the device (i.e. a device driver). In this way, I can reuse the code for other projects that use similar devices.
Pay special attention to the "system clock". Every single project I do has a master system clock based on TMR0. Each time TMR0 rolls over, in interrupt is generated (every 1.024ms in this case). The servicing routine increments the _TIMER register and computes a value in the _TMRCHG register, the bit of which represent bits that have changed when _TIMER was incremented. Later on, you will see this construct is *invaluable* in creating periodic timing signals that your main program can use to drive its execution.
The main program is a simple loop. It does 3 things: it gets the current system time (into the "synchronized" TIMER and TMRCHG registers), it gets the current state of the "keypad" (in your case, only one push button), and processes a "state machine" that drives your LEDs based upon time and pushbutton presses.
I'll let you "discover" how the GETKEY routine works to debounce the pushbutton, as well as how the state machine works (hint: use the simulator and watch windows).
By the way, please notice that the pushbutton is checked every 33ms, regardless of what is going on elsewhere in the program. Therefore, the response, as far as the user is concerned, will be instantaneous.
The code, especially the state machine, could actually be further simplified without departing from my "good practices" principles, but I wanted to try to keep things clear so as not to confuse you too greatly.
The code is written to use the 8mhz intosc. Put both files into a directory, make a project and add 'fourleds.asm' to the project. Set 'case sensitivity' to off and radix to decimal, otherwise you will get build errors.
Be sure to remove the .txt from the end of the file names.
BTW, I wrote this quick, and only simulated it. Hopefully it will work without problems on your hardware.
When you have questions, I'll be happy to explain more. Good luck and have fun!
EDIT: I just notice some of the comments in the state machine are wrong...I copied and paste and didn't update the comments. Sorry.
Attachments
-
5.3 KB Views: 39
-
7.5 KB Views: 44
Last edited: