LGT8F328P – LQFP32 Boards

Über den Beitrag

In diesem Beitrag gehe ich auf den Mikrocontroller LGT8F328P ein und zeige, wie ihr auf ihm basierende Entwicklungsboards der Version LQFP32 in der Arduino Umgebung programmiert. Da der LGT8F328P dem ATmega328P nicht nur namentlich nahesteht, werde ich vor allem die Unterschiede erläutern.

Folgendes kommt auf euch zu:

Überblick

Der LGT8F328P wird von dem chinesischen Unternehmen LogicGreen (mittlerweile anscheinend: Prodesign Semiconductor) hergestellt. Er ist mit dem AVR-Befehlssatz und dem ATmega328P weitestgehend kompatibel. Dabei ist der LGT8F328P günstiger und weist gegenüber dem ATmega328P einige technische Vorteile und zusätzliche Funktionen auf. Deshalb verwundert es nicht, dass er dafür verwendet wurde, Pendants zum Arduino UNO R3, Arduino Nano 3 und Arduino Pro Mini zu entwickeln. Und genau diese Boards behandle ich in diesem Beitrag.

Am weitesten verbreitet sind die Nano und Pro Mini kompatiblen Boards, die ihr unter der Bezeichnung „LGT8F328P LQFP32 MiniEVB“ in Online-Shops für wenige Euro bekommt. Das von mir getestete Arduino UNO kompatible Board stammt von AZ-Delivery. Es ist in einigen Aspekten etwas anders als die anderen Boards.

Den LGT8F238P IC gibt es als SSOP Ausführung mit 20 Pins (SSOP20), als QFP mit 48 Pins (QFP48L) oder als QFP mit 32 Pins (QFP32L). In diesem Beitrag behandele ich nur QFP32L basierten Boards, die den ATmega328P basierten Vertretern am nächsten stehen. Meistens sind sie mit dem Aufdruck „LQFP32“ versehen. In einem Folgebeitrag werde ich noch einmal auf alle drei Ausführungen eingehen. Sie besitzen dieselben Register und unterscheiden sich im Wesentlichen nur darin, welche Funktionen durch Pins tatsächlich zugänglich sind.

Das Pinout Schema des QFP32L zeigt viele Übereinstimmungen mit dem des ATmega328, aber auch ein paar zusätzliche Funktionen.

Ein gewisser Nachteil beim Arbeiten mit dem LGT8F328P ist, dass der Hersteller kein englisches Datenblatt zur Verfügung stellt. Dank einiger fleißiger Mitmenschen gibt es aber Übersetzungen, beispielsweise hier auf GitHub. 

Vorteile gegenüber dem ATmega328P

Ich möchte hier nicht alle technischen Eigenschaften des LGT8F328P „herunterbeten“. Wie der ATmega328P hat er 2 KB SRAM, 32 KB Flash, die Versorgungsspannung liegt zwischen 1.8 und 5.5 Volt usw.

Deswegen hier eine Auswahl wichtiger Unterschiede zum ATmega328P (ohne Anspruch auf Vollständigkeit):

LGT328P vs. ATmega328P: die wichtigsten Unterschiede
LGT328P vs. ATmega328P: einige wichtige Unterschiede

Pinout der LGT8F328P-LQFP32 basierten Boards

Vergleicht man die LGT8F328P Boards mit ihren ATmega328P Pendants, muss man schon zweimal hinschauen, um Unterschiede zu erkennen. Das gilt insbesondere für die Pinbezeichnungen. Hier seht ihr einen originalen Arduino Nano (blau), umrahmt von zwei LGT8F328P Nano Boards:

Arduino Nano (ATmega328P) zwischen zwei LGT8F328P Nano Boards

Auch hinsichtlich der Pinfunktionen gibt es große Übereinstimmungen mit den Originalen:

Pinout LGT8F32P basiertes Nano Board
Pinout LGT8F32P basiertes Nano Board

Ein entsprechendes Schema für den LGT8F328P basierten Pro Mini findet ihr beispielsweise hier. Für das von mir getestete Board im Arduino UNO Stil (Bezugsquelle: AZ-Delivery) habe ich kein solches Schema gefunden. Das sollte aber kein Problem sein, da der UNO die gleichen Ausgänge hat. Lediglich SWC und SWD sind nicht ausgeführt.

Installation der Board Pakete

Nano / Pro Mini Style

Um die LGT8F328P Boards mit der Arduino IDE programmieren zu können, müsst ihr zunächst ein geeignetes Board Paket installieren. Für den „L-Nano“ (als Abkürzung für „LGT8F328P basiert“) und den „L-Pro Mini“ habe ich das großartige Board Paket lgt8fx von David Buezas verwendet. Es basiert auf Larduino_HSP und LGT ist aber einfacher handhabbar, nutzt die Vorteile des LGT8F328P besser und ist besser dokumentiert. Mehr zu den Unterschieden erfahrt ihr hier.

Eine detaillierte Installationsanleitung für das Board Paket mit Screenshots findet ihr hier. Die Kurzversion lautet:

  • Geht auf Datei → Voreinstellungen.
  • Klickt auf den Button hinter „Zusätzliche Boardverwalter-URLs“.
  • Tragt dort die Zeile „https://raw.githubusercontent.com/dbuezas/lgt8fx/master/package_lgt8fx_index.json“ (ohne Anführungsstriche) ein.
  • Navigiert zu Werkzeuge → Board: „xxx“ → Boardverwalter.
  • Sucht nach „lgt8fx“ und installiert das Paket „LGT8fx Boards“ von dbuezas.

Nach erfolgreicher Installation wählt ihr als Board „LGT8F328“. Unter „Variant“ habe ich „328P-LQFP32 (e.g. MiniEVB nano-style or WAVGAT)“ ausgewählt. 

LGT8F328P Boardoptionen
LGT8F328P Boardoptionen

UNO Style

Für das UNO Board von AZ-Delivery verfahrt ihr im Prinzip genauso. Der Eintrag für die Boardverwalter-URL lautet:

  • http://www.az-arduino.de/package_AZ-Boards_index.json

Im Boardverwalter Menü sucht ihr nach „AZ-Boards“. Eine detaillierte Beschreibung gibt es im kostenlosen E-Book von AZ-Delivery.

Prüfung der Installation

Nehmt irgendeinen Sketch, beispielsweise Blink.ino aus den „Basics“ Beispielsketchen der Arduino IDE und ladet ihn hoch. Die Board-LED sollte im Sekundentakt blinken.

Wie ihr Sketche per USB-zu-TTL Adapter (z.B. für den Pro Mini) oder per LGTISP hochladet und den Bootloader brennt, erkläre ich gegen Ende des Beitrages (hier). 

LGT8F328P-LQFP32 Boards in Action

Genug der Vorbereitungen und allgemeinen Erklärungen – jetzt kommen wir zum interessanteren, praktischen Teil.

Ich habe sehr viele Arduino Sketche mit unterschiedlichsten Funktionen unverändert auf den LGT8F328P Boards ausprobiert. Lediglich beim Watchdog Timer und bei den Sleep-Funktionen waren Anpassungen notwendig. Alles andere funktionierte 1:1. Im Wesentlichen können wir uns also auf die zusätzlichen Funktionen konzentrieren.

Analog-Digital Wandler

Ihr könnt, wie bei den ATmega328P basierten Boards, die Spannung an einem analogen Pin per analogRead() auslesen. Die Voreinstellung für die Auflösung ist 10 Bit. Die Standard-Referenzspannung ist die Betriebsspannung.

Dabei ist zu beachten, dass das „Nano-like“ Board eine Diode zum Schutz des USB-Ports in der 5 Volt-Leitung verbaut hat. Die effektive Betriebsspannung liegt deshalb bei USB-Betrieb bei ca. 4.6 Volt. Ihr findet die Diode oberhalb der Pins D10 / D11.

Wie bei den herkömmlichen ATmega328P Boards könnt ihr externe Spannungsreferenzen verwenden. Ihr schließt sie an den Eingang REF an. Alternativ stellt der LGT8F328P interne Referenzen von 1.024, 2.048 oder 4.096 Volt zur Verfügung. Überdies erlaubt euch der LGT8F328P, die Auflösung auf 11 oder 12 Bit zu erhöhen. Der folgende Sketch zeigt, wie es geht:

/*
    Parameter:      Reference:
    DEFAULT         VCC
    EXTERNAL        External Reference (REF)
    INTERNAL1V024   Internal 1.024 volts
    INTERNAL2V048   Internal 2.048 volts
    INTERNAL4V096   Internal 4.096 volts
*/

void setup(){
    Serial.begin(9600);
    analogReference(INTERNAL4V096);
    analogReadResolution(10); // Resolution = 10, 11 or 12 Bit
}
void loop(){
    int adcValue = analogRead(A1);  // Raw value 
    float voltage = adcValue * 4.096/1024.0;  // Voltage calculation
    Serial.print("Analog Value: ");
    Serial.println(adcValue);
    Serial.print("Voltage [V]: ");
    Serial.println(voltage,3);
    delay(1000);      
}

Qualität des AD-Wandlers

Die gute Nachricht: Der AD-Wandler des LGT8F328P arbeitet ziemlich linear. Die schlechte Nachricht: Das Rauschen ist mindestens so schlecht wie bei den AVR basierten Boards. Selbst in der 10 Bit Auflösung musste ich mindestens 100 Messwerte mitteln, um stabile Werte zu erhalten. Da nützt einem eine 11- oder 12-Bit Auflösung leider herzlich wenig! Evtl. lässt sich das Rauschen noch durch Stabilisierung der Versorgungsspannung senken, da muss ich noch mal ein wenig experimentieren.

Konzentrieren wir uns auf das Positive. Ich habe den AD-Wandler geprüft, indem ich mit meinem Labornetzteil Spannungen vorgegeben und mit dem LGT8F328P gewandelt habe. Die Spannungen habe ich mit einem zuverlässigen Multimeter überprüft. Bei der A/D-Wandlung kam die interne 4.096 Volt Referenz zum Einsatz und je 100 Messwerte wurden gemittelt. Die Messung habe ich mit verschiedenen Boards durchgeführt. Hier ein typisches Ergebnis:

Prüfung des ADC - Blau: gegebene Spannung, Orange: Messwert
Prüfung des ADC – Blau: gegebene Spannung, Orange: Messwert

Jedes Board hatte eine individuelle, geringe und reproduzierbare Abweichung. Wer es genau braucht, der könnte sich also mit Excel o. ä. eine Ausgleichsfunktion erstellen.

Analoge Differenzmessungen und Verstärkung

Der LGT8F328P ermöglicht euch Differenzmessungen zwischen bestimmten analogen Eingängen. Die Differenzspannungen könnt ihr 1-, 8-, 16- oder 32-fach verstärken. Die Funktion dazu lautet analogDiffRead(). Sie erwartet als Argumente den negativen Eingang, den positiven Eingang und den Verstärkungsfaktor. Um die Funktion nutzen zu können, müsst ihr die Bibliotheksdatei differential_amplifier.h einbinden.

Hier ein Beispiel:

#include<differential_amplifier.h>
/*
    Parameter:      Reference:
    DEFAULT         VCC
    EXTERNAL        External Reference (REF)
    INTERNAL1V024   Internal 1.024 volts
    INTERNAL2V048   Internal 2.048 volts
    INTERNAL4V096   Internal 4.096 volts

 Available combinations:
| -\+ | A0  | A1  | A2  | A3  | A4  | A5  | A6  | A7  |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | 
| A0  |     |  +  |     |     |     |     |     |     |
| A1  |  +  |     |     |     |     |     |     |     |
| A2  |  +  |  +  |     |  +  |  +  |  +  |  +  |  +  |
| A3  |  +  |  +  |  +  |     |  +  |  +  |  +  |  +  |
| A4  |  +  |  +  |     |     |     |     |     |     |
| A5  |  +  |  +  |     |     |     |     |     |     |
| A6  |  +  |  +  |     |     |     |     |     |     |
| A7  |  +  |  +  |     |     |     |     |     |     |
*/

void setup(){
    Serial.begin(9600);
    analogReference(INTERNAL4V096); 
}
void loop(){
    int raw = analogDiffRead(A2,A3,GAIN_16); // GAIN_x mit x = 1, 8, 16, 32
    float voltage = raw / 1024.0 * 4096.0 / 16.0; // considers resolution, reference and gain
    Serial.println(voltage);
    delay(1000);      
}

Wie ihr an der Tabelle im Sketch seht, sind nur bestimmte Kombinationen analoger Eingänge erlaubt. Wählt ihr eine unzulässige Kombination, erhaltet ihr als Rohwert -1.

Die Dokumentation zur Differential Amplifier Bibliothek findet ihr hier.

Um differential_amplifier.h mit dem LGT8F328P basierten UNO von AZ-Delivery zu nutzen, ladet ihr euch am einfachsten das Paket lgt8fx von hier herunter und extrahiert die entsprechenden „.cpp“- und „h.“-Dateien aus dem Ordner lgt8f/libraries/differential_amplifier und speichert sie Ordner des Sketches. Bei der #include Anweisung müsst ihr die Bibliotheksdatei in Anführungsstriche setzen.

Digital-Analog Wandler

„Quasi-analoge“ PWM Signale

Wie von den ATmega328P Boards gewohnt, könnt ihr mit analogWrite() an den (Arduino) Pins 3, 5, 6, 9, 10 und 11 ein PWM-Signal abgreifen. Ebenso ist die PWM-Frequenz an den Pins 5 und 6 doppelt so hoch wie an den anderen Pins. Zu beachten ist, dass die Frequenzen von der Taktrate abhängen. Bei 16 MHz entsprechen sie mit 490 bzw. 980 Hertz denen der ATmega328P Boards. Der Wert ändert sich proportional mit der Taktrate.

Wie ihr an den Pins 1 (TX) und 2 (D2) PWM Signale erzeugt, erkläre ich, wenn wir zu den Timern kommen. Einige Pinout Schemata, die man im Netz findet, weisen zusätzlich den Pin 8 als PWM Pin aus. Das ist jedoch falsch.

Ein echtes analoges Signal erzeugen

Am Pin 4 (D4 / PD4 / DAO) könnt ihr ein echtes analoges Signal mit einer Auflösung von 8 Bit einstellen. Standardmäßig ist VCC die Referenz, sprich 255 ≙ VCC.  Alternativ könnt ihr eine externe Referenz oder die internen Referenzen 1.024, 2.048 oder 4.096 Volt nutzen. Der folgende kleine Sketch zeigt, wie es funktioniert:

/*
    Parameter:      Reference:
    DEFAULT         VCC
    EXTERNAL        External Reference (REF)
    INTERNAL1V024   Internal 1.024 volts
    INTERNAL2V048   Internal 2.048 volts
    INTERNAL4V096   Internal 4.096 volts
*/

void setup(){
    analogReference(DEFAULT); 
    pinMode(DAC0, ANALOG);
    analogWrite(DAC0, 125); // 0...255
}
void loop(){}

Genauigkeit des DAC

Ich habe die Genauigkeit des analogen Signals unter Verwendung der 4.096 Volt Referenz überprüft und war von der Übereinstimmung zwischen kalkulierten und gemessenen Wert ziemlich beeindruckt:

Überprüfung des analogen Ausganges bei Verwendung der internen Referenz
Überprüfung des analogen Ausganges bei Verwendung der internen Referenz

80 mA Ausgänge nutzen

Wegen der Doppeldeutigkeit der unterschiedlichen Pinbezeichnungen (z.B. 1 vs. TX vs. PD1 vs. D1) verwende ich im Folgenden die Bezeichnungen auf dem LGT8F328P Nano Board, wenn ich mich auf die Boardpins beziehe.

Die Pins TX,  D2, D5 und D6 sind bis zu 80 mA belastbar. Allerdings müsst ihr diese „Hochstrom“-Funktion erst aktivieren, indem ihr die entsprechenden Bits im HDR Register setzt. Dann könnt ihr – jedenfalls für die Pins D5 und D6 – die Ausgänge mit pinMode() und digitalWrite() nutzen. Hier ein Beispiel:

/*
       HDR      Port/Pin     Pin Label (Nano)
       HDR0 --> PD5       --> D5
       HDR1 --> PD6       --> D6
       HDR2 --> PF1       --> TX
       HDR3 --> PF2       --> D2 
       HDR4 --> PE4 / PF4 --> none
       HDR5 --> PE5 / PF5 --> none 
*/

void setup(){    
    HDR |= (1<<HDR0); // Activate high current for Pin 5
    pinMode(5,OUTPUT);
    digitalWrite(5, HIGH);
}
  
void loop(){}

Wenn ihr die 80 mA Option an den Pins TX und D2 aktivieren wollt, dann gibt es eine kleine Komplikation. PD1 und PF1 teilen sich TX, PD2 und PF2 teilen sich D2 (was eine eindeutige Benennung der Pins noch schwieriger macht). Die Hochstromoption wird aber nur an PF1 und PF2 bereitgestellt und nicht an PD1 oder PD2. Da aber PF1 und PF2 nicht als Arduino Pins implementiert sind (beim QFP32L), müsst ihr sie direkt über die Ports ansprechen. Hier ein „Hochstrom-Blink-Sketch“ für PF2:

/*
       HDR     Port/Pin      Pin Label (Nano)
       HDR0 --> PD5       --> D5
       HDR1 --> PD6       --> D6
       HDR2 --> PF1       --> TX
       HDR3 --> PF2       --> D2 
       HDR4 --> PE4 / PF4 --> none
       HDR5 --> PE5 / PF5 --> none 
*/


void setup(){    
/* example for activating PF2 for high current */
    HDR |= (1<<HDR3);
    DDRF = (1<<PF2);  // ~ pinMode OUTPUT
}
  
void loop(){
    PORTF |= (1<<PF2); // Pin HIGH
    delay(1000);
    PORTF &= ~(1<<PF2); // Pin LOW
    delay(1000); 
}

Aber hier ist Vorsicht geboten: Wenn PF1 oder PF2 sich im Zustand OUTPUT / HIGH befinden und PD1 bzw. PD2 auf OUTPUT / LOW könntet ihr einen Kurzschluss verursachen (ausprobiert habe ich es nicht). Und PD1 könntet ihr versehentlich über seine TX-Funktion in diesen Zustand bringen.

Falls ihr euch mit Bitoperationen und Portmanipulationen wie beispielsweise PORTF &= ~(1<<PF2) nicht auskennt, empfehle ich meinen Beitrag zu diesem Thema

Watchdog Timer (WDT)

Vielleicht hat der/die eine oder andere meinen Beitrag über den Watchdog Timer des ATmega328P gelesen. Die Sketche funktionieren auf den LGT8F328P Boards nur eingeschränkt. Wenn ihr den WDT verwenden wollt, dann nutzt ihr am besten die im Board Paket lgt8fx enthaltene Bibliothek WDT (anstelle avr/wdt.h). Hier ein Beispielsketch:

/*
    Parameter   WDT Reset Period
    WTO_64MS         64 ms   
    WTO_128MS       128 ms
    WTO_256MS       256 ms
    WTO_512MS       512 ms
    WTO_1S            1 s
    WTO_2S            2 s
    WTO_4S            4 s
    WTO_8S            8 s
    WTO_16S          16 s
    WTO_32S          32 s
*/

#include <WDT.h>

void setup() {
    Serial.begin(9600);
    Serial.println("Sketch started");
    wdt_enable(WTO_8S);
}

void loop() {
    for(int i=0; i<1000; i++){
        Serial.print("Runtime [s]: ");
        Serial.println(i);
        delay(1000);
//        wdt_reset();
    }
}

Nach jeweils acht Sekunden wird ein Neustart ausgelöst. Wenn ihr jedoch die Zeile 28 entkommentiert, läuft der Sketch durch.

WDT Interrupt

Um die WDT ISR zu nutzen, verwendet ihr die Funktion wdt_ienable(). Hier ein Beispiel:

#include <WDT.h>

volatile boolean isrflag;

void setup() {
    pinMode(LED_BUILTIN, OUTPUT);
    digitalWrite(LED_BUILTIN, LOW);
    Serial.begin(9600);
    Serial.println("Sketch started.");
    isrflag = false;
    wdt_ienable(WTO_4S);
}

void loop() {
    int n = 0;
    do {
        Serial.print("Elapsed time: ");
        Serial.print(n);
        Serial.println(" s");
        delay(1000);
        if (digitalRead(LED_BUILTIN)) 
            digitalWrite(LED_BUILTIN, LOW );
        else
            digitalWrite(LED_BUILTIN, HIGH);
        if (isrflag) {
            Serial.println("--There was a wdt interrupt.--");
            isrflag = false;
            wdt_enable(WTO_4S); // uncomment this line...
            // wdt_ienable(WTO_4S);   // ...comment this line and see the change
        }
        // wdt_reset(); // if uncommented, no interrupt will occur
    } while( n++ < 1000 );
}

ISR (WDT_vect)
{
    isrflag = true;
    wdt_reset();
}

 

Wenn ihr den LGT8F328P nach dem Interrupt nicht neu starten wollt, dann wechselt in Zeile 28/29 von wdt_enable() auf wdt_ienable().

Schlaf- und Power-Modi

Wenn ihr euer LGT8F328P Board in den Schlaf schicken wollt, dann nutzt ihr am besten die im Board Paket enthaltene Bibliothek PMU.

Um den folgenden Beispielsketch auszuprobieren, verbindet ihr Pin 2 (D2) über einen Taster mit GND. Mit den im Sketch gewählten Einstellungen wird euer Board in den Schlaf geschickt, wacht nach vier Sekunden wieder auf, um dann wieder in den Schlaf geschickt zu werden usw. Drückt ihr den Taster, wacht euer Board durch den externen Interrupt sofort auf.

/*
    Power Modes
    PM_IDLE0:     disable core clock only
    PM_POWERDOWN: disable most clocks and switch main clock to rc32k
    PM_POFFS0:    power off all core functions (digital and analog)
                  wake up by external interrupt or periodly
    PM_POFFS1:    the lowest power mode which close all modules and clocks 
                  wake up by external interrupt only

    Sleep Duration:
    SLEEP_xMS with x = 64, 128, 256, 512 (milliseconds), e.g. SLEEP_256MS
    SLEEP_yS  with y = 1, 2, 4, 8, 16, 32 (seconds), e.g. SLEEP_4S
    SLEEP_FOREVER
*/

#include <PMU.h>
const int interruptPin = 2;

void setup(){
    Serial.begin(9600);
    Serial.println("Sketch started!");
    pinMode(interruptPin, INPUT);
    digitalWrite(interruptPin, HIGH); 
    attachInterrupt(digitalPinToInterrupt(interruptPin), noAction, FALLING); 
}
 
void loop(){
    Serial.flush();
    PMU.sleep(PM_POWERDOWN, SLEEP_4S);
    Serial.println("Woke Up!");
    delay(200); // for debouncing 
}

void noAction(){} // dummy

Fastio – Funktionen

Die Funktionen pinMode(), digitalWrite() und digitalRead() haben eine ganze Menge Ballast, den man in den meisten Fällen nicht braucht. Wenn ihr ein paar Mikrosekunden einsparen wollt, dann könnt ihr die schon erwähnte Portmanipulation anwenden. Wem das zu kryptisch ist, dem stehen fastioMode(), fastioWrite(), fastioRead() und fastioToggle() als Alternativen zur Verfügung.

Der Nachteil an den Funktionen ist, dass ihr ihnen keine Variablennamen übergeben dürft, sondern nur die Pinnummer. Versucht ihr es trotzdem, hagelt es Fehlermeldungen.

Hier ein Beispiel für den korrekten Gebrauch:

void setup() {
    fastioMode(8, OUTPUT); // equals: DDRB |= (1<<PB0);
}

void loop() {
    fastioWrite(8, HIGH); // equals (in essence): PORTB |= (1<<PB0);
    delay(100);
    fastioWrite(8, LOW);  // equals (in essence): PORTB &= ~(1<<PB0);
    delay(100);
    fastioToggle(8); // equals: PINB = (1 << PB0);
    delay(1000);
    fastioToggle(8); // equals: PINB = (1 << PB0);
    delay(1000);
}

Die Verwendung von fastioRead() ist genauso einfach:

int status = fastioRead(8); // equals: int status = (PINB >> PB0) & 1;

Die fastio-Funktionen sind kein besonderes Merkmal des LGT8F32P, sondern nur ein nettes Feature des Board Paketes. 

Timer und PWM

Der LGT8F328P besitzt zwei 8-Bit und zwei 16-Bit Timer, also einen 16-Bit Timer mehr (Timer3) als der ATmega328P. Zu den Timern 0 bis 2 gehören je zwei Output Compare Register, mit deren Hilfe ihr an den zugeordneten Ausgängen OCxA/OCxB PWM Signale erzeugen könnt. Wie das funktioniert, habe ich in meinen Beiträgen über die Timer0 und Timer1 und über den Timer2 erklärt. Alle dort verwendeten Sketche funktionieren auch auf dem LGT8F328P.

Zum Timer3 gehören drei Output Compare Register und die drei Ausgänge OC3A, OC3B und OC3C.

Die Timer und zugehörigen Ausgänge des LGT8F328P
Die Timer und zugehörigen Ausgänge des LGT8F328P

Das Pinoutschema des LGT8F328P-QFP32L (siehe ganz oben) ist diesbezüglich etwas irreführend. Richtig ist, dass OC3C keinen Ausgangspin hat. Ebenso stimmt, dass OC3A und OC3B an den Pins für PD1 und PD2 ausgeführt sind. Um ein PWM Signal auszugeben, müsst ihr allerdings PF1 und PF2 auf OUTPUT setzen. PF1 und PF2 teilen sich, wie schon erwähnt, ihre Ausgänge mit PD1 und PD2.

Hier ein Beispiel für ein Fast PWM Signal an PF1 (Board Pin: TX) mit einem Duty Cycle von 25 % und einer PWM-Frequenz von 2 kHz (wenn der LGT8F328P auf 32 MHz läuft):

void setup(){ 
  // Clear OC3A on Compare Match / Set OC3A at Bottom; Wave Form Generator: Fast PWM 14, Top = ICR3
  TCCR3A = (1<<COM3A1) + (1<<WGM31); 
  TCCR3B = (1<<WGM33) + (1<<WGM32) + (1<<CS30); // prescaler = 1; 
  ICR3 = 15999;
  OCR3A = 3999;
  DDRF |= (1<<PF1);
} 

void loop() {} 

Weitere Erklärungen würden hier zu weit führen – bei Interesse schaut in die schon erwähnten Beiträge über die Timer des ATmega328P. Oder, wer keine Lust hat zu rechnen und sich die ganzen Bits aus den Tabellen herauszusuchen, mag vielleicht dieses tolle Tool von David Buezas ausprobieren: Arduino Web Timers. Die Dokumentation dazu gibt es hier auf GitHub.

Ihr könnt den Timer0 und Timer1 Counter übrigens auch auf die doppelte Systemfrequenz einstellen, also auf bis zu 64 MHz! Dazu müsst ihr im „Timer Counter Clock Control und Status Register“ (TCKCSR) das F2XEN-Bit setzen. Durch Setzen der Bits TC2XS0 für Timer0 bzw. TC2XS1 für Timer1 teilt ihr dem LGT8F328P mit, welcher Timer auf doppelter Geschwindigkeit laufen soll. Also z.B.:

TCKCSR = (1 << F2XEN) | (1 << TC2XS0);

Ein Grund mehr, das Tool von David Buezas einzusetzen.

Den LGT8F328P basierten Pro Mini programmieren

Upload per USB-zu-TTL Adapter

Dem Pendant zum Arduino Pro Mini („L-Pro Mini“) fehlt, genau wie dem Original, der USB-zu-TTL Adapter. Ihr braucht also einen solchen als externes Modul. Die Programmierung ist am bequemsten, wenn ihr einen Adapter mit DTR Pin nehmt. 

Links: USB-zu-TTL Adapter, rechts: LGT8F328P basierter Pro Mini

Der hier abgebildete Adapter hat zudem den Vorteil, dass ihr ihn direkt auf den „L-Pro Mini“ stecken könnt, sofern ihr zuvor eine Pinleiste aufgelötet habt.

Solltet ihr hingegen alles per Kabel zusammenstecken, müsst ihr darauf achten, dass ihr RX mit TX und TX mit RX verbindet.

Der Upload ist einfach: Wählt den Port aus, an dem euer Adapter hängt und dann einfach hochladen.

Falls ihr einen USB-zu-TTL Adapter ohne DTR-Pin habt, ist es ein kleines bisschen komplizierter. Wenn ihr den Hochladevorgang einleitet, dann drückt ihr auf die Reset-Taste des zu programmierenden Boards. Lasst die Taste los, wenn die Kompilierung beendet ist und das eigentliche Hochladen starten soll.

Upload per LArduinoISP (ohne Bootloader)

Wie wir schon gesehen haben, weist der LGT8F328P bei aller Kompatibilität mit dem ATmega328P „unter der Haube“ deutliche Unterschiede auf. Das zeigt sich auch darin, dass er nicht über seine Pins 11, 12 und 13 per ISP programmierbar ist. Stattdessen besitzt er zu diesem Zweck die Pins SWC und SWD.

Aber wie beim ATmega328P, könnt ihr auch ein LGT8F328P basiertes Board zu einem ISP Programmer umfunktionieren.

Für den „LGT8F328P – UNO“ von AZ-Delivery macht das wenig Sinn, da dieses Board den SWC und SWD-Pin nicht ausgeführt hat. 

Ein LGT8F328P Board zum Programmer machen

Ihr nehmt das LGT8F328P basiertes Board, das als Programmer dienen soll und verbindet es mit eurem Rechner. Schließt am besten nichts weiter an das Board an. Dann führt ihr die folgenden Schritte aus:

  • Wählt in der Arduino IDE das angeschlossene Board aus.
  • Unter Werkzeuge → „Arduino as ISP“ stellt ihr die Option „[To burn an ISP] SERIAL_RX_BUFFER_SIZE to 250“ ein (s.u., blaue Auswahl).
  • Geht im Menü auf Datei → Beispiele → „Beispiele für LGT8F328“ und wählt dort den Sketch LarduinoISP aus.
  • Ladet den Sketch hoch.
  • Stellt „Arduino as ISP“ in den Werkzeugen wieder zurück auf Default (64).

 

Einstellungen für Arduino as ISP

Fertig ist der Programmer!

Sketche hochladen

Nehmt euren Programmer und verbindet ihn wie folgt mit dem zu programmierenden Board („Target“):

Programmer Target
GND GND
5V VCC
Pin 12 (D12)
SWD
Pin 10 (D10)
RST
Pin 13 (D13)
SWC

Um beim Upload auf das Target einen Reset des Programmers zu verhindern, verbindet ihr den RST-Pin des Programmers über einen 10 µF Kondensator mit dem 5 V Pin. Notfalls könnt ihr die beiden Pins auch direkt verbinden, dann dürft ihr aber keinesfalls den Reset-Knopf des Programmers drücken (Kurzschluss!).

So sieht das Ganze beispielsweise mit einem „L-Nano“ als Programmer und einem „L-Pro Mini“ als Target aus:

LarduinoISP-Programmierung: "L-Nano" Programmer und "L-Pro Mini" Target
LarduinoISP-Programmierung: „L-Nano“ als Programmer und „L-Pro Mini“ als Target

Wählt die folgenden Einstellungen in der Arduino-IDE:

  • Als Board wählt ihr das Target-Board aus.
  • Unter Werkzeuge → Programmer wählt ihr „AVR ISP“.

Zum Hochladen geht ihr dann auf Sketch → Hochladen mit Programmer oder ihr verwendet die Tasten-Kombi: Strg + Umschalt + U.

Den Bootloader zurückbringen

Wenn ihr einen Sketch per LarduinoISP hochgeladen habt, dann ist der Bootloader gelöscht. Um das Board wieder direkt per USB bzw. USB-zu-TTL Adapter programmieren zu können, müsst ihr den Bootloader zurückbringen. Nichts einfacher als das: Verwendet dieselben Einstellungen wie beim Hochladen per LarduinoISP-Programmer und geht auf Werkzeuge → Bootloader brennen.

Probleme mit dem lilafarbenen LQFP32 Board als LArduinoISP Programmer

Das lilafabene LQFP32 Board mit dem CH9340C USB-zu-TTL Chip lässt sich aus mir bisher nicht bekannten Gründen nicht als LArduinoISP Programmer einsetzen.

Fazit

LGT8F328P basierte Boards sind eine leistungsstarke Alternative zu ihren ATmega328P Pendants. Dank der verfügbaren Board Pakete sind die LGT8F328P Boards einfach zu handhaben. Sketche, die ihr für die ATmega328P Boards geschrieben habt, sind mit wenigen Ausnahmen direkt übertragbar. Dazu bietet der LGT8F328P einige zusätzliche, sinnvolle Features.

Danksagung

Ich danke David Buezas, LaZsolt und dwillmore für die hervorragende Arbeit an dem Board Paket lgt8fx und für das Review dieses Artikels.

27 thoughts on “LGT8F328P – LQFP32 Boards

  1. Hallo,
    danke für die ausführliche Behandlung dieser MCU . Die LGT8F328P – basierten Boards sind schon seit Jahren der Standard für meine Projekte. Aus meinen Experimenten noch folgende Beobachtungen :
    – der interne Oszillator lässt sich auf ca.40 MHz per Software übertakten – das Register heisst RCMCAL.
    – ebenso können die Referenzspannungen in einem weiten Bereich angepasst werden (VCAL)
    – der maximale Wert , den der ADC ausgibt, ist 4063 (0x0FDF) statt 4095. Da die Auflösung 12 bit ist, fehlen also die letzten 32 codes vor full range. Das führt schon mal zu unerwarteten Ergebnissen.
    -das SPI Modul hat für beide Datenrichtungen einen 4 byte FIFO und damit wesentlich bessere Performance als die Atmegas.
    – Eine weitere Abweichung vom AVR-Vorbild ist, dass der Flash-Speicher aus dem laufenden Programm beschrieben und in 1 kB pages gelöscht werden kann. Man kann also EEPROM -ähnliche Funktionen direkt programmieren, ohne den „EEPROM-Simulator“ (Hardware, führt bei jedem Schreibvorgang einen page swap/erase aus) benutzen zu müssen.

    Rätselhaft auch die Tatsache, dass die Chips in allen Gehäuseformen keine Typ- oder Herstellerbezeichnung haben – irgend eine Idee, warum ?

    Auch wenn die Architektur und die Performance des LGT8F328P nicht mehr „leading edge“ sind , schätze ich speziell die flexiblen Schnittstellen und Analogfunktionen . Vielen Dank nochmal für die exzellente Darstellung.

    Viele Grüße

    Georg Grossmann

  2. Hallo Wolfgang, toller Artikel, vielen Dank! Ich habe 30 Stück aus China bekommen zu je 1,50€ etwa. Eine wirkliche Alternative zu den aktuellen Preisen des 328p!

    1. PS: genau waren es 49,40€, versandkostenfrei für die 30 Stück, also 1,66€ das Stück. Aktuell liegt der Preis bei dem Verkäufer bei etwa 2€.

  3. Hallo Wolfgang, eine Frage zum Thema ‚Ein LGT8F328P Board zum Programmer machen‘: Kann ich auch z.B. ein UNO Board zum Programmer machen?
    Mein Problem: getreu dem Motto ‚erst probiert, dann studiert‘ habe ich versucht mein LGT8F328P Board als Arduino Nano zu flashen – das hat vermutlich den Bootloader zerstört. Könnte ich also mit der von dir beschriebenen Methode versuchen den Bootloader zu restaurieren? Habe im Moment nur ein LGT Board aber einige UNOs.
    Übrigens finde ich Deine Website sehr schön und informativ gemacht! Danke dass Du dein Wissen teilst.

    1. Hallo, ich hätte gar nicht gedacht, dass man das LGT8F328P Board überhaupt „erreicht“, wenn man als Board den Arduino Nano eingestellt hat. Aber ich habe es eben selbst mal ausprobiert und ich konnte tatsächlich einen Blinksketch hochladen. Erläuft dann nur zu langsam. Danach habe ich wieder auf den LGT8F328P umgestellt und konnte immer noch Sketche hochladen. Also ist der Bootloader nicht zerstört, weswegen sich die Frage stellt, ob vielleicht irgendetwas anderes im Argen liegt. Könntest du noch einmal genauer beschreiben, welches Board du genau einsetzt (LQF32 MiniEVB)? Und was sind die Einstellungen in der Arduino IDE, insbesondere Board, Clock Source, Upload Speed und Variant? Und – letzte Frage – was genau ist die Fehlermeldung beim Versuch Sketche hochzuladen?
      Und ja, wenn alles nicht hilft, dann würde ich mal versuchen, den Bootloader neu zu brennen. Allerdings brauchst du dafür ein zweites LGT8F328P Board. Bei Amazon bekommt man die Dinger im Doppelpack für 10,50 Euro. Ich möchte nicht ausschließen, dass es auch irgendwie mit dem UNO geht, aber ich selbst habe es nicht probiert.

      1. Hallo Wolfgang, ich verwende ein LQFP32 MiniEVB mit dem CH340 chip drauf (das lilafarbene).
        Einstellungen: Board: LGT8F, ClockSource: Internal 32MHz, Divider: 16, Variant: … WAVGAT, BUFFER_SIZE: 64, Upload speed: 57600
        Fehlermeldung:
        Der Sketch verwendet 6958 Bytes (23%) des Programmspeicherplatzes. Das Maximum sind 29696 Bytes.Globale Variablen verwenden 532 Bytes (25%) des dynamischen Speichers, 1516 Bytes für lokale Variablen verbleiben. Das Maximum sind 2048 Bytes.

        avrdude: stk500_paged_write(): (a) protocol error, expect=0x14, resp=0x94
        avrdude: stk500_cmd(): programmer is out of sync
        avrdude: loadaddr(): (b) protocol error, expect=0x14, resp=0x00

        avrdude: stk500_paged_load(): (a) protocol error, expect=0x14, resp=0x10
        avrdude: stk500_cmd(): protocol error
        avr_read(): error reading address 0x0000
        read operation not supported for memory „flash“
        avrdude: failed to read all of flash memory, rc=-2
        avrdude: stk500_disable(): protocol error, expect=0x14, resp=0x0c
        avrdude: stk500_disable(): protocol error, expect=0x14, resp=0x0c

        Ich habe mir inzwischen nochmal so ein Board bestellt sowie eins mit dem HT42 drauf (grün)
        Werde berichten, ob das mit dem Bootloader brennen funktioniert hat

        1. Ist mir bisher nicht über Weg gelaufen, das Problem. Bootloader brennen hilft vielleicht. Das würde ich auch auf jeden Fall probieren. Wenn das nicht hilft, dann kannst du mir auch gerne so ein Teil zuschicken. Würde ich mir gerne mal anschauen. VG, Wolfgang

        2. So, ich habe jetzt meine neuen LGT8Fs bekommen und gleich probiert: Bootloader neu geflasht und anschliessend einen Testsketch: Verhalten hat sich nicht geändert, gleiche Fehlermeldung. Kleinere Sketche scheinen aber trotz der Fehlermeldung zu laufen (z.B. AnalogReadSerial) Wenn ich in die neuen Boards eine SW flashe tritt keine Fehlermeldung auf.

          1. Sehr merkwürdig, vielleicht hast du einfach Pech gehabt und eine Fehlcharge erwischt. Was auch immer zu diesen Fehlermeldungen führen mag. Immerhin funktioniert es ja mit den neuen Boards.

  4. Hallo Wolfgang,

    vielen Dank für den hilfreichen Artikel.

    Habe mir vor geraumer Zeit einen Nano mit diesem Chip zugelegt ohne den Unterschied zu bemerken.
    Jetzt in der Verwendung fiel mir auf, dass die Ausgabe auf den Seriellen Monitor der 2.2 IDE unter Linux
    nicht lesbar war. Der Serielle Loopback Test lief fehlerfrei. Dank Deines Artikel habe ich den passenden
    Board Manager eingestellt und siehe da, die Ausgabe auf dem Seriellen Monitor war lesbar.

    Viele Grüße!
    Michael

  5. Hallo Wolfgang,
    wo beziehst Du eigentlich die LGT8F328P IC’s. Habe schon einige Händler abgegrast, leider ohne Erfolg. Ein Hinweis wäre toll, passende Boards habe ich…

    VG Frank

      1. Hallo Wolfgang,
        vielen Dank für Deine schnelle Antwort und den Hinweis / Link zu Aliexpress.
        Ebenfalls ein dickes Lob für Deine tollen Artikel über diesen Controller !!!

        Grüße Frank

  6. Hallo,

    super Artikel !

    hab vor einigen Jahren mit dem lgt8f experimentiert, war aber noch nicht so begeistert, da auch die SW wohl noch auf anderem Stand war.

    Hab das Ding heute hervorgekramt und mal den ADC getestet.

    Scheint ca 4,5 x so schnell wie der NANO zu laufen, unabhängig von der Auflösung (10,11,12 bit) , was mich etwas wundert, aber vielleicht mach ich da noch was falsch.

    Was schön wäre, wenn man auf ihm auch den OptiBoot installieren könnte, nich so sehr wegen der Schnelligkeit, sondern wegen des geringeren Platzbedarfs .

    Kalle

  7. Also ich bin mir nicht sicher, ob Sie in einigen Dingen nicht etwas voreilig waren:
    „Wie der ATmega328P hat er … einen EEPROM von 1 KB.“
    Nach dem ich das Datenblatt studiert habe und mich im Netz umschaute, scheint der lgt8f328p überhaupt keinen EEPROM zu haben. [1] Es scheint sich eher um eine Möglichkeit zu handeln, den FLASH als ROM zu „gebrauchen“, was einige als EZPROM bezeichnen. [2]

    „Das Pinout Schema des QFP32L zeigt viele Übereinstimmungen mit dem des ATmega328…“
    Hier wäre es ratsam gewesen, neben dem lgt8f328p auch den ATmega328p als Pinout abzubilden/gegenüber zu stellen. Dann fallen weitere Unterschiede auf.

    Persönlich möchte ich hinzu fügen:
    Um so mehr ich mich mit dem lgt8f328p auseinander setzt, um so deutlicher werden die Unterschiede und mir scheint es so, als handelt es sich beim lgt8f328p um kein anders gearteter Nachbau, sondern möglicherweise um eine Eigenentwicklung der Chinesen. Das geht schon beim Debugging weiter …

    [1] https://forum.arduino.cc/t/trouble-accessing-eeprom-with-an-lgt8f328p-nano-clone/1114780
    [2] https://www.jarutex.com/index.php/2021/10/09/6742/

    1. Absolut richtig – der LGT8F328P ist kein Nachbau, sondern ein Eigenentwicklung. Sonst liefe er nicht auf 32 MHz, hätte nicht die Ports E und F, würde keine 80 Milliampere an ausgewählten Ausgängen zur Verfügung stellen, hätte keinen Timer3, keine analogen Differenzmessungen und und und…
      Allerdings ist es eine Entwicklung, die darauf ausgelegt ist, eine recht weitgehende Kompatibilität mit dem ATmega328P zu gewährleisten, was besonders für die QFP32L Ausfertigung gilt. Und das ist ziemlich gut gelungen.
      Der Beitrag hat keinen Anspruch auf Vollständigkeit. Natürlich gibt es noch mehr Unterschiede, auf die ich zum Teil im nächsten Beitrag eingehe, z.B. alternative PWM Ausgänge, Schlafmodi, Strombedarf. Aber selbst dann werde ich bei weitem nicht alles erfasst haben.
      Zum EEPROM: stimmt, der EEPROM ist keiner, sondern eine Art Emulation eines EEPROM, die auf Kosten des Flash geht und das sogar doppelt, denn 1 kB „EEPROM“ kostet 2 kB Flash. Wer viel macht, macht auch Fehler – Danke für den Hinweis!
      Das Debugging werde ich mir auch noch mal anschauen – Danke auch dafür.

      1. Ich finde Ihre Art mit Kritik umzugehen sehr gelungen und bedanke mich aufrichtig für Ihren Artikel und diese Stellungnahme.

  8. Hallo Wolfgang,
    hier ein paar meiner Erfahrungen mit den LGT8F328P – LQFP32 Nano style Boards, die haben bei mir die Nano Clone fast vollständig abgelöst.
    Der Stromverbrauch im Betrieb unterscheidet sich zwischen dem Grünen und dem Purple Boards deutlich, das Purple ist sparsammer ( 18 zu 11 mA @16 MHz).
    Im vergleich zu meinen Nano Clonen ist der ADC des LGT8F328P deutlich schneller. Auch beim ADC habe ich stabilere Werte bei den Purple Boards, – was aber auch Zufall sein könnte, ich habe da noch keinen Reihentest gemacht…

    Gruß
    Herbert

    1. Hallo Herbert,

      vielen Dank für das Teilen deiner Erfahrungen. Das mit dem ADC, also purple vs. green muss ich mal ausprobieren.

      Mit dem letzten Release des Boardpaketes 2.0.4 kam noch die Bibliothek lgt_LowPower hinzu. Und dazu gibt einen Beispielsketch adcNoiseReduction. Hab ich noch nicht ausprobiert, klingt aber vielversprechend.

      VG, Wolfgang

    1. Kurzes Update zum Stromverbrauch: siehe meine letzte Antwort zum Kommentar von Bernhard.

      VG, Wolfgang

  9. Hallo Wolfgang,

    diese Chips sehen ja interessant aus! Allerdings scheint der Ruhestrom im Sleep-Modus ja ziemlich hoch zu sein, wenn man den Angaben in andern Blogs glauben kann. Damit scheiden sie dann für meine Anwendungen leider aus.

    Grüße
    Bernhard

    P.S.: Bei AliExpress findet man aber 100% kompatible Klone des 328PU für rund €2,50, die als Original verkauft werden (siehe auch: https://hinterm-ziel.de/index.php/de/2023/03/07/atmega328p-original-oder-falschung/).

    1. Vielen Dank.

      Zusatz info: Die Entwickler des Boardpaketes arbeiten derzeit an einer LowPower Bibliothek. Wenn die veröffentlicht wird, werde ich auf das Thema Stromverbrauch noch einmal zurückkommen. Die blanken Chips gibt es ab 1,36€.

      VG, Wolfgang

    2. Hallo Bernhard,

      wie angekündigt komme ich auf das Thema Stromverbrauch zurück. Im wachen Zustand verbraucht das lilafarbene LQFP32 „Nano style“ LGT8F328P Board bei 16 MHz 11.4 mA, wenn man es direkt über den 5 Volt Eingang speist. Unter denselben Bedingungen verbraucht ein Arduino Nano (mit ATmega328P, 5 V) 22 mA.

      Zum Sleep. Hier sind die Modi nicht 1:1 vom ATmega328P übertragbar. Außerdem gibt es einige Randbedingungen zu beachten. Details finden sich hier: https://github.com/dbuezas/lgt8fx/tree/master/lgt8f/libraries/lgt_LowPower .
      Da ich die blanken LGT8F328P Chips noch nicht erhalten habe, habe ich ein „Pro Mini style“ LQFP32 MiniEVB Board genommen und die LEDs und den Spannungsregler entfernt. Damit habe ich ermittelt:

      Wachmodus, 5 V, 32 MHz: 11.2 mA
      Idle Modus: 2.2 mA
      Power-Down, Standby: 14.5 µA
      Extended Standby: 23 µA
      Deep Sleep: 1.1 µA

      Durch Absenken der Betriebsspannung, was beim LGT8F328P ohne Einschränkung der maximalen Taktfrequenz möglich ist, lassen sich die Werte noch weiter herunterbringen.

      Im nächsten Beitrag gehe ich auf die verschiedenen MiniEVB Boards einschließlich des Stromverbrauchs näher ein.

      VG, Wolfgang

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert