Mega16 not behaving like simulator

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

Hi!

I am trying to make a app to control 8 servos with my mega16. The program runs fine in studio's simulator but it doesn't behave the same way at all on the mega16.

Basic description of the code:

mega16 runs @ 8MHz

Timer 0 is on CTC with prescaler 8 and OCR0 150, it then interrupts TIM0_COMP
Timer 1 is on CTC with prescaler 8 and OCR1A 20 000 (50Hz), no interrupts, no nothing.

TIM0_COMP compares the variables pulse0 -> pulse7 to see if they are larger than TCNT1 - if they are it configures that pin to be 0, else 1 (on PORTD)

As you can see port0 = 20000, port1=18000, ... ,port7=6000, so when i step through one 20ms period (TCNT1 = 0,1, ... ,20000) they act like i intend to (in avrstudio4).

However, when i run it on my mega16 all pins are low.

Any thoughts?

;==========================================
; AS I DESCRIBED HERE, SCROLL DOWN TO TIM0_COMP
;==========================================
.include "m16def.inc"
.def pinNumber=R16
.def newPortStatus=R21
.def counterHI=R20
.def counterLO=R19
jmp RESET ; Reset Handler 
jmp DO_NOTHING
jmp DO_NOTHING
jmp DO_NOTHING
jmp DO_NOTHING
jmp DO_NOTHING
jmp DO_NOTHING
jmp DO_NOTHING
jmp DO_NOTHING
jmp DO_NOTHING
jmp DO_NOTHING
jmp DO_NOTHING
jmp DO_NOTHING
jmp DO_NOTHING
jmp DO_NOTHING
jmp DO_NOTHING
jmp DO_NOTHING
jmp DO_NOTHING
jmp DO_NOTHING
jmp TIM0_COMP ; Timer0 Compare Handler 
jmp DO_NOTHING

RESET:

	ldi ZL, LOW(pulse0)
	ldi ZH, HIGH(pulse0)
	ldi R17, HIGH(20000)
	ldi R16, LOW(20000)
	st Z+, R17
	st Z+, R16
	ldi R17, HIGH(18000)
	ldi R16, LOW(18000)
	st Z+, R17
	st Z+, R16
	ldi R17, HIGH(16000)
	ldi R16, LOW(16000)
	st Z+, R17
	st Z+, R16
	ldi R17, HIGH(14000)
	ldi R16, LOW(14000)
	st Z+, R17
	st Z+, R16
	ldi R17, HIGH(12000)
	ldi R16, LOW(12000)
	st Z+, R17
	st Z+, R16
	ldi R17, HIGH(10000)
	ldi R16, LOW(10000)
	st Z+, R17
	st Z+, R16
	ldi R17, HIGH(8000)
	ldi R16, LOW(8000)
	st Z+, R17
	st Z+, R16
	ldi R17, HIGH(6000)
	ldi R16, LOW(6000)
	st Z+, R17
	st Z+, R16
	; Set up stack
	; Set up timer 1A to reset at 20000 (50Hz)
	; Set up timer 1 to run at 1MHz (Prescaler 8/fosc)
	; Set up timer 0 to interrupt every 1200th instruction
	;									Prescaler 8
	;									TOP 150
	; Enable global interrupts

	ldi R16, 0xFF
	out DDRD, R16
	eor R16,R16
	out PORTD, R16

	ldi R16, LOW(RAMEND)
	ldi R17, HIGH(RAMEND)
	out SPL, R16
	out SPH, R17
	;out YL, R16
	;out YH, R17

	ldi R16, LOW(20000)
	ldi R17, HIGH(20000)
	out OCR1AL, R16
	out OCR1AH, R17

	; TCCR1B
	; ICNC1 ICES1 XXXX WGM13 WGM12 CS12 CS11 CS10 
	; 0	    0	  0	   0     1	   0    1	 0
	ldi R16, 0x0A
	out TCCR1B, R16

	ldi R16, 150
	out OCR0, R16

	; TCCR0
	; FOC0 WGM00 COM01 COM00 WGM01 CS02 CS01 CS00
	; 0    0     0     0     1     0    1    0
	ldi R16, 0x0A
	out TCCR0, R16
	
	; TIMSK
	; OCIE2 TOIE2 TICIE1 OCIE1A OCIE1B TOIE1 OCIE0 TOIE0 
	; 0     0     0      0      0      0     1     0
	ldi R16, 0x02
	out TIMSK, R16

	sei

	hang:
	jmp hang
DO_NOTHING:
reti

;===============================================
; TIMO_COMP
;===============================================
; For each vaule in the Pulse Width Variables struct
;  we check their value against TCNT1
TIM0_COMP:
;	st 	-Y,R21						; Save state
;	st	-Y,R20
;	st -Y,R19
;	st -Y,R18
;	st -Y,R17
;	st -Y,R16
;	st -Y,ZL
;	st -Y,ZH
;	in R16,SREG
;	st -Y,R16
	
	
	ldi ZL, low(pulse0)				; Z points to the struct
	ldi ZH, high(pulse0)
	in counterHI, TCNT1H			; Read Counter HI
	in counterLO, TCNT1L			; Read Counter LO

	ldi newPortStatus, 0xFF			; Store the new port status here
	eor pinNumber, pinNumber		; pin number = 0

	loop:
		ld R18, Z+					; R18 = pulse high
		ld R17, Z+					; R17 = pulse low

		cp R17,R19					; Compare low bytes
		cpc R18, R20				; Compare high bytes
		
		brcs dont_set				; dont set any bit if pulse < count

									; set bit if pulse > count
		eor R18, R18				; R18 = 0
		eor R17, R17				; R17 = 1
		inc R17
		set_loop:
			cp pinNumber,R18				
			breq done_loop			; if pin nr = R20 done!
			inc R18					; else R18++
			lsl R17					; R17 = R17 << 1
			jmp set_loop					
			done_loop:
			ldi R18, 0xFF
			eor R17,R18				; R17 = ~R17
			and newPortStatus,R17	; newPortStatus &= R17

		dont_set:
		inc pinNumber				; pinNumber++
		ldi R17, 0x08
		cp pinNumber, R17			; if pinNumber != 7
		brne loop					;  loop again
	
	out PORTD,newPortStatus			; Set port status

	eor R16,R16						; Reset the counter
	out TCNT0,R16
	skip:
;	ld R16,Y+
;	out SREG, R16
;	ld ZH,Y+							; Restore
;	ld ZL,Y+
;	ld R16,Y+
;	ld R17,Y+
;	ld R18,Y+
;	ld R19,Y+
;	ld R20,Y+
;;	ld R21,Y+

reti
;==============================================
; PULSE WIDTHS STRUCT
;==============================================
.dseg
PWStruct:
pulse0: .BYTE 2
pulse1: .BYTE 2
pulse2: .BYTE 2
pulse3: .BYTE 2
pulse4: .BYTE 2
pulse5: .BYTE 2
pulse6: .BYTE 2
pulse7: .BYTE 2
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You are reading the TCNT1 registers in the wrong order. Read about "Accessing 16-bit Registers" in the data sheet.
/Lars

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

Thank you! I will do that

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

Hmm okay i read that i need to read the low byte first and write the high byte first.

Still the same problem, i even rewrote it in a much smarter way with a 0.1ms pulse serializing the pwm (eg. PD0 is low when t=[0,2]ms and PD1 low when t=[1.9,3]ms). All pins of PORTD are still low!

Again, works in avrstudio if i step through it but doesn't work on the chip.
Even if i invert R19 as i did in the last 3 lines of TIMER1_COMPA i get the same result.

Here is the code if anyone is interested:

.include "m16def.inc"
; ===========================================================
; INTERRUPT VECTORS
; ===========================================================
jmp RESET ; Reset Handler 
jmp DO_NOTHING
jmp DO_NOTHING
jmp DO_NOTHING
jmp DO_NOTHING
jmp DO_NOTHING
jmp TIMER1_COMPA
jmp DO_NOTHING
jmp DO_NOTHING
jmp DO_NOTHING
jmp DO_NOTHING
jmp DO_NOTHING
jmp DO_NOTHING
jmp DO_NOTHING
jmp DO_NOTHING
jmp DO_NOTHING
jmp DO_NOTHING
jmp DO_NOTHING
jmp DO_NOTHING
jmp DO_NOTHING
jmp DO_NOTHING
; ===========================================================
; DATA
; ===========================================================
.dseg
cycle: .BYTE 1
current_servo: .BYTE 1
servo0: .BYTE 1				; One variable to store each
servo1: .BYTE 1				;  servo`s pulse time
servo2: .BYTE 1
servo3: .BYTE 1
servo4: .BYTE 1
servo5: .BYTE 1
servo6: .BYTE 1
servo7: .BYTE 1
; ===========================================================
; CODE
; ===========================================================
.cseg


PORT_SETUP:
							; Set up SERVO_PORT for output
	ldi R16, 0xFF
	out DDRD, R16
ret

SET_MEMORY:
	ldi ZL,LOW(cycle)
	ldi ZH, HIGH(cycle)
	eor R16,R16
	st Z+,R16				; cycle
	st Z+,R16				; current_servo
	ldi R16, 0
	st Z+,R16				; servo0
	st Z+,R16				; servo1
	st Z+,R16				; servo2
	st Z+,R16				; servo3
	st Z+,R16				; servo4
	st Z+,R16				; servo5
	st Z+,R16				; servo6
	st Z+,R16				; servo7
ret

START_TIMERS:
							; Set up Timer 1:
	ldi R16, LOW(100)		;  OCR1A = 100us
	ldi R17, HIGH(100)	
	out OCR1AH, R17
	out OCR1AL, R16
	ldi R16, 0x0A			;  Prescaler 8, CTC Mode
	out TCCR1B, R16
	ldi R16, 0x10			;  OCIE1A Enabled
	out TIMSK, R16
	sei						;  Enable interrupts
ret

; Test data:
; Takes 28 cycles in the range where no pulses are generated
; When pulses are generated it takes between 49 and 148 cycles to perform
TIMER1_COMPA:
	ldi ZL, LOW(cycle)
	ldi ZH, HIGH(cycle)
	ld R16, Z+				; R16 = cycle
	ld R17, Z+				; R17 = current_servo
	ldi R19, 10
	cp R16, R19				; if cycle != 10
	brne L1					;  jmp L1
	eor R16,R16				; cycle = 0
	inc R17					; current_servo++
	ldi R19, 20
	cp R17, R19				; if current_servo != 20
	brne L1					;  jmp L1
	eor R17,R17				; current_servo = 0
	L1:						;L1:
	ldi R19,0xFF			; R19 = bitmask
	ldi R18, 9
	cp R17, R18				; if current_servo >= 9
	brsh done				;  jmp done
	ldi R18, 8
	cp R17, R18				;  if current_servo >= 8
	brsh trailing_pulse		;   jmp trailing_pulse
	current_bit_pulse:
	eor R18,R18
	add ZL,R17				; Z += current_servo
	adc ZH, R18
	ld R18, Z				; R18 = current_servo_pulse
	cp R16,R18				; if cycle < current_sevo_pulse
	brlo trailing_pulse		;  jmp trailing_pulse
	ldi R20,1				; R20 = 1
	mov R18,R17				; R18 = current_servo2
	shift_loop_1:
		tst R18				; if current_servo_2 = 0
		breq shift_loop_1_done;  jmp done_1
		lsl R20				; R20 = R20 << 1
		dec R18				; current_servo_2--
		jmp shift_loop_1
	shift_loop_1_done:
	ldi R18,0xFF
	eor R20,R18				; R20 = ~R20
	and R19,R20				; bitmask &= R20
	trailing_pulse:			;trailing_pulse:
	eor R18,R18
	cp R17,R18				; if current_servo = 0
	breq done				;  jmp done
	mov R18, R17			; R18 = current_servo2 = current_servo
	dec R18					; current_servo2--
	ldi R20,1				; R20 = 1
	shift_loop:
		tst R18				; if current_servo2 = 0
		breq shift_loop_end ;  jmp shift_loop_end
		lsl R20				; R20 = R20 << 1
		dec R18				; current_servo2--
		jmp shift_loop
	shift_loop_end:
	ldi R18,0xFF
	eor R20,R18				; R20 = ~R20
	and R19,R20				; bitmask &= R20
	done:					;done:
	inc R16					; cycle++
	ldi ZL, LOW(cycle)
	ldi ZH, HIGH(cycle)
	st Z+,R16				; save cycle
	st Z,R17				; save current_servo
	ldi R17,0xFF
	eor R19,R17
	out PORTD,R19
reti

RESET:
							; Set the stack pointer
	ldi R16, LOW(RAMEND)	
	ldi R17, HIGH(RAMEND)
	out SPL, R16
	out SPH, R17

	call PORT_SETUP
	call SET_MEMORY
	call START_TIMERS
	hang:
	jmp hang

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

I just noticed you have a write order problem in the original version:

   ldi R16, LOW(20000) 
   ldi R17, HIGH(20000) 
   out OCR1AL, R16 
   out OCR1AH, R17 

(OCR1AH must be written first).
There is no similar obvious problem with the new code.

BTW, regarding the solution, you should note that what you are doing is just making a low resolution counter using timer 1. Why not just use the actual timer 1 counter (and the compare register)? A common solution, for servo signal generation, is to use the timer in the normal mode and just add time in OCR1A (for the next time you need something to happen with the port). In C it would look like this:
https://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=38601&postdays=0&postorder=asc&start=24
/Lars

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

That soultion seems nice, do you have any benchmarks on it?

The point in my code really is to have a low res PWM's, in this manner i can squeeze in all the calculations in the first 8ms of the 20ms cycle. In a worst case scenario it only uses 9% of all cycles for the PWM calculations.

Your code might be better, i have to study it further. That approach seems like a very efficient way to do it.

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

Quote:
mega16 runs @ 8MHz
Have you already set the fuses for that?

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

js wrote:
Quote:
mega16 runs @ 8MHz
Have you already set the fuses for that?

Yes but i abandoned that approach and I am making a custom version of Lars's algorithm now. I realize how stupid mine was :) Thanks Lasse!!