Files
pico/pico1w/ampel/pio_pwm.c
T
2026-07-04 02:44:36 +02:00

81 lines
2.8 KiB
C

#include "pio_pwm.h"
#include "pwm.h"
#include "pins.h"
#include "hardware/pio.h"
#include "hardware/clocks.h"
#include "pico/error.h"
#include "pio_pwm.pio.h"
// 5 State Machines koexistieren mit dem CYW43-Chip, der einen SM auf pio0 haelt.
// Wir claimen dynamisch: erst pio1 leerraeumen (4 SMs), dann eine SM aus pio0.
// Das Programm wird pro benutztem PIO-Block einmal geladen.
static struct {
PIO pio;
uint sm;
uint pin;
} g_channels[PWM_PIO_COUNT];
static int try_claim(PIO pio) {
// pio_claim_unused_sm(pio, false) liefert -1 statt panic bei Misserfolg.
return pio_claim_unused_sm(pio, false);
}
void pio_pwm_init(void) {
int offset[2] = {-1, -1}; // Programm-Offset in pio0 / pio1, -1 = noch nicht geladen.
for (unsigned i = 0; i < PWM_PIO_COUNT; i++) {
uint pin = PWM_PIO_FIRST_PIN + i;
// Erst pio1 abgrasen (kein CYW43 dort), dann pio0.
PIO pio = pio1;
int sm = try_claim(pio1);
if (sm < 0) {
pio = pio0;
sm = try_claim(pio0);
if (sm < 0) {
panic("Keine freie PIO-SM fuer PWM-Kanal %u", i);
}
}
unsigned idx = (pio == pio0) ? 0 : 1;
if (offset[idx] < 0) {
if (!pio_can_add_program(pio, &pio_pwm_program)) {
panic("PIO-Programm passt nicht mehr auf PIO%u", idx);
}
offset[idx] = (int)pio_add_program(pio, &pio_pwm_program);
}
g_channels[i].pio = pio;
g_channels[i].sm = (uint)sm;
g_channels[i].pin = pin;
// Cycle-Bilanz pro PWM-Periode:
// 3 Zyklen fuer pull/mov/mov (einmalig)
// + 3 Zyklen * (PWM_MAX_LEVEL + 1) fuer die Zaehlschleife
// Bei PWM_MAX_LEVEL=999 sind das 3003 PIO-Zyklen pro 1-kHz-Periode.
float cycles_per_period = 3.0f * (PWM_MAX_LEVEL + 1) + 3.0f;
float div = (float)clock_get_hz(clk_sys) / (cycles_per_period * PWM_FREQ_HZ);
pio_pwm_program_init(pio, (uint)sm, (uint)offset[idx], pin, div);
// Periode ins ISR laden: FIFO->OSR->ISR via SM-EXEC, ohne dass die
// Hauptschleife laeuft. Danach wird der eigentliche Level (Startwert 0)
// ins FIFO gelegt, den die Schleife via `pull noblock` in X uebernimmt.
pio_sm_set_enabled(pio, (uint)sm, false);
pio_sm_clear_fifos(pio, (uint)sm);
pio_sm_put_blocking(pio, (uint)sm, PWM_MAX_LEVEL);
pio_sm_exec(pio, (uint)sm, pio_encode_pull(false, false));
pio_sm_exec(pio, (uint)sm, pio_encode_out(pio_isr, 32));
pio_sm_put_blocking(pio, (uint)sm, 0);
pio_sm_set_enabled(pio, (uint)sm, true);
}
}
void pio_pwm_set(unsigned index, uint16_t level) {
if (index >= PWM_PIO_COUNT) return;
if (level > PWM_MAX_LEVEL) level = PWM_MAX_LEVEL;
pio_sm_put_blocking(g_channels[index].pio, g_channels[index].sm, level);
}