N76E003 Serial und ADC        


Elektronik-Labor  Projekte   


Der N76E003 von Nuvoton ist weiter verbreitet, als ich vermutet hatte. Besonders in Indien scheint er in zu sein (siehe Shawon Shahryiar, embedded-lab.com/blog/getting-started-nuvoton-8-bit-microcontrollers-coding-part-1/). Dort wird ein preiswertes kleines Testboard verwendet, das man sogar bei Amazon bestellen kann. Nur der Brenner fehlt noch. Das wäre z.B. des halbe Entwicklungsboard von Nuvoton mit genau der passenden Schnittstelle. Die meisten Leute scheinen den Keil-Compiler einzusetzen. Ich möchte aber mit SDCC arbeiten. Dazu sind noch einige kleine Vorarbeiten nötig.

Als erstes muss ich die serielle Schnittstelle zähmen. In SDCC gibt es das File stdio.h mit solchen schönen Funktionen wie printf. Aber wo und wie die Zeichen landen, muss jeder selbst programmieren. Ich muss ja z.B. festlegen ob ich den UART0 oder den UART1 verwenden will, oder vielleicht eine ganz andere Ausgabe über Morsezeichen. In der Datei stdio.h findet man zwei vordefinierte Funktionen, an die man sich halten muss:

extern char getchar(void);
extern void putchar(char);

So soll also ein einzelnes Zeichen empfangen und gesendet werden. Außerdem wird noch eine Init-Funktion für den UART gebraucht. Die Funktion UART0_Timer1 habe ich aus den Nuvoton-Beispielprojekten übernommen und in der Schreibweise angepasst. Darin stecken anscheinend einige kleinere Abweichungen vom normalen 8051. Dagegen sehen getchar und putchar ganz traditionell aus. Diese beiden grundlegenden Funktionen sollen hier zunächst einmal ohne die stdio.h verwendet werden. Der Controller soll jedes Byte, das er empfängt, unverändert wieder zurücksenden.

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

void InitUART0_Timer1(long Baudrate);
char getchar(void);
void putchar(char);
void delay(void);

void main(void)
{
unsigned char n;
P0M1=0;P0M2=0;P1M1=0;P1M1=0;
InitUART0_Timer1(9600);
while (1)
{
n= getchar();
putchar(n);
}
}

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++);
}





Soweit funktioniert alles. Was immer ich im Terminal eintippe, kommt als Echo zurück. Jetzt möchte ich auch vordefinierte Texte und variable Zahlen ausgeben. Dazu wird die stdio.h mit der printf-Funktion eingesetzt. Eine Variable N wird laufend erhöht und am Bildschirm ausgegeben.

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

void InitUART0_Timer1(long Baudrate);
char getchar(void);
void putchar(char);
void delay(void);

void main(void)
{
int n;

P0M1=0;P0M2=0;P1M1=0;P1M1=0;
InitUART0_Timer1(9600);

printf ("UART0, 9600 Baud\r\n");
n=0;

while (1)
{
n++;
printf("N = %d\r\n", n);
delay();
}
}








Der AD-Wandler




Der AD-Wandler hat eine Auflösung von 12 bit und bis zu acht Eingänge. Ein neunter Eingang führt auf die interne Bandgap-Referenz von 1,2 V. Leider kann diese Spannung nicht als Referenz des AD-Wandlers genutzt werden, sondern man kann sie nur messen. Zusätzlich kann man den bei der Produktion gemessenen Wert aus dem ROM lesen. Weil man dann die Referenz genau kennt, lässt dich rückwärts die genaue Betriebsspannung VDD berechnen.

Für eine Messung muss man mit ADCCON1=1; den AD-Wandler einschalten und mit ADCCON0=Input; den Eingang auswählen. Dann wird der Wandler mit ADCS = 1 gestartet und mit while(ADCF == 0); das Ende der Messung  abgewartet. Danach steht das Ergebnis in ADCRH und ADCRL bereit.

Zuerst wurde ADC0 abgefragt. Der Pin liegt an P1.7. Leider liegen die Eingänge nicht in einer Reihe, sondern sind überall verteilt. Ich habe alle Pins als quasi-bidirektionale Ports stehen gelassen. damit haben sie einen internen Pullup nach VDD. Im Leerlauf wurde daher an ADC0 eine Spannung von 3 V gemessen, genauer 2998 mV. Dann habe ich eine gelbe LED mit internem Vorwiderstand von 1 k gegen Masse angeschlossen. Die Spannung sank auf 2030 mV. Die hohe Auflösung von ca. 1 mV ist schon eine feine Sache. In dieser Beziehung ist der N76E003 sogar einem ATmega überlegen.


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

void InitUART0_Timer1(long Baudrate);
char getchar(void);
void putchar(char);
void delay(void);


void main(void)
{
int n;
float u;
P0M1=0;P0M2=0;P1M1=0;P1M1=0;
InitUART0_Timer1(9600);
ADCCON1=1; //ADC on

printf ("ADC 12 Bit, 9600 Baud\r\n");
n=0;

while (1)
{
ADCCON0 = 0; //ADC0 = P17
//ADCCON0 = 8; //ADC8 = Uref 1,2 V
ADCS = 1; // ADC start trig signal
while(ADCF == 0);
n=ADCRH;
n=16 * n+ ADCRL;
printf ("\r\n ADC = %d",n);
u = n * 3000.0 / 4095.0;
n = u;
printf ("\r\n U = %d mV",n);
delay();
}
}





Mit einer kleinen Änderung wird der Eingang ADC8 verwendet und damit die interne Referenz gemessen:  ADCCON0 = 8;  /ADC8 = Uref 1,22 V.

while (1)
{
//ADCCON0 = 0; //ADC0 = P17
ADCCON0 = 8; //ADC8 = Uref 1,2 V
ADCS = 1; // ADC start trig signal
while(ADCF == 0);
n=ADCRH;
n=16 * n+ ADCRL;
printf ("\r\n ADC = %d",n);
u = n * 3000.0 / 4095.0;
n = u;
printf ("\r\n U = %d mV",n);
delay();
}



Die Referenz wurde mit 1265 mV gemessen. Im ROM des Controllers steht der genauere Wert. Aber für die meisten Anwendungen kommt man mit der Genauigkeit von VDD aus, also letztlich mit der Toleranz des Spannungsreglers.





Elektronik-Labor  Projekte