N76E003 Datenlogger        

Elektronik-Labor  Projekte Nuvoton            


Einer der guten Gründe für die Verwendung des N76E003 ist sein großer AROM-Speicherbereich, der nicht nur Programme, sondern auch Daten aufnehmen kann. Unter den Nuvoton-Beispielen findet man Hinweise zur Speicherung von Daten in diesen Bereich. Das Lesen ist im Prinzip einfach, weil der 8051 immer schon Daten aus seinem ROM lesen konnte. Allerdings hatte ich Schwierigkeiten, das mit dem SDCC umzusetzen. Eine Hilfe habe ich an der Universität Trier gefunden:  http://karlchenofhell.org/uni/uc.pdf
Man kann eine Variable oder ein Array im ROM ablegen, wenn man das die Anweisung __code verwendet, genauso wie __xdata das externe RAM meint. Pro forma musste ich das erste Byte beschrieben.

    __xdata unsigned char dat[500];
__code unsigned char arom[] = {0x99};

Allerdings wird dieses Array an das Ende des Programms angehängt. Die genaue Adresse ist nur mühsam zu erkunden. Mit einem Offset kann ich dann tatsächlich auf eine bestimmte Adresse zugreifen. Besser wäre es, wenn ich das Array gleich an einen gewünschten Ort setzen könnte. Es ist mir allerdings nicht gelungen, herauszufinden, ob und wie das geht. 

Also wunde zum Auslesen ein Weg gewählt, der ähnlich auch für das Beschreiben von AROM-Speicherzellen aussieht. Wenn man ein Byte speichern will, muss man zuerst einen Block von 128 Bytes löschen (AromErase). Mit dem TA-geschützten Kommando IAGO friert der ganze Controller ein bis der Vorgang abgeschlossen ist, was 5 ms dauert. Das Brennen eines Bytes (AromWrite) dauert dagegen nur 23,5 µs. Die Funktion AromRead liest ein Byte von der angegeben Adresse aus. Zusätzlich gibt es noch die Funktion UID_BYTE zum Auslesen der UID und anderer fest gespeicherter Informationen aus dem ROM. Zum Test habe ich 12800 Bytes ab Adresse 0x0A00 beschreiben und dann wieder ausgelesen und über die serielle Schnittstelle verschickt. Nur den ersten Block habe ich gelöscht, weil der Rest schon beim Flashen des Programms gelöscht war. Nach einem Refresh sieht man dann den gesamten Speicherbereich, der in diesem Fall ab 0x03C00 bis 0x03FF noch leer geblieben ist (siehe Bild oben).


void AromErase (unsigned int Addr)
{
//Erase APROM DATAFLASH page
IAPAL = (Addr&0xff);
IAPAH = (Addr>>8)&0xff;
IAPFD = 0xFF;
TA=0xAA;TA=0x55;CHPCON|=0x01; //set_IAPEN
TA=0xAA;TA=0x55;IAPUEN|=0x01; //set_APUEN;
IAPCN = 0x22;
TA=0xAA;TA=0x55;IAPTRG|=0x01; // set_IAPGO;
TA=0xAA;TA=0x55;IAPUEN&=~0x01; //clr_APUEN;
TA=0xAA;TA=0x55;CHPCON&=~0x01; //clr_IAPEN;
}

void AromWrite (unsigned int Addr, unsigned char Dat)
{
TA=0xAA;TA=0x55;CHPCON|=0x01; //set_IAPEN
TA=0xAA;TA=0x55;IAPUEN|=0x01; //set_APUEN;
IAPCN = 0x21; //Write AROM
IAPAL = (Addr&0xff);
IAPAH = (Addr>>8)&0xff;
IAPFD = Dat;
TA=0xAA;TA=0x55;IAPTRG|=0x01; // set_IAPGO;
TA=0xAA;TA=0x55;IAPUEN&=~0x01; //clr_APUEN;
TA=0xAA;TA=0x55;CHPCON&=~0x01; //clr_IAPEN;
}

unsigned char AromRead(unsigned int Addr)
{
unsigned char Dat;
TA=0xAA;TA=0x55;CHPCON|=0x01; //set_IAPEN
TA=0xAA;TA=0x55;IAPUEN|=0x01; //set_APUEN;
IAPCN = 0x00; //read_AROM
IAPAL = (Addr&0xff);
IAPAH = (Addr>>8)&0xff;
TA=0xAA;TA=0x55;IAPTRG|=0x01; // set_IAPGO;
Dat = IAPFD;
TA=0xAA;TA=0x55;CHPCON&=~0x01; //clr_IAPEN
return Dat;
}

unsigned char UID_BYTE(unsigned char Addr)
{
unsigned char Dat;
TA=0xAA;TA=0x55;CHPCON|=0x01; //set_IAPEN
IAPAL = Addr;
IAPAH = 0x00;
IAPCN = 0x04; //read_UID
TA=0xAA;TA=0x55;IAPTRG|=0x01; // set_IAPGO;
Dat = IAPFD;
TA=0xAA;TA=0x55;CHPCON&=~0x01; //clr_IAPEN
return Dat;
}


void Timer0_Delay1ms(long ms)
{
CKCON &= ~4; //T0M=0, Timer0 Clock = Fsys/12
TMOD |= 0x01; //Timer0 is 16-bit mode
TR0 = 1; //Start Timer0
while (ms != 0)
{
TL0 = 0xCA; //65536-1334 =64202
TH0 = 0xFA;
while (TF0 != 1); //Check Timer0 Time-Out Flag
TF0=0;
ms --;
}
TR0=0; //Stop Timer0
}


void InitUART0_Timer1(long Baudrate) //T1M = 1, SMOD = 1
{
SCON = 0x50; //UART0 Mode1,REN=1,TI=1
TMOD |= 0x20; //Timer1 Mode1
PCON |= 128; //SMOD = 1 UART0 Double Rate Enable
CKCON |=16; //set_T1M
T3CON &= ~64; //BRCK = 0 Serial port 0 baud rate clock source = Timer1
TH1 = 256 - (1000000/Baudrate+1); /*16 MHz */
TR1 = 1;
TI = 1; //For printf
}


char getchar(void)
{
char c;
while (!RI);
c = SBUF;
RI = 0;
return (c);
}

void putchar(char c)
{
while (!TI);
TI = 0;
SBUF = c;
}


void delay(void)
{
int i,j;
for(i=0;i<0xff;i++)
for(j=0;j<0xff;j++);
}






Wartezeiten

Auch eine flexible Wartefunktion fehle bisher noch. Ein Beispiel aus den Nuvoton-Projekten ließ sich recht einfach dien SDCC umsetzen. Die Funktion Timer0_Delay1ms verwendet den Timer0, während der Timer1 für den UART eingesetzt wird.

void Timer0_Delay1ms(long ms)
{
CKCON &= ~4; //T0M=0, Timer0 Clock = Fsys/12
TMOD |= 0x01; //Timer0 is 16-bit mode
TR0 = 1; //Start Timer0
while (ms != 0)
{
TL0 = 0xCA; //65536-1334 =64202
TH0 = 0xFA;
while (TF0 != 1); //Check Timer0 Time-Out Flag
TF0=0;
ms --;
}
TR0=0; //Stop Timer0
}

...
while (1)
{
printf ("x \r\n");
Timer0_Delay1ms(1000);
}




Der Datenlogger


Jetzt ist alles vorhanden, was man für einen praktikablen Datenlogger braucht. Das Diagramm zeigt eine Messung der Helligkeit über einen Zeitraum von ca. 20 Minuten. Die Daten wurden mit einem LDR in Reihe zu einem 10k-Widersatnd am Analogeingang ADC0 (P1.7) aufgenommen, mit dem Arduino Seriellen Monitor aufgenommen und mit Excel dargestellt.

#include <N76E003.h>
#include <stdio.h>

unsigned char AromRead(unsigned int Addr);
void AromWrite (unsigned int Addr, unsigned char Dat);
void AromErase (unsigned int Addr);
unsigned char UID_BYTE(unsigned char Addr);
void InitUART0_Timer1(long Baudrate);
char getchar(void);
void putchar(char);
void delay(void);
void Timer0_Delay1ms(long ms);


void main(void)
{
unsigned int i;
unsigned int Addr;
unsigned char dat;

P0M1=0;P0M2=0;P1M1=0x80;P1M2=0;
InitUART0_Timer1(9600);
ADCCON1=1; //ADC on

for(i=0;i<1280;i++) //Auslesen
{
printf ("\n %d",AromRead(0x0A00+i));
}
Timer0_Delay1ms(60000); //1 min warten

Addr = 0x0A00;
for(i=0;i<10;i++) //10 Blöcke löschen
{
AromErase (Addr);
Addr = Addr+128;
}
for(i=0;i<1280;i++) //Messen und speichern
{
ADCCON0 = 0; //ADC0 = P17
ADCS = 1;
while(ADCF == 0);
dat=ADCRH;
AromWrite (0x0A00+i,dat);
Timer0_Delay1ms(100);
}
while(1);
}

Um die Bedienung möglichst einfach zu halten, wurde folgender Ablauf festgelegt: Zuerst werden 1280 Daten ausgelesen. Dann folgt eine Wartezeit von einer Minute, sodass man die Daten mit einem Reset beliebig oft auslesen kann. Lässt man die Minute verstreichen, beginnt eine neue Messung. Das Programm löscht zunächst den verwendeten Datenbereich. Dann werden mit dem AD-Wandler 1280 Werte im Abstand von jeweils einer Sekunde erfasst und im ARM gespeichert. Die Datenmenge und die Intervallzeit lassen sich leicht für die jeweilige Anwendung anpassen.


Elektronik-Labor  Projekte Nuvoton