Assembler timer

Thread Starter

TwoPlusTwo

Joined Oct 14, 2010
51
Hi, I am trying to write a program for a timer. The operation is given as follows:

- The diodes show the number of seconds/minutes elapsed since start/restart.

- By pressing Switch2 the diodes show the number of minutes.

- By pressing Switch3 the diodes show the number of seconds.

- After 60 minutes the timer is reset.

I am pretty much a complete beginner when it comes to Assembler programming, so I feel like this is way out of my league. But I think I figured out which registers to set at least. And I also know that I need to set the program up for interrupts and initialize the stack pointer. The following is what I have so far.

.include "M32def.inc"
.def temp=R16

.org 0 jmp start
.org 2 jmp Sw2
.org 4 jmp Sw3

start:
;---PortB as out-port---
ldi temp, 0xFF
out DDRB, temp

;---Initialize for interrupts INT0 and INT1---
ldi temp, 0xC0
out GICR, temp ; set bits 6 and 7 to enable interrupts from INT0 and INT1
ldi temp, 0x0A
out MCUCR, temp ;set bits 3 and 1 to trigger interrupts when the switches are pressed down

;---other registers---
ldi temp, 0x0B
out TCCR1B, temp ; count frequency = 65.5k (0xF424)
ldi temp, 0xF4
out OCR1AH, temp
ldi temp, 0x24
out OCR1AL, temp ; interrupt every second, when the contents of the count registers
ldi temp, 0x0f ; is equal to the "output compare" registers.
out TIMSK, temp ; set bit 4 to enable Timer1 CompA match interrupt.

;---Initialize stack pointer-----
ldi temp, HIGH (RAMEND)
out sph, temp
ldi temp, LOW (RAMEND)
out spl, temp

sei ; global interrupt enable

Of course, I have neither the main program nor the two subroutines. Is what I have so far correct, though? And how should I approach writing the stuff that I'm missing?

Bit of a long question here, but hopefully someone who knows this stuff can give me a few pointers.

Thanks!
 
Last edited:

hgmjr

Joined Jan 28, 2005
9,027
Rich (BB code):
.include "M32def.inc"
def temp=R16  ; Should be .def not def....
 
.org 0 jmp start
.org 2 jmp Sw2
.org 4 jmp Sw3
 
start:
---PortB as out-port---  ; comments must begin with a semicolon...
ldi temp, 0xFF
out DDRB, temp
 
---Initialize for interrupts INT0 and INT1---
ldi temp, 0xC0
out GICR, temp
ldi temp, 0x08
out MCUCR, temp
 
---Set other registers---
ldi temp, 0x0B
out TCCRIB, temp  ; The label should be TCCR1B not TCCRIB....
ldi temp, 0xF4
out OCRIAH, temp ; The label should be OCR1AH not OCRIAH...
ldi temp, ox24  ; The hex constant should be 0x24 not ox24...
out OCRIAL, temp  ; The label should be OCR1AL not OCRIAL..
 
---Stack-pointer-----
ldi temp, 0x02
out sph, temp
ldi temp, 0x5F
out spl, temp
 
sei
Some edits that you need among others....

hgmjr
 
Last edited:

hgmjr

Joined Jan 28, 2005
9,027
Rich (BB code):
.include "M32def.inc"
def temp=R16  ; Should be .def not def....
 
.org 0 jmp start
.org 2 jmp Sw2
.org 4 jmp Sw3
 
start:
---PortB as out-port---  ; comments must begin with a semicolon...
ldi temp, 0xFF
out DDRB, temp
 
---Initialize for interrupts INT0 and INT1---
ldi temp, 0xC0
out GICR, temp
ldi temp, 0x08
out MCUCR, temp
 
---Set other registers---
ldi temp, 0x0B
out TCCRIB, temp  ; The label should be TCCR1B not TCCRIB....
ldi temp, 0xF4
out OCRIAH, temp ; The label should be OCR1AH not OCRIAH...
ldi temp, ox24  ; The hex constant should be 0x24 not ox24...
out OCRIAL, temp  ; The label should be OCR1AL not OCRIAL..
 
---Stack-pointer-----
ldi temp, HIGH(RAMEND)  ; This method is AVR micro non-specific...
out sph, temp
ldi temp, LOW(RAMEND) ; This method is AVR micro non-specific...
out spl, temp
 
sei

Added changes to the initialization of the stack pointer that can be used
across all members of the AVR family.

hgmjr
 

Thread Starter

TwoPlusTwo

Joined Oct 14, 2010
51
I didn't have time to work any more on this last week, but I really want to finish it now. I've drawn the flow chart that we were given and attached it. I also added some more comments to the original sketch.

My thinking now goes like this:

Now that I have an interrupt every second, I need to define a register (sec) that counts those interrupts. It needs to be reset at 60 seconds, and increment another minute counting register (min) everytime that happens. Is this correct?

Next, I need to write the subroutine like the flow chart indicates, sending the contents of sec and min to PortB when their respective switches have been pressed.

In the flow chart there is also a register called showmin. I'm guessing this is controlled by the switches, where one makes it go high and the other makes it go low. So that when the switches activate the subroutine, it can start by comparing the value of showmin to 0 and then select the appropriate way to go. For that I need to use the breq command.

If all this is right, I need to know how to increment the sec counting register every time there is a CompA match interrupt. And also how to make that register count only to 60 and increment the minute counting register every time it overflows.
 

Attachments

Last edited:

Thread Starter

TwoPlusTwo

Joined Oct 14, 2010
51
Here is a flow chart for an interrupt routine for updating the timer. I'll try write it out, and hopefully I can get some feedback on whether or not I got it right.
 

Attachments

Last edited:

Thread Starter

TwoPlusTwo

Joined Oct 14, 2010
51
Here's my attempt. How does it look?

sub:
push temp
in temp, sreg
push temp

inc sec
cpi sec, 60
breq minute

End:
pop temp
out sreg, sreg
pop temp

minute:
inc min
clr sec
cpi min, 60
breq End
clr min
rjmp End

ret
 

hgmjr

Joined Jan 28, 2005
9,027
Allow me to suggest a change in the strategy used.

It is always a good idea to keep the amount of time spent inside an interrupt service routine to an absolute minimum.

How about if you simply use the interrupt service routine to increment a memory location once every second and then write your code for testing secs and mins in your main loop.

hgmjr
 

Thread Starter

TwoPlusTwo

Joined Oct 14, 2010
51
Allow me to suggest a change in the strategy used.

It is always a good idea to keep the amount of time spent inside an interrupt service routine to an absolute minimum.

How about if you simply use the interrupt service routine to increment a memory location once every second and then write your code for testing secs and mins in your main loop.

hgmjr
I think maybe this is what the flow charts we were given suggests as well. I could have a loop that goes something like this:

loop:
cpi showmin, 0
breq displaysec
cli
out min, PortB
sei

displaysec:
cli
out sec, PortB
sei

And let the value of showmin be determined by which switch has been pressed.

That's how I understand the flow chart at least. Not sure I get why I have to disable interrupts (cli) and enable them again (sei) though.
 

hgmjr

Joined Jan 28, 2005
9,027
Here's my attempt. How does it look?

Rich (BB code):
sub:
push temp
in temp, sreg
push temp
 
inc sec
cpi sec, 60
breq minute
 
End:
pop temp
out sreg, sreg
pop temp
 
minute:
inc min
clr sec
cpi min, 60
breq End
clr min
rjmp End
 
ret
Here is your code encloded in code tags. These tags are indicated by the # sign up in the post edit feature.

hgmjr
 

Thread Starter

TwoPlusTwo

Joined Oct 14, 2010
51
Ok, so I tried putting it all together. How does it look? Maybe I shouldn't keep so much of the program in the subroutines, but I think that is what I'm asked to do. I really appreciate your help so far, hgmjr. You're a life saver!

Rich (BB code):
.include "M32def.inc"
.def temp=R16
.def seconds=R17
.def min=R18
.def showmin=R19

.org 0 jmp start
.org 2 jmp Sw2
.org 4 jmp Sw3
.org 0x0C count
.org 0x2A

start:
;---PortB as out-port---
ldi temp, 0xFF
out DDRB, temp

;---Initialize for interrupts INT0 and INT1---
ldi temp, 0xC0
out GICR, temp   ; set bits 6 and 7 to enable interrupts from INT0 and INT1
ldi temp, 0x0A      
out MCUCR, temp  ;set bits 3 and 1 to trigger interrupts when the switches are pressed down

;---Other registers---
ldi temp, 0x0B
out TCCR1B, temp   ; count frequency = 65.5k (0xF424)
ldi temp, 0xF4
out OCR1AH, temp  
ldi temp, 0x24        
out OCR1AL, temp  ; interrupt every second, when the contents of the count registers 
ldi temp, 0x0f     ; is equal to the "output compare" registers.
out TIMSK, temp    ; set bit 4 to enable Timer1 CompA match interrupt.

;---Initialize stack pointer-----
ldi temp, HIGH (RAMEND)
out sph, temp
ldi temp, LOW (RAMEND)
out spl, temp

sei   ; global interrupt enable

;---Switches---
Sw2:
clr showmin
reti

Sw3:
ldi showmin, 0xff
reti

;---Show on diodes---
loop:
cpi showmin, 0
breq showsec
cli
out min, PortB
sei
rjmp loop
  
showsec:
cli
out seconds, PortB
sei
rjmp loop

;---Seconds and minutes---
count:
push temp
in temp, sreg
push temp
 
inc seconds
cpi seconds, 60
breq minute
 
end:
pop temp
out sreg, sreg
pop temp
reti
 
minute:
inc min
clr seconds
cpi min, 60
breq end
clr min
rjmp end
 
Last edited:

hgmjr

Joined Jan 28, 2005
9,027
A couple of things that I have encountered immediately are:

1. You cannot use sec as a variable since it is a keyword.
2. There are a couple of other syntax errors.

hgmjr
 

hgmjr

Joined Jan 28, 2005
9,027
Take a look at your statement for your timer1 interrupt vector. You will notice that it set up for the incorrect vector 0xC. OxC corresponds to the ICP (input capture interrupt).

By the way, do you already have the hardware set so that you can test your program?

hgmjr
 

Thread Starter

TwoPlusTwo

Joined Oct 14, 2010
51
Take a look at your statement for your timer1 interrupt vector. You will notice that it set up for the incorrect vector 0xC. OxC corresponds to the ICP (input capture interrupt).

By the way, do you already have the hardware set so that you can test your program?

hgmjr
oops, it was supposed to be 0x0C, not 0xC. I don't have the hardware at home, but I'll be able to test the program at my school lab the next time I'm there.
 

hgmjr

Joined Jan 28, 2005
9,027
The vector table entry for timer1 is not 0x0c.

I noticed that the output to PORTB for minutes and seconds is in binary format. Is the intent to express them in BCD instead?

hgmjr
 

hgmjr

Joined Jan 28, 2005
9,027
Rich (BB code):
.include "M32def.inc"
.def temp  =R16
.def seconds =R17
.def min  =R18
.def showmin =R19
 
.org 0 
jmp start
.org INT0addr
jmp Sw2
.org INT1addr 
jmp Sw3
.org OC1Aaddr 
jmp count
 
.org INT_VECTORS_SIZE  // Set the start of executable code at the end of the interrupt table... 
start:
;---PortB as out-port---
 ldi temp, 0xFF
 out DDRB, temp
;---Initialize for interrupts INT0 and INT1---
 ldi temp, 0xC0
 out GICR, temp   ; set bits 6 and 7 to enable interrupts from INT0 and INT1
 ldi temp, 0x0A      
 out MCUCR, temp  ;set bits 3 and 1 to trigger interrupts when the switches are pressed down
;---Other registers---
 ldi temp, 0x0B
 out TCCR1B, temp   ; count frequency = 65.5k (0xF424)
 ldi temp, 0xF4
 out OCR1AH, temp  
 ldi temp, 0x24        
 out OCR1AL, temp  ; interrupt every second, when the contents of the count registers 
 ldi temp, 0x0f     ; is equal to the "output compare" registers.
 out TIMSK, temp    ; set bit 4 to enable Timer1 CompA match interrupt.
;---Initialize stack pointer-----
 ldi temp, HIGH (RAMEND)
 out sph, temp
 ldi temp, LOW (RAMEND)
 out spl, temp
 sei   ; global interrupt enable
rjmp loop ; This jump jumps around the two interrupt service routines...
;---Switches---
Sw2:
 clr showmin
 reti
Sw3:
 ldi showmin, 0xff
 reti
;---Show on diodes---
loop:
 cpi showmin, 0
 breq showsec
 cli
out PortB, min  ; Corrected syntrax error...
 sei
 rjmp loop
 
showsec:
 cli
out PORTB, seconds    ; Corrected syntrax error...
 sei
 rjmp loop
 
;---Seconds and minutes---
count:
 push temp
 in temp, sreg
 push temp
 inc seconds
 cpi seconds, 60
 breq minute
end:
 pop temp
 out sreg, temp
 pop temp
 reti
minute:
 inc min
 clr seconds
 cpi min, 60
 breq end
 clr min
 rjmp end
Assuming that this is your first effort, I commend you. Assembly Language is not a easy language to pick up. It appeals to programmers who like the low level control it provides. It also allows you to write the tightest code possible.

Here is my early corrective effort. There may still be some tweaks needed but it is pretty close.

You will notice that I make liberal use of labels rather than using what are referred to in the programming world as "MAGIC" values. You can get the labels by looking in the include file where they are defined. I highlighted in red lines that I touched.

hgmjr
 

Thread Starter

TwoPlusTwo

Joined Oct 14, 2010
51
The vector table entry for timer1 is not 0x0c.

I noticed that the output to PORTB for minutes and seconds is in binary format. Is the intent to express them in BCD instead?

hgmjr
Actually, the assignment is to display the time in binary form on the diodes, which makes it a pretty useless clock in real life, unless the person who operates it is some sort of binary savant:D

Assuming that this is your first effort, I commend you. Assembly Language is not a easy language to pick up. It appeals to programmers who like the low level control it provides. It also allows you to write the tightest code possible.

Here is my early corrective effort. There may still be some tweaks needed but it is pretty close.

You will notice that I make liberal use of labels rather than using what are referred to in the programming world as "MAGIC" values. You can get the labels by looking in the include file where they are defined. I highlighted in red lines that I touched.

hgmjr
I looked over your corrections and they all pretty much make sense to me. I might have been using the wrong table from my course book when I set the interrupt vectors, so in the future I'll make sure to just use the include file in stead. The labels in there are definitely much easier to relate to than a bunch of hex numbers.

And again, thank you so much for taking the time to go over my work. You and the other contributors to the forum are doing a great thing helping all of us rookies. I hope to be able to contribute the same way when I get better. Until then I'm probably just going to keep asking lots of questions:D
 
Top