uC News Programmer Apps & More Downloads Info & Shop Contact Forum

Beginner's example: chaser

This is a simple chaser using 5 LEDs. An additional switch can be used to change the direction of the running leds. This is a very simple circuit, especially made for beginners.

The 100 nF cap is used to stabilize the AVR's power supply, the two other caps and the quartz form the standard oscillator described in the data book. The 4K7 resistor on the reset pin could also be set to 0 or left away because there is also an internal pull-up inside the chip. There should be no questions concerning the LEDs and the switch.

And now take a look at the listing:

 

.device AT90S1200

.def	delay	= R16		; define the registers we want to work with
.def	leds	= R17		; (only 16..31 can be loaded with a constant !)
.def	work	= R18

.equ	PortB	= $18		; these are I/O-registers we require
.equ	DDRB	= $17
.equ	PinB	= $16

reset:	ldi	work,$1f
	out	DDRB,work	; Set Pins PB0..PB4 to output, rest input
	ldi	work,$80
	out	PortB,work	; Enable internal pullup on PB7 and turn off
							; all LEDs

	clr	leds		; Init LED-pattern (all 0)

loop:	sbic	PinB,7		; Check RB7-Pin
	rjmp	left		; branch if Direction should be left

	lsr	leds		; shift LED-pattern to the right
	tst	leds		; Check if all 0
	brne	continue		; no => continue
	ldi	leds,$10		; load new LED pattern (PB4 on, rest off)
	rjmp	continue

left:	lsl	leds		; shift LED-pattern to the left
	tst	leds		; all 0 ?
	brne	continue
	ldi	leds,$01   	; (PB0 on, rest off)

continue:
	mov	work,leds		; copy LED pattern
	sbr	work,$80		; and ensure pullup on PB7 is enabled
	out	PortB,work	; write new pattern to the output
        
	ldi	delay,195		; a little delay loop so we can see the movement
delay1:	ldi	work,255		; internal loop: we'll have to wait
delay2:	nop			; about 250.000 cycles
	nop
	dec	work       
	brne	delay2
	dec	delay
	brne	delay1

	rjmp	loop		; back to the main loop

   

Note: There is small gap if the LEDs are running to the left - can you fix it (exercise !) ?



The stack

All AVRs expect the 90S1200 use a software stack. This means, that you have to set the stack pointer before making the first call or allowing any interrupts.

On the 90S2313 the stack pointer is 8-bit wide only, the other ones use 'full' 16 bits. Commonly the stack pointer is set to the last s-ram location at startup.

Place these lines at the beginning of your code, right after the interrupt table:

 

.equ	sph	= $3e		; Stack pointer (or use include)
.equ	spl	= $3d

.def	wr	= R18		; Register definition

reset:	clr	wr		; init stack pointer
	out	sph,wr		; (hi-byte not required on 90S2313)
	ldi	wr,$df    
	out	spl,wr		; (last s-ram location of an 90S2313)
	ret




Using the UART

These are subs for using the UART in polling-mode (Has especially advantages on the S8515: Avoids the IRQ-Bug !).
Before calling 'send' you have to put the character to send into 'R 18' (wr). After calling 'receive' you can find the received character in 'R 18' (wr).

 

.equ	udr	= $0c		; UART registers (or use include)
.equ	usr	= $0b
.equ	ucr	= $0a
.equ	ubrr	= $09

.def	wr	= R18		; Register definition

init:	ldi	wr,23		; init baudrate (19200 bps @ 7,328 MHz)
        	out	ubrr,wr
        	ldi	wr,$18		; init UART for transmit and receive
        	out	ucr,wr
        	ret

send:	sbis	usr,5		; send character
	rjmp	send
	out	udr,wr
	ret

receive:				; wait for and receive character
	sbis	usr,7   
	rjmp	receive
	in	wr,udr
	ret




Using the PWM

These are subs for using the PWM on an 90S2313 (Should work also on other devices).
At first, calling 'pwmini', the PWM-module is initialized with the output set to zero. Now the output can be changed using 'pwmset' which expects the new control value in 'R 18' (wr).

 

.equ	tccr1a	= $2f		; TMR1 registers (or use include)
.equ	tccr1b	= $2e
.equ	tcnt1h	= $2d
.equ	tcnt1l	= $2c
.equ	ocr1ah	= $2b
.equ	ocr1al	= $2a

.def	wr	= R18		; Register definition

pwmini:	clr	wr		; first clear all registers
	out	tccr1a,wr
	out	tccr1b,wr
	out	tcnt1h,wr		; Remember: always write hi-byte first ! 
	out	tcnt1l,wr
	out	ocr1ah,wr
	out	ocr1al,wr

	ldi	wr,$81		; now select pwm mode (8-bit, non inverted)...
	out	tccr1a,wr
	ldi	wr,$02		; ... and the right clock
	out	tccr1b,wr
	ret

pwmset:	out	ocr1al,wr		; write new pwm-value (hi-byte doesn't care - 8 bit only)
	ret


Note: This is an example for 8-bit PWM. See datasheet how to expand to 9 or 10 bits !



Controlling a small character LC-Display module

The following example explains connecting an LCD-Module featuring an Hitachi-based controller (HD44780).

LCD-Module: schematic & pin assignment
Pin Function
1 GND
2 Vcc
3 Vee
4 C/D
5 R/W
6 E
7 D0
8 D1
9 D2
10 D3
11 D4
12 D5
13 D6
14 D7

First of all, it's important to check this assignment compared to the datasheet delivered with your module ! Some models may use a different assignment and swap GND and Vcc can destroy the module at once ! The module is driven in 4-bit-mode order to save uC-pins (only difference 8-bit-mode: each byte need two cycles be transfered.). Additionally we leave away possibillity read from module. This makes it necessary wait always for maximum time of execution after sending a command or data. The example is timed for a clock of 4 MHz or slower.

First the module must be initialized.'lcdinit' does this also switching to the 4-bit-mode. After this you can send commands or data using 'lcdcmd' or 'lcddata' respectively. Both subs expect the command/character in 'R 18' (work).

Explaining all commands would take too long here. So I only tell you the most important one: 'Set Data Address'. This command lays down where to write the next character inside the display ram - set bit #7 to one and write the address to the other bits (Short: Address or $80). The address depends strongly on the module - my '16 x 1' uses $00 - $07 for the first eight characters and $40 - $47 for the second, others are linear and use $00 - $0f. However, you can look up in the datasheet for this value or simply try them - there nothing which can be damaged then executing these tests. And here's the listing, writing also a few characters to the display as a test:

 

 ; LCD - Controlled by AVR

.equ	portddr	= $11
.equ	portd	= $12
.equ	portdi	= $10
.equ	portbdr	= $17
.equ	portb	= $18
.equ	portbi	= $16
.equ	stack	= $3d

.equ	d7	= 7		; Port B
.equ	d6	= 6
.equ	d5	= 5
.equ	d4	= 4  
.equ	rs	= 5		; Port D
.equ	e	= 6

.def	work	= r18
.def	wt1	= r25
.def	wt2	= r26

start:  

 ; The next two lines are nesccessary if using an other AVR than
 ; 90S1200. They initialize the software stack !

	ldi	work,$df		; init stack
	out	stack,work

	ldi	work,$60
	out	portddr,work
	ldi	work,$f0
	out	portbdr,work
	ldi	work,$00
	out	portd,work
	ldi	work,$00
	out	portb,work

	rcall	lcdinit

	ldi	work,$80
	rcall	lcdcmd
	ldi	work,'H'
	rcall	lcddata
	ldi	work,$81
	rcall	lcdcmd
	ldi	work,'a'
	rcall	lcddata
	ldi	work,$82
	rcall	lcdcmd
	ldi	work,'l'
	rcall	lcddata
	ldi	work,$83
	rcall	lcdcmd
	ldi	work,'l'
	rcall	lcddata
	ldi	work,$84
	rcall	lcdcmd
	ldi	work,'o'
	rcall	lcddata

finito:	rjmp	finito

 ; Write character into the display memory -
 ; it must be supplied in 'R18' (work).

lcddata:        
	sbi     portd,rs     
	rjmp    docmd        

 ; Send a command to the display module -
 ; it must be supplied in 'R18' (work).

lcdcmd: 
	cbi	portd,rs     
docmd:	cbi	portd,e      
        cbi	portb,d7
        sbrc	work,7
        sbi	portb,d7
        cbi	portb,d6
        sbrc	work,6
        sbi	portb,d6
        cbi	portb,d5
        sbrc	work,5
        sbi	portb,d5
        cbi	portb,d4
        sbrc	work,4
        sbi	portb,d4
        rcall	epulse      
        cbi	portb,d7
        sbrc	work,3
        sbi	portb,d7
        cbi	portb,d6
        sbrc	work,2
        sbi	portb,d6
        cbi	portb,d5
        sbrc	work,1
        sbi	portb,d5
        cbi	portb,d4
        sbrc	work,0
        sbi	portb,d4
        rcall	epulse      
        rcall	delay5
        ret     

 ; This sub initializes the display for the
 ; 4-Bit-mode and clears it.

lcdinit:        
	rcall	delay5        
	rcall	delay5          
	rcall	delay5
	cbi	portd,rs
	cbi	portd,e
	sbi	portb,d4
	sbi	portb,d5
	cbi	portb,d6
	cbi	portb,d7
	rcall	epulse		; set 8-Bit
	rcall	delay5
	rcall	epulse		; set 8-Bit
	rcall	delay5
	rcall	epulse		; set 8-Bit
	rcall	delay5
	cbi	portb,d4
	sbi	portb,d5
	cbi	portb,d6
	cbi	portb,d7
	rcall	epulse		; set 4-Bit
	rcall	delay5
	cbi	portb,d4
	sbi	portb,d5
	cbi	portb,d6
	cbi	portb,d7
	rcall	epulse		; set Font-A
	cbi	portb,d4
	cbi	portb,d5
	sbi	portb,d6
	sbi	portb,d7
	rcall	epulse		; set Font-B
	rcall	delay5
	cbi	portb,d4
	cbi	portb,d5
	cbi	portb,d6
	cbi	portb,d7
	rcall	epulse		; set Dsp-off-A
	cbi	portb,d4
	cbi	portb,d5
	cbi	portb,d6
	sbi	portb,d7
	rcall	epulse		; set Dsp-off-B
	rcall	delay5
	cbi	portb,d4
	cbi	portb,d5
	cbi	portb,d6
	cbi	portb,d7
	rcall	epulse		; set Cursor-A
	cbi	portb,d4
	cbi	portb,d5
	sbi	portb,d6
	sbi	portb,d7
	rcall	epulse		; set Cursor-B
	rcall	delay5
	cbi	portb,d4
	cbi	portb,d5
	cbi	portb,d6
	cbi	portb,d7
	rcall	epulse		; set Entry-A
	cbi	portb,d4
	cbi	portb,d5
	sbi	portb,d6
	cbi	portb,d7
	rcall	epulse		; set Entry-B
	rcall	delay5
	ret

epulse:	sbi	portd,e		; pulse 'E-Clock' 
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	cbi	portd,e
	ret

delay5:	ldi	wt1,50		; delay loop 
wtlp1:	ldi	wt2,255
wtlp2:	dec	wt2
	brne	wtlp2
	dec	wt1
	brne	wtlp1
	ret



Using the internal A/D-Converter

The following subs show how to use the internal ADC of several AVRs - they have been tested on an 90S8535.

First you habe to turn on and init the ADC by calling 'init_adc'. Then you can query any channel using 'do_adc' which expects the channel number in 'R 18' (wr). If you do not need the ADC any more you can turn it off with the 'pd_adc'-sub.

Important note: The first conversion after initializing or changing the channel is INVALID so you have to call 'do_adc' twice in this case.


 

.def	wr	= R18
.def	resH	= R10
.def 	resL	= R11

.equ	admux	= $07
.equ	adcsr	= $06
.equ	adch	= $05
.equ	adcl	= $04

.equ	adsc	= 6

init_adc:
	ldi	wr,$96		; init ADC-control
	out	adcsr,wr		; clr irq, clk = 8 MHz

	ret

do_adc:	andi	wr,$07		; select valid ADC-channel 
	out	admux,wr		; (supplied in wr)

	sbi	adcsr,adsc	; start conversion                           

wt_adc:	sbic	adcsr,adsc	; wait until conversion is
	rjmp	wt_adc		; completed

	in	resL,adcl		; read ADC-result, read 
	in	resH,adch		; always adcl first !

	ret


pd_adc:	ldi	wr,$10		; turn off the ADC
	out	adcsr,wr   

	ret




 Last update: 2003-05-15
© 1998-2003 by G.Müller 
 

This document may not be copied from this server neither to an other electronic media nor in any other way. The only exception is a single hardcopy for learning with it. This is only allowed for clear private purposes, any commercial usage requires the permission from the author. This means the explanations as well as the source codes which have been included on it. The author and the runners of this server don't give any warrenties for consequences of using the information supplied in this document. All brand names which have possible been mentioned belong to their respective owners. AVR is a registered trademark of the Atmel Corporation. For hints about errors or abuse the author will be grateful.