Autor Tema: AVRPLC: Clon de un PLC de Siemens basado en FreeRTOS  (Leído 23436 veces)

0 Usuarios y 1 Visitante están viendo este tema.

Desconectado jgpeiro06

  • Colaborador
  • PIC18
  • *****
  • Mensajes: 276
AVRPLC: Clon de un PLC de Siemens basado en FreeRTOS
« en: 28 de Octubre de 2010, 21:56:47 »
Hola a todos,

Después de 10 meses de trabajo, por fin estoy terminando mi PFC y voy a aprovechar la necesidad que tengo de redactar toda la documentación para publicar aquí un pequeño esbozo.

El proyecto AVRPLC consiste en un clon de un autómata S7-200 de Siemens, compatible con la herramienta de desarrollo de Siemens STEP 7-Micro/WIN. Esta basado en un micro ATmega1280 + FreeRTOS y es posible portarlo a otros micros como PIC o ARM.

La placa tiene las entradas y salidas típicas de un PLC, ademas de una conexión inalámbrica que permite programar, monitorizar y controlar toda una red de AVRPLCs desde un solo PC.

El corazón del programa es una VM(maquina virtual) de AWL (el lenguaje ensamblador de los PLCs de Siemens) que se encarga de ejecutar los programas como lo haría un PLC de la familia S7-200. Además, para el PC, he escrito un programa en C, llamado AWLParser, que convierte un programa AWL en "bytecodes" para la VM.

Os dejo con algunas imagenes del proyecto...
Saludos

Un esquema de la estructura del programa...

El esquematico...

El PCB...
« Última modificación: 28 de Octubre de 2010, 21:59:16 por jgpeiro06 »

Desconectado MGLSOFT

  • Moderador Local
  • DsPIC33
  • *****
  • Mensajes: 7912
Re: AVRPLC: Clon de un PLC de Siemens basado en FreeRTOS
« Respuesta #1 en: 28 de Octubre de 2010, 22:01:24 »
Excelente proyecto!!
Me anoto a seguirlo!!
Todos los dias aprendo algo nuevo, el ultimo día de mi vida aprenderé a morir....
Mi Abuelo.

Desconectado Suky

  • Moderadores
  • DsPIC33
  • *****
  • Mensajes: 6758
Re: AVRPLC: Clon de un PLC de Siemens basado en FreeRTOS
« Respuesta #2 en: 28 de Octubre de 2010, 22:08:08 »
Excelente! Sin duda la parte de firmware debe estar muy interesante, el hardware habría que reforzarlo para que trabaje como un Siemens y en el ambiente industrial, pero eso es externo  :)

Atentos al hilo para aprender!  :lol:
No contesto mensajes privados, las consultas en el foro

Desconectado barral

  • PIC10
  • *
  • Mensajes: 37
Re: AVRPLC: Clon de un PLC de Siemens basado en FreeRTOS
« Respuesta #3 en: 29 de Octubre de 2010, 07:45:31 »
Esto tiene muy buena pinta... estaré atento.  :-/

Excelente proyecto, como siempre

Desconectado jgpeiro06

  • Colaborador
  • PIC18
  • *****
  • Mensajes: 276
Re: AVRPLC: Clon de un PLC de Siemens basado en FreeRTOS
« Respuesta #4 en: 29 de Octubre de 2010, 08:51:38 »
Citar
el hardware habría que reforzarlo para que trabaje como un Siemens y en el ambiente industrial
El diseño esta pensado para el control de instalaciones en edificios, donde el ruido electromagnetico es mucho menor.
Para la interfaz con sensores y actuadores trabajé un tiempo en una placa con reles y octos que encajaria encima del AVRPLC, pero lo deje por falta de tiempo. Aqui se pueden ver el esquematico y el PCB a medio camino.

Funcionamiento del programa
Como se ve en la estructura del programa, este esta dividido en tareas y dirvers. Las tareas implementan todo el programa, y los drivers solo manejan los perifericos del micro.
   Task_Xbee:   Controla la comunicacion con el Xbee.
   Task_WDT:   Supervisa la tarea Task_AWL.
   Task_AWL:   Ejecuta el codigo AWL.
   USARTdriver:   Maneja las USARTs
   ADCdriver:   Maneja el ADC
   PWMdriver:   Maneja los PWMs

Despues de un reset el sistema arranca las tareas TaskXbee y TaskWDT. La tarea TaskXbee espera continuamente nuevos datos desde el Xbee.
Cuando llega un programa a la tarea TaskXbee se graba en la EEPROM e informa a la tarea TaskWDT que hay un nuevo programa. Ahora la tarea TaskWDT arranca la tarea TaskAWL y comprueba periodicamente que esta funcionando correctamente.
La tarea TaskAWL ejecuta indefinidamente el codigo AWL, con un tiempo de "ciclo de programa" de 20mS. En cada ciclo de programa lee las entradas, actualiza las salidas, ejecuta todo el codigo AWL y espera un tiempo, hasta el siguiente multiplo de 20mS.

La tarea TaskAWL basicamente es un gran "switch/case" dentro de un bucle. En cada vuelta se ejecuta una instruccion de AWL de la siguiente manera:
    Lee el OP(operando) desde la dirreccion de ROM apuntada por el PC.
    Salta al correspondiente "case".
    Ejecuta la operacion, leyendo y/o escribiendo en la RAM.
    Incrementa el PC para apuntar a la siguiente direccion.
Soporta unas 70 instrucciones de AWL, incluyendo las tipicas logicas, aritmeticas, transferencia, control de flujo... y algunas mas avanzadas como timers(TON, TOF y TONR), contadores (CTU, CTD, CTUD ), subrutinas...  

Tipos de instrucciones soportadas(en verde)

Desconectado Nocturno

  • Administrador
  • DsPIC33
  • *******
  • Mensajes: 18286
    • MicroPIC
Re: AVRPLC: Clon de un PLC de Siemens basado en FreeRTOS
« Respuesta #5 en: 29 de Octubre de 2010, 09:05:35 »
También me anoto al hilo. Magnífico como siempre, jgpeiro.

Desconectado J1M

  • Moderadores
  • PIC24H
  • *****
  • Mensajes: 1960
Re: AVRPLC: Clon de un PLC de Siemens basado en FreeRTOS
« Respuesta #6 en: 29 de Octubre de 2010, 10:23:27 »
Una obra de arte macho.

Desconectado scientist

  • Colaborador
  • PIC24F
  • *****
  • Mensajes: 999
Re: AVRPLC: Clon de un PLC de Siemens basado en FreeRTOS
« Respuesta #7 en: 29 de Octubre de 2010, 11:17:45 »
excelente proyecto jgpeiro06, como siempre, tus articulos de gran calidad
NO le digas a DIOS que tienes un gran problema, dile a tu problema que tienes un GRAN DIOS!!
Alma mia y castillo mio, mi Dios en quien yo confiare....

Desconectado jgpeiro06

  • Colaborador
  • PIC18
  • *****
  • Mensajes: 276
Re: AVRPLC: Clon de un PLC de Siemens basado en FreeRTOS
« Respuesta #8 en: 29 de Octubre de 2010, 12:40:12 »
muchas gracias por vuestros comentarios...

Antes de meterme con las tareas, voy a explicar unas cosillas sobre los drivers, que tambien tienen su trabajo...En el pasado tambien publique algunas cosas sobre drivers en el post de"Juego del Pong basado en FreeRTOS", y desde entonces he aprendido alguna cosilla mas qur voy a comentar.
 
Introduccion
Uno de los problemas más pesados cuando uno cambia de arquitectura es tener que aprender de nuevo a controlar los periféricos. Cada fabricante, al diseñar el periférico, escoge la estructura y los registros de configuración/estado de acuerdo a sus necesidades, y esto siempre dificulta la portabilidad.

Distintos periféricos en un microcontrolador AVR

El driver ayuda a resolver este problema, a costa de reducir la funcionalidad de un periférico al máximo común divisor de todos los fabricantes. Escribir programas portables con drivers es mucho más sencillo ya que, desde el punto de vista externo, los drivers tienen siempre la misma “forma”, independientemente de la estructura interna del periférico e incluso del tipo de periférico.

La estructura del driver generico desarrollado para el proyecto

La estructura
La estructura de los drivers desarrollados para este proyecto se divide en 4 partes: funciones, estructuras, interrupciones y macros.

Las funciones
Todo la interacción necesaria entre un programa y un periférico se puede reducir a 4 simples funciones. Adicionalmente siempre se pueden definir mas funciones como Reset() o WriteBuffer() si el programa lo requiere. Las funciones son:
   Open()      Resetea el periférico, lo activa y lo configura según los argumentos que le pasemos.
   Close()      Desactiva el periférico.
   Read()      Lee datos desde el periférico.
   Write()      Enviad datos al periférico.

Las estructuras
   Normalmente un microcontrolador posee varios periféricos del mismo tipo, con un funcionamiento idéntico. Utilizar una estructura que englobe todos los registros de un periférico permite utilizar uno u otro cambiando solamente una variable en el programa. Una estructura simplificada* de una USART podría tener esta forma:
   
Código: C
  1. struct sUSART{  int *pControl;  int *pStatus;   int *pData; };
Para poder utilizar un periférico u otro las funciones deben aceptar un parámetro que sea la estructura, por ejemplo:  Open( struct sUSART *this )...
*La estructura implementada para AVRPLC tiene 14 campos.

Las interrupciones
Normalmente un periférico tarda mucho más tiempo en “procesar” con un dato que el que tarda la CPU, además, un periférico puede “generar” un dato en cualquier momento, de manera impredecible para la CPU. Por este motivo los periféricos incorporan las interrupciones, que liberan a la CPU de la necesidad de comprobar continuamente el estado de los periféricos manteniendo el “ancho de banda” al máximo permitido por los periféricos.
Según el ancho de banda necesario se pueden utilizar interrupciones con mayor o menor relación complejidad/rendimiento. Aquí voy a citar 4:
   -Sondeo
      …todo te da miedo y te sientes muy solo, te quedas cerquita del periférico y le dices por favor que te avise cuando termine….
   -Interrupción
      Es el modo más común.  Cuando el periféricos esta listo genera la interrupción, la CPU copia/lee los datos en la ISR. Es sencillo y eficiente, pero también tiene sus inconvenientes: Si la CPU produce 2 datos seguidos tendrá que esperar a que el periférico termine con el primero y lo que es peor, si el periférico produce 2 datos seguidos el primero se perderá.
   -Interrupción con buffer
      Este es el método utilizado en el AVRPLC. La CPU ya no escribe los datos directamente en el periférico, lo hace en un buffer. Cuando el periférico termina con un dato genera una IRQ y en la ISR la CPU saca el siguiente dato del buffer. De esta manera los periféricos como que normalmente envían/reciben “ráfagas” de datos trabajan mucho más eficientemente
Funcionamiento del buffer circular con doble puntero:

   -Interrupción con DMA
      El DMA es en si un periférico cuyo objetivo es mover bloques de datos de un lugar a otro dentro del micro. La CPU solo tiene que escribir los datos en el buffer y el DMA se ocupa del resto. Es el más eficiente, con mucha diferencia, pero normalmente los microcontroladores no disponen de DMA.

Los buffers y las condiciones de carrera
Una de las complicaciones al utilizar el buffer y las interrupciones es evitar la  posibilidad de que se produzcan condiciones de carrera. Resumiendo, una condición de carrera se puede producir si se modifica un dato mientras este se estaba leyendo. En los sistemas embebidos esto puede ocurrir únicamente si la ISR y la función principal pueden modificar la misma variable.
Los buffers implementados en AVRPLC son buffers circulares que disponen de dos punteros llamados Head y Tail. Head siempre apunta a la primera posición vacia del buffer y Tail a la ultima posición llena. De esta manera ni los datos del buffer, ni los punteros Head y Tail pueden ser escritos al mismo tiempo, impidiendo la condición de carrera.

   En el caso de la USARTwrite, la operación para introducir un dato en el buffer es:
      
Código: C
  1. pUSART->buffTX[ pUSART->buffTXptrH++ & BUFF_TX_MASK ] = data;
   y el código para sacar un dato en la ISR de USART_TX es:
      
Código: C
  1. pUSART->UDRn = pUSART->buffTX[pUSART->buffTXptrT++ & BUFF_TX_MASK ];
   Puesto no comparten el puntero, y se comprueba previamente que los punteros no tengan el mismo valor(operación permitida ya que solo es de lectura), no se pueden producir condiciones de carrera.

Las macros
Las macros permiten al programador controlar el estado del periférico sin tener que acceder directamente al hardware. Se pueden usar por ejemplo para activar/desactivar las interrupciones, borrar el flag de interrupción, comprobar si los buffers están llenos o vacios… El driver de la USART tiene 18 macros definidas.


Los drivers y el RTOS
El RTOS desactiva las interrupciones globales cuando entra en una región critica. Esto es necesario para el RTOS ya que así impide el cambio de contexto durante la ejecución de su propio kernel.  Esto puede reducir ligeramente la velocidad de los periféricos sin necesidad.  Lo que se ha hecho es cambiar la definición de las macros taskENTER_CRITICAL, taskEXIT_CRITICAL, taskENABLE_INTERRUPTS y taskDISABLE_INTERRUPTS para que solo desactiven el interrupción del TIMER del RTOS, impidiendo el cambio de contexto en las requines criticas y al mismo tiempo permitiendo a las IRQs ser atendidas sin espera.
De momento esto lo tengo en pruebas, pero aparentemente no hay ningún motivo que pueda provocar que el sistema se vuelva inestable.

Adjunto el codigo de lod archivod UartDriver.c y .h
Código: C
  1. /**********/
  2. /* UsartDriver.h */
  3. // Based on UART library by Peter Fleury
  4. /**********/
  5. #ifndef USARTDRIVER_H
  6. #define USARTDRIVER_H 1
  7.  
  8. #include <avr/io.h>
  9. #include <avr/interrupt.h>
  10.  
  11. #define BUFF_RX_SIZE    128     // Must be a power of two.
  12. #define BUFF_TX_SIZE    128     // Must be a power of two.
  13.  
  14. #define TXIrqEnable( pUSART )   ( BitS( *(pUSART)->UCSRnB, UDRIE0 ) )
  15. #define TXIrqDisable( pUSART )  ( BitR( *(pUSART)->UCSRnB, UDRIE0 ) )
  16. #define RXIrqEnable( pUSART )   ( BitS( *(pUSART)->UCSRnB, RXCIE0 ) )
  17. #define RXIrqDisable( pUSART )  ( BitR( *(pUSART)->UCSRnB, RXCIE0 ) )
  18. #define TXFlagSet( pUSART )             ( BitS( *(pUSART)->UCSRnA, UDRE0 ) )
  19. #define TXFlagClear( pUSART )   ( BitR( *(pUSART)->UCSRnA, UDRE0 ) )
  20. #define RXFlagSet( pUSART )             ( BitS( *(pUSART)->UCSRnA, RXC0 ) )
  21. #define RXFlagClear( pUSART )   ( BitR( *(pUSART)->UCSRnA, RXC0 ) )
  22.  
  23. #define TXOn( pUSART )                  ( BitS( *(pUSART)->UCSRnB, TXEN0 ) )
  24. #define TXOff( pUSART )                 ( BitR( *(pUSART)->UCSRnB, TXEN0 ) )
  25. #define RXOn( pUSART )                  ( BitS( *(pUSART)->UCSRnB, RXEN0 ) )
  26. #define RXOff( pUSART )                 ( BitR( *(pUSART)->UCSRnB, RXEN0 ) )
  27.  
  28. #define USARTPinEnable( pUSART )        { BitR( *(pUSART)->DDRx, (pUSART)->PIN ); BitS( *(pUSART)->DDRx, (pUSART)->PIN+1 ); }
  29. #define USARTPinDisable( pUSART )       { BitR( *(pUSART)->DDRx, (pUSART)->PIN ); BitR( *(pUSART)->DDRx, (pUSART)->PIN+1 ); }
  30.  
  31. #define isBufferTXFull( pUSART )        ( (((pUSART)->buffTXptrH - (pUSART)->buffTXptrT) & BUFF_TX_MASK) == BUFF_TX_MASK )
  32. #define isBufferTXEmpty( pUSART )       ( (pUSART)->buffTXptrH == (pUSART)->buffTXptrT )
  33. #define isBufferRXFull( pUSART )        ( (((pUSART)->buffRXptrH - (pUSART)->buffRXptrT) & BUFF_RX_MASK) == BUFF_RX_MASK )
  34. #define isBufferRXEmpty( pUSART )       ( (pUSART)->buffRXptrH == (pUSART)->buffRXptrT )
  35.  
  36. #define BAUD_9600       ( ( (F_CPU) / ( (9600UL) * 8 ) - 1 ) )
  37. #define BAUD_57600      ( ( (F_CPU) / ( (57600UL) * 8 ) - 1 ) )
  38. #define BAUD_115200     ( ( (F_CPU) / ( (115200UL) * 8 ) - 1 ) )
  39. #define BAUD_115200_XBEE        ( ( (F_CPU) / ( (111111UL) * 8 ) - 1 ) )
  40. #define BAUD_230400     ( ( (F_CPU) / ( (230400UL) * 8 ) - 1 ) )
  41.  
  42. struct USART {
  43.         volatile unsigned char *UDRn;
  44.         volatile unsigned char *UCSRnA;
  45.         volatile unsigned char *UCSRnB;
  46.         volatile unsigned char *UCSRnC;
  47.         volatile unsigned char *UBRRnL;
  48.         volatile unsigned char *UBRRnH;
  49.         volatile unsigned char *DDRx;
  50.         volatile unsigned char PIN;
  51.         volatile unsigned char *buffRX;
  52.         volatile unsigned char *buffTX;
  53.         volatile unsigned char buffTXptrH;
  54.         volatile unsigned char buffTXptrT;
  55.         volatile unsigned char buffRXptrH;
  56.         volatile unsigned char buffRXptrT;
  57. };
  58.  
  59. extern struct USART USART0;
  60. extern struct USART USART1;
  61. extern struct USART USART2;
  62. extern struct USART USART3;
  63.  
  64.  
  65. void USARTopen( struct USART *pUSART, unsigned int baudrate );
  66. void USARTclose( struct USART *pUSART );
  67. unsigned char USARTread( struct USART *pUSART );
  68. void USARTwrite( struct USART *pUSART, unsigned char data );
  69. void USARTputs( struct USART *pUSART, char *s );
  70. void USARTputc( struct USART *pUSART, char c );
  71. void USARTputInt( struct USART *pUSART, unsigned int i );
  72.  
  73. #endif

UsartDriver.c
Código: C
  1. /**********/
  2. /* UsartDriver.c */
  3. // Based on UART library by Peter Fleury
  4. /**********/
  5. #include "UsartDriver.h"
  6. #include "Utils.h"
  7.  
  8.  
  9.  
  10. #define BUFF_RX_MASK    ( BUFF_RX_SIZE - 1 )
  11. #define BUFF_TX_MASK    ( BUFF_TX_SIZE - 1 )
  12.  
  13. static unsigned char buffRX0[ BUFF_RX_SIZE ];
  14. static unsigned char buffTX0[ BUFF_TX_SIZE ];
  15. //static unsigned char buffRX1[ BUFF_RX_SIZE ];
  16. //static unsigned char buffTX1[ BUFF_TX_SIZE ];
  17. static unsigned char buffRX2[ BUFF_RX_SIZE ];
  18. static unsigned char buffTX2[ BUFF_TX_SIZE ];
  19. //static unsigned char buffRX3[ BUFF_RX_SIZE ];
  20. //static unsigned char buffTX3[ BUFF_TX_SIZE ];
  21.  
  22. struct USART USART0 = {
  23.         &UDR0,  &UCSR0A,        &UCSR0B,        &UCSR0C,        &UBRR0L,        &UBRR0H,
  24.         &DDRE, 0,
  25.         &buffRX0[0],    &buffTX0[0],
  26.         0,      0,      0,      0
  27. };
  28.  
  29. /*struct USART USART1 = {
  30.         &UDR1,  &UCSR1A,        &UCSR1B,        &UCSR1C,        &UBRR1L,        &UBRR1H,
  31.         &DDRD, 2,
  32.         &buffRX1[0],    &buffTX1[0],
  33.         0,      0,      0,      0
  34. };*/
  35.  
  36. struct USART USART2 = {
  37.         &UDR2,  &UCSR2A,        &UCSR2B,        &UCSR2C,        &UBRR2L,        &UBRR2H,
  38.         &DDRH, 0,
  39.         &buffRX2[0],    &buffTX2[0],
  40.         0,      0,      0,      0
  41. };
  42.  
  43. /*struct USART USART3 = {
  44.         &UDR3,  &UCSR3A,        &UCSR3B,        &UCSR3C,        &UBRR3L,        &UBRR3H,
  45.         &DDRJ, 0,
  46.         &buffRX3[0],    &buffTX3[0],
  47.         0,      0,      0,      0
  48. };*/
  49.  
  50.  
  51.  
  52. #define ISR_USART_RX( VECTOR, USART )                                                   \
  53. ISR( VECTOR ){                                                                                                  \
  54.         LEDRunON();                                                                                                     \
  55.         if( !isBufferRXFull( &(USART) ) )                                                       \
  56.                 (USART).buffRX[ (USART).buffRXptrH++ & BUFF_RX_MASK] = *(USART).UDRn;   \
  57.         else{                                                                                                           \
  58.                 RXIrqDisable( &(USART) );                                                               \
  59.         }                                                                                                                       \
  60. }
  61.  
  62. ISR_USART_RX( USART0_RX_vect, USART0 )
  63. //ISR_USART_RX( USART1_RX_vect, USART1 )
  64. ISR_USART_RX( USART2_RX_vect, USART2 )
  65. //ISR_USART_RX( USART3_RX_vect, USART3 )
  66.  
  67.  
  68. #define ISR_USART_TX( VECTOR, USART )                                                   \
  69. ISR( VECTOR ){                                                                                                  \
  70.         LEDRunON();                                                                                                     \
  71.         if( !isBufferTXEmpty( &(USART) ) ){                                                     \
  72.                 *(USART).UDRn = (USART).buffTX[ (USART).buffTXptrT++ & BUFF_TX_MASK ];  \
  73.         }else{                                                                                                          \
  74.                 TXIrqDisable( &(USART) );                                                               \
  75.         }                                                                                                                       \
  76. }
  77.  
  78. ISR_USART_TX( USART0_UDRE_vect, USART0 )
  79. //ISR_USART_TX( USART1_UDRE_vect, USART1 )
  80. ISR_USART_TX( USART2_UDRE_vect, USART2 )
  81. //ISR_USART_TX( USART3_UDRE_vect, USART3 )
  82.  
  83.  
  84. void USARTopen( struct USART *pUSART, unsigned int baudrate ){
  85.         TXIrqDisable( pUSART );
  86.         RXIrqDisable( pUSART );
  87.        
  88.         // Enable 2x speed
  89.         BitS( *pUSART->UCSRnA, U2X0 );
  90.        
  91.         // Set Baud Rate
  92.     *pUSART->UBRRnH = (baudrate>>8) & 0x00FF;
  93.     *pUSART->UBRRnL = baudrate & 0x00FF;
  94.    
  95.     // Set frame format: asynchronous, 8data, no parity, 1stop bit
  96.         BitS( *pUSART->UCSRnC, UCSZ00 );        // 8 data
  97.         BitS( *pUSART->UCSRnC, UCSZ01 );
  98.         BitR( *pUSART->UCSRnB, UCSZ02 );
  99.         BitR( *pUSART->UCSRnB, UPM00 );         // no parity
  100.         BitR( *pUSART->UCSRnB, UPM01 );
  101.         BitS( *pUSART->UCSRnC, USBS0 );         // 1 stop bit
  102.    
  103.         pUSART->buffTXptrT = 0;
  104.         pUSART->buffTXptrH = 0;
  105.         pUSART->buffRXptrT = 0;
  106.         pUSART->buffRXptrH = 0;
  107.  
  108.        
  109.  
  110.         RXOn( pUSART );
  111.         TXOn( pUSART );
  112.  
  113. //      USARTPinEnable( pUSART );
  114.        
  115.         TXFlagClear( pUSART );
  116.         RXFlagClear( pUSART );
  117.  
  118.         TXIrqEnable( pUSART );
  119.         RXIrqEnable( pUSART );
  120. }
  121.  
  122.  
  123.  
  124. void USARTclose( struct USART *pUSART ){
  125.         TXIrqDisable( pUSART );
  126.         RXIrqDisable( pUSART );
  127.        
  128.         // Disable USART receiver and transmitter
  129.     RXOff( pUSART );
  130.         TXOff( pUSART );
  131.        
  132.         USARTPinDisable( pUSART );
  133. }
  134.  
  135.  
  136. unsigned char USARTread( struct USART *pUSART  ){
  137.         unsigned char data;
  138.  
  139.         while( isBufferRXEmpty( pUSART ) ){
  140.                 irqONOFF();
  141.         }      
  142.  
  143.         data = pUSART->buffRX[ pUSART->buffRXptrT++ & BUFF_RX_MASK ];
  144.        
  145.         RXIrqEnable( pUSART );
  146.  
  147.         return data;
  148. }
  149.  
  150. void USARTwrite( struct USART *pUSART, unsigned char data){
  151. //      Modo de espera, todos los caracteres son transmitidos.
  152. //      No puede ser usado en modo DEBUG con TRACE_VISUAL activado y tasas de transmision altas
  153.         while( isBufferTXFull( pUSART ) ){
  154.                 irqONOFF();
  155.         }
  156.         pUSART->buffTX[ pUSART->buffTXptrH++ & BUFF_TX_MASK ] = data;
  157.         TXIrqEnable( pUSART ); 
  158.        
  159. //      Modo SIN espera, No se garantiza la transmision de todos los caracteres
  160. //      Recomendado para Debug con TRACE VISUAL y tasas de transmision rapidas.
  161. //      if( !isBufferTXFull( pUSART ) ){
  162. //              pUSART->buffTX[ pUSART->buffTXptrH++ & BUFF_TX_MASK ] = data;
  163. //      }
  164. //      TXIrqEnable( pUSART );
  165. }
  166.  
  167.  
  168.  
  169. void USARTputc( struct USART *pUSART, char c ){
  170.         USARTwrite( pUSART, c );
  171. }
  172.  
  173.  
  174.  
  175. void USARTputs( struct USART *pUSART, char *s ){
  176.         while( *s != '\0' ){
  177.                 USARTwrite( pUSART, *s );
  178.                 s++;
  179.         }
  180. }
  181.  
  182. void USARTputInt( struct USART *pUSART, unsigned int i ){
  183.         USARTwrite( pUSART, toHex( (i>>12)&0x0F ) );
  184.         USARTwrite( pUSART, toHex( (i>>8 )&0x0F ) );
  185.         USARTwrite( pUSART, toHex( (i>>4 )&0x0F ) );
  186.         USARTwrite( pUSART, toHex( (i>>0 )&0x0F ) );
  187. }
« Última modificación: 29 de Octubre de 2010, 12:44:01 por jgpeiro06 »

Desconectado micro_cadaver

  • Colaborador
  • PIC24H
  • *****
  • Mensajes: 2102
    • blog microembebidos
Re: AVRPLC: Clon de un PLC de Siemens basado en FreeRTOS
« Respuesta #9 en: 29 de Octubre de 2010, 13:42:15 »
de lujoooooo, gracias por compartirlo !!!
a cosechar!!!... :P
pic32... ahi voy....
aguante el micro 16f84  !!!!

visita mi pagina: http://www.microembebidos.wordpress.com

Desconectado jgpeiro06

  • Colaborador
  • PIC18
  • *****
  • Mensajes: 276
Re: AVRPLC: Clon de un PLC de Siemens basado en FreeRTOS
« Respuesta #10 en: 31 de Octubre de 2010, 20:19:41 »
La tarea TaskXBee.

Esta tarea se encarga de controlar la comunicación con el modulo Xbee y de ejecutar los comandos recibidos. Se apoya en unas librerías para el Xbee
modificadas, originalmente creadas por Making Things.
Primero llama a la función XBee_GetPacket(), esta función devuelve un 1 si ha recibido un paquete desde el Xbee entero y correcto (se
verifica el tamaño del paquete y el CRC). Después se llama a la función XBee_ReadZBRX64Packet(), que simplemente extrae los parámetros del paquete
(dirección, datos, tamaño...) para facilitar su manejo. Luego comprueba el primer byte del campo de datos, este indica el tipo de comando.

Los comandos disponibles son:
   -CMD_PING:   Permite identificar todos los AVRPLCs que hay en una red. Cuando se recibe este comando simplemente se reenvía el mensaje al host.
               Hay dos tipos de PING, PING0 y PING1, que por el momento se comportan de la misma manera.
   -CMD_IO:      Permite conocer el estado de todas las entradas y salidas de un AVRPLC. Cuando se recibe este comando se lee(dentro de una región critica) el estado de todas las IOs, se "encapsulan" en un mensaje y se reenvían al host.
   -CMD_RC:      Permite controlar individualmente el estado de cualquier salida y entrada del AVRPLC. Cuando se recibe este mensaje se actualiza el valor de las IOs y de unas mascaras.
   -CMD_AWL:   Permite reprogramar el programa de AWL ejecutado por la VM. Cuando se recibe este mensaje se graba el contenido en la EEPROM. Hay 2 tipos de mensaje de AWL, AWL_START y AWL_BLOCK. El primero indica que se va a recibir un programa, el segundo contiene una parte del programa. Se necesitan 8 mensajes de tipo AWL_BLOCK para transmitir los 512 bytes de programa en AWL.


Comando AWL y la memoria EEPROM
Para que el sistema sea lo mas fiable posible, la VM debe tener siempre un programa que ejecutar. Para conseguir esto hay dos zonas de EEPROM, una contiene el programa que se esta ejecutando actualmente y la otra esta disponible para almacenar un nuevo programa. Solo cuando se graba con éxito el ultimo byte de un programa en AWL se cambian la zonas de AWL. La dirección 1024 de la EEPROM indica cual es la zona con el programa a ejecutar y cual esta disponible para almacenar un nuevo programa.
La tarea TaskXbee graba en la EEPROM, y la tarea TaskWDT lee. Ambas tareas están sincronizadas mediante dos semáforos. Esto permite dos cosas:
   1 Que no se acceda al recurso al mismo tiempo.
   2 Indicar a la tarea TaskWDT que hay un nuevo programa que aun no ha sido leído.


El comando RC
Cada una de las entradas y salidas de la VM, incluidas las analógicas, puede ser controlada desde un PC remoto. El comando RC lleva varios campos de "mascara" y "datos", y las entradas y salidas de la VM serán las indicadas en el PC según el valor en de la mascara.

Imagen del funcionamiento del control remoto.(Estructura del código y supuesta aplicación en el PC)

El mensaje RC contiene una estructura de tipo Remote Control, que tiene la forma:
Código: C
  1. struct REMOTECONTROL{ int DIn, AIn[4], DOut, AOut[4], DInMsk, AInMsk[4], DOutMsk, AOutMsk[4]; };

Código para leer las entradas:
Código: C
  1. RAM[I0] = INPUTS;
  2. RAM[I0] = ( RAM[I0] & ~RmtCntrl.DInMsk) | ( RmtCntrl.DIn & RmtCntrl.DInMsk );

Código para escribir las salidas
Código: C
  1. RAM[Q0] = ( RAM[Q0] & ~RmtCntrl.DOutMsk) | (RmtCntrl.DOut & RmtCntrl.DOutMsk);
  2. OUTPUTS = RAM[Q0];

Esto tiene varias ventajas como:
   -Forzar una salida del AVRPLC a un estado concreto.
   -Hacer creer al AVRPLC que un sensor tiene un valor concreto.
   -Trasladar todo el sistema a controlar a un simulador( LabView o similar)en un PC remoto, simulando el comportamiento de un programa para controlar un sistema, sin tener el sistema.

Ejemplo de un "sistema virtual":

Adjunto el codigo de los archivos TaskXbee.c y .h
Código: C
  1. #ifndef TASK_XBEE_H
  2. #define TASK_XBEE_H     1
  3.  
  4. #include <avr/eeprom.h>
  5.  
  6. #include "FreeRTOS.h"
  7. #include "task.h"
  8. #include "queue.h"
  9. #include "semphr.h"
  10.  
  11. #include "Task_AWL.h"
  12. #include "USARTDriver.h"
  13.  
  14. #define TASK_XBEE_STACK_SIZE    (configMINIMAL_STACK_SIZE + 75)
  15. #define TASK_XBEE_PRIORITY              (tskIDLE_PRIORITY + 1)
  16.  
  17. xTaskHandle xHandlevTaskXbee;
  18. xQueueHandle xQueueXbee;
  19.  
  20. void vTaskXbee( void *pvParameters );
  21.  
  22.  
  23. #endif


Código: C
  1. #ifndef TASK_XBEE_C
  2. #define TASK_XBEE_C     1
  3.  
  4. #include <stdlib.h>
  5. #include "Task_Xbee.h"
  6. #include "Task_AWL.h"
  7. #include "Utils.h"
  8.  
  9. #include "Xbee.h"
  10.  
  11. #define         CMD_PING0       0
  12. #define         CMD_PING1       1
  13. #define         CMD_AWL         2
  14. #define         CMD_IO          3
  15. #define         CMD_RC          4
  16.  
  17. #define         CMD_PING_LNGTH          1
  18. #define         CMD_AWL_START_LNGTH     2
  19. #define         CMD_AWL_BLOCK_LNGTH     (2+ROM_SIZE/8)
  20. #define         CMD_IO_LNGTH            1
  21. #define         CMD_RC_LNGTH            (sizeof(struct REMOTECONTROL) + 1)
  22.  
  23. #define         CMD_PING_RSPNS_LNGTH            1
  24. #define         CMD_AWL_START_RSPNS_LNGTH       2
  25. #define         CMD_AWL_BLOCK_RSPNS_LNGTH       2
  26. #define         CMD_IO_RSPNS_LNGTH                      (C0 + 1)
  27. #define         CMD_RC_RSPNS_LNGTH                      1
  28.  
  29.  
  30. extern unsigned char RAM[RAM_SIZE];             // declared in Task_AWL.c
  31. extern struct REMOTECONTROL RmtCntrl;   // declared in Task_AWL.c
  32.  
  33. static XBeePacket rxPacket,txPacket;    // If possible, packets must be global variable because sizeof(XBeePacket) = 110 bytes.
  34. static uint64 srcAddr;
  35. //static uint8 sigstren;
  36. static uint8 *rxPlayload, *txPlayload;
  37. static uint8 length;
  38. static int i;
  39. static unsigned char blocks = 0;
  40. static unsigned int eepromOffset;
  41.  
  42. // HardwareTestProgram. this program only copy inputs to outputs (analog and digital).
  43. static const char HardwareTestProgram[] = { 1,222,0,64,0,0,0,10,0,1,222,0,64,0,6,0,16,0,1,222,0,64,0,8,0,18,0,0,74,74 };
  44. // VoidProgram. while(1);
  45. static const char VoidProgram[] = { 67,0,0,74,74 };
  46.  
  47.  
  48. /*-----------------------------------------------------------*/
  49.  
  50.  
  51. void vTaskXbee( void * pvParameters ){
  52.  
  53. #if( INCLUDE_uxTaskGetStackHighWaterMark == 1 )
  54.         int freeStack = uxTaskGetStackHighWaterMark( NULL );
  55. #endif
  56.  
  57.         if( pdTRUE == xSemaphoreTake( xSemaphoreAWLTail, portMAX_DELAY) ){
  58.                
  59. #if( __DEBUG__ == 1 )  
  60.                 eeprom_write_block( HardwareTestProgram, &eeprom[EE_BLOCK_A], sizeof( HardwareTestProgram ) );
  61.                 eeprom_write_block( HardwareTestProgram, &eeprom[EE_BLOCK_B], sizeof( HardwareTestProgram ) );
  62.                 eeprom_write_byte( &eeprom[EE_FLAG], EE_FLAG_A );
  63. #else
  64.                 eepromOffset = eeprom_read_byte( &eeprom[EE_FLAG] );
  65.                 if( eepromOffset != EE_FLAG_A && eepromOffset != EE_FLAG_B ){
  66.                         eeprom_write_block( VoidProgram, &eeprom[EE_BLOCK_A], sizeof( VoidProgram ) );
  67.                         eeprom_write_block( VoidProgram, &eeprom[EE_BLOCK_B], sizeof( VoidProgram ) );
  68.                         eeprom_write_byte( &eeprom[EE_FLAG], EE_FLAG_A );
  69.                 }
  70. #endif
  71.                
  72.                 xSemaphoreGive( xSemaphoreAWLHead );
  73.         }
  74.  
  75.  
  76.         XBee_SetActive( 1 );
  77.         XBeeConfig_SetPacketApiMode( MODE_API2 );
  78.  
  79.         blocks = 0;
  80.         while(1){
  81.                 vTaskDelay(10);
  82.  
  83.                 XBee_ResetPacket( &rxPacket );
  84.                 if( XBee_GetPacket( &rxPacket, 0 ) ) {
  85.  
  86.                         if( XBee_ReadZBRX64Packet( &rxPacket, &srcAddr, NULL, NULL, &rxPlayload, &length  ) ){
  87.                                
  88.                                 if( 0 < length ){
  89.  
  90.                                         switch( rxPlayload[0] ){
  91.  
  92.                                                 case CMD_PING0: // PING
  93.                                                 case CMD_PING1: // PING
  94.                                                         if( length == CMD_PING_LNGTH ){
  95.                                                                 XBee_CreateZBTX64Packet( &txPacket, 0, srcAddr, ZIGBEE_NET_BROADCAST, 0, 0, &rxPlayload[0], CMD_PING_RSPNS_LNGTH );
  96.                                                                 XBee_SendPacket( &txPacket, CMD_PING_RSPNS_LNGTH );
  97.                                                         }
  98.                                                         break;
  99.  
  100.                                                 case CMD_AWL:
  101.                                                         if( length == CMD_AWL_START_LNGTH ){
  102.                                                                 if( rxPlayload[1] == 0xFF ){
  103.                                                                         blocks = 0;
  104.                                
  105.                                                                         XBee_CreateZBTX64Packet( &txPacket, 0, srcAddr, ZIGBEE_NET_BROADCAST, 0, 0, &rxPlayload[0], CMD_AWL_START_RSPNS_LNGTH );
  106.                                                                         XBee_SendPacket( &txPacket, CMD_AWL_START_RSPNS_LNGTH );              
  107.                                                                 }
  108.                                                         }else if( length == CMD_AWL_BLOCK_LNGTH ){
  109.                                                                 if( 0x00 <= rxPlayload[1] && rxPlayload[1] <= 0x07 ){
  110.                                                                         if( pdTRUE == xSemaphoreTake( xSemaphoreAWLTail, 0 ) ){
  111.                                        
  112.                                                                                 if( eeprom_read_byte( &eeprom[EE_FLAG] ) == EE_FLAG_A )
  113.                                                                                         eepromOffset = EE_BLOCK_B;
  114.                                                                                 else
  115.                                                                                         eepromOffset = EE_BLOCK_A;
  116.                                                                        
  117.                                                                                 eeprom_write_block ( &rxPlayload[2], &eeprom[ rxPlayload[1] * 64 + eepromOffset ], 64 );
  118.                                                                        
  119.                                                                                 BitS( blocks, rxPlayload[1] );
  120.                                                                        
  121.                                                                                 if( blocks == 0xFF ){
  122.                                                                                         blocks = 0;
  123.                                                                                         if(     eeprom_read_byte( &eeprom[EE_FLAG] ) == EE_FLAG_A )
  124.                                                                                                 eeprom_write_byte( &eeprom[EE_FLAG], EE_FLAG_B );
  125.                                                                                         else
  126.                                                                                                 eeprom_write_byte( &eeprom[EE_FLAG], EE_FLAG_A );
  127.                                                                                
  128.                                                                                         xSemaphoreGive( xSemaphoreAWLHead );
  129.                                                                                 }else{
  130.                                                                                         xSemaphoreGive( xSemaphoreAWLTail );
  131.                                                                                 }
  132.                                                                
  133.                                                                                 XBee_CreateZBTX64Packet( &txPacket, 0, srcAddr, ZIGBEE_NET_BROADCAST, 0, 0, &rxPlayload[0], CMD_AWL_BLOCK_RSPNS_LNGTH );
  134.                                                                                 XBee_SendPacket( &txPacket, CMD_AWL_BLOCK_RSPNS_LNGTH );              
  135.                                                                         }
  136.                                                                 }
  137.                                                         }
  138.                                                         break;
  139.  
  140.                                                 case CMD_IO: // IO request
  141.                                                         if( length == CMD_IO_LNGTH ){
  142.                                                                 txPlayload = txPacket.zbtx64.data;
  143.                                                                 txPlayload[0] = rxPlayload[0];
  144.                                                
  145.                                                                 taskENTER_CRITICAL();
  146.                                                                 for( i = 0 ; i < C0 ; i++ ){
  147.                                                                         txPlayload[i+1] = RAM[i];
  148.                                                                 }
  149.                                                                 taskEXIT_CRITICAL();
  150.                                                
  151.                                                                 XBee_CreateZBTX64Packet( &txPacket, 0, srcAddr, ZIGBEE_NET_BROADCAST, 0, 0, &txPlayload[0], CMD_IO_RSPNS_LNGTH );
  152.                                                                 XBee_SendPacket( &txPacket, CMD_IO_RSPNS_LNGTH );
  153.                                                         }
  154.                                                         break;
  155.  
  156.                                                 case CMD_RC: // Remote control receive
  157.                                                         if( length == CMD_RC_LNGTH ){
  158.                                                                 taskENTER_CRITICAL();
  159.                                                                 RmtCntrl = ReCast( struct REMOTECONTROL, rxPlayload[1] );
  160.                                                                 taskEXIT_CRITICAL();
  161.  
  162.                                                                 XBee_CreateZBTX64Packet( &txPacket, 0, srcAddr, ZIGBEE_NET_BROADCAST, 0, 0, &rxPlayload[0], CMD_RC_RSPNS_LNGTH );
  163.                                                                 XBee_SendPacket( &txPacket, CMD_RC_RSPNS_LNGTH );
  164.                                                         }
  165.                                                         break;
  166.  
  167.                                                 default:
  168.                                                         break;
  169.  
  170.                                         }
  171.                                 }
  172.                         }
  173.                 }
  174.         }
  175. }
  176.  
  177.  
  178. #endif

Desconectado LABmouse

  • Moderador Local
  • DsPIC30
  • *****
  • Mensajes: 3575
    • Juntos es mejor
Re: AVRPLC: Clon de un PLC de Siemens basado en FreeRTOS
« Respuesta #11 en: 31 de Octubre de 2010, 20:55:00 »
HOla, simplemente grandioso cada uno de los proyectos que enfrentas y mas grande eres al compartir con todos tus avances!!

Felicitaciones!

Hace un tiempo estaba intentando reconocer y documentar el protocolo del PLC Siemens con el PC al estar conectado por RS485.  Diseñe un pequeño espía USB con el cual veo los datos que van y vienen entre PC-PLC, pero es tan arduo el trabajo que quedo un poco abandonado tal proyecto. Mi intención era en su momento lograr que el Microcontrolador se identificara ante el PC tal cual como lo hace el PLC, que permitiera hasta debugger en tiempo real.

Dime logras que el microcontrolador cumpla con esa tarea?, existe algún documento que explique la serie de comandos que envia el Step7 al PLC para controlarlo y hacer debugger?.

SALUDOS!

Desconectado ema

  • Colaborador
  • PIC24H
  • *****
  • Mensajes: 1078
Re: AVRPLC: Clon de un PLC de Siemens basado en FreeRTOS
« Respuesta #12 en: 31 de Octubre de 2010, 21:18:49 »
Excelente!  :-/ :-/  gracias por compartirlo.
Me apunto al hilo!

Saludos

Desconectado Nocturno

  • Administrador
  • DsPIC33
  • *******
  • Mensajes: 18286
    • MicroPIC
Re: AVRPLC: Clon de un PLC de Siemens basado en FreeRTOS
« Respuesta #13 en: 01 de Noviembre de 2010, 02:33:55 »
Hola jgpeiro, entiendo tu diagrama "Ejemplo de un sistema virtual" pero no he entendido bien cómo consigues eso mediante máscaras.

Desconectado jgpeiro06

  • Colaborador
  • PIC18
  • *****
  • Mensajes: 276
Re: AVRPLC: Clon de un PLC de Siemens basado en FreeRTOS
« Respuesta #14 en: 01 de Noviembre de 2010, 08:05:20 »
Citar
entiendo tu diagrama "Ejemplo de un sistema virtual" pero no he entendido bien cómo consigues eso mediante máscaras
Mira la imagen "Imagen del funcionamiento del control remoto". Las entradas que lee la VM estan en la variable RAM[I0]. El valor de esta variable sera el de los pines de entrada del AVRPLC o el valor recibido en el ultimo conmando CMD_RC desde el PC. Para seleccionar uno u otro el comando CMD_RC tambien tiene una mascara( que quizas se podria llamar seleccion). Si el bit X de la mascara vale 0, la VM leera el bit X real, si vale 1, la VM leera el bit enviado por el PC. Lo mismo se puede hacer con las salidas, si la mascara vale 0, el valor de las salidas del AVRPLC sera el de la VM, si la mascara vale 1 sera el del PC.
Si el PC puede conocer el estado de todas las salidas del AVRPLC con el comando CMD_IO, y forzar cualquier entrada del AVRPLC a un estado determiando con el comando CMD_RC, se puede simular un sistema dentro del PC y hacer que el AVRPLC lo controle.


 

anything