Advertisement

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.

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:

 [stextbox id=”grey”]

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

; 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

[/stextbox]

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.

Advertisement


SHARE YOUR THOUGHTS & COMMENTS

Please enter your comment!
Please enter your name here