ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 14.06.2025

Просмотров: 3122

Скачиваний: 0

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.

12

Digital Voltmeter with RS232 Interface

12.1 In this Chapter

This chapter contains a didactic example on how to use the AVR development board described in Chap. 10 as an 8-channel digital voltmeter that can be interrogated over the RS232 interface. The characters received from the serial line are interpreted as the address of the analog channel to be read and reported.

12.2 The Hardware

The voltage levels expected by the A/D converter are in the range [0,Vref] and Vref is connected to Vcc. The analog inputs are presented as positive voltage signals to the connector X100, pins 1–8, referred to the ground line X100-9, then filtered by the RC filters implemented by R11–R26 and C11–C19, and applied to the analog inputs ADC0–ADC7 of the MCU.

The output of the ADC system, corresponding to Vin is a 10-bit binary value:

ADC_value = 1023 × Vin/Vref.

(12.1)

Since Vref = Vcc = +5 V, an input voltage Vin = 3.1 V, for example, is converted to decimal 634, which corresponds to the hexadecimal value $27A.

The example presented in this chapter uses only the serial communication interface RS232 to report the values read by the ADC, but optionally a local display, like the one presented in Appendix A14, can be connected to the board on the synchronous peripheral interface SPI.

12.3 The Software

The software application is structured according to the principles described in Chap. 10. After the initialization sequence, the program enters an infinite loop, where

158 12 Digital Voltmeter with RS232 Interface

it reads all the analog inputs of the ADC, and stores the results in a RAM buffer. If an ASCII character in the range ‘0’ to ‘7’ is received on the asynchronous serial interface, the two bytes needed to represent the 10-bit value of the corresponding analog input are converted to four ASCII digits and transmitted on the serial communication line.

Here is the listing of the main module VMMAIN.ASM:

.include

‘‘8535def.inc’’

.include

‘‘map.asm’’

;local variables

.cseg

.org

0

reset:

rjmp

init

.org

$10

init:

.include

‘‘init.asm’’

;calls to init routines

sei

main_loop:

rcall

get_adc

;read analog inputs

rcall

get_cmd

;check for data from UART

brcc

main_loop

rcall

exec_cmd

;answer query over

;serial line

rjmp

main_loop

.include

‘‘uart.asm’’

;UART specific routines

.include

‘‘lib.asm’’

;misc routines

.include

‘‘adc.asm’’

;ADC specific routines

Note that the main program loop only contains calls to three subroutines. Get_adc reads all the analog inputs, get_cmd checks the UART receiver data register for an ASCII character between ‘0’ and ‘7’, and returns the CARRY flag set if one of these characters has been received. Finally, exec_cmd extracts the value of the analog line indicated by the command character, converts it to four ASCII digits, and sends them to the serial line.

All other modules contain either variable definitions (MAP.ASM), calls to the initialization routines (INIT.ASM), peripheral specific routines (UART.ASM, SDC.ASM) or miscellaneous data conversion or execution routines (LIB.ASM).

MAP.ASM contains a number of definitions for constants, and RAM variables:

.def

rint=r1

;some aliases for regs

.def

rsav=r2

.def

tmp1=r16

.def

tmp2=r17

.def

tmp3=r18

.def

op1l=r19


12.3 The Software

159

.equ

prescaler_adc=7

;ADC clock=CK/128

.equ

adc_max_ch=7

;max # of ADC channels

.equ

f_baud=51

;9600 baud

.dseg

;RAM variables

buf_adc: .byte

16

;buffer for ADC data

.equ

f_baud=51

;9600 baud

INIT.ASM is a block of code containing the initialization of the stack pointer SP, and calls to the initialization routines associated with the peripheral interfaces used by the application. The resource-specific initialization routines are located in the corresponding module: init_uart in UART.ASM and init_adc in ADC.ASM. The whole block of code contained in INIT.ASM is inserted in the main program at the point specified by the .include directive.

init_sp:

ldi tmp1,high(ramend) out sph,tmp1

ldi tmp1,low(ramend) out spl,tmp1

rcall init_uart rcall init_adc

end_init:

The UART initialization routine, init_uart. has been described in the previous chapters. Below is the listing of the subroutine for the initialization of the ADC subsystem:

init_adc:

ldi

tmp1,0

;channel 0 first

out

admux,tmp1

ldi

tmp1,prescaler_adc

;ADC clock=CK/128

out

adcsr,tmp1

sbi

adcsr,aden

;ADC enabled

sbi

adcsr,adsc

;start first

;conversion

ret

This code selects channel number 0 by writing to admux, then selects the ADC clock by programming the prescaler to divide the system clock by 128, and enables the ADC system by setting the bit ADEN (A/D converter enable) in ADCSR. Finally, it starts the conversion on the selected channel, by setting the bit ADSC (Start A/D conversion) in ADCSR.

The subroutine get_adc checks the bit ADIF in ADCSR to determine if the last conversion is completed. If ADIF = 1, then the ADC result value (read as two bytes) is stored in a RAM buffer buf_adc, using the channel number as index in this buffer, then a new conversion is started on the next channel:


160

12 Digital Voltmeter with RS232 Interface

get_adc:

sbis

adcsr,adif

;exit if ADIF=0

ret

in

tmp1,admux

;else, get channel number

andi

tmp1,0b00000111

ldi

xl,low(buf_adc)

;x points to start

;of buffer

ldi

xh,high(buf_adc)

clr

tmp2

push

tmp1

;save channel number

add

tmp1,tmp1

;two bytes foe each channel

add

xl,tmp1

;add offset to x

adc

xh,tmp2

in

tmp1,adcl

;get ADC conversion result

in

tmp2,adch

st

x+,tmp1

;and store it in buffer

st

x,tmp2

pop

tmp1

;restore channel number

inc

tmp1

;increment

cpi

tmp1,adc_max_ch+1 ;check if last channel

brlo

getadc1

clr

tmp1

;if so, return to channel 0

getadc1:

out

admux,tmp1

;select new channel

sbi

adcsr,adsc

;start a new conversion

ret

The RS232 communication is performed by means of two subroutines called in the main program loop: get_cmd and exec_cmd, both located in the module LIB.ASM.

Get_cmd checks the UART receiver data register for an ASCII code between ‘0’ and ‘7’. If no character is received, or the ASCII code is outside the expected range, get_cmd returns CARRY = 0, otherwise it returns the valid code in the register r16 (tmp1) and CARRY = 1.

get_cmd:

rcall

get_uart

;check if character received

brcs

getcmd1

;exit if none

ret

getcmd1:

clc

cpi

tmp1,$30

;$30 is the ASCII code for ‘0’

brlo

exit_gcmd

cpi

tmp1,$38

;$38 is ASCII ‘8’

brsh

exit_gcmd

sec

;set carry

exit_gcmd:

ret


12.3 The Software

161

Exec_cmd sends on the serial line a data packet, having the following structure:

N : AAAA CR LF

where N is the ASCII code of the channel number, ‘:’ is used as delimiter, AAAA is the last converted value in hexadecimal of the selected analog channel, and CR, LF are the ASCII codes for ‘carriage return’ ($0D) and ‘line feed’ ($0A). This is required to allow the use of any ASCII terminal emulator, such as Windows™ HyperTerminal, to test the program.

Note that exec_cmd calls hex_asc – a subroutine that converts the content of tmp1 to two ASCII characters, returned in tmp1 and tmp2 corresponding to the nibbles of the input value.

Therefore, the string output to the terminal by exec_cmd in case of reading on channel 3 the binary value 1100100111b is: 3:0C27

Here is the listing of exec_cmd:

exec_cmd:

rcall

put_uartw

rcall

asc_hex

ldi

xl,low(buf_adc)

ldi

xh,high(buf_adc)

clr

tmp2

add

tmp1,tmp1

add

xl,tmp1

adc

xh,tmp2

adiw

xh:xl,2

ldi

tmp1,’:’

rcall

put_uartw

ld

tmp1,-x

rcall

hex_asc

rcall

put_uartw

mov

tmp1,tmp2

rcall

put_uartw

ld

tmp1,-x

rcall

hex_asc

rcall

put_uartw

mov

tmp1,tmp2

rcall

put_uartw

ldi

tmp1,$0d

rcall

put_uartw

ldi

tmp1,$0a

rcall

put_uartw

ret

;display channel number ;convert it to hex

;x points to start of buf

;compute offset in buf_adc

;send ‘:’ as separator

;get high byte from buffer ;convert to ascii

;send first character

;send second chr ;get low byte ;convert it ;send it

;CR

;LF

Note that the input for exec_cmd is the channel number in ASCII, as received from the serial line. Exec_cmd starts by echoing this value. Then, the ASCII value of the channel number is converted to its corresponding hexadecimal value by the subroutine asc_hex. The resulting binary value is used to compute the offset in the


162 12 Digital Voltmeter with RS232 Interface

buffer buf_adc, to retrieve the result of the last conversion performed on the selected analog channel.

The accompanying CD contains a terminal emulator, Voltm.exe, which automatically interrogates the voltmeter for the values of all eight analog channels, and displays the values on the screen. The default Comm port is COM2, but this can be changed by the user. The Start/Stop button initiates and interrupts the communication process. The program runs under Windows™ 9x.

12.4 Exercises

X 12.1

Write a subroutine CBCD16 that converts a 16-bit binary number, lower than 9999, received as input in the registers tmp1:tmp2, to four BCD digits placed in the same registers.

X12.2

Modify the software so that the data sent on the serial line contains the decimal representation of the analog value read from the requested channel.

X12.3

Modify the software so that the decimal value of channel 0 is also displayed on the SPI display described in Appendix A14.