¿Un temporizador PWM para dos salidas?

primera publicación aquí, lo siento por cualquier mal estilo.

Estoy trabajando en un proyecto grupal y usando un Atmega328, sin entrar en detalles, necesito controlar dos ventiladores, dos servos y 1 función basada en CTC de 3 temporizadores. A mi pregunta entonces; hay registros OCRx A y B que estoy usando para impulsar los ventiladores a velocidades variables y usando un método similar para los servos. Al configurar un script PWM muy básico, noté que ambos registros impulsarán un ventilador, sin embargo, cuando intento usar cualquiera de los registros de forma independiente (ventilador 1 encendido, 2 apagado, luego cambio), OCRxB no producirá una salida.

Aquí hay un recorte de mi código de configuración:

void setup_timers () {
// Set Timer0 to Fast PWM mode
TCCR0A |= (1 << WGM01) | (1 << WGM00);

// Set Timer0 to clear OCR0 A/B on match
TCCR0A |= (1 << COM0A1) | (1 << COM0A0) | (1 << COM0B1) | (1 << COM0B0);
TCCR0B |= (1 << CS01);
TCNT0 = 0;

// Set OCR0 A/B
DDRD = 0xFF;}

Ahora, idealmente, esto configurará ambos registros en el temporizador 0 para las salidas. Estoy enviando esta señal a un transistor, que luego alimenta los ventiladores según lo determinado por el siguiente bit:

int main () {
setup_timers();

while (1) {

    /* Timer 0; OCR0A (i = 0, L), OCR0B (i = 1, R), i = 2 denotes both fans */

    for (uint8_t i = 0; i <= 3; i++) {
        // Cycle Fans Left/Right/Both

        for (uint8_t j = 1; j <= 4; j++) {
            // Cycle Speed 

            if (i == 2) {
                // Check motor; run timer 0 A/B as required
                OCR0A = 0xFF * j / 4;
                OCR0B = 0xFF * j / 4;
                _delay_ms(2000);
            }
            else if (i == 1) {
                OCR0A = 0xFF * j / 4;
                OCR0B = 0;
                _delay_ms(2000);
            }
            else if (i == 0) {
                OCR0A = 0;
                OCR0B = 0xFF * j / 4;
                _delay_ms(2000);
            }
        }
    }
}
return 1; }

Ahora sé que esto podría hacerse mejor, este segmento tiene un montón de otras pruebas recortadas. El problema principal aquí es que el ventilador 1 se ejecutará (dirigido por OCR0A), pero el ventilador 2 se ignora por completo. He leído la hoja de datos varias veces, pero no estaba un poco claro si esto es realmente posible, decía algo sobre que OCRxB no se almacenaba, y he visto otras publicaciones preguntando sobre eso. Por lo que entiendo son registros independientes, pero es posible que se conserve OCRxA, pero despues de escribir OCRxB es imposible ejecutar?

O tal vez estoy completamente equivocado, ciertamente no sería la primera vez.

Es posible que sus pines de salida no compartan el mismo temporizador. verifique la asignación de salida del temporizador a los pines físicos que está utilizando.
Deberia de funcionar. ¿ Está seguro de que el ventilador está conectado al pin PD5 (PCINT21/OC0B/T1) ? Por cierto: creo que el bucle externo debería ir solo a <3 y 0xFF * j/4 probablemente no proporcione 0xFF para j=4 sino 252 .
@TMa: es posible que desee reconsiderar su comentario sobre "j". Si j=4 y observando que 0xFF=255 se sigue que (255*4)/4 == 255, no 252.
Creo que está compilado como 255/4 * j=63.75-->63 * j . Y en tiempo de ejecución evaluó solo 63 * j , es decir 63 * 4 = 252 . Al menos depende de la optimización del compilador. Pero no es un tema importante en la prueba. Lo siento si me equivoco.
@KyranF, soy consciente de que hay dos pines de salida, he podido hacer que cada uno funcione únicamente desde OCR0A, pero no tener OCR0B (D6) actuando independientemente de OCR0A (D5).
¡@Oman también pueden usar diferentes temporizadores!
@KyranF, ¿a qué te refieres? Tengo 3 temporizadores y los estoy usando todos, pero necesito poner las cosas en un temporizador.
¿Sabes si ambos periféricos PWM pueden usar el temporizador 0?
@KyranF ese es el problema. Puedo ejecutar PWM a través de cualquier puerto sin configurar OCR0B, pero cuando trato de configurar OCR0B y todo el registro de comunicaciones, etc., no hay nada en el pin de salida. En el peor de los casos, ¿saben si puedo ejecutar un servo con digital, no con PWM?
Sí, claro. Nunca ejecutaría un servo con PWM, es completamente inadecuado. Si aún no está utilizando Arduino para su proyecto Atmega328, al menos puede ver cómo lo hacen y aplicar ingeniería inversa al código C. arduino.cc/en/reference/servo Usan temporizadores e ISR (creo que, de lo contrario, son simples cronometrajes y usan el tiempo transcurrido para hacer cambios en la salida) para dar servicio al pin de salida digital de manera adecuada. Esto es mucho mejor que PWM, que con una resolución de 8 bits y estando atascado entre 1 y 2 ms de tiempo, es muy difícil obtener PWM de baja velocidad, a 500 Hz solo tiene 10 posiciones para PWM de 8 bits.
(continuación de las 10 posiciones) Debido a que va de 1/50 de segundo a 2/50 de segundo es la ventana de comando de su servo, y 500 Hz PWM le brinda 0-255 pasos en 1/500 de segundo. Entonces, 10/500 y 20/500, tendrá un control de posición muy granular. Por supuesto, puede aumentar la frecuencia PWM, pero sí ... solo hágalo con temporizadores simples y E/S digital, en la tierra de los microcontroladores, hacer algo cada 20 ms no es difícil.
Hm, los temporizadores son una idea razonablemente buena, ¿cómo podría ejecutar dos CTC desde 1 temporizador? Por el momento, mi ventana de tiempo es muy pequeña, por lo que algo simple sería genial. Gracias por toda la ayuda hombre.
Ah, y estoy usando Atmel Studio, ejecutando un Atmega328 solo, conectándolo a un circuito personalizado.

Respuestas (1)

Parece que hubo algún problema con los valores predeterminados, al configurar COM1A0 y COM1B0, el temporizador 1 se puede configurar para dar señales independientes. Este es el código de instalación que funciona:

void setup_timers () {

TCCR1A |= (1 << WGM11) | (1 << WGM10);
TCCR1A |= (1 << COM1A1) | (1 << COM1A0) | (1 << COM1B1) | (1 << COM1B0);
TCCR1B |= (1 << CS11);
TCNT1 = 0;

DDRB = 0xFF;}

Todavía tengo problemas para que controle correctamente los motores, pero los servos funcionan bien y se puede hacer.

Nota: el temporizador 1 en realidad tiene dos salidas separadas, pero el temporizador 0/2 no. Para obtener salidas duales, debe alternar los registros WGMx2 y COMxB 0/1, cuando WGMx2 está configurado, OCRxA no emitirá correctamente, pero OCRxB sí, con algunos cambios. Cuando WGMx2 no está configurado, deshabilita OCRxB, y OCRxA funcionará normalmente, para obtener salidas independientes, deberá tener esto en cuenta.