Risposte:
La risposta breve: NON PUOI leggere in modo affidabile PWM su Raspberry Pi.
La lettura di PWM richiede una precisione di microsecondi (a meno che non si stia leggendo una PWM molto lenta) e che non è disponibile su Raspberry Pi per il software userland senza armeggiare con i moduli del kernel.
Il modo più semplice per acquisire PWM sarebbe ottenere un microcontrollore economico (<$ 0,5) con uscita seriale o I 2 C e collegarlo al tuo Raspberry Pi e leggere i valori effettivi dal microcontrollore. Funzionerà in modo molto affidabile ed è abbastanza preciso.
Posso eseguire misurazioni della larghezza degli impulsi abbastanza precise usando la libreria piGpio C: http://abyz.me.uk/rpi/pigpio/index.html
Questa libreria consente di installare una funzione di callback che si innescherà su qualsiasi transizione di fronte su un pin gpio e ti darà un timestamp di livello di microsecondi per ogni transizione. Non pensare di poter contare su questo per l'accuratezza dei microsecondi, ma i miei test suggeriscono che l'accuratezza è almeno +/- 10us, forse migliore.
Molto meglio che eseguire un loop affollato eseguendo il polling di una gpio per il cambio di livello da soli.
Questa è una domanda interessante e la tua è corretta nel dire che Ricerca Google non fornisce una soluzione ovvia! (Mi mancano i giorni in cui Google poteva rispondere a tutto ciò che volevo sapere per la mia istruzione / compiti in pochi secondi.)
Presumo che tu capisca i principi di PWM . Pertanto, non entrerò in questo. Tuttavia, credo che in teoria potresti leggere un valore PWM su un normale pin di ingresso digitale con una codifica intelligente.
Devo ammettere che non ho provato questo da solo, ma dovresti essere in grado di misurare il tempo in cui il pin è alto e il tempo per cui è basso (dandoti la tua lettura PWM) e quindi utilizzare qualsiasi formula matematica fornita dal fornitore del sensore per convertirlo nella lettura effettiva.
Questo metodo funziona per me su un problema simile in cui avevo bisogno di leggere la lunghezza dell'impulso da un modulo ultrasonico e quindi convertirlo in distanza. I problemi che posso prevedere riguardano la garanzia di letture affidabili!
Se pensi che possa aiutare e vuoi vedere il codice che ho usato per il modulo ultrasonico, dillo e lo copierò quando torno a casa.
Ho iniziato a copiare il codice ma per qualche motivo il sito web mi permette di copiarlo solo una piccola sezione alla volta (e sono troppo pigro per portare il mio pi fuori dal garage), quindi ecco il link ad esso. ignora la maggior parte delle funzioni nella parte inferiore poiché sono correlate all'uso del modulo come sensore di prossimità. http://pibot.webnode.com/products/ultrasonic-range-sensor/
La lunga risposta: in realtà puoi! (bene con un piccolo aiuto da parte dei nostri amici resistore e condensatore)
Puoi convertire un'uscita PWM in un livello di tensione analogico, (DAC) e leggerlo con il pin ADC sul tuo raspberry pi.
Ciò di cui hai bisogno è un resistore 4k7 e un condensatore 0,1uF:
simula questo circuito - Schema creato usando CircuitLab
Il semplice filtro RC passa basso sopra converte il segnale PWM in una tensione proporzionale al ciclo di lavoro che può essere letto dal tuo lampone pi come valore analogico.
Se sei soddisfatto di una risposta lenta, puoi leggere un PWM veloce eseguendo il sottocampionamento. Basta leggere il GPIO in un ciclo e applicare un filtro passa basso. La probabilità di leggere un 1 per ogni ciclo è proporzionale alla larghezza dell'impulso. Un filtro passa basso IIR facile da implementare è:
double acc=0.5;
const double k=0.01;
for(;;) {
bool x=GPIO.read();
acc+=k*(x?1:0-acc);
}
Man mano che k diminuisce, la risoluzione migliora ma diminuisce la larghezza di banda.
Sebbene la mia risposta non provenga dai pin, potresti usare qualcosa basato su un oscilloscopio per schede audio per leggere un ingresso pulsato.
Le persone usano schede audio nei PC desktop da anni per creare oscilloscopi. Sembra che con una moderna scheda audio interna sia possibile ottenere risultati utilizzabili fino a 10kHz. Con una scheda audio collegata Raspberry Pi USB, la frequenza massima potrebbe essere inferiore.
Ecco un esempio di un progetto di oscilloscopio per schede audio per Linux: http://www.yann.com/en/diy-turn-your-gnulinux-computer-into-a-free-oscilloscope-29/09/2010.html
Questo script Python che ho scritto funziona bene per me per la lettura dei segnali PWM di un ricevitore RC. I segnali PWM ad alta frequenza ovviamente non funzioneranno come è già stato sottolineato.
Ho collegato direttamente i dieci pin di uscita del segnale del ricevitore RC ai pin Raspberry GPIO. Il ricevitore è alimentato dai pin + 5V e GND dell'RPI.
Ho semplificato la sceneggiatura in quanto fa molte altre cose, se trovi errori o avanzi, fammelo sapere
import RPi.GPIO as GPIO
import time
import numpy as np
inPINS = [2,3,4,14,15,18,17,27,22,23]
smoothingWindowLength=4
def getTimex():
return time.time()
GPIO.setup(inPINS, GPIO.IN)
upTimes = [[0] for i in range(len(inPINS))]
downTimes = [[0] for i in range(len(inPINS))]
deltaTimes = [[0] for i in range(len(inPINS))]
def my_callback1(channel):
i = inPINS.index(channel)
v = GPIO.input(inPINS[i])
#GPIO.output(outPINS[0], v) # mirror input state to output state directly (forward servo value only) - don't set PWM then for this pin
if (v==0):
downTimes[i].append(getTimex())
if len(downTimes[i])>smoothingWindowLength: del downTimes[i][0]
else:
upTimes[i].append(getTimex())
if len(upTimes[i])>smoothingWindowLength: del upTimes[i][0]
deltaTimes[i].append( (downTimes[i][-1]-upTimes[i][-2])/(upTimes[i][-1]-downTimes[i][-1]) )
if len(deltaTimes[i])>smoothingWindowLength: del deltaTimes[i][0]
GPIO.add_event_detect(inPINS[0], GPIO.BOTH, callback=my_callback1)
GPIO.add_event_detect(inPINS[1], GPIO.BOTH, callback=my_callback1)
try:
while True:
ovl = deltaTimes[0][-smoothingWindowLength:] # output first pin PWM
ov = sorted(ovl)[len(ovl) // 2] #ov = np.mean(ovl)
print ov
time.sleep(0.1)
except KeyboardInterrupt:
GPIO.cleanup()
È molto possibile e relativamente semplice leggere gli input PWM su Raspberry Pi utilizzando la libreria pigpio C. Se vuoi una buona prestazione ti consiglio di usare C, non Python. Di seguito ho fornito un breve codice di esempio. Contrariamente a quanto alcuni affermano, questo ha eccellenti prestazioni di timing e un jitter piuttosto basso. Le letture sono costantemente entro 5 noi sul mio RPi 3 B e può misurare impulsi fino a 5 noi. Si noti che il codice fornito è solo una prova del concetto, non gestisce correttamente l'assenza di impulsi (0% / 100% duty cycle) o il wrapping "tick" che si verifica ogni 72 minuti. Il programma funziona alla perfezione in modalità utente, ma per la migliore resistenza ai problemi di temporizzazione esegui il tuo programma a un buon livello negativo come questo: sudo nice -n -20 ./program
Vedi i documenti di pigpio su: http://abyz.me.uk/rpi/pigpio/pdif2.html
#include <stdio.h>
#include <stdbool.h>
#include <unistd.h>
#include "pigpiod_if2.h"
static uint32_t rise_tick = 0; // Pulse rise time tick value
static uint32_t pulse_width = 0; // Last measured pulse width (us)
// Callback function for measuring PWM input
void pwm_cbfunc(int pi, unsigned user_gpio, unsigned level, uint32_t tick) {
if (level == 1) { // rising edge
rise_tick = tick;
}
else if (level == 0) { // falling edge
pulse_width = tick - rise_tick; // TODO: Handle 72 min wrap-around
}
}
int main(int argc, char **argv)
{
const unsigned int pwm_in = 23; // GPIO Pin # for PWM in, change as reqd
int pi = pigpio_start(0, 0);
if (pi < 0) {
fprintf(stderr, "pigpio initialization failed (%d)\n", pi);
return pi;
}
// Set up callback for PWM input
callback(pi, pwm_in, EITHER_EDGE, pwm_cbfunc);
while (true) {
printf("PWM pulse width: %u\n", pulse_width);
usleep(500000);
}
}
Soluzione semplice con elevata precisione:
L'uso di un Arduino come dispositivo iic slave o UART sembra funzionare perfettamente. Il microcontoller è in grado di leggere le informazioni tramite il metodo pulseIn.
Per informazioni dettagliate: https://www.youtube.com/watch?v=ncBDvcbY1l4