¿Bus de bajo costo / complejidad para módulos expandibles?

4

Estoy tratando de elegir un autobús para un sistema expandible. Los requisitos incluirían idealmente:

  • Número de módulos desconocido (potencialmente hasta un par de docenas).
  • Se permiten varios módulos idénticos (puede ser difícil darles IDs únicas a los módulos por adelantado)
  • Pequeño y barato de implementar (cada módulo puede contener una MCU barata)
  • No necesita ser de larga distancia, un par de pulgadas como máximo, pero no necesariamente en el mismo tablero.
  • > 1Mbps sería bueno.
  • Bus compartido. No hay líneas de selección por módulo, etc. Lo ideal es manejar ramas y ciclos en el bus.

El candidato principal parece ser I2C, pero tener varios módulos idénticos es complicado.

Estaba pensando que podría llegar a algún esquema en capas sobre I2C. Por ejemplo: el maestro consulta los esclavos a los que no se les asigna una dirección mediante el envío de un mensaje a una dirección de "transmisión" predeterminada, y los módulos que no tienen una dirección responden aleatoriamente después de un cierto número de encuestas para evitar la contención del bus. El módulo responderá con un número aleatorio + CRC, por lo que si hay una contención, el maestro puede decirle que vuelva a intentarlo más tarde. Después de algunas encuestas sin respuesta, el maestro asume que todos los esclavos han respondido y pueden continuar con la inicialización. Creo que este esquema podría ser compatible con dispositivos I2C estándar. No sé si el soporte I2C integrado en las MCU es compatible.

¿Esto es innecesariamente complejo? ¿Existen otros protocolos de bus simples que puedan manejar este escenario?

    
pregunta tlrobinson

4 respuestas

3

Diseñé [1] algo como esto usando I2C una vez. (Ya que lo hice por trabajo, no puedo publicar el código). Mientras tenga control sobre todos los nodos (todos ellos son MCU programados por usted), esto debería funcionar.

Básicamente, los dispositivos se organizan en una cadena de margaritas usando I2C de manera normal. Además del I2C, tiene una línea lógica punto a punto, que utiliza dos pines PIO por nodo. Un pin ("sentido ascendente") es solo de entrada y tirado hacia arriba, mientras que el otro pin ("sentido descendente") es solo de salida, pero inicialmente en triple estado (alto-Z hacia afuera) y opcionalmente levantado. Cada pin de detección en sentido ascendente de cada nodo está conectado al pin de detección en sentido descendente del siguiente chip en sentido ascendente. Los pines más lejanos y más alejados se dejan sin conectar. Opcionalmente, cada nodo puede tener un FET externo que conecta resistencias de extracción al bus I2C.

Al encenderse, todos los nodos tienen sus puertos I2C como esclavos con la dirección 0 o algo así (no importa), conducen sus pines de sentido descendente a 0 y esperan un tiempo fijo (depende de cuánto tiempo demore) para que todos sus nodos se enciendan e inicialicen). Lo que buscan recibir es un mensaje de "todas las llamadas" (difusión).

El nodo que se encuentre más arriba no verá cómo se redujo su sentido ascendente en este momento. Por lo tanto, va primero (si los dominios están controlados por FET, los activa), establece su puerto como maestro y transmite un mensaje de llamada a todos los demás nodos, incluida su dirección (cualquiera que sea su nombre). desea utilizar para la primera) y cualquier otra información que identifique qué es para los otros nodos. Luego, espera un tiempo fijo para que otro nodo (debe ser ninguno, pero quién sabe) envíe un mensaje de llamada general que diga que ellos están, de hecho, en la primera dirección. Si recibe un mensaje de este tipo, entonces repite su identificación, pero con la siguiente dirección. Este ciclo se repite hasta que encuentra una dirección disponible. (Este patrón permite que un nodo se reinicie y recupere su dirección sin confundir el bus).

Una vez que esté seguro de su dirección, la configura en el periférico I2C y pasa al modo esclavo, para escuchar otros nodos, e impulsa su línea de sentido descendente hacia arriba, lo que le indica al siguiente nodo que se dirija hacia abajo para que realice el mismo proceso para obtener su direccion En este punto, solo escucha a las personas que intentan reclamar su dirección y registra la información de identificación de los otros nodos. (Los nodos también escuchan la identificación de otros nodos antes de obtener una ventaja ascendente en sentido ascendente, construyendo una tabla de red, pero aún no tienen una dirección reclamada, por lo que no verifican las colisiones). una dirección, puede usar los datos de la tabla para elegir una dirección probable no reclamada.)

Después de todo esto, todos deberían tener direcciones I2C únicas y estar listos para comenzar. Entonces solo usas I2C como normal. (No hace falta decir que, sea cual sea la dirección inicial, no se pudieron usar todos los nodos después de la configuración). En nuestra configuración, la llamada general solo se usaba para la configuración, y el direccionamiento directo solo se usaba para el trabajo real. Si desea utilizar la función de llamada general después de la configuración, deberá diseñar su mensaje de llamada general para indicar en qué modo se encuentra.

Probablemente hay muchas cosas que pueden optimizarse aquí, pero debería darle un comienzo. Usamos esto en una placa de piggyback para una fuente de alimentación de medio ladrillo, de modo que solo podías juntar los ladrillos que necesitabas (agregamos conectores de borde a nuestras tablas para transportar I2C y las otras líneas) y luego conectarlos a un puerto serie en cualquiera de los ladrillos para obtener información de voltaje, corriente y temperatura en todos ellos. Fue muy dulce y le conseguimos a nuestro estudiante (que hizo el trabajo pesado) una A en el laboratorio principal. (Luego corrió lo más rápido que pudo para graduarse de la escuela en todo el país ...)

[1] Por "diseñado" quiero decir que escribí algo similar al texto anterior, la inspiración del 1% por Edison. El 99% de la transpiración fue proporcionado por mi estudiante universitario.

    
respondido por el Mike DeSimone
1

Una vez diseñé algo como esto para un tipo que necesitaba hacer mediciones en una rejilla IIRC 30 x 30 en un invernadero. Utilicé una codificación de pulso corto / pulso largo para evitar la imprecisión del reloj PIC incorporado. Cada cadena de 30 nodos utilizó un chip 74HCT como tampón / regenerador. Sin la intervención del PIC del nodo, cada nodo recibió la señal del "maestro principal". Pero cada nodo podría bloquear la señal al siguiente nodo, que se utilizó durante el inicio (fase de enumeración). Los PIC eran 16F819, en ese momento los PIC más baratos que podían escribir por sí mismos. (Toda la cadena podría actualizarse en paralelo). Las cabezas de cada cadena se encadenaron a su vez de forma similar, por lo que se podía acceder a la red completa de ~ 1k nodos desde una PC. IIRC, el cliente una vez tuvo esta configuración en algún lugar de España, a unos 1000 km de donde vivía, y podía hacer actualizaciones de firmware y medidores desde casa.

IIRC la tasa de baudios no era tan alta, hacer 19k2 o menos. Pero el costo de los nodos fue mínimo: el PIC, el chip 74HCT, algunas resistencias y el siempre presente 100nF y 22uF.

    
respondido por el Wouter van Ooijen
1

Uso mucho SPI y parece funcionar muy bien. Hay una gran cantidad de dispositivos que admiten SPI y lo he usado en varias aplicaciones de control de microcontrolador a controlador.

    
respondido por el nutsfor 8051
0

Sé que querías > 1Mb, pero si está dispuesto a conformarse con 1Mb exactamente, entonces el bus CAN es un bus bastante decente y de bajo costo. Si se trata de un autobús corto, incluso podría escapar sin transceptores, o solo con transistores simples. Aquí le indicamos cómo numerar automáticamente todos los nodos idénticos en el bus. Cada nodo esperaría un número aleatorio de milisegundos y luego enviaría un mensaje CAN simple. El número de mensajes de lata que ve antes de enviarlo le dice qué ID debe ser. Si más de un nodo envía un mensaje al mismo tiempo, ambos mensajes se ignoran y cada nodo espera otro tiempo aleatorio.

Cada uno tendría el siguiente código:

int negotiate_my_id(void)
{
    int pause_time = random number between 2 .. 100
    int my_id = 0;
    int num_messages_this_ms = 0;

    initialise_100kHz_timer();


    while(pause_time)
    {
        if (pause_time == 1)
            Send_CAN_message ( ID = 1, length = 0);


        timer_value = 100;                 // timer_value is decremented by the timer
        while(timer_value)
        {
            if (CAN_message_seen())        // Count num CAN messages seen
            {                              // during this millisecond.
                num_messages_this_ms++;    
                timer_value = 100;         // All nodes synchronise on message.
            }
        }

        if (num_messages_this_ms == 1)
        {
            my_id++;
        }

        if (num_messages_this_ms > 1)      // Saw a collision?
        {
            if (pause_time == 1)           // with my message?
                pause_time = random number between 2 .. 100
        }

        num_messages_this_ms = 0;
        pause_time--;
    }

   return my_id;
}
    
respondido por el Rocketmagnet

Lea otras preguntas en las etiquetas