Elektronik-Labor
Projekte
Mikrocontroller
Raspberry
/ Programm zur Messung der Netzfrequenz auf 1mHz genau und Anzeige auf 7-Segment-Anzeige
// Pico_Freq1 Beispiel Buch Seite 60 Burkhard Kainka, RPi Pico Schaltungen und Projekte
// Ergänzt um Funktion, um 50hz zu erzeugen aus https://www.i-programmer.info/programming/hardware/14849-the-pico-in-c-basic-pwm.html?start=2
// Erweitert um Multicore-Beispiel Seite 62
// Ergänzt um Ideen von Jens Mueller www.netzfrequenzanzeige.de ; www.pc-projekte.de
// Ergänzt um Funktionen für 7-Segment-Anzeige Buch Seite 99
#include "pico/stdlib.h"
#include "hardware/pwm.h"
#include "pico/multicore.h"
//#define TEST // Wenn das aktiv ist, dann ist ein interner PWM-Generator aktiv
// Variablen für Frequenzmessung
#ifdef TEST
const int pin_out = 0; // geeignet für PWM
const int pin_in = pin_out; // 0 ist auch gleichzeitig das PWM-Ausgangspin für Tests ohne externe Beschaltung
#else
//const int pin_in = 1; // Daran ist der Opto-Koppler angeschlossen
const int pin_in = 26; // Daran ist der Draht parallel zur Stromleitung (induktive Einkopplung)
#endif
word Frequenz = 50001; // Frequenz in mHz
const word Ueberfrequenz = 50010; // Überfrequenz ab 50,01 Hz (Totband 10mHz)
const word Unterfrequenz = 49990; // Unterfrequenz unter 49,99 Hz (Totband 10mHz)
uint32_t Startzeit = 0; // Zwischenspeicher für Startzeit
uint32_t Laufzeit = 0; // Zwischenspeicher für Zeitdifferenz
byte Periode = 1; // Laufende Periode
const byte Ziel_Perioden = 50 ; // Anzahl der Perioden über die gezählt werden soll
// 10 Perioden sind ein guter Wert, hier entspricht 1us 0,25mHz, Schwankungen um 1-2 us beeinflussen also die letzte Ziffer nicht
const unsigned long Multiplikator = 1000000 * Ziel_Perioden; // Für die Berechnung der Frequenz in Hz
// (Faktor 1000 für mHz muss in Rechnung ergänzt werden, weil sonst overflow)
// Weil unsigned long maximal
// 4,294,967,295 abbilden können, funktioniert die Rechnung nur bis 4 Perioden ohne Tricks
// 1.000.000 Multiplikator für Hz
// 1.000.000.000 Multiplikator für mHz
// Variablen für LED-Anzeige
const int led_Ueberfrequenz = 15; // GPIO 15 ist an Anzeige-12 (+)
const int led_FrequenzTotband = 14; // GPIO 14 ist an Anzeige-11 (o)
const int led_Unterfrequenz = 17; // GPIO 17 ist an Anzeige-14 (-)
// Nicht verwendete LED an GPIO 6,9,11,22
//const int led_Test = 25; // Buildin
// 7-Segment
const int led_Dezimalpunkt = 10; // GPIO 10 ist an Anzeige-6 (p)
int Ziffer[6]; // Ziffern für Anzeige
uint32_t Segment[14];
// 7-Segment-Ausgabe incl. der Einzel-LED
// GPIO-1.ste 221111111111
// GPIO-2.ste 1098765432109876543210
// Rückwärts decb-g+oa3.p.1f.42....
uint32_t Output = 0b1111111111010110110000; // Diese werden auf einmal auf Output gestellt
uint32_t Maske = 0b1111010011000110110000; // Diese werden über maskierten Output nach Segment auf 1 gestellt
// Watchdog ob die Messung läuft
int Watchdog = 5; // Frequenzanzeige in Core 0 reduziert, Messung in Core 1 erhöht den Wert
void setup() { // Core 0 für Kommunikation und Anzeige
// 7-Segment-Ausgabe
// 221111111111
// 1298765432109876543210
// Rückwärts decb.g..a3...1f.42....
Segment[0] = 0b1111000011000110110000;
Segment[1] = 0b0011000001000100110000;
Segment[2] = 0b1101010011000100110000;
Segment[3] = 0b1011010011000100110000;
Segment[4] = 0b0011010001000110110000;
Segment[5] = 0b1010010011000110110000;
Segment[6] = 0b1110010011000110110000;
Segment[7] = 0b0011000011000100110000;
Segment[8] = 0b1111010011000110110000; // Das ist dezimal: 4.010.416 ; uint32 kann bis 4,294,967,295
Segment[9] = 0b1011010011000110110000;
Segment[10]= 0b0110010001000100110000; // n für Anzeige "nono" wenn keine Frequenzmessung
Segment[11]= 0b1110010001000100110000; // 0 für Anzeige "nono" wenn keine Frequenzmessung
gpio_init_mask(Output);
gpio_set_dir_out_masked(Output); // Stellt alle in Output mit 1 belegten GPIO auf OUTPUT
gpio_set_drive_strength(7, GPIO_DRIVE_STRENGTH_2MA); // Alle Anoden auf 2mA
gpio_set_drive_strength(13, GPIO_DRIVE_STRENGTH_2MA); // Alle Anoden auf 2mA
gpio_set_drive_strength(16, GPIO_DRIVE_STRENGTH_2MA); // Alle Anoden auf 2mA
gpio_set_drive_strength(18, GPIO_DRIVE_STRENGTH_2MA); // Alle Anoden auf 2mA
gpio_set_drive_strength(19, GPIO_DRIVE_STRENGTH_2MA); // Alle Anoden auf 2mA
gpio_set_drive_strength(20, GPIO_DRIVE_STRENGTH_2MA); // Alle Anoden auf 2mA
gpio_set_drive_strength(21, GPIO_DRIVE_STRENGTH_2MA); // Alle Anoden auf 2mA
pinMode(led_Dezimalpunkt, OUTPUT);
gpio_set_drive_strength(led_Dezimalpunkt, GPIO_DRIVE_STRENGTH_2MA);
// LED für Unter-/Überfrequenz/Totband
pinMode(led_Ueberfrequenz, OUTPUT);
pinMode(led_FrequenzTotband, OUTPUT);
pinMode(led_Unterfrequenz, OUTPUT);
gpio_set_drive_strength(led_Ueberfrequenz, GPIO_DRIVE_STRENGTH_2MA); // erspart die Vorwiderstände
gpio_set_drive_strength(led_FrequenzTotband, GPIO_DRIVE_STRENGTH_2MA); // erspart die Vorwiderstände
gpio_set_drive_strength(led_Unterfrequenz, GPIO_DRIVE_STRENGTH_2MA); // erspart die Vorwiderstände
// Allgemein
set_sys_clock_khz(125000, true);
Serial.begin(115200);
}
void loop() {
ZiffernAufteilen();
/*// Nur Test ob die Aufteilung auf Ziffern funktioniert hat
Serial.print(Ziffer[1]); Serial.print(Ziffer[2]); Serial.print(Ziffer[3]); Serial.print(Ziffer[4]); Serial.print(Ziffer[5]);*/
gpio_put(led_Ueberfrequenz, LOW);
gpio_put(led_Unterfrequenz, LOW);
gpio_put(led_FrequenzTotband, LOW);
if (Watchdog >=0) {
Serial.print(Laufzeit);
Serial.print(" Laufzeit ");
Serial.print(Frequenz);
Serial.print(" mHz ");
if (Frequenz >= Ueberfrequenz) {
gpio_put(led_Ueberfrequenz, HIGH);
Serial.println(" + ");
}
else if (Frequenz <= Unterfrequenz) {
gpio_put(led_Unterfrequenz, HIGH);
Serial.println(" - ");
}
else {
gpio_put(led_FrequenzTotband, HIGH);
Serial.println(" O ");
}
Watchdog--;
}
else {
Ziffer[1] = 10; // "n"
Ziffer[2] = 11; // "o"
Ziffer[3] = 10; // "n"
Ziffer[4] = 11; // "o"
}
Anzeige7Segment(); // Summe der Anzeigeschleife ist 1000ms = 1s passt zu 50 Perioden bei 50Hz = 1s
}
void setup1() { // Core 1 für zeitkritische Messung
#ifdef TEST
// Hier wird PIN 0 als Ausgang des PWM-Signals definiert, um für Tests keine externe Quelle zu benötigen
gpio_set_function(pin_out, GPIO_FUNC_PWM);
uint slice_num = pwm_gpio_to_slice_num(pin_out);
uint chan = pwm_gpio_to_channel(pin_out);
//pwm_set_freq_duty(slice_num,chan, 50, 75); // 50 ist 50Hz, 75 ist 75% duty cycle
// unklar, warum das 53 Hz gibt, wenn man die nicht die Systemfrequenz genau auf 125000 einstellt
pwm_set_freq_duty(slice_num,chan, 51, 75); // 50 ist 50Hz, 75 ist 75% duty cycle
pwm_set_enabled(slice_num, true);
#else
//pinMode(pin_in, INPUT_PULLUP); // Hier wird PIN 5 als Eingang definiert mit Pull-Up, damit der Opto-Transistor versorgt wird
pinMode(pin_in, INPUT); // Hier wird PIN 5 als Eingang definiert ohne Pull-Up, um induktive Einkopplung zu testen
#endif
while (true) {
bool in_old = true;
bool in_new = true;
while (gpio_get(pin_in));
while (!gpio_get(pin_in));
// Hier ist pin_in gerade auf true gegangen, die erste Periode beginnt
Startzeit = time_us_32();
while (Periode <= Ziel_Perioden) {
in_new = (gpio_get(pin_in));
if (in_new && !in_old) Periode++;
in_old = in_new;
}
Laufzeit = time_us_32() - Startzeit;
// Frequenz = 1000 * Multiplikator / Laufzeit; // Diese Variante geht nur bis 4 Perioden, weil sonst overflow
Frequenz = Multiplikator * ( 1000.0 / float(Laufzeit)); // Mit diesem Umweg können auch mehr als 4 Perioden gemessen werden
Periode = 1; // Neustart
Serial.print(" NEU ");
Watchdog = 5;
}
}
void loop1() {
}
void ZiffernAufteilen(){
word Rest = Frequenz;
Ziffer[5] = Rest%10; // ....0 mHz
Rest = Rest/10;
Ziffer[4] = Rest%10; // ...0. mHz
Rest = Rest/10;
Ziffer[3] = Rest%10; // ..0.. mHz
Rest = Rest/10;
Ziffer[2] = Rest%10; // .0... mHz
Rest = Rest/10;
Ziffer[1] = Rest%10; // 5.... mHz
Rest = Rest/10;
}
void Anzeige7Segment() {
// Summe der Anzeigeschleife ist 1000ms = 1s passt zu 50 Perioden bei 50Hz = 1s
for (int i=0; i<250; i++) {
gpio_put(led_Dezimalpunkt, 1);
gpio_put_masked(Maske, Segment[Ziffer[1]]);
gpio_put(8,0);
sleep_ms(1);
gpio_put_masked(Maske, Segment[Ziffer[2]]);
gpio_put(4,0);
sleep_ms(1);
gpio_put_masked(Maske, Segment[Ziffer[3]]);
gpio_put(12,0);
sleep_ms(1);
gpio_put_masked(Maske, Segment[Ziffer[4]]);
gpio_put(5,0);
sleep_ms(1);
}
}
#ifdef TEST
// Ergänzt um Funktion, um 50hz zu erzeugen aus https://www.i-programmer.info/programming/hardware/14849-the-pico-in-c-basic-pwm.html?start=2
uint32_t pwm_set_freq_duty(uint slice_num,
uint chan,uint32_t f, int d)
{
uint32_t clock = 125000000;
uint32_t divider16 = clock / f / 4096 + (clock % (f * 4096) != 0);
if (divider16 / 16 == 0)
divider16 = 16;
uint32_t wrap = clock * 16 / divider16 / f - 1;
pwm_set_clkdiv_int_frac(slice_num, divider16/16, divider16 & 0xF);
pwm_set_wrap(slice_num, wrap);
pwm_set_chan_level(slice_num, chan, wrap * d / 100);
return wrap;
}
#endif