Libs for using Nucleo STM32F411 periphery
Introduction
Descruption: This lib uses the hardware peripherie from STM32F411 under serveral conditions. So you can use an quadraturencoder with different timers.
Requirement: Only tested with the nucleo F411. Include the mbed lib! Interfacing details are explained in the documentary of each class.
Overview
- timer modules
- Quadratur Encoder (Version 1.2 - C. Hoyer 12.8.2015)
- SPI modules
- AD5664 (Version 1.1 - C. Hoyer 23.7.2015)
- software modules
- Ringbuffer (Version 0.9 - C. Hoyer 18.8.2015)
- PID-Regler (Version 1.0 - C. Hoyer 17.9.2015)
Revision 0:1acdcc576936, committed 2016-11-28
- Comitter:
- ChrisselH
- Date:
- Mon Nov 28 17:27:43 2016 +0000
- Commit message:
- port from priv lib
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SPI/AD5664.cpp Mon Nov 28 17:27:43 2016 +0000 @@ -0,0 +1,137 @@ +#include "mbed.h" +#include "AD5664.h" + +//==================================================================================================================================== +// Grundlagen, DAC Schreiben und Initialisieren +//==================================================================================================================================== + +AD5664::AD5664(SPI _spi, PinName _daccs): spi(_spi), daccs(_daccs) +{ + sendDAC(0x2F, 0x00, 0x00); //Software Reset, alle Ausgänge auf 0 + } + + +void AD5664::sendDAC(int instruction = 0x00, int data1 = 0x00, int data2 = 0x00) +{ + daccs = 0; // Select Device + + spi.write(instruction); // Schreibt entsprechend die Anweisung und die Adresse + spi.write(data1); // Schreibt den ersten Byte an Daten + spi.write(data2); // Schreibt den zweiten Byte an Daten + + daccs = 1; // Deselect + } + +//==================================================================================================================================== +// Setter-Funktionen +//==================================================================================================================================== + +void AD5664::SelectCS() +{ daccs = 0; // Select Device + } + +void AD5664::DeselectCS() +{ daccs = 1; // Deselect + } + +void AD5664::writeDAC(char channel, int value) +{ + int choch = 0x00; //Temporäre Variable um die Instructions zu übernehmen + + switch(channel){ + case 'A': // DAC A + choch = 0x18; + break; + + case 'B': // DAC B + choch = 0x19; + break; + + case 'C': // DAC C + choch = 0x1A; + break; + + case 'D': // DAC D + choch = 0x1B; + break; + + case 'F': // Alle Dacs + choch = 0x1F; + break; + + default: + choch = 0x1F; // Alle Dacs + } + + sendDAC(choch, *((uint8_t*)&(value)+1), *((uint8_t*)&(value)+0)); //Schreibe via SPI: Anweisung mit Adresse, oberes Byte, unteres Byte) + + } + + + +void AD5664::loadDAC(char channel, int value) +{ + int choch = 0x00; //Temporäre Variable um die Instructions zu übernehmen + + switch(channel){ + case 'A': // DAC A + choch = 0x00; + break; + + case 'B': // DAC B + choch = 0x01; + break; + + case 'C': // DAC C + choch = 0x02; + break; + + case 'D': // DAC D + choch = 0x03; + break; + + case 'F': // Alle Dacs + choch = 0x07; + break; + + default: + choch = 0x07; // Alle Dacs + } + + sendDAC(choch, *((uint8_t*)&(value)+1), *((uint8_t*)&(value)+0)); //Schreibe via SPI: Anweisung mit Adresse, oberes Byte, unteres Byte) + + } + + +void AD5664::updateDAC(char channel) +{ + int choch = 0x00; //Temporäre Variable um die Instructions zu übernehmen + + switch(channel){ + case 'A': // DAC A + choch = 0x08; + break; + + case 'B': // DAC B + choch = 0x09; + break; + + case 'C': // DAC C + choch = 0x0A; + break; + + case 'D': // DAC D + choch = 0x0B; + break; + + case 'F': // Alle Dacs + choch = 0x0F; + break; + + default: + choch = 0x0F; // Alle Dacs + } + + sendDAC(choch, 0x00 , 0x00); //Schreibe via SPI: Anweisung mit Adresse, oberes Byte, unteres Byte) + + }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SPI/AD5664.h Mon Nov 28 17:27:43 2016 +0000 @@ -0,0 +1,145 @@ +#include "mbed.h" + +#ifndef AD5664_h +#define AD5664_h + +/*! Diese Klasse dient zur Ansteuerung des Digital-Analog-Wandlers AD5664 der Firma Analog Devices. Der DAC ist ein 16-Bit Wandler mit 4 Kanälen, Versorgungsspannung von 2,7V bis 5,5V. +Die Maximale Settling Time beträgt 7µs (AD5664), und die Genauigkeit liegt bei +-12 LSBs maximum. Angesteuert wird er mittels SCLK, CS und MOSI mit maximal 50 MHz. Zuvor muss die SPI-Schnittstelle definiert werden. Die +Chip Select Schnittstelle kann durch die Initialisierung mit Übergeben werden. Es ist darauf zu achten das der Chip bzw die Library nur den SPI-Modus "1" (CLK Standart auf 0 und übernahme mit fallender Flanke) mit einer Datenbreite von 8-Bit unterstützt. + + * @code + * #include "mbed.h" + * #include "AD5664.h" + * + * SPI interface(PB_5, PB_4, PB_3); // SPI Interface und Pins (MISO PB.5 , MOSI PB.4, SCLK PB.3) festlegen + * + * int main() { + * + * interface.format(8,1); // Interface Format festlegen: 8-Bit, SPI-Modus 1 + * interface.frequency(1000000); // Übertragungsgeschwindigkeit 1MHz + * + * AD5664 device(interface, PB_2); // Eine Instanz des DACs erzeugen, Chipselect ist PB.2 + * + * device.loadDAC('D', 0x11FF); // Den Wert 0x11FF in den Kanal D laden + * + * device.updateDAC('D'); // Den geladenen Wert am Kanal D ausgeben + * + * device.writeDAC('A', 0x89BD); // Den Wert 0x89BD an dem Kanal A direkt ausgeben + * + * } + * + * @endcode + */ + +class AD5664 +{ + public: + + /*! Konstruktor zum Erstellen einer Instanz für einen vorher spezifizierten SPI-Port und Chip Select-Pin. Nach dem Erstellen dieser Instanz werden alle Ausgänge des DACs auf 0 gesetzt. */ + /*! + \param spi Entsprechner SPI-Port (Datenbreite 8 Bit, SPI Modus 1) + \param daccs Pin für Chipselect + */ + AD5664(SPI _spi, PinName _daccs); + + + + /*! Funktion zum Beschreiben des Chips mit je 8-Bit. Für genauere Details bitte das Datenblatt lesen*/ + /*! + \param instruction <b>Direkte Anweisungen für den AD5664</b><br> + Die 8 Instruction-Bits sind wie folgt aufgebaut:<br> <br> + <CENTER> X X C C C A A A </CENTER><br> + - Die ersten zwei Bits sind don't cares<br> + - Die <EM>Befehle C</EM> sind wie fogt aufgebaut<br> + <ul> + <li> 000 Schreibe Input register N</li> + <li> 001 Update DAC Register N</li> + <li> 010 Schreibe Inputregister N und Update alle DAC Register</li> + <li> 011 Schreibe Inputregister N und Update DAC Register N</li> + <li> 100 Power Down DAC (siehe Datenblatt)</li> + <li> 101 Reset (siehe Datenblatt)</li> + <li> 110 Funktionsregister LDAC (siehe Datenblatt)</li> + </ul> + - Die <EM>Adresse A</EM> zum Register N sind wie fogt aufgebaut<br> + <ul> + <li> 000 DAC A</li> + <li> 001 DAC B</li> + <li> 010 DAC C</li> + <li> 011 DAC B</li> + <li> 111 All DACs</li> + </ul> + + \param data1 <b>Datenbits</b><br> + ersten 8 Datenbits, bzw. oberen zwei Nibble + \param data2 <b>Datenbits</b><br> + zweiten 8 Datenbits, bzw. unteren zwei Nibble + */ + void sendDAC(int instruction, int data1, int data2); + + /*! Destruktor der Klasse */ + virtual ~AD5664(){}; + + /*! Setzt den Chipselect-Pin des Wandlers auf "select" bzw. wählt diesen aus. */ + void SelectCS(); + + /*! Setzt den Chipselect-Pin des Wandlers auf "deselect" bzw. wählt diesen ab. */ + void DeselectCS(); + + + + /*! Schreibt ein 16-Bit Wert in den eingestellten Kanal. Der Wert wird direkt in das DAC Register geschrieben und der Wert wird direkt ausgegen. */ + /*! + \param channel <b>Einstellung des Ausgewählten Kanals</b><br> + <ul> + <li> A Beschreibt Kanal A</li> + <li> B Beschreibt Kanal B</li> + <li> C Beschreibt Kanal C</li> + <li> D Beschreibt Kanal D</li> + <li> F Beschreibt alle DACs </li> + </ul> + Standartwert, falls ein falscher Kanal angegeben wird ist "F" + \param value <b>16-Bit Wert</b><br> Wird Automatisch in zwei 8 Bit Werte angepasst. + */ + void writeDAC(char channel, int value); + + + + /*! Lädt ein 16-Bit Wert in den eingestellten Kanal. Der Wert wird direkt in das Inputregister geschrieben und kann mit der updateDAC-Funktion in das DAC-Register geschrieben werden */ + /*! + \param channel <b>Einstellung des Ausgewählten Kanals</b><br> + <ul> + <li> A Lädt Kanal A</li> + <li> B Lädt Kanal B</li> + <li> C Lädt Kanal C</li> + <li> D Lädt Kanal D</li> + <li> F Lädt alle DACs </li> + </ul> + Standartwert, falls ein falscher Kanal angegeben wird ist "F" + \param value <b>16-Bit Wert</b><br> Wird Automatisch in zwei 8 Bit Werte angepasst. + */ + void loadDAC(char channel, int value); + + + /*! Schreibt zuvor eingestellten Wert in das DAC Register (Laden mittels loadDAC-Funktion). Dieser Wert ist dann am Ausgang des entsprechenden DAC-Kanals zu messen. */ + /*! + \param channel <b>Einstellung des Ausgewählten Kanals</b><br> + <ul> + <li> A Beschreibt Kanal A</li> + <li> B Beschreibt Kanal B</li> + <li> C Beschreibt Kanal C</li> + <li> D Beschreibt Kanal D</li> + <li> F Beschreibt alle DACs </li> + </ul> + Standartwert, falls ein falscher Kanal angegeben wird ist "F" + */ + void updateDAC(char channel); + + protected: + //! SPI Schnittstelle + SPI spi; + //! Chipselect Pin + DigitalOut daccs; + + }; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SWModule/PIDControl.cpp Mon Nov 28 17:27:43 2016 +0000 @@ -0,0 +1,260 @@ +#include "mbed.h" +#include "PIDControl.h" + +//==================================================================================================================================== +// Konstruktor und Regelprozess +//==================================================================================================================================== + +PIDControl::PIDControl(bool P_usr, float KP_usr, bool I_usr, float KI_usr, bool D_usr, float KD_usr){ + + awu_gain = 0; // Anti-Windup deaktivieren + u_max = 32760; // Maximale Stellgröße limitiert auf max. Integer Wert + u_min = -32760; // Minimale Stellgröße limitiert auf min. Integer Wert + lockctrl = false; // Regler frei parametrierbar + + e_last = 0; // Letzter Regelfehler ist 0 + e_sum = 0; // Integralteil ist 0 + u = 0; // Führungsgröße ist 0 + awu_value = 0; // Atin-Windup größe ist 0 + status = 0; // Statusregister bisher nicht gefüllt + extfct = false; // Keine Externe Funktion festgelegt + + P = P_usr; // P-Anteil on/off + KP = KP_usr; // P-Anteil Verstärkung + I = I_usr; // I-Anteil on/off + KI = KI_usr; // I-Anteil Verstärkung + D = D_usr; // D-Anteil on/off + KD = KD_usr; // D-Anteil Verstärkung + + } + + +int16_t PIDControl::ctrltask (int16_t wsoll, int16_t yist, uint16_t time){ + + setStatus(0); // Regeltask aktiv - Flag aktivieren + + int16_t e = wsoll - yist; // Regelfehler bestimmen + e_sum = e_sum + e; // Integralanteil bestimmen + + if(e > 0){ // Regelfehler positiv + setStatus(4); // Regelfehler positiv - Flag aktivieren + resetStatus(3); // Regelfehler negativ - Flag deaktivieren + resetStatus(2); // Regelfehler null - Flag deaktivieren + } + + if(e < 0){ // Regelfehler negativ + resetStatus(4); // Regelfehler positiv - Flag deaktivieren + setStatus(3); // Regelfehler negativ - Flag aktivieren + resetStatus(2); // Regelfehler null - Flag deaktivieren + } + + if(e == 0){ // Regelfehler null + resetStatus(4); // Regelfehler positiv - Flag deaktivieren + resetStatus(3); // Regelfehler negativ - Flag deaktivieren + setStatus(2); // Regelfehler null - Flag aktivieren + } + + int16_t divisor = log2(time); // Bestimmung der Zeller um die das geschoben werden soll + + if(P){ // P-Anteil bestimmen + P_var = (KP * e); // u = Kp * e + } + else{ + P_var = 0; // kein P-Anteil vorhanden + } + + if(D){ // D-Anteil bestimmen + D_var = KD * time * (e - e_last); // u = Kd * Ta * (e - e_last) + P-Anteil + D_var = D_var << 10; // Zeitanpassung, da eingabe in µs + } + else{ + D_var = 0; // kein D-Anteil vorhanden + } + + if(I){ // I-Anteil bestimmen + I_var = (KI * (e_sum - awu_value)); // u = Ki / Ta * (e_sum - awu_value) + P-Anteil + D-Anteil + I_var = I_var << divisor; // Division durch die Zeit + } + else{ + I_var = 0; // kein I-Anteil vorhanden + } + + resetStatus(1); // I-Anteil an Grenze geraten + + if(integrallimiter){ // Limiteriung des I-Anteils erforderlich? + if(e_sum >= i_max){ // Limitierung der Integralsumme zur oberen Schwelle + setStatus(1); // Fehler I-Anteil über Grenzwerte + e_sum = i_max; // Limitierung der Summe + awu_value = (e_sum - i_max) * awu_gain; // Anti-Windup Berechnung + } + + if(e_sum <= i_min){ // Limitierung der Integralsumme zur unteren Schwelle + setStatus(1); // Fehler I-Anteil über Grenzwerte + e_sum = i_min; // Limitierung der Summe + awu_value = (e_sum - i_min) * awu_gain; // Anti-Windup Berechnung + } + } + + u = P_var + D_var + I_var; // Reglerausgang definieren + + resetStatus(6); // Regelgrenze max überschritten zurücksetzen + resetStatus(7); // Regelgrenze min überschritten zurücksetzen + + if(u >= u_max){ // Limitierung des Regelausgangs zur oberen Schwelle + setStatus(7); // Fehler Regelgrenze max überschritten + u = u_max; // Ausgabe des Stellgrößen Limits + } + + if(u <= u_min){ // Limitierung des Regelausgangs zur unteren Schwelle + setStatus(6); // Fehler Regelgrenze min überschritten + u = u_min; // Ausgabe des Stellgrößen Limits + } + + e = e_last; // Regelfehler als letzten Regelfehler übernehmen + resetStatus(0); // Regeltask aktiv - Flag deaktivieren + + return u; // Ausgabe der Stellgröße + + } + +//==================================================================================================================================== +// Setter und Getter Funktionen +//==================================================================================================================================== + +bool PIDControl::setKP(bool P_usr, float KP_usr){ // Setzen des P-Anteils + + if(!lockctrl){ + P = P_usr; // P-Anteil on/off + KP = KP_usr; // P-Anteil Verstärkung + + return true; // Setzen der Werte erfolgreich + } + else{ + return false; // Setzen der Werte nicht erfolgreich + } + } + + +bool PIDControl::setKI(bool I_usr, float KI_usr){ // Setzen des I-Anteils + + if(!lockctrl){ + I = I_usr; // I-Anteil on/off + KI = KI_usr; // I-Anteil Verstärkung + + return true; // Setzen der Werte erfolgreich + } + else{ + return false; // Setzen der Werte nicht erfolgreich + } + } + + +bool PIDControl::setKD(bool D_usr, float KD_usr){ // Setzen des D-Anteils + + if(!lockctrl){ + D = D_usr; // D-Anteil on/off + KD = KD_usr; // D-Anteil Verstärkung + + return true; // Setzen der Werte erfolgreich + } + else{ + return false; // Setzen der Werte nicht erfolgreich + } + } + + +bool PIDControl::setAWU(float awu_gain_usr){ // Setzen des Anti-Windups + + if(!lockctrl){ + awu_gain = awu_gain_usr; // Anti-Windup Verstärkung + + return true; // Setzen des Wertes erfolgreich + } + else{ + return false; // Setzen des Wertes nicht erfolgreich + } + } + +void PIDControl::setIlimits(bool integrallimiter_usr, int16_t i_max_usr, int16_t i_min_usr){ // Setzen des Integrallimits + + if(!lockctrl){ + integrallimiter = integrallimiter_usr; // Integrallimits aktivieren + i_max = i_max_usr; // maximaler I-Anteil + i_min = i_min_usr; // minimaler I-Anteil + } +} + +void PIDControl::setUlimits(int16_t u_max_usr, int16_t u_min_usr){ // Setzen des Ausgangsgrößenlimits + + if(!lockctrl){ + u_max = u_max_usr; // maximale Stellgröße + u_min = u_min_usr; // minimale Stellgröße + } +} + + + +void PIDControl::lock(){ + lockctrl = true; // Einstellung der Regelparamieter sperren + } + + +void PIDControl::unlock(){ + lockctrl = false; // Einstellung der Regelparamter freigeben + } + + +uint8_t PIDControl::getStatus(){ + return status; // Rückgabe des aktuellen Statusvektors +} + + +void PIDControl::setERFFCT(void (*EXTERN_CTRL_HANDLER)(void)){ // Adresse zur externen Funktion übergeben und Freigabe setzen + extfct = true; // Externe Funktion vorhanden. Freigabe setzen. + CTRL_HANDLER = EXTERN_CTRL_HANDLER; // Funktionspointer der Funktion übernehmen + } + +//==================================================================================================================================== +// Interne Funktionen +//==================================================================================================================================== + + +int16_t PIDControl::log2(int16_t a){ // Bestimmt die nächstkleinere Zweierpotenz der Zahl a + + if(a == time_last){ + return time_last_result; // Aktueller Wert entspricht letztem Wert + } + + a = time_last; // Speichert aktuellen Wert + + static const int DeBruijnFolge[32] = { 0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30, 8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31}; + + a |= a >> 1; // Runden auf die kleinste Stelle + a |= a >> 2; + a |= a >> 4; + a |= a >> 8; + a |= a >> 16; + + time_last_result = DeBruijnFolge[(uint32_t)(a * 0x07C4ACDDU) >> 27]; // Ausgabe der passenden Zweierpotenz + + return time_last_result; + } + + +void PIDControl::setStatus(int bit){ + + status |= (1 << bit); // Setzt das vorher ausgewählte Bit + + if((extfct) && (bit >= 5 )){ // Wenn externe Funktionen aktiviert sind und ein schwerwiegender Fehler abgelegt wird + (*CTRL_HANDLER)(); // Aufruf der Externen Funktion + } +} + + +void PIDControl::resetStatus(int bit){ + + int temp = 0xFF; // Maske erstellen + temp ^= (1 << bit); // Maske anpassen + status &= temp; // Resetzen des entsprechenden Bits + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SWModule/PIDControl.h Mon Nov 28 17:27:43 2016 +0000 @@ -0,0 +1,226 @@ +#include "mbed.h" + +#ifndef PIDControl_h +#define PIDControl_h + +/*! Diese Klasse erstellt einen PID-Regler. die einzelnen Regeranteile können bei der Initialisierung seperat einsgestellt werden. + Bei der Initialisierung des Reglers bzw. der Klasse wird der Statusvektor resettet und die einzelnen Anteile werden auf null gesetzt und gesperrt. + Solange die Variable Lock nicht wahr ist, kann über die Funktionen setKP(), setKI() und setKD() der Regler parametriert werden. + Es ist zudem möglich die Regelgrenzen des Regelers festzulegen. Bei Überschreiten der Regelgrenzen wird ein Eintrag in dem Statusvektor erzeugt. + Bei einer Änderung im Statusvektor kann eine externe Funktion aufgerufen werden, die dann den Statusvektor auswertet. + Der Regeltask ctrltask sollte in regelmäßigen Abständen aufgerufen werden. Wichtig dabei ist eine Stänige Übergabe des Ist und Soll-Werts sowie die Regelzeit. + \image html http://developer.mbed.org/media/uploads/ChrisselH/pid-controller.jpg "Schematischer Regleraufbau ohne Statusvektoren und Fehlermeldungen" + * @code + * #include "mbed.h" + * #include "PIDControl.h" + * + * int16_t ist_value; // Ist-Wert der Strecke + * int16_t soll_value; // Soll-Wert der Strecke + * int16_t stell_value; // Stell-Wert der Strecke + * + * uint8_t statusvektor; // Statusvektor des Reglers + * + * int main() { + * + * PIDControl PIregler(1,50.8,1,0.5,0,0); // PI-Regler mit KP = 50,8 und KI = 0,5 + * + * PIregler.setIlimits(1,6000,1000); // Aktivierung der maximale und minimale Größe des I-Anteils + * + * PIregler.setAWU(5); // Die Differenz zwischen I-Anteil und Limit wird mit + * // Verstärkung zurückgeführt + * + * PIregler.setUlimits(7000,500); // Limitierung der Stellgröße + * + * PIregler.lock(); // Sperrt weitere Einstellung an dem Regler + * + * while(1){ + * + * stell = PIregler.ctrltask(ist, soll, 1000); // Die Schleife wiederholt sich alle 1kHz + * + * statusvektor = PIregler.getStatus(); // Status des Reglers wird ausgelesen + * + * wait(0.001); // Regler wird alle 1kHz aufgerufen + * } + * } + * + * @endcode + */ + +class PIDControl +{ + public: + + /*! Konstruktor zum Erstellen einer Instanz mit einer vom User festgelegten Größe */ + /*! + \param P <b>P-Anteil</b> Aktivert den P-Anteil, wenn der Wert 1 ist + \param KP <b>P-Anteil</b> Verstärkung des P-Anteils + \param I <b>I-Anteil</b> Aktivert den I-Anteil, wenn der Wert 1 ist + \param KI <b>I-Anteil</b> Verstärkung des I-Anteils + \param D <b>D-Anteil</b> Aktivert den D-Anteil, wenn der Wert 1 ist + \param KD <b>D-Anteil</b> Verstärkung des D-Anteils + */ + PIDControl(bool P_usr, float KP_usr, bool I_usr, float KI_usr, bool D_usr, float KD_usr); + + /*! Destruktor entfernt den Regler */ + ~PIDControl(){}; + + /*! Reglerprozess. Rückgabewert ist die Führungsgröße */ + /*! + \param usoll <b>SOLL-Wert</b> Soll-Wert der Regelstrecke + \param yist <b>IST-Wert</b> Ist-Wert der Regelstrecke + \param time <b>Zeitkonstatnte</b> vergangene Zeit seit dem letzen Regeltask in ganzen Mirkosekunden (1ms = 1000µs) + */ + int16_t ctrltask (int16_t wsoll, int16_t yist, uint16_t time); + + + /*! Ermöglich Zugriff auf die Reglerparameter des P-Anteils. Rückgabe Wert ist eins, wenn die Reglerwerte gespeichert worden sind.*/ + /*! + \param P <b>P-Anteil</b> Aktivert den P-Anteil, wenn der Wert 1 ist + \param KP <b>P-Anteil</b> Verstärkung des P-Anteils + */ + bool setKP(bool P_usr, float KP_usr); + + /*! Ermöglich Zugriff auf die Reglerparameter des I-Anteils. Rückgabe Wert ist eins, wenn die Reglerwerte gespeichert worden sind.*/ + /*! + \param I <b>I-Anteil</b> Aktivert den I-Anteil, wenn der Wert 1 ist + \param KI <b>I-Anteil</b> Verstärkung des I-Anteils + */ + bool setKI(bool I_usr, float KI_usr); + + /*! Ermöglich Zugriff auf die Reglerparameter des D-Anteils. Rückgabe Wert ist eins, wenn die Reglerwerte gespeichert worden sind.*/ + /*! + \param D <b>D-Anteil</b> Aktivert den D-Anteil, wenn der Wert 1 ist + \param KD <b>D-Anteil</b> Verstärkung des D-Anteils + */ + bool setKD(bool D_usr, float KD_usr); + + /*! Ermöglich Zugriff auf den Anti-Windup zum begrenzen des I-Anteils*/ + /*! + \param awu_gain <b>Anti-Windup Verstärkung</b> Rückführung der Differenz zwischen Stellgröße und Begrenzung mit einer Verstärkung in den I-Anteil + */ + bool setAWU(float awu_gain_usr); + + /*! Setzt die Limits der Stellgröße U bzw. des Ausgangs des Reglers*/ + /*! + \param u_max <b>maximales Limit</b> maximale Ausgangsgröße + \param u_min <b>minimales Limit</b> minimale Ausgangsgröße + */ + void setUlimits(int16_t u_max_usr, int16_t u_min_usr); + + /*! Setzt die Limits des Integralanteils */ + /*! + \param integrallimiter <b>Aktiviert den Limiter</b> aktiviert eine seperate Begrenzung des I-Anteils + \param i_max <b>maximales Limit</b> maximaler I-Anteil + \param i_min <b>minimsler Limit</b> minimaler I-Anteil + */ + void setIlimits(bool integrallimiter_usr, int16_t i_max_usr, int16_t i_min_usr); + + /*! Sperrt den Zugriff auf die Reglerparameter */ + void lock(); + + /*! Erlaubt den Zugriff auf die Reglerparameter */ + void unlock(); + + /*! Liest den Status des Reglers aus */ + uint8_t getStatus(); + + /*! Setzt eine Routine die verwendet wird, wenn ein schwerwiegender Fehler im Statusvektor gesetzt wird.*/ + /*! + \param BUFFER_HANDLER Adresse zur weiteren Routine + */ + void setERFFCT(void (*CTRL_HANDLER)(void)); + + private: + /*! Setzt ein Bit im Statusvektor. Wenn der Rückgabewert True ist, würde der Wert übernommen*/ + /*! + \param bit Welches Bit der Zelle gesetzt werden soll + */ + void setStatus(int bit); + + /*!ResSetzt ein Bit im Statusvektor. Wenn der Rückgabewert True ist, würde der Wert übernommen*/ + /*! + \param bit Welches Bit der Zelle zurück gesetzt werden soll + */ + void resetStatus(int bit); + + //! Zeitwert der letztes Berechnung des Verschiebungswerts + int16_t time_last; + //! Dazugehöriger Verschiebungswert + int16_t time_last_result; + //! Führungsgröße und Rückgabe des Reglertasks + int16_t u; + //! Freigabe zur Verwendung externer Funktionen + bool extfct; + //! Externe Funktion bei schwerwiegenden Fehlern + void (*CTRL_HANDLER)(void); + //! Reglereinstellungen können nicht mehr verändert werden. + bool lockctrl; + //! Limits für den Integralanteil aktiv + bool integrallimiter; + //! Vorheriger Ístwert des Regelfehlers + int16_t e_last; + //! Integral über alle bisherigern Werte + int16_t e_sum; + //! Antiwindup Wert mit Verstärkung awu_gain + int16_t awu_value; + //! Regleranteil des P-Regler + int16_t P_var; + //! Regleranteil des I-Regler + int16_t I_var; + //! Regleranteil des D-Regler + int16_t D_var; + + //! Funktion zum bestimmen der zweier Potenz + /*! + \param a 16-Bit Integer bei der die Zweierpotenz bestimmt werden soll + */ + int16_t log2(int16_t a); + + + protected: + + //! Aktiviert den Proportional-Anteil + bool P; + //! Aktiviert den Integral-Anteil + bool I; + //! Aktiviert den Differnetial-Anteil + bool D; + + //! Verstärkung für den Proportional-Anteil + float KP; + //! Verstärkung für den Integral-Anteil + float KI; + //! Verstärkung für den Differnetial-Anteil + float KD; + + //! Maximale Führungsgröße + int16_t u_max; + //! Minimale Führungsgröße + int16_t u_min; + //! Maximaler Integralanteil + int16_t i_max; + //! Minimaler Integralanteil + int16_t i_min; + //! Anti-Windup Verstärkung + float awu_gain; + + + /*! 8-Bit Statusvektor: + - [7] Regelgrenze max überschritten (<b>EXTFCT</b>)<br> + - [6] Regelgrenze min unterschritten (<b>EXTFCT</b>)<br> + - [5] Fehler bei der Berechnung (<b>EXTFCT</b>)<br> + - [4] aktueller IST-Wert < Soll-Wert<br> + - [3] aktueller IST-Wert > Soll-Wert<br> + - [2] Regelabweichung ist null<br> + - [1] I-Anteil Limit erreicht<br> + - [0] Reglertask aktiv<br> + Bei schwerwiegenden Fehlern wird eine externe Routine (<b>EXTFCT</b>) aufgerufen, falls diese angegeben wurde. + */ + uint8_t status; + + }; + + + + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SWModule/RingBuffer.cpp Mon Nov 28 17:27:43 2016 +0000 @@ -0,0 +1,112 @@ +#include "mbed.h" +#include "RingBuffer.h" + +//==================================================================================================================================== +// Konstruktor und Schreib/Lese Funktionen +//==================================================================================================================================== + +RingBuffer::RingBuffer(const int size){ + + buffer_size = size; // Ringbuffergröße festlegen + buffer.data = new uint32_t [size]; // Setzt die Dimension des Daten Arrays + buffer.status = new uint8_t [size]; // Setzt die Dimension des Status Arrays + buffer.status[0] = 0x00; // Kein Status vorhanden bei Beginn + + extfct = false; // Externe Funktion bei fast vollem Buffer + bufferfull = false; // Buffer ist voll, kein Schreiben möglich + bufferempty = true; // Buffer ist leer, kein Lesen möglich + read_pointer = 0; // Lesezeiger auf Null setzen + write_pointer = 0; // Schreibezeiger auf Null setzen + +} + +bool RingBuffer::writeBuffer(uint32_t inputdata){ // Funktion zum beschreiben des Buffers + + if(!bufferfull){ // Prüfen ob der Buffer voll ist + + buffer.data[write_pointer] = inputdata; // Schreibt Daten in die aktuelle Zelle + write_pointer++; // Zählt den Schreibezeiger um eins Hoch + buffer.status[write_pointer] = buffer.status[write_pointer - 1]; // Übernehme den vorherigen Statuswert + + if(write_pointer == buffer_size){ // Zeiger am Ende des Buffers + write_pointer = 0; // Schreibezeiger auf Null setzen + } + + if(extfct && (read_pointer + buffer_size - write_pointer) < 10){ // Externe Methode bei einem Abstand im Fifo von kleiner 10 + (*BUFFER_HANDLER)(); // Aufruf der Externen Funktion + } + + if(write_pointer == read_pointer){ // Schreibzeiger steht auf Lesezeiger + bufferfull = true; // Buffer voll, kann nicht mehr Beschrieben werden + + } + else{ + bufferempty = false; // Buffer ist nicht leer, kann gelesen werden + + } + return true; // Rückgabe das der Wert beschrieben worden ist + } + return false; // Rückgabe das der Wert nicht beschrieben worden ist +} + +uint32_t RingBuffer::readBuffer(){ // Funktion zum beschreiben des Buffers + + uint32_t outputdata; // Variable zur Übergabe der Daten + + if(!bufferempty){ // Prüfen ob der Buffer leer ist + + outputdata = buffer.data[read_pointer]; // Auslesen des Wertes an der aktuellen Stelle + read_pointer++; // Zählt den Lesezeiger um eins Hoch + + if(read_pointer == buffer_size){ // Zeiger am Ende des Buffers + read_pointer = 0; // lesezeiger auf Null setzen + } + + if(read_pointer== write_pointer){ // Lesezeiger steht auf Schreibzeiger + bufferempty = true; // Buffer leer, kann nicht mehr gelesen werden + } + else{ + bufferfull = false; // Buffer ist nicht voll, kann geschrieben werden + } + + } + else{ // Buffer leer + outputdata = 0x00; // Rückgabe = 0 + } + + return outputdata; // Ausgabe des Werts +} + +//==================================================================================================================================== +// Setter und Getter Funktionen +//==================================================================================================================================== + +uint8_t RingBuffer::getBufferStatus(){ + return buffer.status[read_pointer]; // Rückgabe des aktuellen Statuswerts +} + +bool RingBuffer::setBufferStatus(int bit){ + + if(bit < 8){ + buffer.status[write_pointer] |= (1 << bit); // Setzt das vorher ausgewählte Bit + return true; // Setzen erfolgreich. + } + + return false; // Setzen nicht erfolgreich. +} + +bool RingBuffer::resetBufferStatus(int bit){ + + if(bit < 8){ + buffer.status[write_pointer] ^= (1 << bit); // Resetzt das vorher ausgewählte Bit + return true; // Restzen erfolgreich. + } + + return false; // Resetzen nicht erfolgreich. +} + + +void RingBuffer::setBF_METHODE(void (*EXTERN_BUFFER_HANDLER)(void)){ // Adresse zur externen Funktion übergeben und Freigabe setzen + extfct = true; // Externe Funktion vorhanden. Freigabe setzen. + BUFFER_HANDLER = EXTERN_BUFFER_HANDLER; // Funktionspointer der Funktion übernehmen + } \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SWModule/RingBuffer.h Mon Nov 28 17:27:43 2016 +0000 @@ -0,0 +1,117 @@ +#include "mbed.h" + +#ifndef RingBuffer_h +#define RingBuffer_h + +/*! Diese Klasse erstellt einen 32 Bit Ringbuffer mit einen vom Nutzer vorgegebenen Größenbereich. In dem Ringbuffer können +entsprechend 32 Bit Daten für Messaufzeichnungen genutzt werden. Zudem gibt es 8 Bit in einem Statusregister die frei definiert +werden können. Es gibt für jedes einzelne Bit des Statusregisters eine Setter und Getter Methode. Nachdem der Ringbuffer komplett +beschrieben wurde, wird wieder der erste Wert überschrieben. Wenn der Abstand zwischen Lese- und Schreibezeiger zu gering wird, +kann eine vom User definierte Funktion aufgerufen werden. + * @code + * #include "mbed.h" + * #include "RingBuffer.h" + * + * int Event_Routine(){ .... } // Irgendeine Funktion die bei fast vollem Fifo aufgerufen wird + * + * bool write; + * uint8_t status; + * + * int main() { + * + * Ringbuffer adcbuffer(250); // Erstellen einer Instanz mit 250 Werten + * + * write = adcbuffer.writeBuffer(16658); // Schreibt einen Wert in den Buffer. Wenn Rückgabe write + * // true ist wurde dieser erfolgreich beschrieben. + * + * adcbuffer.setBF_METHODE(Event_Routine); // Bei fast vollem Fifo wird die Routine aufgerufen + * + * adcbuffer.setBufferStatus(4); // Setzt das 4. Bit des Statusregisters + * + * adcbuffer.resetBufferStatus(6); // Löscht das 6. Bit des Statusregisters + * + * status = adcbuffer.getBufferStatus(); // Liest das Statusregister aus + * + * while(1){ + * printf("Value: %i\r\n", adcbuffer.readBuffer()); // Ausgabe der geschriebenen Werte + * } + * } + * + * @endcode + */ + + +class RingBuffer +{ + + public: + /*! Konstruktor zum Erstellen einer Instanz mit einer vom User festgelegten Größe */ + /*! + \param size <b>Größe</b> Anzahl der maximal gespeicherten Werte im Buffer + */ + RingBuffer(const int size); + + /*! Destruktor entfernt den RingBuffer */ + ~RingBuffer(){}; + + /*! Schreibt Daten in die aktuelle Zelle. Wenn die Rückgabe true ist, dann wurde der Wert geschrieben. Wenn nicht, ist der Buffer voll. */ + /*! + \param inputdata <b>Daten</b> 32 Bit Integer Daten für die aktuelle Zelle + */ + bool writeBuffer(uint32_t inputdata); + + /*! Setzt ein Bit im Statusvektor des Buffers. Wenn der Rückgabewert True ist, würde der Wert übernommen*/ + /*! + \param bit Welches Bit der Zelle gesetzt werden soll + */ + bool setBufferStatus(int bit); + + /*!ResSetzt ein Bit im Statusvektor des Buffers. Wenn der Rückgabewert True ist, würde der Wert übernommen*/ + /*! + \param bit Welches Bit der Zelle zurück gesetzt werden soll + */ + bool resetBufferStatus(int bit); + + + /*! Liest den Status aus der aktuelle Zelle und gibt diesen zurück. */ + uint8_t getBufferStatus(); + + + /*! Liest Daten aus der aktuelle Zelle. Wenn die Werte nicht ausgelesen werden konnten, dann ist der Rückgabewert 0 */ + uint32_t readBuffer(); + + /*! Setzt eine weitere Routine die verwendet werden kann, wenn der Buffer fast voll ist. Um die Daten z.B. zu übernehmen */ + /*! + \param BUFFER_HANDLER Adresse zur weiteren Routine + */ + void setBF_METHODE(void (*BUFFER_HANDLER)(void)); + + protected: + //! Größe des Buffers + int buffer_size; + //! Lesezeiger der auf die nächste zulesenede Stelle zeigt + uint32_t read_pointer; + //! Schreibzeiger der auf die nächste zubeschreibene Stelle zeigt + uint32_t write_pointer; + //! Buffer ist voll + bool bufferfull; + //! Buffer ist leer + bool bufferempty; + //! Freigabe zur Verwendung externer Funktionen + bool extfct; + //! Externe Funktion bei fast vollem Buffer aufrufen + void (*BUFFER_HANDLER)(void); + + private: + //! Eigentlicher Datenspeicher mit einem 32Bit Daten- und einem 8Bit Statusvektor + struct buffer + { uint32_t *data; + uint8_t *status; + }; + //Erstellt eine Instanz des Buffers + buffer buffer; + +}; + + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/TIM/QUADRATURE.cpp Mon Nov 28 17:27:43 2016 +0000 @@ -0,0 +1,226 @@ +#include "mbed.h" +#include "QUADRATURE.h" +#include "cmsis_nvic.h" + +//==================================================================================================================================== +// Initialisieren und mappen der Kanäle, Timer etc +//==================================================================================================================================== +QUADRATURE *QUADRATURE::instance; // Pointer zur Adresse der Instanz + + +QUADRATURE::QUADRATURE(int tim, int mode, char _CI1_PORT, int _CI1_PIN, int CI1_POL, char _CI2_PORT, int _CI2_PIN, int CI2_POL): CI1_PORT(_CI1_PORT), CI1_PIN(_CI1_PIN), CI2_PORT(_CI2_PORT), CI2_PIN(_CI2_PIN) +{ + + TIMERCARRY = 0; // bei neuem Objekt den entsprechenden Timercarry auf 0 setzen + instance = this; // Instanz zuweisung zum neu generierten Objekt + extfct = false; // Externe Funktion bei Timer Interrupt deaktiviert + + //Konfiguration des Timers + switch(tim){ // Konfiguration jedes einzelnen Timers, durch Auswahl + case 1: // TIM1 + RCC->APB2ENR |= RCC_APB2ENR_TIM1EN; // Timer 1 aktivieren + tipo = TIM1; // Pointer auf Timer1 zuweisen + temp_ITM = TIM1_UP_TIM10_IRQn; // NVIC Nummer für den Updateinterrupt Timer 1 + NVIC->ISER[0] &= ~(1 << TIM1_UP_TIM10_IRQn); // Interrupt Handler für den Timer 1 aktivieren + break; + + case 2: // TIM2 + RCC->APB1ENR |= RCC_APB1ENR_TIM2EN; // Timer 2 aktivieren + tipo = TIM2; // Pointer auf Timer2 zuweisen + temp_ITM = TIM2_IRQn; // NVIC Nummer für den Updateinterrupt Timer 2 + NVIC->ISER[0] &= ~(1 << TIM2_IRQn); // Interrupt Handler für den Timer 2 aktivieren + break; + + case 3: // TIM3 + RCC->APB1ENR |= RCC_APB1ENR_TIM3EN; // Timer 3 aktivieren + tipo = TIM3; // Pointer auf Timer3 zuweisen + temp_ITM = TIM3_IRQn; // NVIC Nummer für den Updateinterrupt Timer 3 + NVIC->ISER[0] &= ~(1 << TIM3_IRQn); // Interrupt Handler für den Timer 3 aktivieren + break; + + case 4: // TIM4 + RCC->APB1ENR |= RCC_APB1ENR_TIM4EN; // Timer 4 aktivieren + tipo = TIM4; // Pointer auf Timer4 zuweisen + temp_ITM = TIM4_IRQn; // NVIC Nummer für den Updateinterrupt Timer 4 + NVIC->ISER[0] &= ~(1 << TIM4_IRQn); // Interrupt Handler für den Timer 4 aktivieren + break; + + case 5: // TIM5 + RCC->APB1ENR |= RCC_APB1ENR_TIM5EN; // Timer 5 aktivieren + tipo = TIM5; // Pointer auf Timer5 zuweisen + temp_ITM = TIM5_IRQn; // NVIC Nummer für den Updateinterrupt Timer 5 + NVIC->ISER[1] &= ~(1 << (TIM5_IRQn-31)); // Interrupt Handler für den Timer 5 aktivieren + break; + + default: // keinen Timer ausgewählt und Fehlermeldung + printf("\n \n \r \n Falscher Timer ausgewahlt! \n \n \r"); + return; + } + + tipo->SMCR = mode; // Schreibt den aktuellen Modus in das SMCR Register (Slave Mode) + tipo->CCMR1 = 0xF1F1; // Mappen der Capture Inputs 1 und 2 auf Timer Input 1 und 2 + tipo->CCMR2 = 0x0000; // Kein mappen der Capture Inputs 3 und 4 + tipo->CCER = 0x0011; // Capture Inputs 1 und 2 aktivieren + + // Konfiguration der Pins + setGPIO(CI1_PORT, CI1_PIN, tim); // Setzt den Port Pin für CI1 + setGPIO(CI2_PORT, CI2_PIN, tim); // Setzt den Port Pin für CI2 + + // Konfiguration der Polarität + if(CI1_POL == 1){ // Polarität für CI1 ändern auf fallende Flanke + tipo->CCER |= 0x0002; + } + if(CI2_POL == 1){ // Polarität für CI2 ändern auf fallende Flanke + tipo->CCER |= 0x0020; + } + + // Konfiguration der Interrupt Routine + NVIC_SetVector(temp_ITM, (uint32_t) &_UPDATE_HANDLER); // Adresse zum IRQ Handler des entsprechenden Timers + NVIC_SetPriority(temp_ITM, 0); // Festlegen der Priorität (Höchste, damit das nächste Increment mit gezählt wird) + tipo->DIER |= 0x0001; // Update Interrupt Einstellung im Timer aktivieren + + + } + + + +void QUADRATURE::setGPIO(char port, int pin, int tim){ // Funktion zum Mappen des Port Pins auf die Alternative Funktion für den Timer Eingang + + GPIOchoose(port); // Wählt den passenden Pointer zum Port aus + + portpo->MODER |= (1 <<((2*pin)+1)); // Alternative Funktion 0x10 mit Maske (2 Bit pro Pin) + portpo->OTYPER |= (1 << pin); // Definition als Eingang 0x1 mit Open Drain + + if(pin > 7){ // Ausgänge 8 bis 15 sind im höheren Register AFR[1] + if(tim < 3){ // Für Timer 1 und Timer 2 (Alternatve Funktion 01) + portpo->AFR[1] |= (1 << (4*(pin-8))); // Alternative Funktion mit Maske für AF01 + } + else{ // Für Timer 3, Timer 4 und Timer 5 (Alternative Funktion 10) + portpo->AFR[1] |= (1 << (4*(pin-8))+1); // Alternative Funktion mit Maske für AF02 + } + } + else{ // Ausgänge 0 bis 7 sind im im unteren Register AFR[0] + if(tim < 3){ // Für Timer 1 und Timer 2 (Alternatve Funktion 01) + portpo->AFR[0] |= (1 << (4*pin)); // Alternative Funktion mit Maske für AF01 + } + else{ // Für Timer 3, Timer 4 und Timer 5 (Alternative Funktion 10) + portpo->AFR[0] |= (1 << ((4*pin)+1)); // Alternative Funktion mit Maske für AF02 + } + } + } + +//==================================================================================================================================== +// Eventhandler für Timer und Externer Eingang +//==================================================================================================================================== + +void QUADRATURE::UPDATE_HANDLER(void){ // Interrupt Service Routine für Update Events + + tipo->SR ^= 0x0001; // Flag löschen (Register xor mit Flag) + + if (getTIM() > 1){ + TIMERCARRY--; // Carryvariable -1 da Unterlauf + } + else{ + TIMERCARRY++; // Carryvariable 1 da Überlauf + } + + if(extfct == true){ // Externe Funktion vorhanden? + (*IRQ_HANDLER_EXTERN)(); // Aufruf der Externen Funktion + } + } + + +//==================================================================================================================================== +// Setter- und Getter-Funktionen +//==================================================================================================================================== + +void QUADRATURE::startTIM(){ + NVIC_EnableIRQ(temp_ITM); // Enable den IRQ Handler + tipo->CR1 = 0x0001; // Starte Timer + } + +void QUADRATURE::stopTIM(){ + tipo->CR1 = 0x0000; // Stoppe Timer + NVIC_DisableIRQ(temp_ITM); // Disable den IRQ Handler + } + +unsigned int QUADRATURE::getTIM(){ + return tipo->CNT; // Gibt aktuellen Timerwert zurück + } + +signed short QUADRATURE::getCARRY(){ + return TIMERCARRY; // Gibt aktuellen Timercarry zurück + } + +void QUADRATURE::setTIM(int pre, int arr){ + tipo->PSC = pre; // Prescaler (Wert + 1) + tipo->ARR = arr; // Auto reload + } + +void QUADRATURE::setUpRes(){ + + GPIOchoose(CI1_PORT); // Wählt den passenden Pointer zum Port von CI1 aus + portpo->PUPDR |= (1 << ((2*CI1_PIN))); // Setzt den Pull up für den entsprechenden Port Pin von CI1 + + GPIOchoose(CI2_PORT); // Wählt den passenden Pointer zum Port von CI2 aus + portpo->PUPDR |= (1 << ((2*CI2_PIN))); // Setzt den Pull up für den entsprechenden Port Pin von CI2 + } + +void QUADRATURE::setDownRes(){ + + GPIOchoose(CI1_PORT); // Wählt den passenden Pointer zum Port von CI1 aus + portpo->PUPDR |= (1 << ((2*CI1_PIN)+1)); // Setzt den Pull down für den entsprechenden Port Pin von CI1 + + GPIOchoose(CI2_PORT); // Wählt den passenden Pointer zum Port von CI2 aus + portpo->PUPDR |= (1 << ((2*CI2_PIN)+1)); // Setzt den Pull down für den entsprechenden Port Pin von CI2 + } + +void QUADRATURE::setIRQ_METHODE(void (*IRQ_HANDLER_METHODE)(void)){ // Adresse zur externen Funktion übergeben und Freigabe setzen + extfct = true; // Externe Funktion vorhanden. Freigabe setzen. + IRQ_HANDLER_EXTERN = IRQ_HANDLER_METHODE; // Funktionspointer der Funktion übernehmen + } + + +//==================================================================================================================================== +// Hilfsfunktionen +//==================================================================================================================================== + +void QUADRATURE::GPIOchoose(char port){ + + switch(port){ // Konfiguration jedes Ports durch Auswahl + case 'A': // Port A + RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN; // Port A aktivieren + portpo = GPIOA; // Pointer auf Port A zuweisen + break; + + case 'B': // Port B + RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN; // Port B aktivieren + portpo = GPIOB; // Pointer auf Port B zuweisen + break; + + case 'C': // Port C + RCC->AHB1ENR |= RCC_AHB1ENR_GPIOCEN; // Port C aktivieren + portpo = GPIOC; // Pointer auf Port C zuweisen + break; + + case 'D': // Port D + RCC->AHB1ENR |= RCC_AHB1ENR_GPIODEN; // Port D aktivieren + portpo = GPIOD; // Pointer auf Port D zuweisen + break; + + case 'E': // Port E + RCC->AHB1ENR |= RCC_AHB1ENR_GPIOEEN; // Port E aktivieren + portpo = GPIOE; // Pointer auf Port E zuweisen + break; + + default: // kein Port ausgewählt und Fehlermeldung + printf("\n \n \r \n Falscher Port ausgewahlt! \n \n \r"); + } + } + +void QUADRATURE::_UPDATE_HANDLER(void) // ISR Handler für die Instanz +{ + instance->UPDATE_HANDLER(); // Zuordnung des Handlers zum Handler der Instanz + } + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/TIM/QUADRATURE.h Mon Nov 28 17:27:43 2016 +0000 @@ -0,0 +1,206 @@ +#include "mbed.h" + +#ifndef QUADRATURE_h +#define QUADRATURE_h + +/*! Die Klasse kann aus den Timern 1 bis 5 verschiedene Encoder generieren. Es kann zwischen verschiendenen Encodermodis gewählt werden. Für den STM32 gibt es für die einzelnen Timer verschiedene Kanäle die zu wählen sind: + <CENTER> + <table> + <tr> + <th>Eingang</th> + <th>Timer 1</th> + <th>Timer 2</th> + <th>Timer 3</th> + <th>Timer 4</th> + <th>Timer 5</th> + </tr> + <tr style="text-align: center;"> + <td><strong>CI 1</strong></td> + <td>PA8</td> + <td>PA0</td> + <td>PA6</td> + <td>PB6</td> + <td>PA0</td> + </tr> + <tr style="text-align: center;"> + <td><strong>CI 1</strong></td> + <td>PE9</td> + <td>PA5</td> + <td>PB4</td> + <td>PD12</td> + <td>-</td> + </tr> + <tr style="text-align: center;"> + <td><strong>CI 1</strong></td> + <td>-</td> + <td>PA15</td> + <td>PC6</td> + <td>-</td> + <td>-</td> + </tr> + <tr style="text-align: center;"> + <td><strong>CI 2</strong></td> + <td>PA9</td> + <td>PA1</td> + <td>PB5</td> + <td>PD13</td> + <td>PA1</td> + </tr> + <tr style="text-align: center;"> + <td><strong>CI 2</strong></td> + <td>PE11</td> + <td>PB3</td> + <td>PA7</td> + <td>PB7</td> + <td>-</td> + </tr> + <tr style="text-align: center;"> + <td><strong>CI 2</strong></td> + <td>-</td> + <td>-</td> + <td>PC7</td> + <td>-</td> + <td>-</td> + </tr> +</table> +</CENTER> +<b>Achtung!</b> Es können Doppelbelegungen vorkommen! Bitte vorher prüfen, ob der Pin nicht schon verwendet wird. Zudem kann es passieren das mehrfach der gleiche Timer genutzt wird. Mit einer +Neudeklaration des Timers wird dieser Überschrieben mit den zuletzt verwendeten Parametern. Zudem gibt es einen Interrupt Eventhandler der bei Über- und Unterlauf des Timers eine Timercarry Variable hoch bzw. runter zählt. +So kann man bei Frequenzmessungen den Überlauf mit betrachten. + + * @code + * #include "mbed.h" + * #include "QUADRATURE.h" + * + * int Event_Routine(){ .... } // Irgendeine Funktion die bei Timerüberlauf aufgerufen wird + * + * int main() { + * + * QUADRATURE encoder(1, 3, 'A', 8, 0, 'A', 9, 0); // Instanz mit Timer 1, Modus 3, + * // CI1: Port A, CI1: Pin 8, rising edge + * // CI2: Port A, CI2: Pin 9, rising edge + * + * encoder.setDownRes(); // Setzt die Pull Down Widerstände + * + * encoder.startTIM(); // Startet den Timer + * + * encoder.setIRQ_METHODE(Event_Routine); // Bei Timerüberlauf wird diese Routine aufgerufen + * + * while(1){ + * printf("Value: %i\r\n", encoder.getTIM()); // Ausgabe des aktuellen Timerwerts als Integer + * printf("Carry: %i\r\n", encoder.getCARRY()); // Ausgabe des aktuellen Timerüberlaufs als signed short + * } + * } + * + * @endcode + */ + + class QUADRATURE + { + + public: + /*! Konstruktor zum Erstellen einer Instanz für einen bestimmten Timer und die Input Pins. Nach dem Erstellen der Instanz sollte der Timer nicht für andere + Zwecke verwendet werden. Nach dem Erstellen sind die Encoder voll einsatzbereit und kann mit der "startTIM"-Funktion gestartet werden. */ + /*! + \param tim <b>Entsprechender Timer</b> (Die Funktion wird bei der STM32F-Serie nur von Timer 1-5 unterstützt). + \param mode <b>Encoder Modus</b><br> + - 1: Zählt hoch/runter auf CI2-Flanke unter Bedingung der Spannung an CI1<br> + - 2: Zählt hoch/runter auf CI1-Flanke unter Bedingung der Spannung an CI2<br> + - 3: Zählt hoch/runter auf CI1-Flanke und CI2-Flanke unter Bedinung der Spannung an den jeweiligen anderen Eingang<br> + \param CI1_PORT <b>Capture Input 1 Port</b> Entsprechend den Port z.B. A,B,C,D,E des ausgewählten Eingangs aus der Tabelle entnehmen. + \param CI1_PIN <b>Capture Input 1 Pin</b> Entsprechend den Pin zu dem ausgewählten Eingang aus der Tabelle entnehmen. + \param CI1_POL <b>Capture Input 1 Polarität</b> Invertiert den Eingang CI1. Standartwert = steigende Flanke + \param CI2_PORT <b>Capture Input 2 Port</b> Entsprechend den Port z.B. A,B,C,D,E des ausgewählten Eingangs aus der Tabelle entnehmen. + \param CI2_PIN <b>Capture Input 2 Pin</b> Entsprechend den Pin zu dem ausgewählten Eingang aus der Tabelle entnehmen. + \param CI2_POL <b>Capture Input 2 Polarität</b> Invertiert den Eingang CI2. Standartwert = steigende Flanke + */ + QUADRATURE(int tim, int mode, char _CI1_PORT, int _CI1_PIN, int CI1_POL, char _CI2_PORT, int _CI2_PIN, int CI2_POL); + + + /*! Destruktor der Klasse */ + ~QUADRATURE(){}; + + /*! Startet den Timer und den IRQ Handler. */ + void startTIM(); + + /*! Stoppt den Timer und den IRQ Handler. */ + void stopTIM(); + + /*! Setzt die Pullup Widerstände beider CI Eingänge */ + void setUpRes(); + + /*! Setzt die Pulldown Widerstände beider CI Eingänge */ + void setDownRes(); + + /*! Gibt aktuellen Timerwert als Integer zurück */ + unsigned int getTIM(); + + /*! Gibt aktuellen Über bzw Unterlaufwert als signed short zurück */ + signed short getCARRY(); + + /*! Setzt für den Timer den Prescaler und das Autoload register */ + /*! + \param pre <b>Prescaler</b> Standartwert = (0x0000) + 1. Von 0x0000 bis 0xFFFF möglich + \param arr <b>Auto Reload</b> Bei diesem Wert beginnt der Timer wieder bei 0x0000. Standartwert = 0xFFFF + */ + void setTIM(int pre, int arr); + + /*! Setzt den entsprechenden GPIO auf die Alternative Funktion für den Timer Eingang. */ + /*! + \param port <b>GPIO Port</b> Port der geändert werden soll + \param pin <b>GPIO Pin</b> Pin der geändert werden soll + \param tim <b>Timer</b> Für die Auswahl zwischen den Alternativen Funktionen notwendig + */ + void setGPIO(char port, int pin, int tim); + + + /*! Interrupt Routine bei Überlauf bzw. Unterlauf des Timers. */ + void UPDATE_HANDLER(void); + + /*! Setzt eine weitere Routine die verwendet werden kann, wenn der Timer Interrupthandler aufgerufen wird. */ + /*! + \param IRQ_HANDLER_METHODE Adresse zur weiteren Routine + */ + void setIRQ_METHODE(void (*IRQ_HANDLER_METHODE)(void)); + + protected: + + //! Caputure Input 1 Portname + char CI1_PORT; + //! Caputure Input 2 Portname + char CI2_PORT; + //! Caputure Input 1 Pin + int CI1_PIN; + //! Caputure Input 2 Pin + int CI2_PIN; + //! Pointer zum zuletzt definierten GPIO + GPIO_TypeDef *portpo; + //! Pointer zum aktuell definierten Timer + TIM_TypeDef *tipo; + //! Pointer zum aktuell definierten Interrupt Handler + IRQn_Type temp_ITM; + //! Freigabe zur Verwendung externer Funktionen + bool extfct; + //! Externe Funktion bei Timer Interrupt aufrufen + void (*IRQ_HANDLER_EXTERN)(void); + + //! Timer Über und Unterlauf Zähler (Wenn Unterlauf wird decrementiert, wenn Überlauf wird incrementiert) + signed short TIMERCARRY; + //! Funktion zum Zuweisen der ISR zum Handler der erstellten Instanz + static void _UPDATE_HANDLER(void); + //! Pointer zur Adresse der erstellte Instanz + static QUADRATURE *instance; + + + /*! Wählt das passende GPIO register aus und schreibt es in den Pointer portpo */ + /*! + \param port <b>Portname</b> Welcher Port gewählt werden soll z.B. A,B,... + */ + void GPIOchoose(char port); + + + + +}; + +#endif \ No newline at end of file