SDCC für N76E003           


Elektronik-Labor  Projekte   


Der 51-kompatible N76E003 von Nuvoton soll nun mit SDCC programmiert werden. Weil ich keine Kommandozeilen mag, verwende ich  Jens' File Editor als Oberfläche (siehe Labortagebuch: AT89S8252 und SDCC) . Im ersten Versuch sollte der Chip so behandelt werden, als wäre er ein stinknormaler 8051. Das kleine Programm soll einfach nur mit den Leitungen am Port P1 wackeln. Es wird auch ohne Fehlermeldung kompiliert. Das Ergebnis hat allerdings die Dateiendung ihx statt hex. Man kann die Datei umbenennen oder aber beim Laden in das Brenner-Tool alle Dateitypen zulassen. Der Code kommt jedenfalls richtig an. 




Auch der Brennvorgang wird ohne Fehler ausgeführt. Aber dann passiert - Nichts!  Das war ja auch eigentlich zu erwarten, denn man muss dem Chip erst sagen, welche Eigenschaften der Port haben soll. Im Datenblatt steht dazu im Abschnitt I/O-Ports, dass dafür zwei Register pro Port zuständig sind. Und nach einem Reset stehen die Ports im Modus Eingang.



Daraus ergibt sich, dass man eigentlich nur ein Register auf Null setzen muss, dann hat man "normale" 51er Ports. Die Adresse findet sich am schnellsten in der Datei N76E003.h in den Nuvoton-Ordnern zum Keil-Compiler: sfr P1M1 = 0xB3;  Für SDCC muss die Schreibweise etwas angepasst werden: __sfr __at (0xB3) P1M1;
#include <8051.h>

__sfr __at (0xB3) P1M1;

void delay(void);

void main(void)
{
unsigned char n;
n=0;
P1M1=0;
while(1)
{
n++;
P1 = n;
delay();
}
}

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


Damit ist also jetzt das Register P1M1 an der Adresse 0xB3 bekannt und wird auf Null gesetzt. Das andere Register P1M2 stand ja schon vorher auf Null. Also übersetzen und brennen: Es blinkt! Und jetzt das Ganze noch mal für zwei Ports:

#include <8051.h>

__sfr __at (0xB1) P0M1;
__sfr __at (0xB2) P0M2;
__sfr __at (0xB3) P1M1;
__sfr __at (0xB4) P1M2;

void delay(void);

void main(void)
{
unsigned char n;
n=0;
P0M1=0;P0M2=0;P1M1=0;P1M1=0;

while(1)
{
n++;
P0 = n;
P1 = n;
delay();
}
}

void delay(void)

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

Auch das funktioniert. Nun klappern insgesamt 16 Leitungen. Damit ist klar, was zu tun ist. Die ganze Datei N76E003.h muss in die Schreibweise für SDCC übertragen werden. Dann kann diese Datei statt 8051.h eingebunden werden, sodass alle Register des Chips bekannt sind. Das ist eine reine Fleißarbeit, die vermutlich am Anfang nicht ganz ohne Fehler gelingen wird. Hier ein Auszug:


/* BYTE Register */
__sfr __at (0x80) P0 ;
__sfr __at (0x81) SP ;
__sfr __at (0x82) DPL ;
__sfr __at (0x83) DPH ;
__sfr __at (0x84) RCTRIM0 ;
__sfr __at (0x85) RCTRIM1 ;
__sfr __at (0x86) RWK ;
__sfr __at (0x87) PCON ;

__sfr __at (0x88) TCON ;
__sfr __at (0x89) TMOD ;
__sfr __at (0x8A) TL0 ;
__sfr __at (0x8B) TL1 ;
__sfr __at (0x8C) TH0 ;
__sfr __at (0x8D) TH1 ;
__sfr __at (0x8E) CKCON ;
__sfr __at (0x8F) WKCON ;

__sfr __at (0x90) P1 ;
__sfr __at (0x91) SFRS ; //TA Protection
__sfr __at (0x92) CAPCON0 ;
__sfr __at (0x93) CAPCON1 ;
__sfr __at (0x94) CAPCON2 ;
__sfr __at (0x95) CKDIV ;
__sfr __at (0x96) CKSWT ; //TA Protection
__sfr __at (0x97) CKEN ; //TA Protection

__sfr __at (0x98) SCON ;
__sfr __at (0x99) SBUF ;
__sfr __at (0x9A) SBUF_1 ;
__sfr __at (0x9B) EIE ;
__sfr __at (0x9C) EIE1 ;
__sfr __at (0x9F) CHPCON ; //TA Protection

... usw ...

Download (update 19.9.19): N76E003.zip

Nun sieht das erste Beispielprogramm so aus:

#include <N76E003.h>

void delay(void);

void main(void)
{
unsigned char n;
n=0;
P0M1=0;P0M2=0;P1M1=0;P1M1=0;

while(1)
{
n++;
P0 = n;
P1 = n;
delay();
}
}

Und blinkt, und blinkt, und blinkt ...


Taktumschaltung

Weil nun SDCC alle Register des Controllers kennt, kann ich mich auch an andere Dinge wagen. Mich interessieren z.B. die Möglichkeiten des Taktgenerators. Normalerweise läuft der interne Taktoszillator mit 16 MHz. Der Clock-Teiler CKDIV steht dann auf Null. Aber es ist jedes gerade Teilerverhältnis bis 510 möglich. Andere Controller erlauben nur Zweierpotenzen, hier hat man ganz andere Möglichkeiten. Zum Test habe ich CKDIC = 160 probiert. Die Taktfreqeunze beträgt dann 50 kHz. Den aktuellen Takt kann man auf den Pin P1.1 schalten und dort mit dem Oszi oder einem Frequenzzähler begutachten.

void main(void)
{
P0M1=0;P0M2=0;P1M1=0;P1M1=0;
// CKDIV= 160; 16 MHz /160/2 = 50 kHz

TA=0xAA; // Timed access
TA=0x55;
CKSWT = 4;// 10 kHz oscillator

CKDIV= 250;// 10 kHz /250/2 = 20 Hz

CKCON |=2; //Clock out at P1.1
P1=255;
while(1);
}

50 kHz ist schon sehr langsam und stromsparend. Aber es gibt auch noch den 10-kHz-RC-Oszilaltor. Er wird über  das Register CKSWT  ausgewählt, das TA-protected ist. Das bedeutet, man kann nicht einfach so darauf zugreifen, sondern muss sich vorher einen Timed Access freischalten. Dazu braucht es zwei ganz bestimmte Zugriffe auf das TA-Register, erst 0xAA und dann 0x55. Das soll fatale Fehler verhindern. Wenn ich z.B. eine bemannte Flugtaxi-Drohne damit steuere und dann durch einen Programmfehler auf 10 kHz runtertakte, wird die Steuerung zu langsam, und die Drohne stürzt ab. Wenn ich allerdings fehlerbedingt den Takt durch 160 teile, geht das zwar ohne TA, führt aber dennoch zum Absturz. Man sieht. Flugsicherheit ist relativ. Ich bleibe deshalb lieber am Boden.

10 kHz geht und konnte am Pin P1.1 gemessen werden. Aber funktioniert dann auch noch der Clock-Teiler? Tut er! Und deshalb läuft jetzt hier die vermutlich niedrigste Taktrate aller Zeiten und Controller. Der Controller arbeitet mit einem Takt von 20 Hz! Ich weiß zwar noch nicht, ob es jemals eine sinnvolle Anwendung dafür geben wird, aber vorerst ist am Clock-Ausgang P1.1 eine LED angeschlossen, die nun gut sichtbar vor sich hin flackert.



Elektronik-Labor  Projekte