Finalmente obtuve una comprensión completa (ish) del archivo de encabezado del controlador bcm2835.h, así que pensé en publicar y responder mi propia pregunta para los demás.
Los bits relevantes del encabezado:
PWM
El BCM2835 admite hardware PWM en un subconjunto limitado de pines GPIO. Esta biblioteca bcm2835 proporciona funciones para configurar y controlar la salida PWM en estos pines.
El BCM2835 contiene 2 canales PWM independientes (0 y 1), cada uno de los cuales se conecta a un subconjunto limitado de pines GPIO. Los siguientes pines GPIO se pueden conectar a los siguientes canales 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
Para que un pin GPIO emita una salida de su canal PWM, debe configurarse con la función Alt que se indicó anteriormente. Tenga en cuenta que las versiones actuales de Raspberry Pi solo exponen uno de estos pines (GPIO 18 = RPi Pin 1-12) en los encabezados IO y, por lo tanto, este es el único pin IO en el RPi que se puede usar para PWM. Además, debe establecerse en ALT FUN 5 para obtener la salida PWM.
Ambos canales PWM están controlados por el mismo reloj PWM, cuyo reloj dvider se puede variar usando bcm2835_pwm_set_clock()
. Cada canal se puede habilitar por separado con bcm2835_pwm_set_mode()
. La salida promedio del canal PWM está determinada por la relación de DATOS / RANGO para ese canal. Use bcm2835_pwm_set_range()
para establecer el rango y
bcm2835_pwm_set_data()
para establecer los datos en esa proporción
Cada canal PWM puede ejecutarse en modo equilibrado o de marca-espacio. En modo equilibrado, el hardware envía una combinación de pulsos de reloj que da como resultado pulsos de DATOS generales por pulsos de RANGO. En el modo Mark-Space, el hardware establece la salida HIGH para los pulsos de reloj DATA, seguido de LOW para los pulsos de reloj RANGE-DATA.
El reloj PWM se puede configurar para controlar los anchos de pulso PWM. El reloj PWM se deriva de un reloj de 19.2MHz. Puede configurar cualquier divisor, pero algunos son proporcionados porbcm2835PWMClockDivider
Por ejemplo, supongamos que desea conducir un motor de CC con PWM a aproximadamente 1 kHz y controlar la velocidad en incrementos de 1/1024 desde 0/1024 (detenido) hasta 1024/1024 (completamente encendido). En ese caso, puede configurar el divisor del reloj en 16 y el RANGO en 1024. La frecuencia de repetición de pulso será 1.2MHz / 1024 = 1171.875Hz.
bcm2835PWMClockDivider
Especifica el divisor utilizado para generar el reloj PWM a partir del reloj del sistema. Las siguientes figuras muestran el divisor, el período del reloj y la frecuencia del reloj. El reloj dividido se basa en la frecuencia nominal de reloj base PWM de 19.2MHz. Las frecuencias mostradas para cada divisor han sido confirmadas por medición
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;
En resumen:
Si desea hardware PWM, está atrapado con el pin 12 (BCM18), otros pines GPIO utilizarán el software PWM.
Probablemente necesitará configurar el modo PWM en el modo 'Marcar espacio' para la mayoría de los casos de uso y razones de cordura como se describió anteriormente.
En este modo, la duración de que cada "pulso" sea ALTO frente a BAJO está determinada por la relación de los datos PWM con respecto al rango PWM; esto es independientemente de la velocidad del reloj PWM.
El rango PWM es efectivamente la 'resolución' o el número de 'divisiones' posibles de cada pulso. Cuantas más divisiones, mayor será la resolución y, por lo tanto, más estados codificables para un ancho de pulso dado.
El 'ciclo de trabajo' es la relación de datos PWM a rango PWM expresada como un porcentaje. Un rango PWM de 10 con datos PWM de 8 es un ciclo de trabajo del 80%.
La velocidad del reloj PWM es una potencia de dos divisores. Por lo tanto, la velocidad de reloj elegida para PWM debe ser divisor & (divisor -1) == 0
Aunque los 12 valores válidos se enumeran arriba.
Al dividir la frecuencia del reloj PWM por la frecuencia de salida deseada se obtiene el valor del rango del pulso.
Cuando estaba codificando audio y usando una sonda piezoeléctrica, necesitaba un ciclo de trabajo del 50% para maximizar la oscilación piezoeléctrica y, por lo tanto, el volumen. El valor de los datos PWM es, por lo tanto, siempre la mitad del valor del rango PWM: 50% ALTO 50% BAJO.
Para calcular su frecuencia requerida, elija un divisor de reloj que tenga sentido para su aplicación: elegí 16, que equivale a 1.2Mhz. Entonces:
La nota de A es 440Hz, F # es 370Hz, C # es 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;
Puede cambiar fácilmente el rango PWM hacia arriba y hacia abajo octavas en múltiplos: el rango * 2 lo bajará una octava, el rango * 0.5 lo tomará uno.
Si desea conducir un servo a unos 50 Hz, el mismo cálculo de rango es válido:
PWM Range = PWM frequency / Desired Output Frequency
(El valor máximo del rango de PWM según algunas publicaciones anecdóticamente es 4096; en mi experiencia, esto no es cierto ya que jugar un C # como se muestra arriba da un rango de PWM de 4332 que funciona como se esperaba).
Como la mayoría de las cosas, es fácil cuando sabes cómo.
~ N