PWM auf Pins 0 bis 20
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
#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);
|
||||
}
|
||||
Reference in New Issue
Block a user