Entrada PWM en Raspberry Pi

10

¿Hay alguna manera de leer una señal PWM de los pines en Raspberry Pi?

Intenté buscarlo en Google, pero solo encontré salida PWM , y no entrada .

Caio Keto
fuente

Respuestas:

4

La respuesta corta: NO PUEDE leer PWM de manera confiable en Raspberry Pi.

Leer PWM requiere una precisión de microsegundos (a menos que esté leyendo un PWM muy, muy lento), y eso no está disponible en Raspberry Pi para el software del usuario sin modificar los módulos del núcleo.

La forma más fácil de capturar PWM sería obtener un microcontrolador barato (<$ 0.5) con salida serial o I 2 C y conectarlo a su Raspberry Pi y leer los valores reales del microcontrolador. Esto funcionará de manera muy confiable y es bastante preciso.

lenik
fuente
1
¿Alguna idea de dónde obtener un IC?
Pepijn
@Pepijn lo que estás buscando es un convertidor analógico a digital (ADC). Una muy común que uso con mucha frecuencia es la unidad MCP3008 de 8 bits y 10 canales, y otra que tengo permanentemente en mi unidad que prueba Pi para mi software Perl es la unidad ADS1115 de 4 canales y 16 bits. El último necesita un poco de trabajo de soldadura, el primero no. Hay muchos ejemplos de código disponibles para usar estas dos unidades (C, Python, Perl, etc.), por lo que si no quiere / no puede escribir el suyo, debería ser trivial comenzar.
stevieb
Cualquier microcontrolador simple servirá. Solía ​​usar productos Microchip para este tipo de cosas. La llegada del Arduino ha vuelto a poner en popularidad los chips AVR. Ahora solo uso una pequeña placa de conexión que contiene un microcontrolador y cualquier soporte vital que necesite el chip. Mi favorita es la serie Teensy 3.x.
NomadMaker
3

Puedo hacer una medición de ancho de pulso bastante precisa utilizando la biblioteca piGpio C: http://abyz.me.uk/rpi/pigpio/index.html

Esta biblioteca le permite instalar una función de devolución de llamada que se activará en cualquier transición de borde en un pin gpio y le proporciona una marca de tiempo de nivel de microsegundos para cada transición. No creo que pueda contar con esto para una precisión de microsegundos, pero mis pruebas sugieren que la precisión es de al menos +/- 10us, tal vez mejor.

Mucho mejor que ejecutar un bucle ocupado sondeando un gpio para que el nivel cambie usted mismo.

stevec
fuente
+1 Vale la pena señalar que la precisión puede variar en función de la carga de la CPU (especialmente los núcleos individuales), pero por lo demás es una buena respuesta. :)
Jacobm001
Gracias stevec, esta respuesta es mucho mejor que decir "no se puede hacer". Vea mi respuesta por separado para obtener más información, incluido el código real (es muy corto).
Georgie
2

¡Esta es una pregunta interesante y correcta al decir que la Búsqueda de Google no proporciona una solución obvia! (Extraño los días en que Google podía responder cualquier cosa que quisiera saber para mis estudios / tareas en cuestión de segundos).

Supongo que entiendes los principios de PWM . Por lo tanto, no voy a entrar en eso. Sin embargo, creo que en teoría podría leer un valor PWM en un pin de entrada digital normal con un poco de codificación inteligente.

Admito que no lo he intentado yo mismo, pero deberías poder medir el tiempo que el pin es alto y el tiempo que es bajo (lo que te da la lectura PWM) y luego usar cualquier fórmula matemática que proporcione el proveedor del sensor para convertir esto a la lectura real.

Este método funciona para mí en un problema similar en el que necesitaba leer la longitud del pulso desde un módulo ultrasónico y luego convertirlo a distancia. ¡Los problemas que puedo imaginar implican garantizar lecturas confiables!

Si cree que ayudará y desea ver el código que utilicé para el módulo ultrasónico, dígalo y lo copiaré cuando llegue a casa.

Comencé a copiar el código, pero por alguna razón el sitio web solo me permite copiar una pequeña sección a la vez (y soy demasiado vago para sacar mi pi del garaje), así que aquí está el enlace. ignore la mayoría de las funciones en la parte inferior ya que están relacionadas con el uso del módulo como sensor de proximidad. http://pibot.webnode.com/products/ultrasonic-range-sensor/

D Mason
fuente
Será bueno ver el código, así que puedo obtener una base para comenzar mi propio código, si puedes pegar aquí te lo agradeceré ^^
Caio Keto
El problema con esta sugerencia es que el kernel de Linux no le da el tiempo suficiente para leer los ciclos de trabajo típicos de RC PWM (que suelen ser de 1000-2000 microsegundos cada 16 milisegundos) con suficiente precisión. Si pudiéramos instalar un controlador de interrupciones para un cambio de pin GPIO y capturar la marca de tiempo en transiciones altas / bajas, en un módulo de kernel, entonces esta podría ser una solución útil.
Jon Watte
1

La respuesta larga: ¡en realidad puedes! (Bueno, con un poco de ayuda de nuestros amigos resistencia y condensador)

Puede convertir una salida PWM a un nivel de voltaje analógico (DAC) y leerlo con el pin ADC en su raspberry pi.

Lo que necesita es una resistencia 4k7 y un condensador de 0.1uF:

esquemático

simular este circuito : esquema creado con CircuitLab

El simple filtro de paso bajo RC anterior convierte la señal PWM en un voltaje proporcional al ciclo de trabajo que puede leer su raspberry pi como un valor analógico.

Búsqueda
fuente
44
El pin ADC? ¿Y cuál sería?
Ghanima
@Ghanima Bueno, el nuevo controlador Microsoft Lightning está en desarrollo, pero cumple bastante bien este propósito (está documentado en sus páginas). Por lo que encontré para BCM2837, podría tener un ADC de hardware que está en GPIO0 (lamentablemente, este no está vinculado a ningún pin de encabezado) ... al menos vincularon canales pwm de hardware
Quest
0

Si está satisfecho con una respuesta lenta, puede leer un PWM rápido submuestreando. Simplemente lea el GPIO en un bucle y aplique un filtro de paso bajo. La probabilidad de leer un 1 en cada ciclo es proporcional al ancho del pulso. Un filtro de paso bajo IIR fácil de implementar es:

double acc=0.5;
const double k=0.01;
for(;;) {
  bool x=GPIO.read();
  acc+=k*(x?1:0-acc);
}

A medida que k disminuye, la resolución mejora pero el ancho de banda disminuye.

wdg
fuente
0

Aunque mi respuesta no es de los pines, podría usar algo basado en un osciloscopio de tarjeta de sonido para leer una entrada pulsada.

La gente ha estado usando tarjetas de sonido en PC de escritorio durante años para crear osciloscopios. Parece que con una tarjeta de sonido interna moderna puede obtener resultados utilizables de hasta 10 kHz. Con una tarjeta de sonido con conexión USB Raspberry Pi, su frecuencia máxima podría ser menor.

Aquí hay un ejemplo de un proyecto de osciloscopio de tarjeta de sonido para Linux: http://www.yann.com/en/diy-turn-your-gnulinux-computer-into-a-free-oscilloscope-29/09/2010.html

pierce.jason
fuente
0

Este script de Python que escribí funciona bien para mí para leer señales PWM de un receptor RC. Las señales PWM de alta frecuencia obviamente no funcionarán como ya se ha señalado.

Conecté directamente los diez pines de salida de señal del receptor RC a los pines Raspberry GPIO. El receptor está alimentado por los pines + 5V y GND del RPI.

Simplifiqué el script ya que hace muchas otras cosas, si encuentra algún error o sobra, avíseme

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()
user2707001
fuente
0

Es muy posible y relativamente fácil leer entradas PWM en Raspberry Pi usando la biblioteca pigpio C. Si desea un buen rendimiento, le recomiendo usar C, no Python. He proporcionado un pequeño código de muestra a continuación. Al contrario de lo que algunas personas dicen, esto tiene un excelente rendimiento de sincronización y una fluctuación bastante baja. Las lecturas están consistentemente dentro de 5 us en mi RPi 3 B y puede medir pulsos tan cortos como 5 us. Tenga en cuenta que el código provisto es solo una prueba de concepto, no maneja adecuadamente la ausencia de pulsos (ciclo de trabajo del 0% / 100%) o la envoltura 'tick' que ocurre cada 72 minutos. El programa funciona bien en modo de usuario, pero para obtener la mejor resistencia a las fallas de tiempo, ejecute su programa en un nivel agradable negativo como este: sudo nice -n -20 ./program

Vea los documentos de Pigpio en: 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);
    }
}
Georgie
fuente
-3

Solución fácil con alta precisión:

Usar un Arduino como esclavo iic o dispositivo UART parece funcionar perfectamente bien. El microcontoller es capaz de leer la información a través del método pulseIn.

Para obtener información detallada: https://www.youtube.com/watch?v=ncBDvcbY1l4

Simon
fuente
¡Bienvenido a Raspberry Pi! Si bien esto puede responder teóricamente la pregunta, sería preferible incluir aquí las partes esenciales de la respuesta y proporcionar el enlace para referencia. Estamos probando una nueva política con respecto a las respuestas sin información de solo enlace aquí . Si esta publicación no se edita para contener información que pueda ser una respuesta, por mínima que sea, en 48 horas se convertirá a Community Wiki para simplificar que la comunidad la corrija.
Ghanima