Ingresso PWM in Raspberry Pi


10

C'è un modo per leggere un segnale PWM dai pin su Raspberry Pi?

Ho provato a cercarlo su Google, ma ho trovato solo output PWM e non input .

Risposte:


4

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.


1
Qualche idea su dove ottenere un simile IC?
Pepijn,

@Pepijn quello che stai cercando è un convertitore da analogico a digitale (ADC). Uno molto comune che utilizzo molto frequentemente è l' MCP3008 a 8 canali, unità a 10 bit e un altro che ho permanentemente sulla mia unità per testare Pi per il mio software Perl è l'unità ADS1115 a 4 canali a 16 bit. Quest'ultimo ha bisogno di un po 'di lavoro di saldatura, il primo no. Ci sono molti esempi di codice disponibili per usare entrambe queste unità (C, Python, Perl ecc.), Quindi se non vuoi / non puoi scrivere le tue, dovrebbe essere banale iniziare.
Stevieb,

Qualsiasi semplice microcontrollore farà. Usavo i prodotti Microchip per questo genere di cose. L'avvento di Arduino ha portato di nuovo in popolarità i chip AVR. Ora uso solo una piccola scheda breakout contenente un microcontrollore e qualunque supporto vitale abbia bisogno del chip. La mia preferita è la serie Teensy 3.x.
NomadMaker

3

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.


+1 Potrebbe valere la pena notare che l'accuratezza può variare in base al carico della CPU (in particolare i singoli core), ma per il resto una buona risposta. :)
Jacobm001

Grazie Stevec, questa risposta è molto meglio che dire "non si può fare". Vedi la mia risposta separata per ulteriori informazioni incluso il codice effettivo (è molto breve).
Georgie,

2

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/


Sarà bello vedere il codice, quindi posso ottenere una base per iniziare il mio codice, se riesci a incollare qui sarò molto grato ^^
Caio Keto

Il problema con questo suggerimento è che il kernel di Linux non fornisce un tempismo sufficiente per leggere i tipici cicli di lavoro RC PWM (che sono in genere 1000-2000 microsecondi ogni 16 millisecondi) con sufficiente precisione. Se potessimo installare un gestore di interrupt per una modifica del pin GPIO e catturare il timestamp su transizioni alte / basse, in un modulo del kernel, questa potrebbe essere una soluzione utile.
Jon Watte,

1

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:

schematico

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.


4
Il pin ADC? E quale sarebbe?
Ghanima

@Ghanima Bene, il nuovo driver Microsoft Lightning è in fase di sviluppo ma serve abbastanza bene a questo scopo (è documentato sulle loro pagine). Per quello che ho trovato per BCM2837 potrebbe avere un ADC hardware che si trova su GPIO0 (purtroppo questo non è collegato a nessun pin di intestazione) .. almeno hanno collegato i canali pwm hardware
Quest

0

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.


0

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


0

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()

0

È 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);
    }
}

-3

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


Benvenuto in Raspberry Pi! Sebbene ciò possa teoricamente rispondere alla domanda, sarebbe preferibile includere qui le parti essenziali della risposta e fornire il collegamento come riferimento. Stiamo provando una nuova politica per quanto riguarda le risposte di solo collegamento senza informazioni qui . Se questo post non viene modificato per contenere informazioni che possono rappresentare una risposta, per quanto minima, in 48 ore verrà convertito in Wiki della comunità per semplificarne la correzione da parte della community.
Ghanima
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.