Ich möchte sehr einfache Timings auf einem STM32 vornehmen. Zum Beispiel möchte ich meinen STM32 so programmieren, dass er 1 Minute lang Bytes auf dem UART ausgibt. Welche Uhr/Timer soll ich verwenden?
Wenn Sie das Referenzhandbuch durchsehen , sind viele Uhren verfügbar. Es scheint, dass einige der am besten geeigneten Uhren die Echtzeituhr (RTC), ein Allzweck-Timer oder ein Advanced-Control-Timer wären. Welche Uhr wäre am einfachsten zu verwenden, um grundlegende feste Timings zu erstellen?
Für periodische Aufgaben würde ich die Verwendung eines SysTick-Interrupts empfehlen. Hier ist eine Beispielanwendung:
void SysTick_Handler(void) {
static uint8_t tick = 0;
static uint8_t second = 0;
static uint8_t minute = 0;
switch (tick++) {
case 0:
// task 0 here
break;
case 1:
// task 1 here
break;
// and so on
case 99:
tick = 0;
second = (second < 59) ? (second + 1) : 0;
minute = (second == 0) ? ((minute < 59) ? (minute + 1) : 0) : minute;
break;
}
}
int main(void)
{
// interrupt at 100Hz
SysTick_Config(SystemCoreClock/100);
while (1);
}
SysTick_Handler()
es sich um eine Interrupt-Routine handelt, kommt es dann zu einem Stapelüberlauf, wenn Sie zu viel Code einfügen, oder ist dies eine gute Vorgehensweise?Eine Einschränkung von SysTick besteht darin, dass seine Geschwindigkeit an die Hauptuhr gebunden ist. Wenn Sie die Hauptuhr verlangsamen, um den Stromverbrauch zu reduzieren, verlangsamt sich SysTick entsprechend. Viele (alle?) STM32-Teile haben eine Art "Echtzeituhr"-Funktion, die unabhängig von der Hauptuhr laufen kann; in einigen Fällen kann es sogar unabhängig von der Hauptstromversorgung betrieben werden. Die Echtzeituhrfunktionen können so programmiert werden, dass sie die CPU zu bestimmten Zeiten aufwecken, obwohl ihre Genauigkeit ziemlich begrenzt ist (z. B. beschränken einige das Aufwachen auf Ein-Sekunden-Schritte, es sei denn, man wendet einige ziemlich eklige Tricks an). Beachten Sie auch, dass einige Chips aus irgendeinem Grund, der mich völlig verwirrt, die Zeit eher ärgerlicherweise als (0-9) Sekunden + (0-5) Zehner-Sekunden + (0-9) Minuten + (0-5) Zehner speichern Minuten + (0-9) Stunden bis 23, plus (0-2) Zehnerstunden bis 24 usw.
uint32_t present_time; uint32_t last_clock_reading; uint32_t last_raw_reading; uint32_t get_time(void) { // Sekunden und Minuten 0-9 im BCD-Format erfassen uint32_t this_clock_reading = (RTC->TR) & 0x0FFF; // Vorzeitiges Beenden, wenn keine Änderung if (this_clock_reading == last_raw_reading) return present_time; last_raw_reading = this_clock_reading; // Alle zehn Minuten sollten 600 Sekunden lang sein, nicht 4096 this_clock_reading -= (4096-600)*(this_clock_reading & 0x0F00) // Jede Minute sollte 60 Sekunden lang sein, nicht 256 this_clock_reading -= (256-60)*(this_clock_reading & 0x0F00) // Alle zehn Sekunden sollten 10 Sekunden sein, nicht 16 this_clock_reading -= (16-10)*(this_clock_reading & 0x00F0); // Aktuelle Zeit aktualisieren if (last_clock_reading > this_clock_reading) present_time += 3600 + this_clock_reading - last_clock_reading; anders present_time += this_clock_reading - last_clock_reading; last_clock_reading = this_clock_reading; return present_time; } int set_alarm(uint32_t alarm_time) // Gibt -1 zurück, wenn die Alarmzeit bereits abgelaufen ist { int32_t temp; // Wie weit in der Zukunft (wenn überhaupt) ist die Weckzeit? temp = (alarm_time - get_time()); if (temp 3000) // Alarm auf maximal 50 Minuten in der Zukunft einstellen Temperatur = 3000; // Berechnen Sie die Uhrzeit, wann wir unseren Wecker haben sollten temp += last_clock_reading; wenn (temp > 3600) Temperatur -= 3600; // In BCD umwandeln, indem Korrekturen rückgängig gemacht werden, die beim Lesen durchgeführt würden Temperatur += (16-10)*(Temperatur / 10) + (256-60)*(Temperatur / 60) + (4096-60)*(Temperatur / 600); // Speichern Sie nun den Alarmwert (zuerst die Register schreiben) RTC -> WPR = 0xCA; RTC->WPR = 0x53; RTC->ALARM = temp | 0x80800000; // Alarm einstellen (passt nur Minuten und Sekunden an) }
Bei Verwendung von Routinen wie der obigen wird die von der Anwendung verwendete Zeit in einem schönen, gleichmäßig inkrementierenden 32-Bit-Wert gehalten. Man kann bis zu 50 Minuten im Voraus einen Wecker stellen und den Chip dann einschlafen lassen; Der Chip wird aufwachen, wenn die Weckzeit eintrifft. Wenn Sie einen Alarm mehr als 50 Minuten im Voraus einstellen, wird er 50 Minuten im Voraus eingestellt. Wenn die CPU aufwacht, kann sie "zurück ins Bett" gehen, wenn die Weckzeit noch nicht gekommen ist.
Auf dem STM32L151 kann man die Uhr auf Wunsch so einstellen, dass sie mit 2x, 4x, 16x oder verschiedenen anderen Vielfachen der normalen Geschwindigkeit läuft. Der obige Code würde sich dabei nicht ändern; Die einzigen Auswirkungen wären (1) die Werte in present_time und der Parameter für set_alarm würden eine schnellere Zeiteinheit als Sekunden darstellen, und (2) die maximale Zeit vor dem nächsten Alarm würde entsprechend skaliert. Es ist etwas hässlich, mit BCD-Einheiten zu arbeiten, die zB 37,5 Sekunden, 3,75 Sekunden, 0,625 Sekunden und 0,0625 Sekunden darstellen (was die „Minuten“ und „Sekunden“ bei 16-facher Geschwindigkeit darstellen würden), aber so ist das Leben.
Wenn Sie Daten über einen bestimmten Zeitraum auf einem UART senden möchten, können Sie eine System-Timing-Methode verwenden, wie andere vorgeschlagen haben, um zu entscheiden, wann das Senden beendet werden soll. Dies wäre jedoch leicht asynchron zum Sendevorgang, der selbst Zeit verbraucht.
Ich persönlich wäre versucht, die zum Senden der Zeichen erforderliche Zeit als Quelle für das Timing zu verwenden. Vorausgesetzt, dass Sie es nie versäumen, ein neues Zeichen in das „Sendebereit“-Register zu schreiben, bevor das vorhandene Zeichen vollständig ausgetaktet ist, können Sie die serielle Übertragungsleitung ständig verwenden und (Baudrate) / (Zeichenlänge) Zeichen übertragen pro Sekunde.
In vielen Fällen sind beide Methoden ausreichend genau.
m.Alin
Oldtimer