Finalmente ho ottenuto una comprensione completa (ish) dal file di intestazione del driver bcm2835.h, quindi ho pensato di pubblicare e rispondere alla mia domanda per gli altri.
I bit rilevanti dell'intestazione:
PWM
BCM2835 supporta PWM hardware su un sottoinsieme limitato di pin GPIO. Questa libreria bcm2835 fornisce funzioni per la configurazione e il controllo dell'uscita PWM su questi pin.
Il BCM2835 contiene 2 canali PWM indipendenti (0 e 1), ognuno dei quali è collegato a un sottoinsieme limitato di pin GPIO. I seguenti pin GPIO possono essere collegati ai seguenti canali PWM:
GPIO PIN RPi pin PWM Channel ALT FUN
12 0 0
13 1 0
18 1-12 0 5
19 1 5
40 0 0
41 1 0
45 1 0
52 0 1
53 1 1
Affinché un pin GPIO emetta output dal suo canale PWM, deve essere impostato sulla funzione Alt indicata sopra. Nota attentamente che le versioni attuali di Raspberry Pi espongono solo uno di questi pin (GPIO 18 = RPi Pin 1-12) sulle intestazioni IO, e quindi questo è l'unico pin IO sull'RPi che può essere usato per PWM. Inoltre, deve essere impostato su ALT FUN 5 per ottenere l'uscita PWM.
Entrambi i canali PWM sono gestiti dallo stesso clock PWM, il cui clock dvider può essere variato usando bcm2835_pwm_set_clock()
. Ogni canale può essere abilitato separatamente con bcm2835_pwm_set_mode()
. L'uscita media del canale PWM è determinata dal rapporto DATA / RANGE per quel canale. Utilizzare bcm2835_pwm_set_range()
per impostare l'intervallo e
bcm2835_pwm_set_data()
per impostare i dati in quel rapporto
Ogni canale PWM può funzionare in modalità Bilanciata o Mark-Space. In modalità Bilanciata, l'hardware invia una combinazione di impulsi di clock che si traduce in un totale di impulsi DATA per impulsi RANGE. In modalità Mark-Space, l'hardware imposta l'uscita HIGH per gli impulsi di clock DATA, seguito da LOW per gli impulsi di clock RANGE-DATA.
L'orologio PWM può essere impostato per controllare la larghezza degli impulsi PWM. Il clock PWM è derivato da un clock a 19.2MHz. È possibile impostare qualsiasi divisore, ma alcuni comuni sono forniti dabcm2835PWMClockDivider
Ad esempio, supponiamo che tu voglia guidare un motore DC con PWM a circa 1kHz e controllare la velocità con incrementi di 1/1024 da 0/1024 (fermo) a 1024/1024 (pieno acceso). In tal caso, è possibile impostare il divisore di clock su 16 e RANGE su 1024. La frequenza di ripetizione dell'impulso sarà 1,2 MHz / 1024 = 1171,875 Hz.
bcm2835PWMClockDivider
Specifica il divisore utilizzato per generare l'orologio PWM dall'orologio di sistema. Le figure seguenti mostrano il divisore, il periodo e la frequenza di clock. Il clock diviso si basa su una frequenza di clock di base PWM nominale di 19,2 MHz. Le frequenze mostrate per ciascun divisore sono state confermate dalla misurazione
typedef enum
{
BCM2835_PWM_CLOCK_DIVIDER_2048 = 2048, /*!< 2048 = 9.375kHz */
BCM2835_PWM_CLOCK_DIVIDER_1024 = 1024, /*!< 1024 = 18.75kHz */
BCM2835_PWM_CLOCK_DIVIDER_512 = 512, /*!< 512 = 37.5kHz */
BCM2835_PWM_CLOCK_DIVIDER_256 = 256, /*!< 256 = 75kHz */
BCM2835_PWM_CLOCK_DIVIDER_128 = 128, /*!< 128 = 150kHz */
BCM2835_PWM_CLOCK_DIVIDER_64 = 64, /*!< 64 = 300kHz */
BCM2835_PWM_CLOCK_DIVIDER_32 = 32, /*!< 32 = 600.0kHz */
BCM2835_PWM_CLOCK_DIVIDER_16 = 16, /*!< 16 = 1.2MHz */
BCM2835_PWM_CLOCK_DIVIDER_8 = 8, /*!< 8 = 2.4MHz */
BCM2835_PWM_CLOCK_DIVIDER_4 = 4, /*!< 4 = 4.8MHz */
BCM2835_PWM_CLOCK_DIVIDER_2 = 2, /*!< 2 = 9.6MHz, fastest you can get */
BCM2835_PWM_CLOCK_DIVIDER_1 = 1 /*!< 1 = 4.6875kHz, same as divider 4096 */
} bcm2835PWMClockDivider;
In sintesi:
Se vuoi PWM hardware - sei bloccato con il pin 12 (BCM18), altri pin GPIO useranno il software PWM.
Probabilmente dovrai impostare la modalità PWM sulla modalità "Mark-Space" per la maggior parte dei casi d'uso e ragioni di sanità mentale come descritto sopra.
In questa modalità, la durata in cui ciascun 'impulso' è ALTO rispetto a BASSO è determinata dal rapporto tra i dati PWM e l'intervallo PWM, indipendentemente dalla velocità di clock PWM.
La gamma PWM è effettivamente la "risoluzione" o il numero di possibili "divisioni" di ciascun impulso. Più divisioni maggiore è la risoluzione e quindi più stati codificabili per una data larghezza di impulso.
Il 'duty cycle' è il rapporto tra i dati PWM e l'intervallo PWM espresso in percentuale. Una gamma PWM di 10 con dati PWM di 8 è un duty cycle dell'80%.
La velocità di clock PWM è una potenza di due divisori. Quindi la velocità di clock selezionata per PWM deve essere divisor & (divisor -1) == 0
Sebbene i 12 valori validi siano elencati sopra.
Dividendo la frequenza di clock PWM per la frequenza di uscita desiderata si ottiene il valore della gamma di impulsi.
Mentre stavo codificando l'audio e usando un ecoscandaglio piezoelettrico, avevo bisogno di un ciclo di lavoro del 50% per massimizzare l'oscillazione piezoelettrica e quindi il volume. Il valore dei dati PWM è quindi sempre la metà del valore dell'intervallo PWM - 50% ALTO 50% BASSO.
Per calcolare la frequenza richiesta, scegli un divisore di clock che abbia senso per la tua applicazione: ho scelto 16, che equivale a 1,2 Mhz. Così:
La nota di A è 440Hz, F # è 370Hz, C # è 277Hz
PWMClock = 16; // 1.2Mhz
const A4_RANGE = 1.2e6 / 440; // 1.2Mhz/440Hz
A4Data = A4_RANGE / 2;
const F4S_RANGE = 1.2e6 / 370; // 1.2Mhz/370Hz
F4SData = F4S_RANGE / 2;
const C4S_RANGE = 1.2e6 / 277; // 1.2Mhz/277Hz
C4SData = C4S_RANGE / 2;
Puoi facilmente spostare l'intervallo PWM su e giù per le ottave in multipli - l'intervallo * 2 lo porterà giù di un'ottava, l'intervallo * 0,5 lo porterà su di uno.
Se si desidera guidare un servo a 50Hz, lo stesso calcolo della portata è valido:
PWM Range = PWM frequency / Desired Output Frequency
(Il valore massimo dell'intervallo PWM secondo alcuni post aneddoticamente è 4096 - nella mia esperienza questo non è vero nel suonare un C # come sopra dà un intervallo PWM di 4332 che funziona come previsto.)
Come la maggior parte delle cose - è facile quando sai come.
~ N