A simple program to make an LED gradually cycle from bright to dim and back again. In this code, I don't use the AVR chip's built-in PWM hardware, mainly because I hadn't worked out how to set it up. At least, I hadn't in 2006; this code is quite old now. A better way to control the brightness of an LED would be to connect it to one of the AVR's PWM pins and do the pulse-width modulation in hardware.
Atmel ATmega8 assembler source code
; ledpwm.asm --- drive a blue LED with PWM 21/04/2006
; Copyright (c) 2006 John Honniball
.include "m8def.inc"
.org 0x0000
; Blue LED on Port B bit 2
.equ LEDPORT = PortB
.equ LEDBIT = 2
; This program drives a single LED connected to the AVR's I/O port. It
; is connected so that the cathode of the LED is wired to the AVR pin,
; and the anode of the LED is wired to the 5V power supply via a
; resistor. The value of that resistor depends on the colour of the LED,
; but is usually a few hundred ohms.
; We control the brightness of the LED with Pulse Width Modulation (PWM),
; for two reasons. Firstly, we have no analog outputs on the AVR chip,
; only digital ones. Secondly, a LED's brightness does not respond
; linearly to variations in supply voltage, but it responds much better
; to PWM.
; Pulsating LED looks better if it never quite goes "off", but cycles from
; full brightness to a dim state, and back again
.equ MINBRIGHT = 10
.equ MAXBRIGHT = 255
; This value controls how fast the LED cycles from bright to dim. It is
; the number of PWM cycles that we generate for each step in the brightness
; ramp, up and down. Larger numbers will make the pulsation slower.
.equ NCYCLES = 8
; Block of interrupt vectors for the AVR ATmega8 (unused in this program)
rjmp RESET ; Reset Handler
rjmp EXT_INT0 ; IRQ0 Handler
rjmp EXT_INT1 ; IRQ1 Handler
rjmp TIM2_COMP ; Timer2 Compare Handler
rjmp TIM2_OVF ; Timer2 Overflow Handler
rjmp TIM1_CAPT ; Timer1 Capture Handler
rjmp TIM1_COMPA ; Timer1 CompareA Handler
rjmp TIM1_COMPB ; Timer1 CompareB Handler
rjmp TIM1_OVF ; Timer1 Overflow Handler
rjmp TIM0_OVF ; Timer0 Overflow Handler
rjmp SPI_STC ; SPI Transfer Complete Handler
rjmp USART_RXC ; USART RX Complete Handler
rjmp USART_UDRE ; UDR Empty Handler
rjmp USART_TXC ; USART TX Complete Handler
rjmp ADCONV ; ADC Conversion Complete Handler
rjmp EE_RDY ; EEPROM Ready Handler
rjmp ANA_COMP ; Analog Comparator Handler
rjmp TWSI ; Two-wire Serial Interface Handler
rjmp SPM_RDY ; Store Program Memory Ready Handler
; Start of program execution after a Reset
RESET: ldi r16,low(RAMEND) ; Initialise stack to top of RAM
out SPL,r16
ldi r16,high(RAMEND)
out SPH,r16
; Initialise the hardware
ldi r16,0xff ; Set Port B to all outputs
out DDRB,r16
sbi LEDPORT,LEDBIT ; Switch off blue LED by setting output pin high
; Start with LED at its lowest level, then ramp up to maximum
dopwm: ldi r17,MINBRIGHT ; R17 holds current brightness level
l1: ldi r18,NCYCLES ; R18 counts PWM cycles, and hence pulsation speed
l2: cbi LEDPORT,LEDBIT ; Output pin low, LED on
mov r16,r17 ; R16 controls length of delay (= R17)
rcall delayn4us ; Call delay subroutine
sbi LEDPORT,LEDBIT ; Output pin high, LED off
ldi r16,255
sub r16,r17 ; R16 controls length of delay (= 255 - R17)
rcall delayn4us ; Call delay subroutine
dec r18 ; Decrement PWM cycle counter
brne l2
inc r17 ; Increase brightness by one step
brne l1
; Now ramp back down to the minimum brightness
ldi r17,MAXBRIGHT ; R17 holds current brightness level
l3: ldi r18,NCYCLES ; R18 counts PWM cycles, and hence pulsation speed
l4: cbi LEDPORT,LEDBIT ; Output pin low, LED on
mov r16,r17 ; R16 controls length of delay (= R17)
rcall delayn4us ; Call delay subroutine
sbi LEDPORT,LEDBIT ; Output pin high, LED off
ldi r16,255
sub r16,r17 ; R16 controls length of delay (= 255 - R17)
rcall delayn4us ; Call delay subroutine
dec r18 ; Decrement PWM cycle counter
brne l4
dec r17 ; Decrease brightness by one step
cpi r17,MINBRIGHT ; Have we reached the minimum?
brne l3
rjmp dopwm ; Loop back to start
; DELAYN4US
; Delay for (R16 * 4) microseconds
delayn4us: tst r16 ; R16 = 0? (no delay)
breq dly4
dly2: ldi r24,low(16)
ldi r25,high(16)
dly3: sbiw r24,1 ; 2 cycles
brne dly3 ; 2 cycles
dec r16
brne dly2
dly4: ret ; Return to caller
; Block of dummy interrupt routines (we don't use interrupts in this program)
EXT_INT0: reti ; IRQ0 Handler
EXT_INT1: reti ; IRQ1 Handler
TIM2_COMP: reti ; Timer2 Compare Handler
TIM2_OVF: reti ; Timer2 Overflow Handler
TIM1_CAPT: reti ; Timer1 Capture Handler
TIM1_COMPA: reti ; Timer1 CompareA Handler
TIM1_COMPB: reti ; Timer1 CompareB Handler
TIM1_OVF: reti ; Timer1 Overflow Handler
TIM0_OVF: reti ; Timer0 Overflow Handler
SPI_STC: reti ; SPI Transfer Complete Handler
USART_RXC: reti ; USART RX Complete Handler
USART_UDRE: reti ; UDR Empty Handler
USART_TXC: reti ; USART TX Complete Handler
ADCONV: reti ; ADC Conversion Complete Handler
EE_RDY: reti ; EEPROM Ready Handler
ANA_COMP: reti ; Analog Comparator Handler
TWSI: reti ; Two-wire Serial Interface Handler
SPM_RDY: reti ; Store Program Memory Ready Handler
Return to Creative Technology page
Return to John Honniball's home page
Copyright © 2006-2009 by John Honniball. All rights reserved.