In continuation of the embedded control applications explained in Part 2, let’s now examine the use of inbuilt functions of AVR ATmega8535 (such as output compare, ADC and UART) for various applications.

PWM operation of ATmega8535
When the AVR is configured for pulse-width modulated (PWM) operation, the PWM outputs become available at output-compare pins 18 (OC1A) and 19 (OC1B) of ATmega8535. PWM, in conjunction with an analogue filter, can be used to generate analogue output signals and thus it serves as a digital-to-analogue converter.

Fig. 15: Variation of pulse width (constant period) with time of a typical PWM wave
Fig. 15: Variation of pulse width (constant period) with time of a typical PWM wave
Fig. 16: View of filtered low-frequency sine wave and unfiltered PWM output on an oscilloscope
Fig. 16: View of filtered low-frequency sine wave and unfiltered PWM output on an oscilloscope

76F_Parts

Explore Circuits and Projects Explore Videos and Tutorials

Principle of pulse-width modulation. To generate different analogue levels, the duty cycle and thereby the pulse-width of the digital signal (base frequency) is changed. If a high analogue level is needed, the pulse width is increased and vice versa (see Figs 15 and 16).

A digital pulse train with a constant period (fixed base frequency) is used as the basis. The base frequency, which can be programmed suitably, should be much higher than the frequency of the output analogue signal obtained after filtering out the base frequency component. For example, to generate a sinewave signal of low frequency (say, 10 Hz, as used for drives or controls), the base frequency of rectangular pulses (with varying duty cycle) may be of the order of 1 kHz or more.

Pulse generation method. The scheme for pulse generation is as follows: Timer/counter 1 is used to count clock ticks. If 8-bit PWM is selected, after the timer counts up to ‘255,’ its count is decremented with each clock tick. Thus, the number increases up to ‘255’ and then decreases, resembling a triangular pattern.

When the number stored in the output-compare register (OCR) matches the loaded count value, pin 19 (output-compare action pin) becomes high or low, as programmed. For example, if the OCR is loaded with a value of ‘l00,’ the logic state of OCR pin will be:

Count value   OCR pin

0 to 100        Low logic

100 to 255     High logic

255 to 100     High logic

100 to 0        Low logic

Thus, for the total time taken to count 255×2=510 clock ticks, the output pin (pin 19) will be high for (2×155)/(2×256) or 60.5 per cent of the total triangular wave time of one PWM pulse (or the PWM pulse will have a duty cycle of 60.5 per cent). Thus, effectively the voltage transmitted in this period is 60.5 per cent of the maximum, because the pulse is high only for this period of time.

Fig. 17: Circuit for PWM-based sinewave generation
Fig. 17: Circuit for PWM-based sinewave generation
Fig. 18: ADCH and ADCL registers
Fig. 18: ADCH and ADCL registers
Fig. 19: ADMUX register bits
Fig. 19: ADMUX register bits
Fig. 20: ADCSRA register bits
Fig. 20: ADCSRA register bits

The following program (avrsine.asm) will generate a 1Hz sine wave (after filtering) on pin 19 using PWM:

 

AVRSINE.ASM
;———————————————————————
; File: avrsine.asm
; Description: Example of how to use the fast PWM
; of the Avr to generate “sine-wave” signal. The PWM
; output requires filtering to shape the sine wave
; form.
;———————————————————————
.include “m8535def.inc”
rjmp init

;Interrupt vector table

.org OVF1Addr ;OC1Aaddr
; Interrupt vector for timer1 output compare match A
rjmp TOF_isr

;Main code
init:
ldi R16,low(RAMEND)
; Load low byte address of end
of RAM into register R16
out SPL,R16
; Initialize stack pointer to end of internal RAM
ldi R16,high(RAMEND)
; Load high byte address of end of RAM into
register R16
out SPH, R16
; Initialize high byte of stack pointer to end of
internal RAM
ldi r16,$ff
out ddrb,r16
ldi r16,$55
out portb,r16

ldi r16, (1<
out DDRd, r16 ;since that is the PWM
output pin 19

;SELECT CLOCK SOURCE VIA TCCR1B
LDI R16,$81

;8 BIT PWM NON-INV.
; Set PWM mode: toggle OC1A on compare
out TCCR1A, r16
; Enable PWM

ldi r16, 0xFF
; Set PWM top value: OCR1C = 0xFF
out OCR1AL, r16
LDI R16,0
OUT OCR1AH, R16

; Enable Timer/Set PWM clock prescaler
LDI R16,02
OUT TCCR1B,R16 ;ck/8 as pwm clock
(1MHz/8 = 125 kHz)

ldi r16, (1<
Ovrflow interrupt
out TIMSK, r16
clr r17
clr r18
sei
; Enable global interrupts
idle:
ldi r16, (1<
out MCUCR, r16
sleep
rjmp idle

TOF_isr:
ldi ZH, high(sine_table*2)
; Set up Z to point to the beginning of
sine_table
ldi ZL, low(sine_table*2)
add ZL, r17
; Offset Z by r18:r17
adc ZH, r18
lpm
; Load sine_table[Z] into OCR1A
out OCR1AL, r0
inc r17

reti

sine_table: ; 256 values
.db 128,131,134,137,140,144,147,150,153,156,159,162,
165,168,171,174
.db 177,179,182,185,188,191,193,196,199,201,204,206,
209,211,213,216
.db 218,220,222,224,226,228,230,232,234,235,237,239,
240,241,243,244
.db 245,246,248,249,250,250,251,252,253,253,254,254,
254,254,254,254
.db 254,254,254,254,254,254,254,253,253,252,251,250,
250,249,248,246
.db 245,244,243,241,240,239,237,235,234,232,230,228,
226,224,222,220
.db 218,216,213,211,209,206,204,201,199,196,193,191,
188,185,182,179
.db 177,174,171,168,165,162,159,156,153,150,147,144,
140,137,134,131
.db 128,125,122,119,116,112,109,106,103,100,97,94,91,
88,85,82
.db 79,77,74,71,68,65,63,60,57,55,52,50,47,45,43,40
.db 38,36,34,32,30,28,26,24,22,21,19,17,16,15,13,12
.db 11,10,8,7,6,6,5,4,3,3,2,2,2,1,1,1
.db 1,1,1,1,2,2,2,3,3,4,5,6,6,7,8,10
.db 11,12,13,15,16,17,19,21,22,24,26,28,30,32,34,36
.db 38,40,43,45,47,50,52,55,57,60,63,65,68,71,74,77
.db 79,82,85,88,91,94,97,100,103,106,109,112,116,119,
122,125

For observation of the sine wave on an oscilloscope, use a low-pass filter comprising a 1-kilo-ohm resistor (series element) and a 1µF capacitor (shunt element). However, with an analogue multimeter, the sine wave can be directly observed at pin 19.

Fig. 21: Circuit for temperature display on either the LCD or the PC
Fig. 21: Circuit for temperature display on either the LCD or the PC
Fig. 22: Screenshot of ADC_CHSEL application
Fig. 22: Screenshot of ADC_CHSEL application

In the avrsine.asm program, for each of the triangle wave periods, we read a table of sine values (multiplied by ‘256’) and load these values one by one into the OCR. Since the values vary in a sinusoidal pattern, the pulses that come out are also pulse-width modulated as per these values (see the oscilloscope pattern shown in Fig. 16).

To do the table look-up (as given in the example program ‘LCD Table.ASM’ of Part 1), the LPM instruction is used. The Z register is used as an indirect indexed register. As stated earlier, the LPM instruction fetches from the table one byte into r0. The actual loading of the OCR value is done by the instruction:

 

out OCR1AL, r0

where OCR1AL refers to pin 19. (OCR1BL refers to pin 18, which is not used here.)

The program contains suitable comments for easy understanding. The table in the program has 256 elements (corresponding to the samples in one complete sinewave period), while each sample period = pulse period (high and low parts) = 510 clock ticks. Thus 256 (samples) × 510 (clock ticks) = 130,560 clock ticks will produce one sinewave cycle. Thus for producing exactly 1Hz frequency, the base frequency should be 130.56 kHz (the nearest value of 125 kHz has been used here).

The circuit for realising the PWM-based sinewave generator is shown in Fig. 17.

The avrsine.asm file and the assembled .hex file are given in the CD. Using the AT-PROG programmer, load the program into an ATmega8535. Then fix it in a breadboard and make connections as per Fig. 17. Connect the circuit to 5V power supply and observe approximately 1Hz sine wave at pin 19 using an analogue multimeter. The needle on the multimeter will move with the sine wave as a pendulum.

READ
TELEPHONE-OPERATED CALLING SYSTEM

Using the ADC
The inbuilt analogue-to-digital converter (ADC) of ATmega8535 is an 8-channel device with 10-bit resolution and maximum conversion time of 65 µs. The reference voltage for the ADC is connected across pins 32 (positive) and 31 (ground). The 5V Vcc supply (either directly or through a potmeter) can be used as reference voltage, but a capacitor at pin 32 is to be used for decoupling.

To access the ADC, you need to select the ADC channel; while the use of ADC interrupt is left to the discretion of the programmer. The ADC is read after conversion of a sample via the ADCH and ADCL registers (8 bits from the ADCL register and only two bits from the ADCH register) as shown in Fig. 18.

ADMUX and ADCSRA are the other registers used in conjunction with the ADC. Functions of various bits of these registers are explained below.

ADMUX register. The ADMUX register bits are shown in Fig. 19.

Bits 4 through 0 of ADMUX select the ADC channels for single-ended or differential operation including channels with gain. (For full selection details, see Table 85 of the ATmega8535(L) datasheet.)

Bit 5 (ADLAR, or AD left adjust result) affects selection of results in ADCH and ADCL registers. If this bit is made ‘0,’ the ADCL contains the least eight bits and the ADCH contains the remaining two high-order bits in its D1:D0 bit positions. When the ADLAR bit is set to ‘1,’ the ADCH contains the most significant eight bits, while the ADCL contains the least two significant bits in bit positions 7 and 6.

Bits 6 and 7 (REFS0 and REFS1) are reference-selection bits. With bit 7 as ‘0’ and bit 6 as ‘1,’ the external reference voltage is applied to pin Aref (32).

We write E0 (1110 0000b) to ADMUX register in the ADC_LCD.ASM program. That means we choose channel-0 (pin 40) for the signal input, ADCH to give us the most significant eight bits and external 5V reference at pin 32 for analogue-to-digital conversion.

ADCSRA register. This is the control-and-status register for the ADC. Its bit positions are shown in Fig. 20.

The bits of the ADCSRA stand for the following signals: ADC enable (bit 7), ADC start (bit 6), ADC auto-trigger enable for free-running (bit 5), ADC interrupt flag set on completion of conversion (bit 4), ADC interrupt enable when set (bit 3) and ADC prescaler for speed (bits 0, 1 and 2). Bits 0, 1 and 2 determine the division factor between the clock frequency and the input clock to the ADC. The division factor can be selected from ‘2’ to ‘128’ as per Table 86 of the datasheet.

Program for displaying the ADC output on the LCD
The following program (adc_lcd.asm) takes the ADC data, converts the 10-bit data into five decimal digits and then shows it continuously on the LCD screen:

 

ADC_LCD.ASM
; *********************************************************
; *This program uses channel -0 ADC of ATmega8535
; It reads the ADC and outputs the five-digit
; number on LCD.
; Program authored by Prof. K. Padmanabhan
; ***********************************************************
.NOLIST
.INCLUDE “m8535def.inc”
;device =ATMega8535
.LIST
.EQU xyz = 12345
.EQU fq=1000000; clock freq. of internal oscillator
.EQU baud=9600; Baudrate of SIO comm.
.EQU bddiv=(fq/(16*baud))-1; Baudrate divider
.DEF rmpr = R16
.DEF temp = R14
.DEF result=R12
.DEF mpr =R16
.CSEG
.ORG $0000
; Reset- and Interrupt-vectors
rjmp Start ; Reset-vector
.org OVF0Addr ; timer-0 overflow interrupt vector
address
rjmp timer0prg
timer0prg: ;here take ADC sample at every 64 µs
ldi r16,$cc
out portc,r16
push r16
in r16,SREG
PUSH R16
here2:in r16,adcsra
andi r16,0b01000000
brne here2 ;value got
in r16,adcl
in r17,adch
rcall lcddisp
POP R16
out SREG,R16
POP R16 ;restart adc
ldi r16,0b11000101 ;prescale /32
(1×32=32 µs)
;adc enable,adc start,adc
freerun,adcflag,adcno int,
adcprescale/32
out adcsra,r16
RETI ;End of ISR
cmd: cbi portc,2 ;command entry to
LCD routine
cbi portc,3
cbi portc,4
out portb,r16
sbi portc,4
nop
nop
nop
nop
nop
cbi portc,4
rcall delay1
ret
lcdwr:cbi portc,2; wrtite to LCD
routine
cbi portc,3
cbi portc,4
sbi portc,2
out portb,r16
sbi portc,4
nop
nop
nop
nop
nop
cbi portc,4
rcall delay1
ret
busy: cbi portc,2
sbi portc,3 ;read/write high?
cbi portc,4 ;chip select low
nop
nop
sbi portc,4 ;chip select high
busy1:lds R16,pinb
rol R16
brcs busy1
cbi portc,4
ret
init_lcd: ;initialise LCD
ldi R16,$38
rcall cmd
rcall delay1
rcall delay1
ldi R16,$0e
rcall cmd
rcall delay1
ldi R16,6
rcall cmd
ldi r16,1
rcall cmd
rcall delay1
ret
delay1:clr result
loop22:ldi R16,$f0
loop2:inc R16
brne loop2
inc result
brne loop22
ret
lcddisp: push r16
ldi r16,128 ;cursor to left end
rcall cmd
pop r16
rcall binbcd
mov r16,r15
andi r16,0x0f
ori r16,0x30
rcall lcdwr ; 1
mov r16,r14
andi r16,0b11110000
ror r16
ror r16
ror r16
ror r16
ori r16,0x30
rcall lcdwr ;2
mov r16,r14
andi r16,0x0f
ori r16,0x30
rcall lcdwr ;3
mov r16,r13
andi r16,0b11110000
ror r16
ror r16
ror r16
ror r16
ori r16,0x30
rcall lcdwr ;4
mov r16,r13
andi r16,0x0f
ori r16,0x30
rcall lcdwr ;5
ret
binbcd:
;* “bin2BCD16” – 16-bit Binary to BCD conversion
;* converts 16-bit number (fbinH:fbinL) to a 5-digit
;* packed BCD number represented by 3 bytes
(tBCD2:tBCD1:tBCD0).
;* MSD of 5-digit number is placed in lowermost
nibble of tBCD2.
;* Number of words :25
;* Number of cycles :751/768 (Min/Max)
;* Low registers used :3 (tBCD0,tBCD1,tBCD2)
;* High registers used :4(fbinL,fbinH,cnt16a,tmp16a)
;* Pointers used :Z
Subroutine register variables
.equ AtBCD0 =13
;address of tBCD0
.equ AtBCD2 =15
;address of tBCD1
.def tBCD0 =r13
;BCD value digits 1 and 0
.def tBCD1 =r14
;BCD value digits 3 and 2
.def tBCD2 =r15
;BCD value digit 4
.def fbinL =r16
;binary value Low byte
.def fbinH =r17
;binary value High byte
.def cnt16a =r18
;loop counter
.def tmp16a =r19
;temporary value
bin2BCD16:
ldi cnt16a,16 ;Init loop counter
clr tBCD2
;clear result (3 bytes)
clr tBCD1
clr tBCD0
clr ZH
;clear ZH (not needed for AT90Sxx0x)
bBCDx_1:lsl fbinL ;shift input value
rol fbinH
;through all bytes
rol tBCD0 ;
rol tBCD1
rol tBCD2
dec cnt16a
;decrement loop counter
brne bBCDx_2 ;if counter not zero
ret ; return

bBCDx_2:ldi r30,AtBCD2+1
;Z points to result MSB + 1
bBCDx_3: ld tmp16a,-Z ;get (Z) with
pre-decrement
subi tmp16a,-$03 ;add 0x03
sbrc tmp16a,3 ;if bit 3 not clear
st Z,tmp16a ;store back
ld tmp16a,Z ;get (Z)
subi tmp16a,-$30 ;add 0x30
sbrc tmp16a,7 ;if bit 7 not clear
st Z,tmp16a ;store back
cpi ZL,AtBCD0 ;done all three?
brne bBCDx_3 ;loop again if not
rjmp bBCDx_1

READ
Electronics Projects: Solar Light For A Portable Toilet

; Main program routine starts here
Start:ldi R16,low(RAMEND);Load low byte address
of end of RAM into register R16
out SPL,R16; Initialize stack
pointer to end of internal RAM
ldi R16,high(RAMEND);Load
high byte address of end of
RAM into register R16
out SPH, R16; Initialize high byte of stack
pointer to end of internal RAM
ldi rmpr,0b00000001;TIMER 0 INTERRUPT ENABLE
out TIMSK,rmpr
ldi rmpr,05 ; So, we get once 1×10^6/1024=1000 Hz
out TCCR0,rmpr ;prescalar 1024 so that timer
interrupt occurs at 1KHz rate
ldi r16,$c0 ;c0 for int. ref, e0 with adch alone used.
out admux,r16 ;channel 0 is selected
ldi r16,0b11000101 ;prescale /32 (1×32=33 usec)
;adc enable,adc start,adc freerun,adcflag,adcno int,
adcprescale/32
out adcsra,r16
ldi r16,0
out sfior,r16 ;write 0-0-0 to bits d7-d5 for free run
adc
here1:in r16,adcsra
andi r16,0b01000000
breq here1 ;value got
ldi R16,255
out ddrb,R16 ; port b is all bits output
out ddrc,R16 ; so is port c
ldi r16,0
out ddra,r16 ;port a input
init: sei ;enable global interrupt
LCD: rcall init_lcd
ldi R16,$80
rcall cmd
here3:in r16,adcsra
andi r16,0b01000000
brne here3 ;value got
in r16,adcl
in r17,adch
rcall lcddisp
idle: ldi r16,(1<
out mcucr,r16
sleep
rjmp idle
restrt:ldi r16,$80 ;point to first cursor
rcall cmd ; command to lcd to position cursor
rcall delay1
ldi r16,0b11000101 ;prescale /32 (4.43/32=138
usec)=7.2Khz
;adc enable,adc start,adc freerun,adcflag,adcno int,
adcprescale/32
out adcsra,r16
here4: in r16,adcsra
andi r16,0b01000000
brne here4 ;value got
in r16,adcl
in r17,adch
sbi adcsra,6 ;restart adc
hh: rcall lcddisp
RJMP restrt ; Test of the serial interface

Note. The adc_lcd.asm program together with the .hex file, for directly programming into the chip, is provided in the EFY-CD.

Fig. 21 shows the circuit for viewing the analogue temperature (°C) output of an LM35 temperature sensor IC connected to ADC Ch.0 (pin 40) of the AVR on the LCD screen in 5-digit decimal format after analogue-to-digital conversion using the ATmega8535 chip with the adc_lcd program. The same circuit with addition of MAX232 chip and ATmega8535 can be used for interfacing to a PC for viewing the temperature data on the PC screen. However, for that you have to program the AVR with the firmware as described in the succeeding paragraphs.

Using the UART in the ATmega8535
Serial communication between the microcontroller and a PC is essential for data transfer to the microcontroller and reading of its ADC output by the PC. The universal asynchronous receiver transmitter (UART) built into the microcontroller can be programmed to operate at certain baud rates.
The adc_ch.asm sample program given below is useful for UART applications:

 

ADC_CH.ASM
; *********************************************************
; This program read one of ADC channels (0 to 7).
The Channel can be selected by sending Channel
number.
; ATmega8535 receives the Channel no. and outputs
the five digit ADC value
; on RS232 port for reading by a PC’s XTALK
program or a VB project.
; Software features: It is possible to read the ADC
value and also
; transmit to the PC for data logging.
; *********************************************************
.NOLIST
.INCLUDE “m8535def.inc”
;device =ATMega8535
.LIST
.EQU xyz = 12345
; Constants for Sio properties
.EQU fq=1000000; clock frequency of m8535 with
internal oscillator
.EQU baud=4800; Baudrate for SIO communication
.EQU bddiv=(fq/(16*baud))-1; Baudrate divider
.DEF rmpr = R16
.DEF temp = R14
.DEF result=R12
.DEF mpr =R16
.CSEG
.ORG $0000
rjmp Start ; Reset-vector
.org $000b
rjmp USART_RXC
.org $0100
InitSio:
LDI rmpr,bddiv ; Init baud generator
OUT UBRRL,rmpr ; set divider in UART
baud rate register
ldi rmpr, 0
out ubrrh,rmpr
LDI rmpr,(1<
(1<
out UCsRB,rmpr
LDI ZL,0 ; Wait some time
LDI ZH,0
InitSio1:
SBIW ZL,1
BRNE InitSio1
ldi r16,(1<
(3<
out ucsrc,r16
RET
USART_RXC:
push r16
in R16,udr
andi R16,07
mov r1,r16
ori r16,$c0
out admux,r16
ldi r16,$43
rcall tout ; intimate new channel to host
ldi r16,$48
rcall tout
mov r16,r1
ori r16,$30
rcall tout
pop r16
reti
cmd: cbi portc,2
cbi portc,3
cbi portc,4
out portb,r16
sbi portc,4
nop
nop
nop
nop
nop
nop
nop
nop
nop
cbi portc,4
rcall delay1
rcall delay1
rcall delay1
ret
lcdwr:cbi portc,2
cbi portc,3
cbi portc,4
sbi portc,2
out portb,r16
sbi portc,4
nop
nop
nop
nop
nop
nop
nop
nop
nop
cbi portc,4
rcall delay1
ret
init_lcd:
ldi R16,$38
rcall cmd
rcall delay1
rcall delay1
ldi R16,$0e
rcall cmd
rcall delay1
ldi R16,6
rcall cmd
ldi r16,1
rcall cmd
rcall delay2
ret
tout: sbis UcSRa,UDRE ;TX COMPLETE check
RJMP tout
OUT UDR,R16
Ret
delay1:push r16
clr result
loop22:ldi R16,$f0
loop2:inc R16
brne loop2
inc result
brne loop22
pop r16
ret
delay2:push r16
clr result
loop221:ldi R16,$f0
loop21:inc R16
brne loop21
inc result
brne loop221
pop r16
ret
delay: clr result
ld: inc result
brne ld
ret
lcddisp:push r16
ldi r16,128 ;cursor to left end
rcall cmd
pop r16
rcall delay1
rcall delay1
rcall binbcd
mov r16,r15
andi r16,0x0f
ori r16,0x30
rcall tout
rcall lcdwr ; 1
mov r16,r14
andi r16,0b11110000
ror r16
ror r16
ror r16
ror r16
ori r16,0x30
rcall tout
rcall lcdwr ;2
mov r16,r14
andi r16,0x0f
ori r16,0x30
rcall tout
rcall lcdwr ;3
mov r16,r13
andi r16,0b11110000
ror r16
ror r16
ror r16
ror r16
ori r16,0x30
rcall tout
rcall lcdwr ;4
mov r16,r13
andi r16,0x0f
ori r16,0x30
rcall tout
rcall lcdwr ;5
ldi r16,$0a
rcall tout
ldi r16,$0d
rcall tout
ret
binbcd:
;* “bin2BCD16” – 16-bit Binary to BCD conversion
;* convert 16-bit number (fbinH:fbinL) to a 5-digit
;* packed BCD number represented by 3 bytes
(tBCD2:tBCD1:tBCD0).
;* MSD of 5-digit number is placed in the lowermost
nibble of tBCD2.
;* Number of words :25
;* Number of cycles :751/768 (Min/Max)
;* Low registers used :3 (tBCD0,tBCD1,tBCD2)
;* High registers used :4(fbinL,fbinH,cnt16a,tmp16a)
;* Pointers used :Z

; Subroutine Register Variables
.equ AtBCD0 =13 ;address of tBCD0
.equ AtBCD2 =15 ;address of tBCD1
.def tBCD0 =r13 ;BCD value digits 1 and 0
.def tBCD1 =r14 ;BCD value digits 3 and 2
.def tBCD2 =r15 ;BCD value digit 4
.def fbinL =r16 ;binary value Low byte
.def fbinH =r17 ;binary value High byte
.def cnt16a =r18 ;loop counter
.def tmp16a =r19 ;temporary value

READ
Electronics Projects: Controlling a Stepper Motor Using a Rotary Encoder

bin2BCD16:
ldi cnt16a,16 ;Init loop counter
clr tBCD2 ;clear result (3 bytes)
clr tBCD1
clr tBCD0
clr ZH ;clear ZH (not
needed for AT90Sxx0x)
bBCDx_1:lsl fbinL ;shift input value
rol fbinH ;through all bytes
rol tBCD0
rol tBCD1
rol tBCD2
dec cnt16a ;decrement loop counter
brne bBCDx_2 ;if counter not zero
ret ; return
bBCDx_2:ldi r30,AtBCD2+1 ;Z points to
result MSB + 1
bBCDx_3:
ld tmp16a,-Z ;get (Z) with pre-decrement
subi tmp16a,-$03 ;add 0x03
sbrc tmp16a,3 ;if bit 3 not clear
st Z,tmp16a ;store back

ld tmp16a,Z ;get (Z)
subi tmp16a,-$30 ;add 0x30
sbrc tmp16a,7 ;if bit 7 not clear
st Z,tmp16a ; store back
cpi ZL,AtBCD0 ;done all three?
brne bBCDx_3 ;loop again if not
rjmp bBCDx_1 ;End of the subroutine
section

; Main program routine starts here
Start:ldi R16,low(RAMEND);Load low byte address
of end of RAM into
register R16
out SPL,R16 ; Initialize stack pointer to
end of internal RAM
ldi R16,high(RAMEND);Load high byte
address of end
of RAM into
register R16
out SPH, R16 ;Initialize high
byte of stack pointer
to end of internal RAM
ldi r16,$c0 ; c0 for int. ref, e0 with adch alone used.
out admux,r16 ; channel 0 is selected
ldi r16,0b11000101 ;prescale /32 (1×32=33 usec)
;adc enable,adc start,adc freerun,adcflag,adcno int,
adcprescale/32
out adcsra,r16
ldi r16,0
out sfior,r16 ; write 0-0-0 to bits d7-d5 for free
run adc
here1:in r16,adcsra
andi r16,0b01000000
breq here1 ;value got
ldi R16,255
out ddrb,R16 ; port b is all bits output
out ddrc,R16 ; so is port c
ldi r16,0
out ddra,r16 ;port a input
init: rcall initsio
sei ;enable global interrupt
LCD: rcall init_lcd
lcd1: ldi R16,$80
rcall cmd
rcall delay1
rcall delay1
rcall delay1
rcall delay1
here3:in r16,adcsra
andi r16,0b01000000
brne here3 ;value got
in r16,adcl
IN R17,adch
push r16
ldi r16,0b11000101 ;prescale /32 (1×32=32
usec)
;adc enable,adc start,adc freerun,adcflag,adcno int,
adcprescale/32
out adcsra,r16
pop r16
rcall lcddisp
rjmp lcd1
in r16,udr
andi r16,07
mov r14,r16
ori r16,$c0
out admux,r16
ldi r16,$43
rcall tout ; intimate new channel to host
ldi r16,$48
rcall tout
mov r16,r14
ori r16,$30
rcall tout
rjmp lcd1

The above program reads the ADC output data, whose decimal value is output to pin 15 (TX) of ATmega8535 at 4800 bauds in 8-bit ASCII data format. If a MAX232 is wired to pin 15, it can be directly connected to the receive pin of the RS-232 com port of a PC. Then, by using any terminal program (such as XTALK), it can be received by the PC.

The program (adc_ch.asm) may be tested as follows:
1. Wire ATmega8535 to the LCD and the serial port through a MAX232 IC as shown in Fig. 21.
2. Connect an analogue signal (e.g., a DC voltage in the 0-5V range tapped from a potmeter or the output of LM35 used in the preceding application) to ADC Ch. 0 (pin 40).
3. Program the adc_ch.asm file into the flash memory of ATmega8535 after compilation.
4. Place the IC on the breadboard and press reset.
5. Connect the RS-232 connector of the PC through a 3-wire cable to the MAX232 pins on the board. The TXD output from ATmega8535 should go to RXD pin of the PC’s com port and the PC’s TXD output should go to RXD pin of ATmega8535.
6. Run the XTALK program on the PC and set the baud rate as ‘4800,’ data as ‘8 bits,’ parity as ‘none,’ stop as ‘1,’ and com as ‘1’ or ‘2,’ type ‘go low,’ then press ‘Enter’ key.
7. Observe the ADC data continuously on the screen.

The PC terminal program can be used to select one of the eight desired channels. For this, type any number from ‘0’ to ‘7.’ For example, to select channel-3 ADC, type ‘3.’ Remember you need not press Enter key thereafter.

Fig. 23: Integrated actual-size PCB layout (including the 5V power supply circuit given in Part 1) for all the applications described in this 3-part article
Fig. 23: Integrated actual-size PCB layout (including the 5V power supply circuit given in Part 1) for all the applications described in this 3-part article
Fig. 24: Component layout for the PCB
Fig. 24: Component layout for the PCB

The data from the PC terminal is received by the USART_RX subroutine in the interrupt mode. The main program configures the received data to interrupt the processor. In the interrupt routine, the number sent is used to change (by altering the value of the bits in the ADMUX register of the chip) the ADC channel currently chosen. Thus all the following data will pertain to this channel only and the same will be informed to the PC terminal also by sending CH3 followed by data stream.

The XTALK terminal program is given only for testing purposes. The Visual Basic program (ADC_CHSEL) provided in the EFY-CD of this month does the same. It has two windows, one of which is a Combo box for selecting the channel and the other shows the 5-digit data continuously. Selection of the channel is possible via the Combo box (Fig. 22).

Application notes with programs
You may visit Atmel’s Website ‘www.atmel.com/dyn/products/app_notes.asp?family_id=607’ for the following application notes.
1. AVR100: Accessing the EEPROM. This application note contains assembly routines for accessing the EEPROM for all AVR devices. It includes the code for reading and writing EEPROM addresses sequentially and at random addresses.
2. AVR223: Digital Filters with AVR. This document focuses on the use of the AVR hardware multiplier and general-purpose registers for accumulator functionality, scaling of coefficients when implementing algorithms on fixed-point architectures, actual implementation examples and possible ways to optimise/modify the implementations suggested.
3. AVR240: 4×4 Keypad-Wake Up on Keypress. This application note describes a simple interface to a 4×4 keypad designed for low-power battery operation.

Also there are application notes for interfacing the AVR to an IR detector much like the TV remote. Other topics of interest relating to the AVR are use of watchdog, power idle modes, SPI interfacing for communication, etc. Many have tried out the SPI interface for data communication, but it is found to be more complex compared to the RS-232 protocol. The RS-232 link for ADC data, which is described above, makes a really useful serial data-acquisition system.

An integrated actual-size PCB layout (including the 5V power supply circuit given in Part 1) for all the applications described in this 3-part article is shown in Fig. 23. The component layout for the same is shown in Fig. 24. Suitable pads (not shown in the component layout) have been provided for wiring the components.


LEAVE A REPLY