81 lines
2.8 KiB
C
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);
|
|
}
|