¿Cómo realizar una salida síncrona en el Arduino?

Tengo un dispositivo que estoy tratando de controlar con el Arduino.

Funciona así.

  1. Arduino establece y mantiene la 'línea lista' a 5V de alto.
  2. El dispositivo ve línea lista alta y envía 16 pulsos de reloj para recibir dos bytes de mensajes.
  3. En cada pulso de reloj, el Arduino establece la salida de datos en el bit que sea (0 o 1)
  4. Cuando se han enviado los dos bytes, la 'línea preparada' vuelve a establecerse en 0v bajo.

El problema que tengo es hacer que el Arduino se sincronice con los pulsos del reloj. Actualmente tengo una declaración if que establece la línea de datos si ve que la línea del reloj es alta, pero esto no parece funcionar. Creo que debería estar buscando el borde ascendente en su lugar.

¿Cómo puedo sincronizar estos dos dispositivos correctamente?

Suena como SPI.
Desafortunadamente no es SPI. Es una interfaz de controlador propietaria para un viejo sintetizador.
Aún así, supongo que el concepto es muy similar, si no el mismo.

Respuestas (3)

Agregue estos pasos a su algoritmo: 0. Prepare el primer bit de datos... 3. Use la interrupción externa en el flanco ascendente para generar el bit preparado y coloque el siguiente bit en su variable de soporte de bits. Cuenta los bits emitidos. * El paso 3 se repetirá automáticamente 16 veces (el dispositivo externo lo activará). 4. Cuando el contador de bits llegue a 16, deshabilite la interrupción externa y restablezca la línea lista.

sugerencia: en atmega168/328, cualquier pin puede usarse como fuente de interrupción externa en la transición de borde entrante con una técnica bastante simple.

Espero que su dispositivo externo no esté impulsando pulsos de reloj demasiado rápido. Arduino no es muy bueno en velocidades de reacción superiores a 1MHz. Diría que debe esperar que este enfoque funcione de manera confiable hasta un reloj de 100 kHz. En velocidades más altas, se debe usar SPI de hardware para registrar sus datos o una codificación de ensamblaje estricta. En cualquier caso, varios MHz es el máximo que puede hacer con AVR desnudo aquí.

El intervalo entre pulsos es ~120us, por lo tanto 1,000,000/120 = 8333, y la velocidad es 8.3Khz?
Exactamente. Eso estará perfectamente bien. También debe tener cuidado si desea que se permitan otras interrupciones durante la transferencia de datos.

El SPI tiene dos modos: Arduino Stock Library de forma nativa solo admite maestro. Su escenario suena exactamente como ESCLAVO, en el sentido de que el otro extremo está manejando el Reloj.

ejemplo completo

Puede vincular su Ready al SS (como entrada) y configurar el SPI con el modo correcto de fase/polaridad. Luego configure el SPCR como Esclavo. Habilite su Listo y luego espere a que se complete la transferencia de 8 bits, ya sea por interrupción o por sondeo. luego suelte el Listo.

ATmega328 Hoja de datos nota sección 18.2 y 18.3.2

Bonito y claro ejemplo. Aunque para este caso, el software SPI está bastante bien y permite usar cualquier pin de arduino, no solo los fijados a hw SPI.

El problema aquí tiene que ver con la sincronización, ¡así que saquemos todos nuestros osciloscopios! Básicamente (y un poco demasiado simplificado), la mayoría de las declaraciones de control de flujo C estándar no pueden garantizar la sincronización en un microcontrolador. Las interrupciones basadas en hardware garantizan que el código comenzará a ejecutarse dentro de un número fijo de ciclos de reloj.

El Arduino (más específicamente, el Arduino Uno con un ATmega328) tiene muchas formas de activar interrupciones de hardware, a veces llamadas "vectores de interrupción" (pág. 57 de la hoja de datos de ATmega). ¿Cuál queremos? Queremos ejecutar el código inmediatamente después de la transición del pin del martillo, por lo que estamos buscando una interrupción de cambio de pin o una interrupción externa. La sección 12 en la hoja de datos entra en demasiados detalles sobre esto, pero la clave es que cada puerto de 8 pines comparte un vector de interrupción. ( arduino.cc/en/Hacking/PinMapping168 ) Esto lo hace complicado, especialmente si eres nuevo en microcontroladores. La comunidad de Arduino creó una gran biblioteca para tal situación, llamada PinChangeInt . (Descargo de responsabilidad: contribuí a PinChangeInt)

el incorporadoLa función de creador de interrupciones de Arduino (attachInterrupt(interrupt, ISR, mode)) y el creador de interrupciones de PinChangeInt (PCintPort::attachInterrupt(PIN1, &ISRfunc, EDGE)) toman argumentos muy similares. Los argumentos pin y edge/mode se explican por sí mismos: el pin en el que desea la interrupción y el borde que le interesa (lo más probable es que aumente, pero verifique el diagrama de tiempo de su reloj). El nombre de la función que especifique se adjunta como la rutina de servicio de interrupción (ISR). Cada vez que el hardware ve esa interrupción, el ISR comienza a ejecutarse. Esta función (ISRfunct() arriba) no toma argumentos y no devuelve valores (escriba VOID). Puede acceder a variables globales y otras variables de alcance como cualquier función. Para implementar la lógica que está describiendo, necesitará una variable global para sus datos y otra para el "índice de transmisión" o el último bit enviado.

Para obtener más información sobre las interrupciones en el Arduino, consulte los enlaces a continuación:

Diagramas del subsistema ATmega168

uchobby.com/index.php/2007/11/24/arduino-interrupts/

gonium.net/md/2006/12/20/handling-external-interrupts-with-arduino/

Sin embargo, para hacer esto, necesitaría poder decirle a la interrupción qué estado generar (0 o 1) e incrementar el contador para que sepa en qué lugar del byte se encuentra. La función de interrupción de Arduino no puede aceptar ningún argumento.
Actualicé mi solución para incluir una breve descripción general de los ISR en Arduino. Esencialmente, necesita una variable global o de alcance amplio para comunicar datos de un ISR a su ciclo principal.