Nano Every - ATmega4809 - am Anfang stand der ... Anfang

Hallo,

jetzt noch ein Bsp. mit mehr Praxisnutzen für den Einen oder Anderen. Es geht um die Ansteuerung einer H-Brücke mit BTS7960 (BTN7960). Gibts bei ebay als fertiges kleines Modul. Eine gute Beschreibung findet man zum Bsp. hier:
BTS7960 Motor Driver (1).pdf (1,1 MB)
Die 43A sollte man allerdings für das Modul nicht so ernst nehmen.
Das Modul hat:

  • 2 Stromüberwachungsausgänge (R_IS und L_IS)
  • 2 Enable Eingänge (R_EN und L_EN)
  • 2 PWM Eingänge (R_PWM und L_PWM)

Meine Überlegung war wie ich den einen PWM Takt vom µC kommend beiden Eingängen zukommen lasse und gleichzeitig die Richtung vorgebe ohne irgendwelche Signale mit Relais o.ä. umschalten zu müssen. Das hatte ich bisher mit einem externen 4 fach NAND IC gelöst.

>              ____         ____
>   (CW)------|    |   +---|    |
>             | &  |o--+   | &  |o---> BTS (R_PWM) 
>         +---|____|   +---|____| 
>         |
>  (PWM)--+
>         |    ____         ____
>         +---|    |   +---|    |
>             | &  |o--+   | &  |o---> BTS (L_PWM) 
>  (CCW)------|____|   +---|____| 

  CW .... Clockwise, Uhrzeigersinn
  CCW ... Counterclockwise, Gegenuhrzeigersinn

Es muss dabei allerdings in der Software beachtet werden, dass CW und CCW nicht gleichzeitig ihr Richtungssignal rausgeben. Ansonsten gebe es in der H-Brücke einen Kurzschluss. Das muss man per Software verriegeln. Solche Sicherheits relevanten Sachen sollte man wenn irgendwie möglich in Hardware lösen. Man müßte entweder noch eine XOR Logik davor schalten was so aussehen könnte.

>        +---------------------+
>        |   ____              |    ____
>  (CW)--+--|    |             +---|    |
>           |XOR |--+              | &  |---> BTS (R_PWM) 
> (CCW)--+--|____|  |          +---|____|
>        |          |   ____   |
>        |          +--|    |  |    ____
>        |             | &  |--+---|    |
>        |   (PWM)-----|____|      | &  |---> BTS (L_PWM) 
>        +-------------------------|____| 

Oder man nutzt die CCL vom ATmega4809. Logisch. :slight_smile: Genau hier steig ich ein, weil die CCL Gatter jeweils 3 Eingänge zur Verfügung stellen, kann ich das Eingangs XOR und AND mit einem Gatter erschlagen und kombinieren. Das Gesamtkunstwerk sieht nach Stundenlanger Überlegung, wegen der Pinauswahl, vollbeschriftet wie folgt aus.

>                                                           ____
>  (D4)(PC6)(Event)>---+------------------------>(EventA)--|    |
>   CW Freigabe        |                                   | &  |---> (CW)(D15)(PD2)(EVOUTD) --> BTS (R_PWM) 
>                      |                     +-->(EventB)--|____|
>                      |             ____    |              LUT1 
>                      +--(EventA)--|    |   |
>  (D2)(PA0)(0-IN0)>----------------|    |---+ 
>   PWM Takteingang    +--(EventB)--|____|   |
>                      |             LUT0    |                             
>                      |                     |              ____
>                      |                     +-->(EventA)--|    |
>                      |                                   | &  |---> (CCW)(D14)(PD3)(2-OUT) --> BTS (L_PWM) 
>  (D7)(PA1)(Event)>---+------------------------>(EventB)--|____|    
>   CCW Freigabe                                            LUT2    

Mein Ziel war es die Pins von I2C, SPI und Timerausgänge frei zu halten was letztlich gelungen ist. Ich hätte am Liebsten noch einen Timerausgang µC intern übers Eventsystem einem LUT0 Eingang zugewiesen, aber wir wissen ja, man kann nur 2 Eingänge über das Eventsystem einer LUTn zuführen. Genau diese zwei A/B Eventmöglichkeiten benötigen wir schon für die Aufteilung (Parallelschaltung) der CW/CCW Richtungsfreigaben. Dafür liegen die Pins der Timerausgänge und der PWM Eingang auf der gleichen Boardseite und können per kurzer Drahtbrücke verbunden werden. Auch nicht schlecht.

Die endgültige Pinbelegung reduziert sich durch das Eventsystem auf das Wesentliche.

 Ardu      |    Event       | EVSYS  | CCL-LUTn |
  Pin Port | Port | Channel |        |          |
   2  PA0  |                |        |  0-IN0   | PWM-IN  
   4  PC6  |  0     2 or 3  |        |          | Freigabe CW
   7  PA1  |  0     0 or 1  |        |          | Freigabe CCW    
  15  PD2  |                | EVOUTD |          | PWM-OUT  CW
  14  PD3  |                |        |  2-OUT   | PWM-OUT  CCW

Ich habe für mich festgelegt das ich Pin D4 über Kanal 2 und Pin D7 über Kanal 1 im Eventsystem verlege. Das sind jeweils die Signal Generatoren im Eventsystem. Diesen beiden Kanälen weise ich jeweils 2 Event-Usern zu. Nämlich einmal einen Eingang von LUT0 und einen Eingang von LUT1 bzw. LUT2.

// Enable-CW auf 2 Eingänge verteilen
EVSYS.CHANNEL2 = EVSYS_GENERATOR_PORT0_PIN6_gc;   // PC6 
EVSYS.USERCCLLUT1A = EVSYS_CHANNEL_CHANNEL2_gc;
EVSYS.USERCCLLUT0A = EVSYS_CHANNEL_CHANNEL2_gc;
  
// Enable-CCW auf 2 Eingänge verteilen
EVSYS.CHANNEL1 = EVSYS_GENERATOR_PORT0_PIN1_gc;   // PA1
EVSYS.USERCCLLUT0B = EVSYS_CHANNEL_CHANNEL1_gc;
EVSYS.USERCCLLUT2B = EVSYS_CHANNEL_CHANNEL1_gc;

Mit dem LUT0 Ausgang mache ich das Gleiche. Hier habe ich freie Kanalwahl, außer das Kanal 1 und 2 schon belegt sind. Ich verwende hierfür Kanal 0, der ist für andere Pins sowieso nicht mehr verwendbar. Auch diesem Kanal 0 weise ich 2 Eingänge, sprich "User" zu. Einer von LUT1 und einer von LUT2. Welcher Gattereingang das später konkret wird, wird hier noch nicht festgelegt. Man legt nur fest, dass es zu einer bestimmten LUTn gehören soll samt deren A/B Zuordnung. A/B muss man sich wie eine weitere Unterverteilung vorstellen. A/B ist LUTn spezifisch und nicht Kanal spezifisch.

// LUT0-OUT auf 2 Eingänge verteilen
EVSYS.CHANNEL0 = EVSYS_GENERATOR_CCL_LUT0_gc;     
EVSYS.USERCCLLUT1B = EVSYS_CHANNEL_CHANNEL0_gc;   
EVSYS.USERCCLLUT2A = EVSYS_CHANNEL_CHANNEL0_gc;  

Für LUT2 kann ich direkt den Gatterausgang 2-OUT am Pin (D14) verwenden. Den anderen Ausgang von LUT1 muss ich übers Eventsystem rausführen. Dafür stehen im Eventsystem die User EVOUTx zur Verfügung. EVOUTD liegt auf Pin 15 und ich verwende dafür den freien Kanal 3.

// LUT1-OUT zum Pin rausführen
EVSYS.CHANNEL3 = EVSYS_GENERATOR_CCL_LUT1_gc;
EVSYS.USEREVOUTD = EVSYS_CHANNEL_CHANNEL3_gc;     // Pin 15 (PD2)

Bleibt noch die LUTn Konfiguration und ich beginne mit dem Eingangsgatter LUT0 an.

void initLUT0CCL(void)
{
  CCL.LUT0CTRLB  = CCL_INSEL0_IO_gc;      // LUT0-IN0 input pin
  CCL.LUT0CTRLB |= CCL_INSEL1_EVENTA_gc;  // LUT0-IN1 input source from Event 
  CCL.LUT0CTRLC  = CCL_INSEL2_EVENTB_gc;  // LUT0-IN2 input source from Event
   
  CCL.TRUTH0 = _BV(5) | _BV(3);           // Configure Truth Table, (IN0&IN1) XOR (IN0&IN2)

  CCL.LUT0CTRLA |= CCL_ENABLE_bm;         // enable LUT0
}

Hierfür noch eine Nebeninformation. Der Gattereingang n-IN0 wird im unteren Nibble und n-IN1 im oberen Nibble im Register LUT0CTRLB zugewiesen. Der Gattereingang n-IN2 wird im unteren Nibble im Register LUT0CTRLC zugewiesen. Am Besten ihr schaut dazu einmal ins Manual Kapitel 27.5.7 und 27.5.8. :slight_smile:

Pin D2 (0-IN0) ist ein direkter Gatterpin ohne weiteren Umweg.
Gatterpin 0-IN1 weise ich vom Eventsystem den "Unterverteiler" A zu.
Gatterpin 0-IN2 weise ich vom Eventsystem den "Unterverteiler" B zu.
Danach wird die Logik die eigentliche Aufgabe des Gatters konfiguriert.
Ich möchte ja eine Verriegelung zwischen CW und CCW haben, sprich ein Exklusiv ODER (XOR). Und ich möchte das der PWM Takt nur dann durchgeleitet wird, wenn das XOR logisch '1' liefert.

Das heißt ich verknüpfe das XOR mit PWM per UND. Wie in der zweiten Zeichnung am Anfang dargestellt. In der Logiktabelle muss ich mir dafür nur die Logikzustände aussuchen die dieser Logik entsprechen. Der LUT0 Ausgang soll nur schalten wenn eine logische '1' von PWM anliegt UND entweder von CW oder von CCW eine Freigabe anliegt. Auf keinen Fall darf der LUT0 Ausgang reagieren wenn CW und CCW zeitgleich anliegen. Die gewünschte Logik wird mit diesen beiden Bitkombinationen erreicht.

  Wahrheitstabelle:
  IN2 | IN1 | IN0 | LUTnOUT [bit number in CCL.TRUTHn]
   0  |  1  |  1  | TRUTHn[3]
   1  |  0  |  1  | TRUTHn[5]

Entweder IN0 und IN1 oder IN0 und IN2. Dafür setze ich Bit 3 und 5 im entsprechenden LUT0 Register 'TRUTH0'.

Für LUT1 und LUT2 gilt ähnliches in der Pin Zuführung mittels Eventkonfiguration.

// Enable-CW auf 2 Eingänge verteilen
EVSYS.CHANNEL2 = EVSYS_GENERATOR_PORT0_PIN6_gc;   // PC6 
EVSYS.USERCCLLUT1A = EVSYS_CHANNEL_CHANNEL2_gc;
EVSYS.USERCCLLUT0A = EVSYS_CHANNEL_CHANNEL2_gc;
  
// Enable-CCW auf 2 Eingänge verteilen
EVSYS.CHANNEL1 = EVSYS_GENERATOR_PORT0_PIN1_gc;   // PA1
EVSYS.USERCCLLUT0B = EVSYS_CHANNEL_CHANNEL1_gc;
EVSYS.USERCCLLUT2B = EVSYS_CHANNEL_CHANNEL1_gc;

Dessen Gattereingänge werden jeweils UND verknüpft. Dafür sorgt Bit 3 im entsprechenden LUTn Register 'TRUTHn'. Eine zusätzliche Konfiguration ist für LUT2 erforderlich. Es wird ja der Gatterausgang direkt verwendet. Deswegen muss dieser Ausgang aktiviert werden.

CCL.LUT2CTRLA = CCL_OUTEN_bm;         // enable Output LUT2

Das wars. Mehr ist das nicht. :slight_smile: Man muss sich nur die Zeit nehmen, sich in Ruhe reindenken, die App Notes lesen und ausprobieren.

Bleibt noch die Frage was ich mit den beiden Enable Eingänge (R_EN und L_EN) vom H-Brückemmodul mache? Die kann ich entweder beide fest auf High-Pegel klemmen. Oder ich schließe einen gemeinsamen "Hauptschalter" an um nochmals losgelöst von allen anderen das Modul generell ein- und ausschalten zu können. Damit könnte man ein Not-Aus bauen. Oder ich führe die Pins gemeinsam dem µC zu und schalte es per Software ein/aus. Das wird sich noch zeigen was sinnvoller ist.

Das alles nochmal etwas anders grafisch dargestellt samt Zugehörigkeit sieht so aus.

Zum einfachen testen habe ich einen Led Blinker auf D13 erstellt und führe das simulierte Taktsignal dem PWM Eingang D2 per Drahtbrücke zu. An Pin 4 und 7 sitzen Taster zu GND. An Pin 14 und 15 sind Leds angeklemmt. Wenn alles richtig gemacht wurde blinkt immer nur eine der Leds syncron zum D13 Blinker. Drückt man beide Taster blinkt keine Led, außer D13 natürlich. Funktioniert bei mir wie gewünscht. :slight_smile:

Lohn des kleinen Aufwandes ist, wenn man sich die loop anschaut, dass diese leer ist. Abgesehen vom Aufruf des D13 Blinkers. Das heißt der gesamte Ablauf belastet nicht die Recheneinheit des µC. Das funktioniert alles nebenbei. Ähnlich wie ein Timer nebenher funktioniert. Jetzt steht der H-Brücke Sicherheitstechnisch gesehen nichts im Weg und man kann unbekümmert einen DC Motor o.ä. per Pulsweite steuern und die Drehrichtung ändern. Mögliche Softwareseitige Programmierfehler sind eliminiert. Für mich dient das der Ansteuerung kleiner DC Spielzeugmotoren. Für größere Motoren sollte man mehr beachten. Dazu sollte mich jedoch niemand fragen. :wink:
https://www.mikrocontroller.net/articles/Motoransteuerung_mit_PWM

Ich hoffe das die Beschreibungen genügend Wissen vermitteln das man hinterher eigene Konfigurationen programmieren kann.

Der komplette Sketch:
CCL_Logic_BTN7960_Verriegelung.zip (2,7 KB)