Vous êtes sur la page 1sur 10

Introduction to the Analog Devices ADuC816

Jean-Michel FRIEDT, July 2003-August 25, 2003

1 Introduction
The ADuC81x series was introduced by Analog Devices as intelligent data acquisition systems: they include a wide range
of instrumentation tools (A/D converters, D/A, timers, ...) around an Intel 8051 compatible microcontroller core. A
simple in-system programming protocol based on a 9600 baud RS232 UART and EEPROM memory storage means that the
development cost is limited to buying the microcontroller and an RS232 level shifter (MAX232 in our case).
We here present two microcontrollers of the series, the ADuC816 and ADuC814. The former offers two A/D high 16 bits
resolution and a single D/A converter, while the latter presents a smaller footprint, two D/A and 6 A/D with a lower 12 bit
resolution. All systems include a low resolution temperature monitoring peripheral. Furthermore, several communication
protocols (I2C, SPI, UART) are supported, facilitating the development of multiple chips systems.

2 Software requirements
We use the ASxxxx Assembler V03.11 (as found at http://shop-pdp.kent.edu/ashtml/asxxxx.htm) and more specifically
the implementation of the 8051 core in as8051. asxxxx was compiled on an IBM-compatible (1.7 GHz Intel Pentium 4 based)
linux system running the kernel v.2.4.9-13 using gcc v.2.96.
We compile the assembly program on the PC, strip the resulting file of unnecessary informations (address location,
symbols) and upload the resulting values to the microcontroller using custom script and program for the last two operations.
The script for converting a .rel file as generated by as8051 to a file containing only a series of opcodes to be uploaded
to the microcontroller (assuming a starting address of 0x0000) is as follows:

#!/bin/tcsh ./as8051 -o $nom.asm


if ( $# == 0 ) then # requires at least 1 argument grep ^T $nom.rel | cut -c9-80 > $nom.out # keep only data
echo $0 "filename[.asm]" # cat header.hex $nom.out > m
else # mv m $nom.out
set nom=$1 # remove extension from name (if given) \rm $nom.rel
if ( ‘echo $1 | grep \.‘ != "" ) then endif
set nom=‘echo $1 | cut -d\. -f1‘
endif # for presenting the output in the latex document:
echo $nom "->" $nom".out" # input foo.asm outputs as foo.out # ./as8051 -l prog.asm followed by expand prog.lst (TAB -> space)

asm: TCSH script for compiling the assembly program in a set of opcodes, ready to be sent to the microcontroller.

The C program for uploading the resulting values to the microcontroller is:

#include "aduc.h" // fclose(f);f=fopen(argv[1],"r");


// cmd=’E’;write_code(fd,f,cmd);
int main(int argc,char **argv) fclose(f);
{int fd,cnt=0;FILE *f; printf("Done \n");
unsigned char cmd; run_code(fd);
fd=init_rs232(); }
listen_aduc(fd); while (1) {cnt++;read(fd,&cmd,1);
init_aduc(fd); DEBUG("%d:(%x)=%d\n",cnt,ctoi(cmd),ctoi(cmd));
free_rs232();fd=init_rs232(); // clear RS232 buffer fflush(stdout);}
if (argc>1) free_rs232(); // ^^^ don’t release the serial port: we want to record what
{f=fopen(argv[1],"r"); return(0); // the MMC tells us
cmd=’A’;erase_all(fd,cmd); }
cmd=’W’;write_code(fd,f,cmd);

aductest.c: program for sending a series of opcodes to the microcontroller, checking the integrity of the data and the
communication protocol, and running the program after upload.

which requires the RS232 header file:

#include <stdio.h> int init_rs232();


#include <stdlib.h> void free_rs232();
#include <unistd.h> void sendcmd(int,char*);
struct termios oldtio,newtio;
#include <sys/types.h>
#include <sys/stat.h> #define BAUDRATE B9600
#include <string.h> /* declaration of bzero() */ // #define BAUDRATE B19200
#include <fcntl.h> #define HC11DEVICE "/dev/ttyS0"
#include <termios.h>

rs232.h: header for the basic RS232 initialization routines.

and the RS232 initialization routines:

1
/* All examples have been derived from miniterm.c */ // newtio.c_cc[VSWTC] = 0; /* ’\0’ */
/* Don’t forget to give the appropriate serial ports the right permissions */ // newtio.c_cc[VSTART] = 0; /* Ctrl-q */
/* (e. g.: chmod a+rw /dev/ttyS0) */ // newtio.c_cc[VSTOP] = 0; /* Ctrl-s */
// newtio.c_cc[VSUSP] = 0; /* Ctrl-z */
#include "rs232.h" // newtio.c_cc[VEOL] = 0; /* ’\0’ */
// newtio.c_cc[VREPRINT] = 0; /* Ctrl-r */
extern struct termios oldtio,newtio; // newtio.c_cc[VDISCARD] = 0; /* Ctrl-u */
// newtio.c_cc[VWERASE] = 0; /* Ctrl-w */
int init_rs232() // newtio.c_cc[VLNEXT] = 0; /* Ctrl-v */
{int fd; // newtio.c_cc[VEOL2] = 0; /* ’\0’ */
fd=open(HC11DEVICE, O_RDWR | O_NOCTTY ); tcflush(fd, TCIFLUSH);tcsetattr(fd,TCSANOW,&newtio);
if (fd <0) {perror(HC11DEVICE); exit(-1); } // printf("RS232 Initialization done\n");
tcgetattr(fd,&oldtio); /* save current serial port settings */ return(fd);
bzero(&newtio, sizeof(newtio)); /* clear struct for new port settings */ }
// newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD;
newtio.c_cflag = BAUDRATE | CS8 | CLOCAL | CREAD; /* _no_ CRTSCTS */ void sendcmd(int fd,char *buf)
newtio.c_iflag = IGNPAR | ICRNL |IXON; {unsigned int i,j;
newtio.c_oflag = ONOCR|ONLRET|OLCUC; if((write(fd,buf,strlen(buf)))<strlen(buf))
// newtio.c_lflag = ICANON; {printf("\n No connection...\n");exit(-1);}
// newtio.c_cc[VINTR] = 0; /* Ctrl-c */ for (j=0;j<5;j++) for (i=0;i<3993768;i++) {}
// newtio.c_cc[VQUIT] = 0; /* Ctrl-\ */ /* usleep(attente); */
// newtio.c_cc[VERASE] = 0; /* del */ }
// newtio.c_cc[VKILL] = 0; /* @ */
// newtio.c_cc[VEOF] = 4; /* Ctrl-d */ void free_rs232(int fd)
newtio.c_cc[VTIME] = 0; /* inter-character timer unused */ {tcsetattr(fd,TCSANOW,&oldtio);close(fd);} /* restore the old port settings */
newtio.c_cc[VMIN] = 1; /* blocking read until 1 character arrives */

rs232.c: basic RS232 initialization routine needed for all programs running under Linux requiring access to the serial port.

as well as the communication protocol to be able to talk with the microcontrollers of the ADuC81x series:

#include "aduc.h" if (res==EOF) data[i]=0;


}
void nothing() {} do {
buf=0x07;write(fd,&buf,1);DEBUG("(%x)",ctoi(buf)); // 0x07
int ctoi(char i) {return( (int)i&0x000000ff);} buf=0x0E;write(fd,&buf,1);DEBUG("(%x)",ctoi(buf)); // 0x0E
buf=num+1+3;write(fd,&buf,1);DEBUG("(%x)",ctoi(buf)); // num data<=0d25
void run_code(int fd) write(fd,&cmd,1);DEBUG("(%x)",ctoi(cmd));
{char buf;unsigned char chksum; chksum=cmd+num+1+3;
unsigned char num=3 ; // number of data (excluding cmd => 0..24) write(fd,&addr2,1);DEBUG("@(%x)",ctoi(addr2));chksum+=addr2;
unsigned char cmd=0x55; // ’U’ write(fd,&addr1,1);DEBUG("@(%x)",ctoi(addr1));chksum+=addr1;
buf=0x07;write(fd,&buf,1);DEBUG("(%x)",ctoi(buf)); // 0x07 write(fd,&addr0,1);DEBUG("@(%x)",ctoi(addr0));chksum+=addr0;
buf=0x0E;write(fd,&buf,1);DEBUG("(%x)",ctoi(buf)); // 0x0E for (i=0;i<num;i++) {
buf=num+1;write(fd,&buf,1);DEBUG("(%x)",ctoi(buf)); // num data=0d25 write(fd,&data[i],1);DEBUG("(%x)",ctoi(data[i]));chksum+=data[i];}
write(fd,&cmd,1);DEBUG("cmd(%x)",ctoi(cmd)); chksum=0-(chksum); // 2 complement of sum of data+num of data
chksum=cmd; write(fd,&chksum,1);DEBUG("chk(%x)",ctoi(chksum));
cmd=0;write(fd,&cmd,1);DEBUG("(%x)",ctoi(cmd)); // start @ read(fd,&buf,1);if (buf==6) DEBUG(" => OK\n"); else
cmd=0;write(fd,&cmd,1);DEBUG("(%x)",ctoi(cmd)); DEBUG(" => NG(%x)\n",ctoi(buf));
cmd=0;write(fd,&cmd,1);DEBUG("(%x)",ctoi(cmd)); } while (buf!=6);
chksum=0-(chksum+num+1); // 2 complement of sum of data+num of data if (addr0<0xfc) addr0+=4; else {addr1+=1;addr0=0;}
write(fd,&chksum,1);DEBUG("chk(%x)",ctoi(chksum)); } while (res!=EOF);
read(fd,&buf,1);if (buf==6) DEBUG(" => OK\n"); }
else DEBUG(" => NG(%x)\n",ctoi(buf));
} void run_aduc(int fd)
{char buf[2];
void erase_all(int fd,unsigned char cmd) /* cmd ’C’=0x43 &’A’=0x41 data+code */ buf[0]=0x28;write(fd,buf,1);DEBUG("cmd(%x)",ctoi(buf[0])); // RUN
{char buf;unsigned char chksum; read(fd,buf,2);DEBUG("-> %x %x ; ",ctoi(buf[0]),ctoi(buf[1]));
unsigned char num=0 ; // number of data (excluding cmd => 0..24) }
// unsigned char cmd=0x43; // ’C’ SHOULD BE 43 instead of 41
printf("Erase all: ");fflush(stdout); void listen_aduc(int fd)
buf=0x07;write(fd,&buf,1);DEBUG("(%x)",ctoi(buf)); // 0x07 {int i;char buf;
buf=0x0E;write(fd,&buf,1);DEBUG("(%x)",ctoi(buf)); // 0x0E for (i=0;i<25;i++)
buf=num+1;write(fd,&buf,1);DEBUG("(%x)",ctoi(buf)); // num data=0d25 {read(fd,&buf,1);
write(fd,&cmd,1);DEBUG("(%x)",ctoi(cmd)); // ’E’ (write data) if ((buf>31) && (buf<127)) DEBUG("%c",buf); else DEBUG(" 0x%d ",ctoi(buf));}
chksum=cmd; printf("\nAnd now the ADuC answers ... (should be the same as above)\n");
chksum=0-(chksum+num+1); // 2 complement of sum of data+num of data }
write(fd,&chksum,1);DEBUG("chk(%x)",ctoi(chksum));
read(fd,&buf,1);if (buf==6) DEBUG(" => OK\n"); else DEBUG(" => NG\n"); void init_aduc(int fd)
} {int i;char buf;
buf=0x21;write(fd,&buf,1);
void write_code(int fd,FILE* f,unsigned char cmd) /* cmd=’W’=0x57 for code */ buf=0x5A;write(fd,&buf,1);
{int i;char buf;unsigned char chksum,data[25]; /* ’E’=0x45 for data */ buf=0x00;write(fd,&buf,1);
unsigned char num=4; // number of data (excluding cmd and address => 0..24) buf=0xA6;write(fd,&buf,1);
unsigned char addr0=0,addr1=0,addr2=0; for (i=0;i<25;i++)
int res=EOF+1; {read(fd,&buf,1);
printf("Write code:\n");fflush(stdout); if ((buf>31) && (buf<127)) DEBUG("%c",buf); else DEBUG(" 0x%d ",ctoi(buf));}
do {memset(data,0,25); /* for (i=0;i<num;i++) {data[i]=0;} */ // 0=NOP printf("\n");
for (i=0;i<num;i++) { }
if (res!=EOF) res=fscanf(f,"%x",&data[i]);

aduc.c: basic routines for communicating with the ADuC81x microcontroller series.

The upload of the opcodes from the PC to the microcontroller is achieved by starting the program ./aductest file.out,
where file.out results from the compilation of file.asm using ./asm file.asm, and then pressing the RESET button on
the microcontroller board. aductest should then display the welcome message of the microcontroller ADI 816 V201 followed
by the hexadecimal values of a few control bytes sent by the microcontroller. The program will then reset the microcontroller,
check that the communication is established by receiving the same welcome string, and then upload the program by packets
of 4 bytes, each of which much be acknowledged. At the end, the program is executed by calling address 0x0000.
Notice that the version of aductest.c for the ADuC814 has been slightly enhanced to listen to the RS232 port and print
any value it receives for MultiMediaCard debugging purposes. Hence, this version of the program does not come back to the
prompt when uploading the program is completed but keeps on listening to the serial port until the user hits CTRL-C.
As an example of the output when uploading a very simple program for blinking an LED connected to port 3.4 (pin 22):

2
ADI 816 V201 0x10 0x10 r( 0x204 0x1 0x3 0x166 0x0 0x0 0x3 (7)(e)(8)(57)@(0)@(0)@(8)(b)(80)(f7)(f8)chk(1f) => OK
And now the ADuC answers ... (should be the same as above) (7)(e)(8)(57)@(0)@(0)@(c)(79)(41)(7a)(c8)chk(99) => OK
ADI 816 V201 0x10 0x10 r( 0x204 0x1 0x3 0x166 0x0 0x0 0x3 (7)(e)(8)(57)@(0)@(0)@(10)(da)(fe)(d9)(fa)chk(e6) => OK
Erase all: (7)(e)(1)(41)chk(be) => OK (7)(e)(8)(57)@(0)@(0)@(14)(d8)(f6)(22)(0)chk(9d) => OK
Write code: Done
(7)(e)(8)(57)@(0)@(0)@(0)(74)(a)(b2)(b4)chk(bd) => OK (7)(e)(4)cmd(55)(0)(0)(0)chk(a7) => OK
(7)(e)(8)(57)@(0)@(0)@(4)(b2)(90)(12)(0)chk(49) => OK

Results of the upload of a simple program to the microcontroller as seen on the terminal in which ./aductest blink.hex had been
run.

3 Hardware setup
The circuit asserts the necessary signal for the microcontroller to run: EA# (pin 40) must be pulled high in order to allow
the microcontroller to access its internal EEPROM memory (if this signal is not defined, it is floating close to ground and
the program stored in EEPROM will not execute) and PSEN# (pin 41) must be pulled to ground to enable program upload
to EEPROM using the UART. Apart from these two signals, we connect RESET (pin 15) to a pushbutton pulling this signal
high via a 1.5 kΩ resistor when the button is closed, and leaving the pin floating (internal pull-down resistor) otherwise.
Finally, the power supply pins (VDD and ground) and the quartz resonator (32.768 kHz) are connected directly to the chip
(no need for external capacitors on the quartz).
As can be seen on the picture of the circuit, we had to work without proper PCB manufacturing tool. A small ' 4×4 cm2
piece of 1.6 mm thick PCB was glued on the copper coated ground plane, and the microcontroller was glued on top of this
copper piece. First, the ground connexions were carefully soldered under low magnification microscope. Then, as advised
in another context in the Application Note 557 of Analog Devices 1 , a small piece of copper foil was glued on top of the
microcontroller to provide a power supply plane (do not use super-glue for that since it will emit very toxic fumes when
heated during the later soldering steps). The power supply pins were connected to this copper foil, which was itself connected
to the +5 V pin of the MAX232 (for mechanical stability of the setup).
After assembling the circuit, check that the baud rate of the RS232 port to which the circuit is connected is set to 9600
bauds (in my case for COM1: stty < /dev/ttyS0 should reply speed 9600 baud; line = 0;). Upon power on or after
temporarily closing and releasing the reset switch, the ADuC816 should reply ADI 816 V201 followed by some binary value
out of the displayable ASCII range.
As mentioned in the application note uC004 (p.6), code/data must be erase prior to any write attempt to EE memory.
I don’t understand why the DOS program download.exe provided by Analog Devices claims that the default address
where program are run from is 0xFF00. I upload my program starting at address 0x0000 (0x0060 if we want to avoid putting
user code in the interrupt vector table, as mentioned in the header of the examples provided by Analog Devices) and jump
to there to execute.
+5V

2 kΩ
x3

15 40 41 32
RESET

EA#

PSEN#

33
32.748 kHz
(CMAC, 90SMX)
22
ADuC816 500 Ω
6 21 35 47
(52 MQFP)
5 20 34 48
TxD

RxD

+5V
16 17

+5V

16 16
All 2.2 µ F
6 2
4 1
tantalum
5
MAX 3
11
232 14 SUB−D9 fem. pin 2 PC RS232
direct
12 13 SUB−D9 fem. pin 3 cable

SUB−D9 fem. pin 5

1 AN-557: An Experimenter’s Project: Incorporating the AD9850 Complete DDS Device as a Digital LO Function in an Amateur Radio
Transceiver

3
4 Some basic introductory software examples
These few examples are provided to get familiar with the asxxxx syntax and with the memory layout, register calling
conventions and instructions of the microcontroller based on a 8051 core.

4.1 Blinking LED


This first simple example introduces how all other programs will be displayed in this document. This output is the results
of as8051 -l prog.asm to generate prog.lst which not only includes the source code on the right column, but also the
address where the opcode is located in the middle column and the assembled opcodes to the left. Only the right part of the
displayed program should be typed in prog.asm.

1 .area code (ABS) 000B F8 9 wait: MOV R0,a


0000 2 .org 0h0000 000C 79 41 10 lo0: mov r1,#065
3 000E 7A C8 11 lo2: mov r2,#0200
0000 74 0A 4 mov A,#0h0a 0010 DA FE 12 lo1: djnz r2,lo1
0002 B2 B4 5 begin: cpl 0hB4 ; T0=P3.4=#22 0012 D9 FA 13 djnz r1,lo2
0004 B2 90 6 cpl 0h90 ; T2=P1.0=#1 0014 D8 F6 14 djnz r0,lo0
0006 12 00 0B 7 lcall wait 0016 22 15 ret
0009 80 F7 8 sjmp begin

blink.asm: example for making an LED connected to pin 22 (port 3.4) blink.

We here see how to define the address location at which the program is to be stored (.org directive) and a few basic
instructions. The port output to which the LED is connected is toggled by computing the complementary value (instruction
cpl) of the current value.

4.2 Asynchronous (RS232) communication


The next two examples are taken from the file ADuC code V35.zip provided by Analog Devices, which includes a wide variety
of program simple little programs for getting used to the microcontroller. The syntax has been adapted to that of as8051
(from the asxxxx package).
We see here for example how to fill a memory location (.string and .byte directives) as used for defining the constant
title to be sent by RS232. We also see that the names of some of the most common and standard registers of the 8051 family
are known to the assembler and the name instead of the register address can be used in the program, making the source
code more readable.

1 .area code (ABS) 29


0000 2 .org 0h0000 0025 30 LOOP2:
3 0025 74 20 31 MOV A, #0h20 ; Transmit a SPACE (=ASCII 20) between
0000 4 MAIN: ; Main program 32 ; transmissions on same line
5 0027 12 00 5C 33 LCALL SENDCHAR
0000 75 CB FF 6 MOV RCAP2H,#0hFF ; config UART for 9830baud 34
0003 75 CA FB 7 MOV RCAP2L,#-5 ; (close enough to 9600baud) 002A E8 35 MOV A, R0 ; Transmit R0 = present data
0006 75 CD FF 8 MOV TH2,#0hFF 002B 12 00 64 36 LCALL SENDVAL
0009 75 CC FB 9 MOV TL2,#-5 002E 08 37 INC R0 ; increment data
000C 75 98 52 10 MOV SCON,#0h52 38
000F 75 C8 34 11 MOV T2CON,#0h34 002F B4 7F 03 39 CJNE A, #0h7F, CONT ; check if data =7F, if no continue
12 0032 02 00 3C 40 LJMP WAIT5S ; if = 7F wait 5s and repeat
0012 13 START: 41
0012 B2 B4 14 CPL 0hB4 ; CPL 0hB4 with each transmission 0035 19 42 CONT: DEC R1 ; decrement R1....
0014 90 00 92 15 MOV DPTR, #TITLE 0036 E9 43 MOV A, R1
0017 12 00 44 16 LCALL SENDSTRING ; write title block on screen 0037 B4 00 EB 44 CJNE A, #0h00, LOOP2 ; and check if new line is required
17 45 ; jump to loop 2 for a space
001A 78 00 18 MOV R0, #0h00 ; Start transmissions from 0 003A 80 E2 46 SJMP LOOP1 ; jump to loop 1 for a new line
001C 79 08 19 MOV R1, #0h08 ; Start a new line after 8 transmissions 47
20 48
001E 21 LOOP1: ; Every eight transmissions start on a 003C 74 32 49 WAIT5S: MOV A, #50 ; wait 5s
22 ; new line 003E 12 00 86 50 LCALL DELAY
001E 74 0A 23 MOV A, #10 ; Transmit a linefeed 0041 02 00 12 51 LJMP START ; start transmissions again
0020 12 00 5C 24 LCALL SENDCHAR 52
25 ; MOV A, #13 ; Transmit a carriage return 53
26 ; LCALL SENDCHAR 54 ;____________________________________________________________________
27 55 ; SENDSTRING
0023 79 08 28 MOV R1, #0h08

UART2.asm: example for transmitting ASCII strings representing hexadecimal values.

4.3 Temperature monitoring


We now define a constant value, LED, to be used in the rest of the program which makes the source code easier to read that
when calling port P3.4 by its register address 0hB4 as was done previously. This program is quite long due to the binary
to ASCII conversion done at the microcontroller level, and memory could be saved by performing the conversion at the PC
level after transmitting the raw binary values resulting from the temperature measurements.

4
00B4 1 LED = 0hb4 0065 90 00 9E 76 MOV DPTR, #DEGREES
2 0068 12 00 86 77 LCALL SENDSTRING
3 .area code (ABS) 006B 74 0A 78 MOV A, #10
0000 4 .org 0h0000 006D 12 00 72 79 LCALL DELAY
5 0070 80 AC 80 SJMP TEMPLOOP
0000 6 MAIN: ; Main program 81 ;____________________________________________________________________
7 82 ; DELAY
0000 75 CB FF 8 MOV RCAP2H,#0hFF ; config UART for 9830baud 0072 83 DELAY: ; Delays by 100ms * A
0003 75 CA FB 9 MOV RCAP2L,#-5 ; (close enough to 9600baud) 84 ; 100mSec based on 1.573MHZ Core Clock
0006 75 CD FF 10 MOV TH2,#0hFF 85
0009 75 CC FB 11 MOV TL2,#-5 0072 FA 86 MOV R2,A ; Acc holds delay variable
000C 75 98 52 12 MOV SCON,#0h52 0073 7B 32 87 DLY0: MOV R3,#50 ; Set up delay loop0
000F 75 C8 34 13 MOV T2CON,#0h34 0075 7C 83 88 DLY1: MOV R4,#131 ; Set up delay loop1
14 0077 DC FE 89 ici2: DJNZ R4,ici2 ; Dec R4 & Jump here until R4 is 0
15 ; Configure ADC 90 ; wait here for 131*15.3us=2ms
0012 75 D1 10 16 MOV 0hd1, #0h10 ; ENABLE AUX Mode - Power down ADCMODE=0hd10079 DB FA 91 DJNZ R3,DLY1 ; Dec R3 & Jump DLY1 until R3 is 0
0015 75 D3 20 17 MOV 0hd3, #0h20 ; USE INTERNAL REFERENCE ADC1CON=0hd3 92 ; Wait for 50*2ms
18 ; PTAT(+) --> PTAT(-) 007B DA F6 93 DJNZ R2,DLY0 ; Dec R2 & Jump DLY0 until R2 is 0
19 ; BIPOLAR MODE 94 ; wait for ACC*100ms
20 ; Fixed +/- 2.5V range 007D 22 95 RET ; Return from subroutine
21 96 ;____________________________________________________________________
0018 90 00 AA 22 MOV DPTR, #TITLE 97 ; SENDCHAR
001B 12 00 86 23 LCALL SENDSTRING ; write title block on screen 007E 98 SENDCHAR: ; sends ASCII value contained in A to UART
24 ;____________________________________________________________________ 99
25 ; TEMP MEASURE LOOP 007E 30 99 FD 100 ici3: JNB TI,ici3 ; wait til present char gone
001E 26 TEMPLOOP: 0081 C2 99 101 CLR TI ; must clear TI
001E 75 D1 12 27 MOV 0hd1, #0h12 ; INITIATE A SINGLE AUX CONV ADCMODE=0hd10083 F5 99 102 MOV SBUF,A
0021 30 DE FD 28 ici1: JNB 0hde,ici1 ; Wait for conversion results RDY1=0hde 0085 22 103 RET
29 104 ;____________________________________________________________________
30 ; conversion result ready 105 ; SENDSTRING
31 ; a value of 80h in AD1H=0degC 0086 106 SENDSTRING: ; sends ASCII string to UART starting at location
32 107 ; DPTR and ending with a null (0) value
0024 E5 DD 33 MOV A, 0hdd ; 80h=0, FFh=+127, 00h=-128 ADC1H=0hdd 0086 C0 E0 108 PUSH ACC
0026 C3 34 CLR C 0088 C0 F0 109 PUSH B
0027 94 80 35 SUBB A, #0h80 ; convert to 2’s comp 008A E4 110 CLR A
36 ; FFh=-1, 80h=-128, 00h=0, 7Fh=+127 008B F5 F0 111 MOV B,A
37 008D E5 F0 112 IO0010: MOV A,B
0029 38 SENDDECs: ; SENDs signed decimal number in Acc up UART (-128->127)008F 05 F0 113 INC B
0029 C0 F0 39 PUSH B 0091 93 114 MOVC A,@A+DPTR
002B C0 E0 40 PUSH ACC 0092 60 05 115 JZ IO0020
002D 30 E7 0B 41 JNB ACC.7, HUNDREDS 0094 12 00 7E 116 LCALL SENDCHAR
0030 74 2D 42 MOV A, #0h2d ; transmit minus sign ’-’ 0097 80 F4 117 SJMP IO0010
0032 12 00 7E 43 LCALL SENDCHAR 0099 D0 F0 118 IO0020: POP B
0035 D0 E0 44 POP ACC ; restore original value of A 009B D0 E0 119 POP ACC
0037 C0 E0 45 PUSH ACC ; remember original value of A 120
0039 F4 46 CPL A 009D 22 121 RET
003A 04 47 INC A 122 ;____________________________________________________________________
48 009E 20 64 65 67 72 65 123 DEGREES: .ascii " degrees C"
003B 49 HUNDREDS: ; check #hundreds 65 73 20 43
003B 75 F0 64 50 MOV B, #100 ; divide remainder by 100 00A8 0A 124 .byte 10
003E 84 51 DIV AB ; A receives integer quotient 00A9 00 125 .byte 0
52 ; B receives the remainder 126
003F D2 D5 53 SETB F0 00AA 0A 127 TITLE: .byte 10
0041 60 07 54 JZ TENS ; if ACC=0 then num=0xx 00AB 0A 128 .byte 10
0043 C2 D5 55 CLR F0 00AC 5F 5F 5F 5F 5F 5F 129 .ascii "____________________________________"
0045 24 30 56 ADD A, #0h30 ; ’0’ 5F 5F 5F 5F 5F 5F
0047 12 00 7E 57 LCALL SENDCHAR 5F 5F 5F 5F 5F 5F
58 5F 5F 5F 5F 5F 5F
004A 59 TENS: ; check tens 5F 5F 5F 5F 5F 5F
004A E5 F0 60 MOV A,B 5F 5F 5F 5F 5F 5F
004C 75 F0 0A 61 MOV B,#10 00D0 0A 130 .byte 10
004F 84 62 DIV AB ; divide remainder by 10 00D1 41 6E 61 6C 6F 67 131 .ascii "Analog Devices MicroConverter ADuC816"
0050 30 D5 02 63 JNB F0, SEND0 ; if F0 is cleared the a number 20 44 65 76 69 63
64 ; exists in the 100s 65 73 20 4D 69 63
0053 60 05 65 JZ UNITS 72 6F 43 6F 6E 76
66 65 72 74 65 72 20
0055 24 30 67 SEND0: ADD A, #0h30 ; ’0’: only send a zero if number 41 44 75 43 38 31
0057 12 00 7E 68 LCALL SENDCHAR ; existed in the 100s 36
69 00F6 0A 132 .byte 10
005A E5 F0 70 UNITS: MOV A,B ; send remainder (even if 0) 00F7 20 20 20 20 20 20 133 .ascii " Temp Sensor Demo Routine"
005C 24 30 71 ADD A, #0h30 ; ’0’ 54 65 6D 70 20 53
005E 12 00 7E 72 LCALL SENDCHAR 65 6E 73 6F 72 20
0061 D0 E0 73 POP ACC 44 65 6D 6F 20 52
0063 D0 F0 74 POP B 6F 75 74 69 6E 65
75 0115 0A 134 .byte 10

TempUart.asm: example for reading the temperature as detected by the chip and sending the results (ASCII string in Celsius degrees)
via RS232.

This version of the program polls the ADC conversion completion bit and waits until the conversion is done. We could
also use the ADC conversion completed interrupt signal as proposed in the Analog Devices examples.
One issue here is the use of software interrupts. This program works by clearing the LED register, and waiting for the
software interrupt linked to the ADC conversion completion to light the LED. Since the interrupt vectors are stored at the
beginning of the EEPROM memory, the first opcode stored must be a jump to a location after the interrupt vector table
(address 0x60 in our case). However, the programming software proposed in this document is unable to cope with program
addresses starting at a point other than 0. We then use the following solution: we manually compile a header which includes
the first three opcode for a jump to address 0x60 upon reset of the microcontroller, as well as the opcode of the interrupt
servicing routine. We store these opcodes in a file named header.hex. Every time we compile TempUart.asm, we edit the
resulting opcode list, remove the first 3 bytes (calling address 0x60) and include the header file and enough zeros to fill the
memory up to location 0x60. The header file is the following:

02 00 60 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 D2 B3 32 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

header.hex: the header for programs using the software interrupts. It includes the first opcodes for jumping to the main program
location (0x60) as well as the ADC conversion completed interrupt servicing routine.

5
and the program using the interrupt looks like this:

00B4 1 LED = 0hb4 009C 7B FF 51 DLY1: MOV R3,#0hFF ; Set up delay loop1
0000 2 CHAN = 0 ; convert this ADC input channel (0 thru 6)
009E DB FE 52 ici: DJNZ R3,ici ; Dec R3 & Jump here until R3 is 0
3 ;____________________________________________________________________ 00A0 DA FA 53 DJNZ R2,DLY1 ; Dec R2 & Jump DLY1 until R2 is 0
4 ; BEGINNING OF CODE 00A2 D9 F6 54 DJNZ R1,DLY0 ; Dec R1 & Jump DLY0 until R1 is 0
5 00A4 22 55 RET ; Return from subroutine
6 .area code (ABS) 56
0060 7 .org 0h0060 57
8 58 ;____________________________________________________________________
0060 9 MAIN: 59
10 00A5 60 SENDVAL: ; converts the hex value of A into two ASCII chars,
11 ; Set up UART 61 ; and then spits these two characters up the UART.
0060 75 CB FF 12 MOV RCAP2H,#0hFF ; config UART for 9600 baud 62 ; does not change the value of A.
0063 75 CA F9 13 MOV RCAP2L,#-7 ; 63
0066 75 CD FF 14 MOV TH2,#0hFF 00A5 C0 E0 64 PUSH ACC
0069 75 CC F9 15 MOV TL2,#-7 00A7 C4 65 SWAP A
006C 75 98 52 16 MOV SCON,#0h52 00A8 12 00 BB 66 LCALL HEX2ASCII
006F 75 C8 34 17 MOV T2CON,#0h34 00AB 12 00 C7 67 LCALL SENDCHAR ; send high nibble
18 ; PRECONFIGURE... 00AE D0 E0 68 POP ACC
19 00B0 C0 E0 69 PUSH ACC
0072 75 EF 80 20 MOV 0hEF,#0h80 ; power up ADC -- ADCCON1=0hEF 00B2 12 00 BB 70 LCALL HEX2ASCII
0075 75 D8 00 21 MOV 0hD8,#CHAN ; select channel to convert -- ADCCON2=0hD8 00B5 12 00 C7 71 LCALL SENDCHAR ; send low nibble
0078 D2 AF 22 SETB 0hAF ; enable interrupts -- EA=0hAF 00B8 D0 E0 72 POP ACC
007A D2 AE 23 SETB 0hAE ; enable ADC interrupt -- EADC=0hAE 73
24 00BA 22 74 RET
25 ; PERFORM REPEATED SINGLE CONVERSIONS... 75
26 76 ;____________________________________________________________________
007C C2 B4 27 AGAIN: CLR LED ; turn the LED off 77 ; HEX2ASCII
007E 74 01 28 MOV A,#0h01 ; Delay length 78
0080 12 00 99 29 LCALL DELAY ; delay 100ms 00BB 79 HEX2ASCII: ; converts A into the hex character representing the
0083 D2 DC 30 SETB 0hDC ; innitiate single ADC conversion -- SCONV=0hDC 80 ; value of A’s least significant nibble
31 ; ADC ISR is called upon completion 81
0085 30 B4 FD 32 ici2: JNB LED,ici2 00BB 54 0F 82 ANL A,#0h00F
0088 E5 DA 33 MOV A,0hDA ; ADCDATAH=0hDA 00BD B4 0A 00 83 ici3: CJNE A,#0h00A,ici3+3
008A 12 00 A5 34 LCALL SENDVAL 00C0 40 02 84 JC IO0030
008D E5 D9 35 MOV A,0hD9 ; ADCDATAL=0hD9 00C2 24 07 85 ADD A,#0h07
008F 12 00 A5 36 LCALL SENDVAL 00C4 24 30 86 IO0030: ADD A,#0h30
37 87
0092 74 01 38 MOV A,#0h01 ; Delay length 00C6 22 88 RET
0094 12 00 99 39 LCALL DELAY ; delay 100ms 89
0097 80 E3 40 SJMP AGAIN ; repeat 90 ;____________________________________________________________________
41 91 ; SENDCHAR
42 ;____________________________________________________________________ 92
43 ; SUBROUTINE 00C7 93 SENDCHAR: ; sends ASCII value contained in A to UART
0099 44 DELAY: ; Delays by 100ms * A 94
45 ; 100mSec based on 2.097152MHZ 00C7 30 99 FD 95 JNB TI,SENDCHAR ; wait til present char gone
46 ; Core Clock 00CA C2 99 96 CLR TI ; must clear TI
47 ; i.e. default ADuC814 Clock 00CC F5 99 97 MOV SBUF,A
48 98
0099 F9 49 MOV R1,A ; Acc holds delay variable 00CE 22 99 RET
009A 7A 22 50 DLY0: MOV R2,#0h22 ; Set up delay loop0

ADCsingl.asm: program for reading the temperature of the ADuC814 chip and compare the output with that of an LM35
temperature sensor connected to ADC0.

70
Start: Thu 17/07/03 @ 22:20
Stop: Tue 22/07/03 @ 13:20
65

60
Sat 19/07 Mon
16:08 16:37
55
Fri 18/07 Sun
16:14 16:18
50 17:48
temperature (oC)

17:46

45

40

35

30

25

20

1000 2000 3000 4000 5000 6000 7000


time (52.97 second/point)

Acquisition of the temperature of the chip using the internal voltage reference and the internal temperature sensor. The
microcontroller was connected to COM1 of a PC running linux, the high byte value was sent in ASCII format and recorded using cat
< /dev/ttyS0 > output file. The red curve is a sliding window average over 60 points of the blue curve.

6
5 Comparison of the internal temperature sensor and the LM35 (ADuC814)
1200 LM35
internal temperature sensor
<LM35>
60

1000

800

600

400

200

0
0.5 1 1.5 2 2.5 3
time (16.1 s/point) 4
x 10

Acquisition of the temperature of the chip using the internal voltage reference and the internal temperature sensor, and comparison
to an external LM35 temperature sensor connected to ADC0. The blue curve is the raw data from the LM35 sensor, the red curve is
the internal temperature of the sensor, and the green curve is a sliding window average over 60 points of the blue curve.

6 Comparison of the internal temperature sensor and the LM35 (ADuC814)


Add pressure sensor ?

7 Data storage on the MultiMedia card (ADuC814)


This example describing the use of the MultiMediaCard (MMC) serves not only as a potentially very useful application of a
mass storage peripheral connected to the tiny microcontroller, but also as an example of SPI communication programming.

Picture of the ADuC814 “board” used for developing the MMC interfacing software. The 5 to 3.3 V converter and MAX232 have
been soldered on a separate board since the ADuC814+MMC are expected to be used in an autonomous application (remote sensing
on a kite).

1 ; REMEMBER DURING TESTS NOT ONLY TO RESET uC BUT ALSO TO SWITCH OFF 0000 02 00 60 18 ljmp MAIN
2 ; MMC (otherwise init once to SPI mode and then always errors) 19
3 20 ; REMEMBER AFTER COMPILATON TO DELETE FIRST LINE AND INCLUDE header.hex
4 ; records 1.2611 points/s (50470 points over 11 hours, 7 min=40020 s) 21
5 22 .area code (ABS)
00B5 6 SS = 0hb5 ; P3.5 drives slave device’s SS pin 0060 23 .org 0h0060
00B3 7 LED = 0hb3 24
0000 8 CHAN = 0 ; convert this ADC input channel (0 thru 6) 0060 75 CB FF 25 MAIN: MOV RCAP2H,#0hFF ; config UART for 9600 baud
9 0063 75 CA F9 26 MOV RCAP2L,#-7 ;
0030 10 MEM0 = 0h30 ; RAM locations: MMC address and a counter 0066 75 CD FF 27 MOV TH2,#0hFF
0031 11 MEM1 = 0h31 0069 75 CC F9 28 MOV TL2,#-7
0032 12 MEM2 = 0h32 006C 75 98 52 29 MOV SCON,#0h52
0033 13 MEM3 = 0h33 006F 75 C8 34 30 MOV T2CON,#0h34
0034 14 CNT = 0h34 31
15 0072 75 EF 80 32 MOV 0hEF,#0h80 ; power up ADC -- ADCCON1=0hEF
16 .area code (ABS) 0075 D2 AF 33 SETB 0hAF ; enable interrupts -- EA=0hAF
0000 17 .org 0h0000 0077 D2 AE 34 SETB 0hAE ; enable ADC interrupt -- EADC=0hAE

7
35 146
0079 D2 B5 36 setb ss 013C 74 FE 147 MOV A,#0hfe ; answer for writing data
007B 75 9C 01 37 mov 0h9c,#0h01 ; cfg814=0h9C 013E 12 01 AC 148 LCALL SENDSPI
007E 75 F8 31 38 mov 0hF8,#0h31 ; SPICON=0hF8 0141 78 00 149 writ3: mov R0,#0h00
39 0143 150 writ2: ; MOV A,R0 ; store running counter val in MMC ...
40 ;-- START INITALIZING MMC: send 10 times 0xff ---------------------------- 151 ; LCALL GETCHAR ; ... OR store value received from RS232 port
0081 74 FF 41 MOV A,#0h0ff ; send 10 times $ff with SS#=hi 0143 C2 B3 152 CLR LED ; ... OR read ADC: turn the LED off
0083 78 0A 42 mov R0,#0h0a 0145 75 D8 00 153 MOV 0hD8,#CHAN ; select channel to convert -- ADCCON2=0hD8
0085 12 01 AC 43 init: LCALL SENDSPI 0148 D2 DC 154 SETB 0hDC ; innitiate single ADC conversion -- SCONV=0hDC
0088 D8 FB 44 DJNZ R0,init 155 ; ADC ISR is called upon completion
45 014A 30 B3 FD 156 iciadc: JNB LED,iciadc
008A 00 46 NOP 014D E5 DA 157 MOV A,0hDA ; ADCDATAH=0hDA
008B 00 47 NOP 014F 12 01 A4 158 LCALL SENDCHAR
008C 00 48 NOP 0152 12 01 AC 159 LCALL SENDSPI ; read value
49 0155 E5 D9 160 MOV A,0hD9 ; ADCDATAL=0hD9
50 ;-- now reset MMC: send 0x40/00/00/00/00/0x95=cmd(00) -------------------- 0157 12 01 A4 161 LCALL SENDCHAR
008D C2 B5 51 clr ss ; 015A 12 01 AC 162 LCALL SENDSPI ; read value
52 015D 74 10 163 MOV A,#0h10 ; Delay length
008F 74 40 53 MOV A,#0h40 ; RESET MMC 015F 12 01 CD 164 LCALL DELAY ; delay 100ms
0091 12 01 AC 54 LCALL SENDSPI ; 0162 D8 DF 165 DJNZ R0,writ2
55 166
0094 74 00 56 MOV A,#0h00 ; RESET MMC: send 4 times ’0x00’ 0164 74 FF 167 MOV A,#0hff ; CRC (0xff)
0096 78 04 57 mov R0,#0h04 ; 0166 12 01 AC 168 LCALL SENDSPI ; x2
0098 12 01 AC 58 rst: LCALL SENDSPI ; 0169 12 01 AC 169 LCALL SENDSPI
009B D8 FB 59 DJNZ R0,rst 016C 12 01 B4 170 LCALL READSPI ; write response: should finish with 5=ACK
60 016F 12 01 A4 171 LCALL SENDCHAR
009D 74 95 61 MOV A,#0h95 ; ... and send CRC 172
009F 12 01 AC 62 LCALL SENDSPI 0172 173 lpwrit: ; mov R0,#100 ; how many retries should we do ?
63 0172 12 01 B4 174 LCALL READSPI ; read SPI ...
00A2 00 64 NOP 0175 B4 00 02 175 CJNE A,#0h00,endwrit ; ... while answer is 0x00
00A3 00 65 NOP 176 ; DJNZ R0,lpwrit ; avoid locking MMC in case we miss a ’0x00’ ?!
00A4 00 66 NOP 177 ; mov A,#0hAA ; signature of failed writing: we send
67 178 ; LCALL SENDCHAR ; AA 55
00A5 12 01 B4 68 loop2: LCALL READSPI 179 ; mov A,#0h55
00A8 12 01 A4 69 LCALL SENDCHAR 180 ; LCALL SENDCHAR
00AB B4 01 F7 70 CJNE A,#0h01,loop2 ; we only continue if the MMC answers ’0x01’ 0178 80 F8 181 SJMP lpwrit
71 017A D2 B5 182 endwrit:setb ss
00AE D2 B5 72 cmd1: setb ss 017C 74 FF 183 MOV A,#0hff ; CRC (0xff)
00B0 74 FF 73 MOV A,#0hff 017E 12 01 AC 184 LCALL SENDSPI
00B2 12 01 AC 74 LCALL SENDSPI 0181 22 185 RET
75 186
00B5 C2 B5 76 clr ss ; 187 ;____________________________________________________________________
00B7 74 41 77 MOV A,#0h41 ; command 0x01 & 0x40 0182 188 SENDVAL: ; converts the hex value of A into two ASCII chars,
00B9 12 01 AC 78 LCALL SENDSPI 189 ; and then spits these two characters up the UART.
00BC 74 00 79 MOV A,#0h00 190 ; does not change the value of A.
00BE 12 01 AC 80 LCALL SENDSPI 191
00C1 74 00 81 MOV A,#0 ; A,#0hc0 0182 C0 E0 192 PUSH ACC
00C3 12 01 AC 82 LCALL SENDSPI 0184 C4 193 SWAP A
00C6 74 00 83 MOV A,#0 ; A,#0hff 0185 12 01 98 194 LCALL HEX2ASCII
00C8 12 01 AC 84 LCALL SENDSPI 0188 12 01 A4 195 LCALL SENDCHAR ; send high nibble
00CB 74 00 85 MOV A,#0h00 018B D0 E0 196 POP ACC
00CD 12 01 AC 86 LCALL SENDSPI 018D C0 E0 197 PUSH ACC
00D0 74 FF 87 MOV A,#0hff 018F 12 01 98 198 LCALL HEX2ASCII
00D2 12 01 AC 88 LCALL SENDSPI 0192 12 01 A4 199 LCALL SENDCHAR ; send low nibble
89 0195 D0 E0 200 POP ACC
00D5 12 01 B4 90 loop3: LCALL READSPI 0197 22 201 RET
00D8 12 01 A4 91 LCALL SENDCHAR 202 ;____________________________________________________________________
00DB B4 01 02 92 CJNE A,#0h01,SPIok ; we continue as long as MMC answers ’0x01’ 0198 203 HEX2ASCII: ; converts A into the hex character representing the
00DE 80 CE 93 SJMP cmd1 ; otherwise MMC should answer 0x00: SPI mode OK 204 ; value of A’s least significant nibble
94 ; AT THIS POINT MMC IS IN SPI MODE 205
00E0 D2 B5 95 SPIok: setb ss 0198 54 0F 206 ANL A,#0h0F
00E2 74 FF 96 MOV A,#0hff 019A B4 0A 00 207 bbb: CJNE A,#0h0A,bbb+3
00E4 12 01 AC 97 LCALL SENDSPI 019D 40 02 208 JC IO0030
98 019F 24 07 209 ADD A,#0h07
00E7 75 30 00 99 mov MEM0,#0 ; RAM location 0, 1, 2 and 3 store the address 01A1 24 30 210 IO0030: ADD A,#0h30 ; ’0’
00EA 75 31 00 100 mov MEM1,#0 01A3 22 211 RET
00ED 75 32 00 101 mov MEM2,#0 212 ;____________________________________________________________________
00F0 75 33 00 102 mov MEM3,#0 01A4 213 SENDCHAR: ; sends ASCII value contained in A to UART
00F3 E5 31 103 contadc:mov A,MEM1 01A4 30 99 FD 214 JNB TI,SENDCHAR ; wait til present char gone
00F5 12 01 A4 104 LCALL SENDCHAR 01A7 C2 99 215 CLR TI ; must clear TI
00F8 E5 32 105 mov A,MEM2 01A9 F5 99 216 MOV SBUF,A
00FA 12 01 A4 106 LCALL SENDCHAR 01AB 22 217 RET
00FD 12 01 16 107 LCALL MMCwriteBlock ; write block at address 0 218 ;____________________________________________________________________
0100 05 31 108 inc MEM1 01AC 219 SENDSPI: ; sends the content of A to the SPI port
0102 05 31 109 inc MEM1 01AC F5 F7 220 MOV 0hF7,A ; trigger data transfer: SPIDAT=0hF7
0104 E5 31 110 mov A,MEM1 01AE 30 FF FD 221 icispi: JNB 0hFF,icispi
0106 70 EB 111 JNZ contadc ; jmp if accumulator is not 0 01B1 C2 FF 222 CLR 0hFF ; clear ISPI
0108 05 32 112 inc MEM2 01B3 22 223 RET
010A E5 32 113 mov A,MEM2 224 ;____________________________________________________________________
010C 70 E5 114 JNZ contadc 01B4 225 READSPI: ; sends the content of A to the SPI port
010E 05 33 115 inc MEM3 01B4 75 F0 08 226 mov B,#8 ; max of 8 ’FF’ answers, otherwise continue ...
0110 E5 33 116 mov A,MEM3 01B7 74 FF 227 retry1: MOV A,#0hff ; generate clocks for reception
0112 70 DF 117 JNZ contadc 01B9 12 01 AC 228 LCALL SENDSPI
118 229
0114 80 FE 119 theend: SJMP theend 01BC E5 F7 230 mov A,0hF7 ; MOV A,SPIDAT: read resulting value
120 ;____________________________________________________________________ 01BE B4 FF 03 231 CJNE A,#0hff,readend ; we only continue if the MMC answers NOT 0xff
121 ; 1/ write command 01C1 D5 F0 F3 232 DJNZ B,retry1
122 ; 2/ write 4 bytes as the writing address, multiple of 512, MSB first 233 ; SJMP readspi
123 ; 3/ write $FF (CRC) 01C4 22 234 readend:RET
124 ; 4/ wait until answer is 0 235 ;____________________________________________________________________
125 ; 5/ write $FE, followed by 512 bytes and 2x$FF as CRC 01C5 236 GETCHAR: ; waits for a single ASCII character to be received
126 ; 6/ read answer and check that it finishes with 5 (ie answer=$X5) 237 ; by the UART. places this character into A.
127 ; 7/ read while 0 -> continue when answer is non-0 (*** might be $FF so take01C5 30 98 FD 238 iciget: JNB RI,iciget
128 ; care of not locking at point 7 ***) 01C8 E5 99 239 MOV A,SBUF
0116 129 MMCwriteBlock: 01CA C2 98 240 CLR RI
0116 C2 B5 130 clr ss 01CC 22 241 RET
0118 74 58 131 MOV A,#0h58 ; command 0d24=0x18 & 0x40 242 ;____________________________________________________________________
011A 12 01 AC 132 LCALL SENDSPI 243 ; SUBROUTINE
011D E5 33 133 MOV A,MEM3 ; hi byte first ... 01CD 244 DELAY: ; Delays by 100ms * A
011F 12 01 AC 134 LCALL SENDSPI 245 ; 100mSec based on 2.097152MHZ
0122 E5 32 135 MOV A,MEM2 246 ; Core Clock
0124 12 01 AC 136 LCALL SENDSPI 247 ; i.e. default ADuC814 Clock
0127 E5 31 137 MOV A,MEM1 248
0129 12 01 AC 138 LCALL SENDSPI 01CD F9 249 MOV R1,A ; Acc holds delay variable
012C E5 30 139 MOV A,MEM0 ; ... and lo byte last 01CE 7A 22 250 DLY0: MOV R2,#0h22 ; Set up delay loop0
012E 12 01 AC 140 LCALL SENDSPI 01D0 7B FF 251 DLY1: MOV R3,#0hFF ; Set up delay loop1
0131 74 FF 141 MOV A,#0hff ; CRC 01D2 DB FE 252 ici: DJNZ R3,ici ; Dec R3 & Jump here until R3 is 0
0133 12 01 AC 142 LCALL SENDSPI 01D4 DA FA 253 DJNZ R2,DLY1 ; Dec R2 & Jump DLY1 until R2 is 0
143 01D6 D9 F6 254 DJNZ R1,DLY0 ; Dec R1 & Jump DLY0 until R1 is 0
0136 12 01 B4 144 LCALL READSPI ; should be 0x00 01D8 22 255 RET ; Return from subroutine
0139 12 01 A4 145 LCALL SENDCHAR

MMC SPI ADC.asm: example for writing data to a MultiMediaCard (MMC). The status of the MMC initialization are transmitted to
the RS232 port (9600, N81 on an ADuC814), followed by the data converted from ADC0 (2 bytes/value). These same values are

8
stored on the MMC.

1 ; REMEMBER DURING TESTS NOT ONLY TO RESET uC BUT ALSO TO SWITCH OFF 009F 70 EB 97 JNZ litout
2 ; MMC (otherwise init once to SPI mode and then always errors) 00A1 05 32 98 inc MEM2
3 00A3 E5 32 99 mov A,MEM2
00B5 4 SS = 0hb5 ; P3.5 drives slave device’s SS pin 00A5 70 E5 100 JNZ litout
5 00A7 05 33 101 inc MEM3
0030 6 MEM0 = 0h30 ; RAM locations: MMC address and a counter 00A9 E5 33 102 mov A,MEM3
0031 7 MEM1 = 0h31 00AB 70 DF 103 JNZ litout
0032 8 MEM2 = 0h32 104
0033 9 MEM3 = 0h33 00AD 80 FE 105 theend: SJMP theend
10 106 ;____________________________________________________________________
11 .area code (ABS) 00AF 107 MMCreadBlock:
0000 12 .org 0h0000 00AF C2 B5 108 clr ss ;
13 00B1 74 51 109 MOV A,#0h51 ; command 0d17=0x11 & 0x40
0000 75 CB FF 14 MAIN: MOV RCAP2H,#0hFF ; config UART for 9600 baud 00B3 12 01 00 110 LCALL SENDSPI
0003 75 CA F9 15 MOV RCAP2L,#-7 ; 00B6 E5 33 111 MOV A,MEM3 ; hi byte first ...
0006 75 CD FF 16 MOV TH2,#0hFF 00B8 12 01 00 112 LCALL SENDSPI
0009 75 CC F9 17 MOV TL2,#-7 00BB E5 32 113 MOV A,MEM2
000C 75 98 52 18 MOV SCON,#0h52 00BD 12 01 00 114 LCALL SENDSPI
000F 75 C8 34 19 MOV T2CON,#0h34 00C0 E5 31 115 MOV A,MEM1
20 00C2 12 01 00 116 LCALL SENDSPI
0012 D2 B5 21 setb ss 00C5 E5 30 117 MOV A,MEM0 ; ... and lo byte last
0014 75 9C 01 22 mov 0h9c,#0h01 ; cfg814=0h9C 00C7 12 01 00 118 LCALL SENDSPI
0017 75 F8 31 23 mov 0hF8,#0h31 ; SPICON=0hF8 00CA 74 FF 119 MOV A,#0hff ; CRC
24 00CC 12 01 00 120 LCALL SENDSPI
25 ;-- START INITALIZING MMC: send 10 times 0xff ---------------------------- 121
001A 74 FF 26 MOV A,#0h0ff ; send 10 times $ff with SS#=hi 00CF 12 01 10 122 LCALL READSPI ; should be 0x00
001C 78 0A 27 mov R0,#0h0a 00D2 12 00 F8 123 LCALL SENDCHAR
001E 12 01 00 28 init: LCALL SENDSPI 00D5 12 01 10 124 LCALL READSPI ; should be 0xfe
0021 D8 FB 29 DJNZ R0,init 00D8 12 00 F8 125 LCALL SENDCHAR
30 126
0023 00 31 NOP 00DB 79 02 127 mov R1,#0h02 ; here we read 512=2x256 values
0024 00 32 NOP 00DD 78 00 128 read1: mov R0,#0h00 ; *** WHY FF AND NOT 00 ??? ***
0025 00 33 NOP 00DF 12 01 08 129 read0: LCALL READSPIVAL ; read value, even if it is 0xFF => we keep
34 00E2 12 00 F8 130 LCALL SENDCHAR ; all values and cannot use READSPI here
35 ;-- now reset MMC: send 0x40/00/00/00/00/0x95=cmd(00) -------------------- 00E5 D8 F8 131 DJNZ R0,read0
0026 C2 B5 36 clr ss ; 00E7 D9 F4 132 DJNZ R1,read1
37 133
0028 74 40 38 MOV A,#0h40 ; RESET MMC 00E9 12 01 08 134 LCALL READSPIVAL ; read CRC POURQUOI PAS ?
002A 12 01 00 39 LCALL SENDSPI ; 00EC 12 00 F8 135 LCALL SENDCHAR
40 00EF 12 01 08 136 LCALL READSPIVAL ; read CRC POURQUOI PAS ?
002D 74 00 41 MOV A,#0h00 ; RESET MMC: send 4 times ’0x00’ 00F2 12 00 F8 137 LCALL SENDCHAR
002F 78 04 42 mov R0,#0h04 ; 138 ; LCALL READSPI ; read end POURQUOI PAS ?
0031 12 01 00 43 rst: LCALL SENDSPI ; 139 ; LCALL SENDCHAR
0034 D8 FB 44 DJNZ R0,rst 00F5 D2 B5 140 setb ss
45 141
0036 74 95 46 MOV A,#0h95 ; ... and send CRC 00F7 22 142 RET
0038 12 01 00 47 LCALL SENDSPI 143 ;____________________________________________________________________
48 00F8 144 SENDCHAR: ; sends ASCII value contained in A to UART
003B 00 49 NOP 00F8 30 99 FD 145 JNB TI,SENDCHAR ; wait til present char gone
003C 00 50 NOP 00FB C2 99 146 CLR TI ; must clear TI
003D 00 51 NOP 00FD F5 99 147 MOV SBUF,A
52 00FF 22 148 RET
003E 12 01 10 53 loop2: LCALL READSPI 149 ;____________________________________________________________________
0041 12 00 F8 54 LCALL SENDCHAR 0100 150 SENDSPI: ; sends the content of A to the SPI port
0044 B4 01 F7 55 CJNE A,#0h01,loop2 ; we only continue if the MMC answers ’0x01’ 0100 F5 F7 151 MOV 0hF7,A ; trigger data transfer: SPIDAT=0hF7
56 0102 30 FF FD 152 icispi: JNB 0hFF,icispi
57 ;-- now reset MMC: send 0x41/00/00/00/00/0xff=cmd(00) -------------------- 0105 C2 FF 153 CLR 0hFF ; clear ISPI
0047 D2 B5 58 cmd1: setb ss 0107 22 154 RET
0049 74 FF 59 MOV A,#0hff 155 ;____________________________________________________________________
004B 12 01 00 60 LCALL SENDSPI 0108 156 READSPIVAL: ; sends the content of A to the SPI port
61 0108 74 FF 157 MOV A,#0hff ; generate clocks for reception
004E C2 B5 62 clr ss ; 010A 12 01 00 158 LCALL SENDSPI
0050 74 41 63 MOV A,#0h41 ; command 0x01 & 0x40 010D E5 F7 159 mov A,0hF7 ; MOV A,SPIDAT: read resulting value
0052 12 01 00 64 LCALL SENDSPI 010F 22 160 RET
0055 74 00 65 MOV A,#0h00 161 ;____________________________________________________________________
0057 12 01 00 66 LCALL SENDSPI 0110 162 READSPI: ; sends the content of A to the SPI port
005A 74 00 67 MOV A,#0h00 0110 75 F0 08 163 retry1: mov B,#8
005C 12 01 00 68 LCALL SENDSPI 0113 74 FF 164 MOV A,#0hff ; generate clocks for reception
005F 74 00 69 MOV A,#0h00 0115 12 01 00 165 LCALL SENDSPI
0061 12 01 00 70 LCALL SENDSPI 166
0064 74 00 71 MOV A,#0h00 0118 E5 F7 167 mov A,0hF7 ; MOV A,SPIDAT: read resulting value
0066 12 01 00 72 LCALL SENDSPI 011A B4 FF 03 168 CJNE A,#0hff,readend ; we only continue if the MMC answers not 0xff
0069 74 FF 73 MOV A,#0hff 011D D5 F0 F0 169 DJNZ B,retry1
006B 12 01 00 74 LCALL SENDSPI 170 ; SJMP readspi
75 0120 22 171 readend:RET
006E 12 01 10 76 loop3: LCALL READSPI 172 ;____________________________________________________________________
0071 12 00 F8 77 LCALL SENDCHAR 0121 173 GETCHAR: ; waits for a single ASCII character to be received
0074 B4 01 02 78 CJNE A,#0h01,SPIok ; we continue as long as MMC answers ’0x01’ 174 ; by the UART. places this character into A.
0077 80 CE 79 SJMP cmd1 ; otherwise MMC should answer 0x00: SPI mode OK 0121 30 98 FD 175 iciget: JNB RI,iciget
80 ; AT THIS POINT MMC IS IN SPI MODE 0124 E5 99 176 MOV A,SBUF
0079 D2 B5 81 SPIok: setb ss 0126 C2 98 177 CLR RI
007B 74 FF 82 MOV A,#0hff 0128 22 178 RET
007D 12 01 00 83 LCALL SENDSPI 179 ;____________________________________________________________________
84 180 ; SUBROUTINE
0080 75 30 00 85 mov MEM0,#0 ; RAM location 0, 1, 2 and 3 store the address 0129 181 DELAY: ; Delays by 100ms * A
0083 75 31 00 86 mov MEM1,#0 ; 64 or 0 182 ; 100mSec based on 2.097152MHZ
0086 75 32 00 87 mov MEM2,#0 183 ; Core Clock
0089 75 33 00 88 mov MEM3,#0 184 ; i.e. default ADuC814 Clock
008C E5 31 89 litout: mov A,MEM1 185
008E 12 00 F8 90 LCALL SENDCHAR 0129 F9 186 MOV R1,A ; Acc holds delay variable
0091 E5 32 91 mov A,MEM2 012A 7A 22 187 DLY0: MOV R2,#0h22 ; Set up delay loop0
0093 12 00 F8 92 LCALL SENDCHAR 012C 7B FF 188 DLY1: MOV R3,#0hFF ; Set up delay loop1
0096 12 00 AF 93 LCALL MMCreadBlock ; read block at address 0x00 012E DB FE 189 ici: DJNZ R3,ici ; Dec R3 & Jump here until R3 is 0
0099 05 31 94 inc MEM1 0130 DA FA 190 DJNZ R2,DLY1 ; Dec R2 & Jump DLY1 until R2 is 0
009B 05 31 95 inc MEM1 0132 D9 F6 191 DJNZ R1,DLY0 ; Dec R1 & Jump DLY0 until R1 is 0
009D E5 31 96 mov A,MEM1 0134 22 192 RET ; Return from subroutine

MMC SPI dumpall.asm: example for reading data from a MultiMediaCard (MMC). The status of the MMC initialization are
transmitted to the RS232 port (9600, N81 on an ADuC814), followed by the content of the MMC. The transfer will continue even if
the end of the MMC is reached so the user must CRTL-C the recording program when all relevant data have been obtained.

The program presented here has been combined with the temperature mesurement program in order to store the tem-
perature readings from the LM35 sensor connector to ADC0 of the ADuC814 in the MMC.

9
60 direct RS232 transmission direct bank 4 22/08
recorded on MMC−2oC sunlight 17:50
50 bank 3
temperature (oC)

20/08
18:07
40 19/08/03 21/08
19:35 PM 17:40 22/08/03
bank 2
09:00 AM
30

20 bank 1

10
0.2 0.4 0.6 0.8 1 1.2 1.4 1.6 1.8 2
point index (0.6306 points/s) 5
x 10
Outdoor temperature reading over 3 days, directly transmitted to the PC via the RS232 link (blue curve) and stored and later
retrieved on the MMC card (red curve, shifted by 2o C for clarity).

Application: autonomous, battery based measurement of temperature, pressure and battery level (voltage divider).

10

Vous aimerez peut-être aussi