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

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

Desconectado Nocturno

  • Administrador
  • DsPIC33
  • *******
  • Mensajes: 18286
    • MicroPIC
Re: AVRPLC: Clon de un PLC de Siemens basado en FreeRTOS
« Respuesta #15 en: 01 de Noviembre de 2010, 08:14:42 »
Perfectamente claro, gracias

Desconectado droky

  • Colaborador
  • PIC24F
  • *****
  • Mensajes: 628
    • Diseños radikales libres
Re: AVRPLC: Clon de un PLC de Siemens basado en FreeRTOS
« Respuesta #16 en: 01 de Noviembre de 2010, 08:34:29 »
 :shock: Guau!
Otra joya de programación y hardware a la que nos tienes acostumbrados.
Me apunto yo tambien.Salu2
Yo... he visto cosas que vosotros no creeríais... atacar naves en llamas más allá de Orión, he visto rayos C brillar en la oscuridad cerca de la puerta Tannhäuser.
Todos esos momentos se perderán en el tiempo como lágrimas en la lluvia.
Es hora de morir.

Si me buscas en twitter, me tienes por @radikaldesig

Desconectado Suky

  • Moderadores
  • DsPIC33
  • *****
  • Mensajes: 6758
Re: AVRPLC: Clon de un PLC de Siemens basado en FreeRTOS
« Respuesta #17 en: 01 de Noviembre de 2010, 11:23:25 »
Me interesa la respuesta que le puedas dar a LabMouse, logras que el Step 7 tome a tu hardware como un PLC Siemmens? De donde podemos informarnos al respecto?


Saludos!
No contesto mensajes privados, las consultas en el foro

Desconectado jgpeiro06

  • Colaborador
  • PIC18
  • *****
  • Mensajes: 276
Re: AVRPLC: Clon de un PLC de Siemens basado en FreeRTOS
« Respuesta #18 en: 01 de Noviembre de 2010, 15:58:52 »
Citar
...el Microcontrolador se identificara ante el PC tal cual como lo hace el PLC, que permitiera hasta debugger en tiempo real....logras que el microcontrolador cumpla con esa tarea?.
Citar
logras que el Step 7 tome a tu hardware como un PLC Siemmens?
No, el micro no se comunica directamente con el STEP 7. El proyecto creado con el STEP 7 se exporta a un archivo .AWL (archivo de texto), que luego es compilado y demas, pero este proyecto no tiene ningún camino de retorno de información hacia el STEP 7.

Citar
existe algún documento que explique la serie de comandos que envia el Step7 al PLC para controlarlo y hacer debugger?
Citar
De donde podemos informarnos al respecto?
Tampoco se de ningún documento que explique como se comunica el PLC con el STEP 7. Por los que he visto yo, Siemens proporciona mucha ayuda, pero toda tiene como objetivo que uses correctamente sus PLCs, de el funcionamiento interno hay lo minimo imprescindible.

PD: menos mal que Suky ha insistido en la pregunta de LabMouse, xq se me habia pasado responder....


Desconectado Suky

  • Moderadores
  • DsPIC33
  • *****
  • Mensajes: 6758
Re: AVRPLC: Clon de un PLC de Siemens basado en FreeRTOS
« Respuesta #19 en: 01 de Noviembre de 2010, 16:50:51 »
Muchas gracias por la atención! Esperamos tus avances, ver como trabajas da gusto  ;-)


Saludos!
No contesto mensajes privados, las consultas en el foro

Desconectado jgpeiro06

  • Colaborador
  • PIC18
  • *****
  • Mensajes: 276
Re: AVRPLC: Clon de un PLC de Siemens basado en FreeRTOS
« Respuesta #20 en: 02 de Noviembre de 2010, 15:11:50 »
La tarea TaskWDT
Esta tarea es la más sencilla de las tres. Se encarga de leer el programa de la EEPROM, arrancar la VM y de verificar que funcionando correctamente.

Funcionamiento
Primero comprueba si hay un nuevo programa de AWL para ejecutar, si es asi lo carga en la ROM de la VM y crea la tarea TaskAWL.
Si no hay ningun programa nuevo mira si ya se esta ejecutando uno (running = 1), si es asi, intenta adquirir el semaforo WDT. Este semaforo debe ser liberado por la VM cada vez que ejecute todo el programa. Si esto no ocurre en un tiempo de 20mS se considerara que el programa de AWL se ha colgado y se parará la VM.
Si no hay ningun programa de AWL corriendo se espera un tiempo para ejeccutar otras tareas.

Para facilitar la localizacion de posibles errores en la VM, algunas de las causas que hacen que la VM se bloquee escriben un valor concreto en la variable AWL_STATUS. De momento esta variable solo se lee al depurar el codigo, pero siempre se podria enviar el codigo de error via Xbee al PC para informar del error.

Los posibles errores(definidos en el archivo Task_AWL.h) son:
   PC_OUT_OF_RANGE
   STACK_PC_OVERFLOW
   STACK_PC_UNDERFLOW
   STACK_LOCALS_OVERFLOW
   STACK_LOCALS_UNDERFLOW


El código del archivo Task_WDT .h y .c
Código: C
  1. #ifndef TASK_WDT_H
  2. #define TASK_WDT_H      1
  3.  
  4. #include "FreeRTOS.h"
  5. #include "task.h"
  6. #include "queue.h"
  7. #include "semphr.h"
  8.  
  9. #include "utils.h"
  10.  
  11. #define TASK_WDT_STACK_SIZE             (configMINIMAL_STACK_SIZE + 50)
  12. #define TASK_WDT_PRIORITY               (tskIDLE_PRIORITY + 3)
  13.  
  14. #define WDT_TIMEOUT_MS                  TASK_AWL_HZ     //(50/portTICK_RATE_MS)
  15.  
  16. #define LEDAWLRunEnable()               ( BitS( DDRL, 1 ) )
  17. #define LEDAWLRunDisable()              ( BitR( DDRL, 1 ) )
  18. #define LEDAWLRunON()                   ( BitS( PORTL, 1 ) )
  19. #define LEDAWLRunOFF()                  ( BitR( PORTL, 1 ) )
  20.  
  21. xTaskHandle xHandlevTaskWDT;
  22. xSemaphoreHandle xSemaphoreWDT;
  23.  
  24. void vTaskWDT( void * pvParameters );
  25.  
  26. #endif

Código: C
  1. #ifndef TASK_WDT_C
  2. #define TASK_WDT_C      1
  3.  
  4. #include "Task_Xbee.h"
  5. #include "Task_AWL.h"
  6.  
  7. #define WDT_TIMEOUT (2*configTICK_RATE_HZ/TASK_AWL_HZ)  //10/portTICK_RATE_MS
  8.  
  9. extern unsigned char AWL_STATUS;        // Declared in Task_AWL.c
  10. extern unsigned char ROM[ROM_SIZE];     // Declared in Task_AWL.c
  11.  
  12. static int eepromOffset = 0;
  13. static char running = 0;
  14.  
  15.  
  16. void vTaskWDT( void * pvParameters ){
  17.  
  18. #if( INCLUDE_uxTaskGetStackHighWaterMark == 1 )
  19.         int freeStack = uxTaskGetStackHighWaterMark( NULL );
  20. #endif
  21.         LEDAWLRunEnable();
  22.         LEDAWLRunOFF();
  23.        
  24.         running = 0;
  25.  
  26.         while(1){
  27.                
  28.                 if( pdTRUE == xSemaphoreTake( xSemaphoreAWLHead, 0 ) ){
  29.                         LEDAWLRunOFF();
  30.                         LEDAWLFreqLowOFF();
  31.  
  32.                         if( NULL != xHandlevTaskAWL ){
  33.                                 vTaskDelete( xHandlevTaskAWL );
  34.                         }
  35.  
  36.                         if(     eeprom_read_byte( &eeprom[EE_FLAG] ) == EE_FLAG_A )
  37.                                 eepromOffset = EE_BLOCK_A;
  38.                         else
  39.                                 eepromOffset = EE_BLOCK_B;
  40.  
  41.                         eeprom_read_block ( ROM, &eeprom[ 0 + eepromOffset ], ROM_SIZE );
  42.                        
  43.                         xSemaphoreGive( xSemaphoreAWLTail );
  44.                                
  45.                         do{    
  46.                                 vTaskDelay( 100/portTICK_RATE_MS );     // Wait for free memory.
  47.                         }while( pdPASS != xTaskCreate( vTaskAWL, ( signed char * ) "AWL", TASK_AWL_STACK_SIZE, NULL,  TASK_AWL_PRIORITY, &xHandlevTaskAWL ) );
  48.                        
  49.                         running = 1;
  50.                         LEDAWLRunON();
  51.                
  52.                 }else if( running == 1 && pdTRUE != xSemaphoreTake( xSemaphoreWDT, WDT_TIMEOUT ) ){
  53.                         running = 0;
  54.                         LEDAWLRunOFF();
  55.                         LEDAWLFreqLowON();
  56.  
  57.                         if( NULL != xHandlevTaskAWL ){
  58.                                 vTaskDelete( xHandlevTaskAWL );
  59.                         }
  60.                
  61.                 }else{
  62.                         vTaskDelay( 100/portTICK_RATE_MS );                    
  63.                 }
  64.  
  65.         }
  66. }
  67.  
  68. #endif


El archivo utils.h

Este archivo contiene definidas varias macros genéricas que son utilizadas en muchas partes del proyecto. Las macros son un arma de doble filo, proporcionan una manera sencilla para simplificar y hacer mas legible el código, pero también son "generadoras" de muchos bugs si no son usadas correctamente. Por este ultimo motivo las macros se desaconsejan en C++ y no están permitidas en Java.

Basicamente el archivo utils.h proporciona macros para tratamiento de datos a nivel de bit y macros para conversión forzada de tipos.
   Manipulacion de bits
   Son una alternativa mas optimizada a las macros para manipulacion de bits basadas en _BV() que vienen en la libreria libc del compilador WinAVR. Estas funciones se basan en el operador de desplazamiento (<<) para crear mascaras como 0x08, 0x04... a partir del 0x01. Son relativamente lentas ya que el AVR solo dispone de la operación de desplazar 1 bit.
   Se basa en definir un array con todos los desplazamientos como valores. Esto es en general mas rapido, y el coste de ROM (ya que el array esta declarado como const) es despreciable.
   Las macros son:
      BitT( byte, bit )       Devuelve 1 si el bit es 1
      BitS( byte, bit )       Pone el bit a 1
      BitR( byte, bit )       Pone el bit a 0
      BitV( byte, bit, v )   Pone el bit a 1 o 0 en funcion del valor de v

Conversión forzada de tipos
   Es una macro muy util para sacar y meter tipos de datos basicos en estructuras y arrays. Consiste en hacer que el compilador "pierda" el tipo del dato convirtiendolo a un puntero a void y despues convertirlo al dato necesario. Este operador existe en C++ y se llama reinterpret_cast.
   Una curiosidad de esta macro es que permite que el dato sea destino, es decir, que se puede utilizar a la izq de una expresion de asignacion.

Un ejemplo:
Código: C
  1. int8 array[4];
  2. int32 value1;
  3. struct strct{ int16 v1; int8 v2, v3; } value2;
  4.  
  5. value1 = 0x12345678;                    // value1 = 0x12345678
  6. ReCast( int32, array[0] ) = value1;     // array[] = { 0x12, 0x34, 0x56, 0x78 }
  7. value2 = ReCast( int32, array[0] );     // value2 = { 0x1234, 0x56, 0x78 }

Los bugs en las macros:
Como he dicho antes, las macros pueden generar bugs si no se escriben correctamente. Estas son algunas reglas que siempre sigo:

   -Todos las "variables" usadas en la definición de la macro van entre paréntesis. La "variable" que acepta la macro no será pasada por valor o por referencia como en una función, sera sustituida por preprocesador, y esta expresion puede verse modificada por la precedencia de los operadores. Ejemplo:
Código: C
  1. #define producto( a, b )        a*b     // en lugar de ((a)*(b))
  2. x = producto(1+2,3+4);          // El resultado esperado seria 21, pero la macro nos devuelve 11.

   -La macro o el define debe encontrarse encerrado en un paréntesis. Ejemplo:
Código: C
  1. #define siete   5+2                     // En lugar de (5+2)
  2. x = siete*2             // El resultado esperado seria 14, pero la macro nos devuelve 9

   -No se pueden utilizar operadores de pre/post incremento/decremento al llamar a una macro. Si la macro utiliza la "variable" varias veces, será modificada por el operador varias veces. Ejemplo:
Código: C
  1. #define min( a, b )     (a<b)?a:b;
  2. v1 = 3;
  3. v2 = 10;
  4. x = producto(v1++,v2);          // El resultado esperado seria x = 3 y v1 = 4, pero la macro deja x = 4 y v1 = 5.

   -Si la macro o define contiene varias instrucciones, todas deben estar dentro de un bloque de instrucciones({}). Ejemplo:
Código: C
  1. #define borra( a, b )   a = 0; b = 0;   // En lugar de {(a) = 0; (b) = 0;}
  2. if(0)
  3.         borra(a,b);     // b = 0 siempre se ejecuta (aunque la condicion no se cumpla )


El codigo del archivo utils.h
Código: C
  1. #ifndef UTILS_H
  2. #define UTILS_H 1
  3.  
  4. static const unsigned char BitsMask[8] = {0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
  5. #define BitT( byte, bit )               (((byte) & BitsMask[bit])!=0)
  6. #define BitS( byte, bit )               ((byte) |= BitsMask[bit])
  7. #define BitR( byte, bit )               ((byte) &= ~BitsMask[bit])
  8. #define BitV( byte, bit, v )    ((v)?BitS(byte,bit):BitR(byte,bit))
  9.  
  10. #define ReCast( newtype, var )  (*((newtype *) (void *) &(var)))        // Make sure CPU can byte addressing.
  11.  
  12. // Esta macro se puede usar si el RTOS nunca desactiva las IRQs globales
  13. #define irqONOFF()
  14.  
  15. /*
  16. // Esta macro se puede usar si el RTOS desactiva las IRQs globales en las regiones criticas
  17. #define irqONOFF() {                    \
  18.         unsigned char tmp1 = SREG;      \
  19.         cli();                                  \
  20.         unsigned char tmp2 = TIMSK1;    \
  21.         TIMSK1 &= ~(1<<OCIE1A);         \
  22.         sei();                                  \
  23.         asm volatile ( "nop" );                 \
  24.         asm volatile ( "nop" );                 \
  25.         cli();                                  \
  26.         TIMSK1 = tmp2;                          \
  27.         SREG = tmp1;                            \
  28. }
  29. */
  30.  
  31. #define toHex(x)                                (((x)<=9)?(x)+'0':(x)-10+'A')
  32.  
  33. #if( __USE_LED_RUN__ == 1 )
  34.         #define LEDRunEnable()          ( BitS( DDRL, 0 ) )
  35.         #define LEDRunDisable()         ( BitR( DDRL, 0 ) )
  36.         #define LEDRunON()                      ( BitS( PORTL, 0 ) )
  37.         #define LEDRunOFF()                     ( BitR( PORTL, 0 ) )
  38. #else
  39.         #define LEDRunEnable() 
  40.         #define LEDRunDisable()
  41.         #define LEDRunON()             
  42.         #define LEDRunOFF()            
  43. #endif
  44.  
  45.  
  46. #endif
« Última modificación: 02 de Noviembre de 2010, 18:41:52 por jgpeiro06 »

Desconectado jgpeiro06

  • Colaborador
  • PIC18
  • *****
  • Mensajes: 276
Re: AVRPLC: Clon de un PLC de Siemens basado en FreeRTOS
« Respuesta #21 en: 03 de Noviembre de 2010, 10:58:32 »
Las opciones de depuración del RTOS

Las aplicaciones concurrentes son más difíciles de depurar, por esto es muy útil un método para "trazar" el funcionamiento interno de todas las tareas.
Además la memoria estática hay que repartirla para todas las tareas, y cada tarea debe disponer suficiente memoria para sus variables locales y su pila de llamadas.
FreeRTOS proporciona un conjunto de herramientas tanto para "trazar" el funcionamiento del programa como para controlar la memoria utilizada por una tarea.

Control del stack
La función vApplicationStackOverflowHook está declarada pero no definida en el RTOS. El RTOS solo la llama cuando se produce un stackOverflow. La podemos definir para que informe del error y bloquee/reinicie el sistema.
La función uxTaskGetStackHighWaterMark permite conocer el tamaño del stack aun NO usado por una tarea.

Control de la ejecucion
Las trace macros son un conjunto de macros insertadas por todo el kernel de FreeRTOS que podemos definir a nuestro gusto para generar un evento externo que nos sirva de testigo.
   Como las macros cargan considerablemente al sistema se han definido 3 tipos de macros:
      __USART0_TARCE__ = 0   Desactivadas. El sistema funciona al 100%
      __USART0_TARCE__ = 1   Modo rápido. Los datos se envían en binario. Es necesario un programa para la representación en el PC.
      __USART0_TARCE__ = 2   Modo visual. Los datos se envían en ASCII. Se puede leer en un hyperterminal.

Este es un tramo de una traza que se puede visualizar:
...
TSIIDLE TCK0057
TSIIDLE TCK0058
TSIAWL QSFswdt TDU
TSIIDLE TCK0059
TSIIDLE TCK005A
TSIWDT QRFsawlh QRswdt TDY      
...

Que más o menos significa lo siguiente:
...
entra en la tarea IDLE, incrementa el TICK
entra en la tarea IDLE, incrementa el TICK
entra en la tarea AWL, libera el semáforo sWDT, espera un tiempo
entra en la tarea IDLE, incrementa el TICK
entra en la tarea IDLE, incrementa el TICK
entra en la tarea WDT, intenta tomar sin éxito el semaforo sAWL, toma el semaforo sWDT, espera un tiempo
...
Esta traza corresponde con el funcionamiento esperado de la tarea WDT.


El codigo del archivo TraceMacros.h

Código: C
  1. // TraceMacros.h
  2. #ifndef TRACE_MACROS_H
  3. #define TRACE_MACROS_H 1
  4.  
  5. #include "UsartDriver.h"
  6.  
  7. #if( __USART0_TARCE__ == 2 )
  8. //Called during the tick interrupt.
  9. #define traceTASK_INCREMENT_TICK( tick ){       \
  10.         USARTputs( &USART0, " TCK" );                           \
  11.         USARTputInt( &USART0, tick );                   }
  12.  
  13. //Called before a new task is selected to run. At this point pxCurrentTCB contains the handle of the task about to leave the Running state.
  14. //#define traceTASK_SWITCHED_OUT(){    
  15. //USARTputs( &USART0, "TSK_SWTCHD_OUT\t" );     }
  16.  
  17. //Called after a task has been selected to run. At this point pxCurrentTCB contains the handle of the task about to enter the Running state.
  18. #define traceTASK_SWITCHED_IN(){        \
  19.         USARTputs( &USART0, "\n\rTSI" );                \
  20.         USARTputs( &USART0, (char*)pxCurrentTCB->pcTaskName );  }
  21.         //USARTputInt( &USART0, pxCurrentTCB ); }
  22.  
  23.  
  24. //Indicates that the currently executing task is about to block following an attempt to read from an empty queue, or an attempt to 'take' an empty semaphore or mutex.
  25. #define traceBLOCKING_ON_QUEUE_RECEIVE(pxQueue){        \
  26.         USARTputs( &USART0, " BQR" );                                           \
  27.         USARTputs( &USART0, (char*)getQueueName(pxQueue) );     }
  28. //      USARTputInt( &USART0, (unsigned int)pxQueue );                  }
  29.  
  30. //Indicates that the currently executing task is about to block following an attempt to write to a full queue.
  31. #define traceBLOCKING_ON_QUEUE_SEND(pxQueue){           \
  32.         USARTputs( &USART0, " BQS" );                                           \
  33.         USARTputs( &USART0, (char*)getQueueName(pxQueue) );     }
  34.  
  35. //Called from within xSemaphoreGiveRecursive().
  36. //#define       traceGIVE_MUTEX_RECURSIVE(pxMutex)     
  37.  
  38.  
  39. //Called from within xQueueCreate() if the queue was successfully created.
  40. #define traceQUEUE_CREATE( pxNewQueue ){        \
  41.         USARTputs( &USART0, " QC" );                            \
  42.         USARTputs( &USART0, (char*)getQueueName(pxNewQueue) );  }
  43.  
  44. //      Called from within xQueueCreate() if the queue was not successfully created due to there being insufficient heap memory available.
  45. #define traceQUEUE_CREATE_FAILED(){     \
  46.         USARTputs( &USART0, " QCF" );           }
  47.  
  48.  
  49. //#define       traceCREATE_MUTEX()     Called from within xSemaphoreCreateMutex() if the mutex was successfully created.
  50. //#define       traceCREATE_MUTEX_FAILED()      Called from within xSemaphoreCreateMutex() if the mutex was not successfully created due to there being insufficient heap memory available.
  51.  
  52. //#define       traceGIVE_MUTEX_RECURSIVE(pxMutex)      Called from within xSemaphoreGiveRecursive() if the mutex was successfully 'given'.
  53. //#define       traceGIVE_MUTEX_RECURSIVE_FAILED(pxMutex)       Called from within xSemaphoreGiveRecursive() if the mutex was not successfully given as the calling task was not the mutex owner.
  54. //#define       traceTAKE_MUTEX_RECURSIVE(pxMutex)      Called from within xQueueTakeMutexRecursive().
  55.  
  56.  
  57. //      Called from within xSemaphoreCreateCounting() if the semaphore was successfully created.
  58. #define traceCREATE_COUNTING_SEMAPHORE(){       \
  59.         USARTputs( &USART0, " CCS" );                           }
  60.  
  61.  
  62. //      Called from within xSemaphoreCreateCounting() if the semaphore was not successfully created due to insufficient heap memory being available.
  63. #define traceCREATE_COUNTING_SEMAPHORE_FAILED(){        \
  64.         USARTputs( &USART0, " CSF" );                                           }
  65.  
  66.  
  67. //      Called from within xQueueSend(), xQueueSendToFront(), xQueueSendToBack(), or any of the semaphore 'give' functions, when the queue send was successful.
  68. #define traceQUEUE_SEND(pxQueue){               \
  69.         USARTputs( &USART0, " QS" );                    \
  70.         USARTputs( &USART0, (char*)getQueueName(pxQueue) );     }
  71.  
  72.  
  73.  
  74. //      Called from within xQueueSend(), xQueueSendToFront(), xQueueSendToBack(), or any of the semaphore 'give' functions when the queue send operation failed due to the queue being full (after any block time that was specified).
  75. #define traceQUEUE_SEND_FAILED(pxQueue){        \
  76.         USARTputs( &USART0, " QSF" );                           \
  77.         USARTputs( &USART0, (char*)getQueueName(pxQueue) );     }
  78.  
  79.  
  80.  
  81. //      Called from within xQueueReceive() or any of the semaphore 'take' functions when the queue receive was successful.
  82. #define traceQUEUE_RECEIVE(pxQueue){    \
  83.         USARTputs( &USART0, " QR" );                    \
  84.         USARTputs( &USART0, (char*)getQueueName(pxQueue) );     }
  85.  
  86. //      Called from within xQueueReceive() or any of the semaphore 'take' functions when the queue receive operation failed because the queue was empty (after any block time that was specified).
  87. #define traceQUEUE_RECEIVE_FAILED( pxQueue ){   \
  88.         USARTputs( &USART0, " QRF" );                                   \
  89.         USARTputs( &USART0, (char*)getQueueName(pxQueue) );     }
  90.  
  91.  
  92.  
  93. //      Called from within xQueuePeek()
  94. //#define       traceQUEUE_PEEK()
  95. //#define       traceQUEUE_SEND_FROM_ISR(pxQueue)       Called from within xQueueSendFromISR() when the send operation was successful.
  96. //#define       traceQUEUE_SEND_FROM_ISR_FAILED(pxQueue)        Called from within xQueueSendFromISR() when the send operation failed due to the queue already being full.
  97. //#define       traceQUEUE_RECEIVE_FROM_ISR(pxQueue)    Called from within xQueueReceiveFromISR() when the receive operation was successful.
  98. //#define       traceQUEUE_RECEIVE_FROM_ISR_FAILED(pxQueue)     Called from within xQueueReceiveFromISR() when the receive operation failed due to the queue already being empty.
  99. //#define       traceQUEUE_DELETE(pxQueue)      Called from within vQueueDelete().
  100.  
  101.  
  102. //      Called from within xTaskCreate() when the task is successfully created.
  103. #define traceTASK_CREATE(pxTask){       \
  104.         USARTputs( &USART0, " TC" );            \
  105.         USARTputs( &USART0, (char*)pxTask->pcTaskName );        }
  106.  
  107.  
  108. //      Called from within xTaskCreate() when the task was not successfully created due to there being insufficient heap space available.
  109. #define traceTASK_CREATE_FAILED( pxNewTCB ){    \
  110.         USARTputs( &USART0, " TCF" );                           \
  111.         USARTputs( &USART0, (char*)pxNewTCB->pcTaskName );      }
  112.  
  113.  
  114. //      Called from within vTaskDelete().
  115. #define traceTASK_DELETE(pxTask){       \
  116.         USARTputs( &USART0, " TD" );            \
  117.         USARTputs( &USART0, (char*)pxTask->pcTaskName );        }
  118.  
  119.  
  120. //      Called from within vTaskDelayUntil().
  121. #define traceTASK_DELAY_UNTIL(){        \
  122.         USARTputs( &USART0, " TDU" );           }
  123.  
  124.  
  125. //      Called from within vTaskDelay().
  126. #define traceTASK_DELAY(){              \
  127.         USARTputs( &USART0, " TDY" );   }
  128.  
  129. //      Called from within vTaskPrioritySet().
  130. #define traceTASK_PRIORITY_SET(pxTask,uxNewPriority){   \
  131.         USARTputs( &USART0, " TPS" );                                                   \
  132.         USARTputs( &USART0, (char*)pxTask->pcTaskName );        }
  133.  
  134.  
  135. //      Called from within vTaskSuspend().
  136. #define traceTASK_SUSPEND(pxTask){      \
  137.         USARTputs( &USART0, " TSP" );           \
  138.         USARTputs( &USART0, (char*)pxTask->pcTaskName );        }
  139.  
  140.  
  141. //      Called from within vTaskResume().
  142. #define traceTASK_RESUME(pxTask){       \
  143.         USARTputs( &USART0, " TRS" );           \
  144.         USARTputs( &USART0, (char*)pxTask->pcTaskName );        }
  145.  
  146.  
  147. //      Called from within xTaskResumeFromISR().
  148. //#define       traceTASK_RESUME_FROM_ISR(pxTask){     
  149. //      USARTputs( &USART0, "TASK_RESUME_FROM_ISR\n\r" );       }
  150.  
  151. #elif( __USART0_TARCE__ == 1 )
  152.  
  153. //Called during the tick interrupt.
  154. #define traceTASK_INCREMENT_TICK( tick ){       \
  155.         USARTwrite( &USART0, 1 );                               \
  156.         USARTwrite( &USART0, (tick>>8)&0xFF );  \
  157.         USARTwrite( &USART0, (tick>>0)&0xFF );  }
  158.  
  159. //Called before a new task is selected to run. At this point pxCurrentTCB contains the handle of the task about to leave the Running state.
  160. //#define traceTASK_SWITCHED_OUT(){    
  161. //USARTputs( &USART0, "TSK_SWTCHD_OUT\t" );     }
  162.  
  163. //Called after a task has been selected to run. At this point pxCurrentTCB contains the handle of the task about to enter the Running state.
  164. #define traceTASK_SWITCHED_IN(){        \
  165.         USARTwrite( &USART0, 2 );               \
  166.         USARTwrite( &USART0, ((int)pxCurrentTCB>>8)&0xFF );     \
  167.         USARTwrite( &USART0, ((int)pxCurrentTCB>>0)&0xFF );     }
  168.        
  169.  
  170.  
  171. //Indicates that the currently executing task is about to block following an attempt to read from an empty queue, or an attempt to 'take' an empty semaphore or mutex.
  172. #define traceBLOCKING_ON_QUEUE_RECEIVE(pxQueue){        \
  173.         USARTwrite( &USART0, 3 );                                       \
  174.         USARTwrite( &USART0, ((int)pxQueue>>8)&0xFF );  \
  175.         USARTwrite( &USART0, ((int)pxQueue>>0)&0xFF );  }
  176.  
  177.  
  178. //Indicates that the currently executing task is about to block following an attempt to write to a full queue.
  179. #define traceBLOCKING_ON_QUEUE_SEND(pxQueue){           \
  180.         USARTwrite( &USART0, 4 );                                       \
  181.         USARTwrite( &USART0, ((int)pxQueue>>8)&0xFF );  \
  182.         USARTwrite( &USART0, ((int)pxQueue>>0)&0xFF );  }
  183.  
  184.  
  185. //Called from within xSemaphoreGiveRecursive().
  186. //#define       traceGIVE_MUTEX_RECURSIVE(pxMutex)     
  187.  
  188.  
  189. //Called from within xQueueCreate() if the queue was successfully created.
  190. #define traceQUEUE_CREATE( pxNewQueue ){        \
  191.         USARTwrite( &USART0, 5 );                               \
  192.         USARTputs( &USART0, (char*)getQueueName(pxNewQueue) );  \
  193.         USARTwrite( &USART0, ((int)pxNewQueue>>8)&0xFF );       \
  194.         USARTwrite( &USART0, ((int)pxNewQueue>>0)&0xFF );       }
  195.  
  196. //      Called from within xQueueCreate() if the queue was not successfully created due to there being insufficient heap memory available.
  197. #define traceQUEUE_CREATE_FAILED(){     \
  198.         USARTwrite( &USART0, 6 );               }
  199.  
  200. //#define       traceCREATE_MUTEX()     Called from within xSemaphoreCreateMutex() if the mutex was successfully created.
  201. //#define       traceCREATE_MUTEX_FAILED()      Called from within xSemaphoreCreateMutex() if the mutex was not successfully created due to there being insufficient heap memory available.
  202.  
  203. //#define       traceGIVE_MUTEX_RECURSIVE(pxMutex)      Called from within xSemaphoreGiveRecursive() if the mutex was successfully 'given'.
  204. //#define       traceGIVE_MUTEX_RECURSIVE_FAILED(pxMutex)       Called from within xSemaphoreGiveRecursive() if the mutex was not successfully given as the calling task was not the mutex owner.
  205. //#define       traceTAKE_MUTEX_RECURSIVE(pxMutex)      Called from within xQueueTakeMutexRecursive().
  206.  
  207.  
  208. //      Called from within xSemaphoreCreateCounting() if the semaphore was successfully created.
  209. #define traceCREATE_COUNTING_SEMAPHORE(){       \
  210.         USARTwrite( &USART0, 7 );                               }
  211.  
  212.  
  213. //      Called from within xSemaphoreCreateCounting() if the semaphore was not successfully created due to insufficient heap memory being available.
  214. #define traceCREATE_COUNTING_SEMAPHORE_FAILED(){        \
  215.         USARTwrite( &USART0, 8 );                                               }
  216.  
  217.  
  218. //      Called from within xQueueSend(), xQueueSendToFront(), xQueueSendToBack(), or any of the semaphore 'give' functions, when the queue send was successful.
  219. #define traceQUEUE_SEND(pxQueue){               \
  220.         USARTwrite( &USART0, 9 );                                       \
  221.         USARTwrite( &USART0, ((int)pxQueue>>8)&0xFF );  \
  222.         USARTwrite( &USART0, ((int)pxQueue>>0)&0xFF );  }
  223.  
  224.  
  225.  
  226.  
  227. //      Called from within xQueueSend(), xQueueSendToFront(), xQueueSendToBack(), or any of the semaphore 'give' functions when the queue send operation failed due to the queue being full (after any block time that was specified).
  228. #define traceQUEUE_SEND_FAILED(pxQueue){        \
  229.         USARTwrite( &USART0, 10 );                                      \
  230.         USARTwrite( &USART0, ((int)pxQueue>>8)&0xFF );  \
  231.         USARTwrite( &USART0, ((int)pxQueue>>0)&0xFF );  }
  232.  
  233.  
  234.  
  235.  
  236. //      Called from within xQueueReceive() or any of the semaphore 'take' functions when the queue receive was successful.
  237. #define traceQUEUE_RECEIVE(pxQueue){    \
  238.         USARTwrite( &USART0, 11 );                                      \
  239.         USARTwrite( &USART0, ((int)pxQueue>>8)&0xFF );  \
  240.         USARTwrite( &USART0, ((int)pxQueue>>0)&0xFF );  }
  241.  
  242.  
  243. //      Called from within xQueueReceive() or any of the semaphore 'take' functions when the queue receive operation failed because the queue was empty (after any block time that was specified).
  244. #define traceQUEUE_RECEIVE_FAILED( pxQueue ){   \
  245.         USARTwrite( &USART0, 12 );                                      \
  246.         USARTwrite( &USART0, ((int)pxQueue>>8)&0xFF );  \
  247.         USARTwrite( &USART0, ((int)pxQueue>>0)&0xFF );  }
  248.  
  249.  
  250.  
  251.  
  252. //      Called from within xQueuePeek()
  253. //#define       traceQUEUE_PEEK()
  254. //#define       traceQUEUE_SEND_FROM_ISR(pxQueue)       Called from within xQueueSendFromISR() when the send operation was successful.
  255. //#define       traceQUEUE_SEND_FROM_ISR_FAILED(pxQueue)        Called from within xQueueSendFromISR() when the send operation failed due to the queue already being full.
  256. //#define       traceQUEUE_RECEIVE_FROM_ISR(pxQueue)    Called from within xQueueReceiveFromISR() when the receive operation was successful.
  257. //#define       traceQUEUE_RECEIVE_FROM_ISR_FAILED(pxQueue)     Called from within xQueueReceiveFromISR() when the receive operation failed due to the queue already being empty.
  258. //#define       traceQUEUE_DELETE(pxQueue)      Called from within vQueueDelete().
  259.  
  260.  
  261. //      Called from within xTaskCreate() when the task is successfully created.
  262. #define traceTASK_CREATE(pxTask){       \
  263.         USARTwrite( &USART0, 13 );              \
  264.         USARTputs( &USART0, (char*)pxTask->pcTaskName );\
  265.         USARTwrite( &USART0, ((int)pxTask>>8)&0xFF );   \
  266.         USARTwrite( &USART0, ((int)pxTask>>0)&0xFF );   }
  267.  
  268.  
  269. //      Called from within xTaskCreate() when the task was not successfully created due to there being insufficient heap space available.
  270. #define traceTASK_CREATE_FAILED( pxNewTCB ){    \
  271.         USARTwrite( &USART0, 14 );      \
  272.         USARTwrite( &USART0, ((int)pxNewTCB>>8)&0xFF ); \
  273.         USARTwrite( &USART0, ((int)pxNewTCB>>0)&0xFF ); }
  274.  
  275.  
  276.  
  277. //      Called from within vTaskDelete().
  278. #define traceTASK_DELETE(pxTask){       \
  279.         USARTwrite( &USART0, 15 );      \
  280.         USARTwrite( &USART0, ((int)pxTask>>8)&0xFF );   \
  281.         USARTwrite( &USART0, ((int)pxTask>>0)&0xFF );   }
  282.  
  283.  
  284.  
  285. //      Called from within vTaskDelayUntil().
  286. #define traceTASK_DELAY_UNTIL(){        \
  287.         USARTwrite( &USART0, 16 );              }
  288.  
  289.  
  290. //      Called from within vTaskDelay().
  291. #define traceTASK_DELAY(){              \
  292.         USARTwrite( &USART0, 17 );      }
  293.  
  294. //      Called from within vTaskPrioritySet().
  295. #define traceTASK_PRIORITY_SET(pxTask,uxNewPriority){   \
  296.         USARTwrite( &USART0, 18 );      \
  297.         USARTwrite( &USART0, ((int)pxTask>>8)&0xFF );   \
  298.         USARTwrite( &USART0, ((int)pxTask>>0)&0xFF );   \
  299.         USARTwrite( &USART0, ((int)uxNewPriority>>0)&0xFF );    }
  300.  
  301.  
  302.  
  303. //      Called from within vTaskSuspend().
  304. #define traceTASK_SUSPEND(pxTask){      \
  305.         USARTwrite( &USART0, 19 );              \
  306.         USARTwrite( &USART0, ((int)pxTask>>8)&0xFF );   \
  307.         USARTwrite( &USART0, ((int)pxTask>>0)&0xFF );   }
  308.  
  309.  
  310.  
  311. //      Called from within vTaskResume().
  312. #define traceTASK_RESUME(pxTask){       \
  313.         USARTwrite( &USART0, 20 );              \
  314.         USARTwrite( &USART0, ((int)pxTask>>8)&0xFF );   \
  315.         USARTwrite( &USART0, ((int)pxTask>>0)&0xFF );   }
  316.  
  317.  
  318. //      Called from within xTaskResumeFromISR().
  319. //#define       traceTASK_RESUME_FROM_ISR(pxTask){     
  320. //      USARTputs( &USART0, "TASK_RESUME_FROM_ISR\n\r" );       }
  321.  
  322. #endif
  323.  
  324.  
  325.  
  326.  
  327. #endif

Desconectado jgpeiro06

  • Colaborador
  • PIC18
  • *****
  • Mensajes: 276
Re: AVRPLC: Clon de un PLC de Siemens basado en FreeRTOS
« Respuesta #22 en: 04 de Noviembre de 2010, 14:54:02 »
La tarea TaskAWL
Esta tarea es el corazón de AVRPLC, pero su funcionamiento es muy sencillo.  Por cada instrucción ejecuta un gran switch/case, donde cada case corresponde a una operación en AWL. Cuando el PC vale 0, lee las entradas y escribe las salidas y espera un tiempo. El típico bucle “lee-procesa-escribe” es sustituido por “lee-escribe-procesa”, esto permite que la salida del sistema se estabilice durante la ejecución del programa.
  
La VM se implementa una memoria ROM de 512 bytes, una memoria RAM de 256 bytes y un registro llamado RLO de 8 bits y un intérprete de AWL.
El RLO es la “pila de resultados lógicos” de 8 niveles.  Solo almacena el bit de resultado de una operación.  Esta pila es la que permite que los programas gráficos con “varios caminos” puedan transformarse a un programa en ensamblador de manera sencilla. Normalmente si los caminos divergen se introduce un valor en la pila y si convergen se extrae.

Aquí se muestra un programa en KOP, su equivalente en AWL y el uso del RLO durante la ejecución.

La ROM contiene todo el programa en AWL incluyendo el principal, las subrutinas y las interrupciones (las IRQs no son soportadas por AVRPLC). Su tamaño es de 512 bytes.  
La RAM contiene todas las variables de un programa incluyendo las entradas, salidas, timers…Su tamaño es de 256 bytes. Aquí se detallan todas las áreas en las que está dividida:

TipoDescripciónTamañoAcceso   Comentario
I0Entradas digitales2bit (I0.0, I1.7…), byte (I0, I1) o word (IW0)
AI0Entradas analógicas4*2word( AIW0, AIW2...)
Q0Salidas digitales2bit(Q0.0, Q1.7…), byte (Q0,QI1) o word(QW0)
AQ0Salidas analógicas4*2word( AQW0, AQW2…)El AVRPLC solo tiene dos salidas analógicas, AIW4 y AIW6
C0Contadores16*3bit, palabra o contador(LD C0, ADDW C0 M0, CTU C0…)
T0Temporizadores16*7bit, palabra o temporizador(LD T0, ADDW T0 MW0, TON T0…)
M0Marcas43bit(M0.0), byte(M0) o word(MW0)
SM0Marcas especiales2bit, byte y WordSolo están implementadas algunas como las SM0.0, SM0.1, SM0.3, SM0.4, SM0.5 y SM0.6
L0Locales16bit, byte y palabraEstos 16 bytes son metidos o sacados de una pila cuando se ejecutan las subrutinas
R0Reservadas4No son accesiblesSe utiliza para implementar  las instrucciones de detección de flanco

El juego de instrucciones
   -Operaciones lógicas con bits.   
      LD, A, O, LDN, AN, ON,  LDI, AI, OI, LDNI, ANI, ONI, NOT, EU, ED, ALD, OLD,LPS, LDS, LRD, LPP, ST, STI,  S, SI, R, RI, AENO, NOP
   -Operaciones lógicas
      INVW, ANDW, ORW, XORW
   -Transferencia
      MOVW
   -Desplazamiento/Rotación   
      SLW, SRW, RLW, RRW
   -Aritméticas de coma fija
      ADDW, SUBW, MULW, DIVW
   -Comparación
      LDWEQ, AWEQ, OWEQ,  LDWNE, AWNE, OWNE,  LDWGE, AWGE, OWGE,  LDWLE, AWLE, OWLE,  LDWGT, AWGT, OWGT,  LDWLT, AWLT, OWLT
   -Control de programa
      FOR, NEXT, JMP, LBL
   -Subrutinas
      CALL, RET, CRET
   -Contadores
      CTU, CTD, CTUD
   -Temporizadores
      TON, TOF, TONR

Tipos de direccionamiento soportados
   -Directo      La instrucción contiene la dirección de memoria que contiene el dato para operar. Ejemplo ANDW AIW0, MW0...
   -Indirecto      Equivalente a los punteros. No es soportado por AVRPLC.
   -Inmediato      La instrucción contiene el dato para operar. Ejemplo ANDW 25, MW0
Tipos de datos soportados
   bit      La instrucción opera con un bit. Si es un temporizador o un contador se accederá a su bit correspondiente. Ejemplo: LD   I0.0
   Byte      La instrucción opera con un byte. Ejemplo: LD   IB0.0
   Word      La instrucción opera con un word. Si es un temporizador o un contador se accederá a su valor correspondiente. Ejemplo LD   IW0.0


Implementación de las instrucciones en C.
Esta parte es la que más tiempo lleva. Está basada en ingeniería inversa utilizando la documentacion proporcionada por Siemens.
Aqui se detalla la implementacion de las instrucciones. Como son muchas y similares, solo comento algunas que he considerado mas interesantes:

Instrucción:   LD ADDR, BIT
Descripción:   Carga el valor del bit de dirección en el nivel superior de la pila.
Tamaño:      3 bytes
Operandos:   ADDR: La dirección de memoria del bit a comprobar.
         BIT: El numero del bit a comprobar.
Código: C
  1. case OP_LD:
  2.         PUSH_RLO( BitT( RAM[ADDR], BIT ) );
  3.         PC += 3;        break;
Comprueba el bit y lo mete en el nivel superior del RLO.


Instrucción:   ST ADDR, BIT
Descripción:   Activa o desactiva el bit.
Tamaño:   3 bytes
Operandos:   ADDR: La dirección de memoria del bit.
      BIT: El numero del bit.
Código: C
  1. case OP_ST:
  2.         BitV( RAM[ADDR], BIT, RLO.b0 );
  3.         PC += 3;        break;
Comprueba el nivel superior de la pila y lo asigna al bit indicado.


Instrucción:   EU
Descripción:   Detectar flanco positivo
Tamaño:      3 bytes
Operandos:   ADDR es una dirección de la memoria reservada (Rx) generada automáticamente por el AWLparser.
         BIT es la posición del bit temporal(Rx.y), generada automáticamente por el AWLparser.
Código: C
  1. case OP_EU:
  2. {       char RLOBCK = RLO.b0;
  3.         if( RLO.b0 && !BitT( RAM[ADDR], BIT ) )
  4.                 RLO.b0 = 1;
  5.         else
  6.                 RLO.b0 = 0;
  7.         BitV( RAM[ADDR], BIT, RLOBCK );
  8. }       PC += 3;    break;
Para detectar un flanco positivo en RLO.b0 lo compara con el ultimo almacenado.
Si se cumple la condición del flanco positivo( actual = 1 y previo = 0), almacena un 1 en RLO.b0. Un 0 si no se cumple.
Almacena el valor actual de RLO en la memoria reservada Rx.y


Instrucción:   ANDW IN, OUT
Descripción:   Combinación Y con palabras
Tamaño:      6 bytes
Operandos:   IN:      Puede ser una dirección de memoria o un dato inmediato de 16 bits      
         OUT:   Es una dirección de memoria
Código: C
  1. case OP_ANDW:
  2.         if( RLO.b0 ){
  3.                 int IN, OUT;
  4.                 if( BitT( ROM[PC+1], 0 ) ){
  5.                         IN = ReCast( int, ROM[PC+2] );
  6.                 }else{
  7.                         IN = ReCast( int, RAM[ROM[PC+2]] );
  8.                 }
  9.        
  10.                 OUT = ReCast( int, RAM[ROM[PC+4]] );
  11.                
  12.                 OUT &= IN;
  13.        
  14.                 ReCast( int, RAM[ROM[PC+4]] ) = OUT;
  15.                 ENO = 1;
  16.         }else{
  17.                 ENO = 0;
  18.         }
  19.         PC += 6;        break;
Esta instrucción solo se ejecuta si RLO.b0 = 1.
Primero determina si el primer operando es inmediato o no.
Lee el segundo operando, realiza la operación AND y lo almacena.
Esta operación propaga un bit llamado ENO.


Instrucción:   CALL SUB, IN0, IN2
Descripción:   Llamada a una subrutina
Tamaño:      12 bytes
Código: C
  1. case OP_CALL:
  2.         if(RLO.b0){
  3.                 PUSH_LOCALS();
  4.                                        
  5.                 int TMP0, TMP2;
  6.                 if( BitT( ROM[PC+1], 0 ) ){
  7.                         TMP0 = ReCast( int, ROM[PC+2] );
  8.                 }else{
  9.                         TMP0 = ReCast( int, RAM[ROM[PC+2]] );
  10.                 }
  11.                 if( BitT( ROM[PC+1], 1 ) ){
  12.                         TMP2 = ReCast( int, ROM[PC+4] );
  13.                 }else{
  14.                         TMP2 = ReCast( int, RAM[ROM[PC+4]] );
  15.                 }
  16.                 ReCast( int, RAM[L0+0] ) = TMP0;
  17.                 ReCast( int, RAM[L0+2] ) = TMP2;
  18.  
  19.                 PUSH_PC( PC );
  20.                 PC = ReCast( int, ROM[PC+10] );
  21.         }else{
  22.                 PC += 12;
  23.         }
  24.         break;
Esta instrucción solo "llama" si RLO.b0 = 1. Si no continúa ejecutando la siguiente instrucción.
Salva las variables locales en una pila.
Extrae los argumentos de llamada. Pueden ser direcciones de memoria o datos inmediatos.
Los asigna a las nuevas variables locales como LW0 y LW2
Salva el PC actual y realiza la llamada.

Ejemplo con un contador. KOP, AWL y cronograma.

Instrucción:   CTU Cx, PV
Descripción:   Incrementa un contador
Tamaño:      4 bytes
Operandos:   Cx es un contador del 0 al 15
Código: C
  1. case OP_CTU:
  2. {       CNTR = ReCast( struct COUNTER, RAM[ROM[PC+1]] );
  3.         int CNTR_PV = ReCast( int, ROM[PC+2] );
  4.         char CNTR_R = POP_RLO();
  5.         char CNTR_CU  = POP_RLO();
  6.  
  7.         if( CNTR_R ){
  8.                 CNTR.CNT = 0;
  9.         }else{
  10.                 if( CNTR_CU && !CNTR.CUprv ){
  11.                         if( CNTR.CNT < 32767 ){
  12.                                 CNTR.CNT++;
  13.                         }
  14.                 }
  15.         }
  16.  
  17.         if( CNTR.CNT >= CNTR_PV )       CNTR.ON = 1;
  18.         else                            CNTR.ON = 0;
  19.                                        
  20.         CNTR.CUprv = CNTR_CU;
  21.         ReCast( struct COUNTER, RAM[ROM[PC+1]] ) = CNTR;
  22. }       PC += 4; break;
Carga el valor del contador en una variable temporal.
Lee el valor PV de la ROM.
Saca las entradas del contador de la pila RLO.
Si el contador tiene su entrada de reset = 1 lo borra.
Si el contador detecta un flanco positivo incrementa(si no ha alcanzado el valor máximo).
Si ha superado el valor de PV activa su bit.
Salva el valor de RLO.b0 para poder detectar el flanco positivo.
Guarda el valor del contador.

Ejemplo con un temporizador. KOP, AWL y cronograma.

Instrucción:   TON Tx, PT
Descripción:   Temporizador como retardo a la conexión. Todos los temporizadores son de 10mS
Tamaño:      4 bytes
Operandos:   Tx es un temporizador del 0 al 15
Código: C
  1. case OP_TON:
  2. {       TMR = ReCast( struct TIMER, RAM[ROM[PC+1]] );
  3.         unsigned int TMR_PT = ReCast( unsigned int, ROM[PC+2] );       
  4.         char TMR_IN = POP_RLO();
  5.                                
  6.         unsigned int TMR_ACTUAL = xTaskGetTickCount()/TMR_RESOLUTION;
  7.  
  8.         if( !TMR_IN ){
  9.                 TMR.START = TMR_ACTUAL;
  10.                 TMR.ON = 0;
  11.         }
  12.                                
  13.         if( TMR_ACTUAL >= TMR.START + TMR_PT ){
  14.                 TMR.ON = 1;                            
  15.         }
  16.                                        
  17.         if( TMR_ACTUAL - TMR.START <= 32767 )
  18.                 TMR.TM = TMR_ACTUAL - TMR.START;
  19.         else
  20.                 TMR.TM = 32767;
  21.  
  22.         ReCast( struct TIMER, RAM[ROM[PC+1]] ) = TMR;
  23. }       PC += 4; break;
Carga el valor del temporizador en una variable temporal.
Saca las entradas del temporizador de la pila RLO.
Lee el tiempo actual del RTOS.
Si la entrada es 0, borra el contador.
Si el tiempo ha superado a PT activa su bit.
Si el temporizador ha alcanzado su valor máximo se para.
Guarda el valor del temporizador.
Incrementa el PC.
« Última modificación: 08 de Noviembre de 2010, 18:37:01 por jgpeiro06 »

Desconectado barral

  • PIC10
  • *
  • Mensajes: 37
Re: AVRPLC: Clon de un PLC de Siemens basado en FreeRTOS
« Respuesta #23 en: 05 de Noviembre de 2010, 04:38:47 »
 :shock: Simplemente espectacular... Es una gozada que compartas este magnífico trabajo con todos nosotros, a mi desde luego me está picando para meterme con el freertos, además voy a estudiarme la parte de VM, me parece muy interesante. Bueno, realmente todo, pero me llama mucho la atención la implementación de la VM.

Un saludo y felicidades por este estupendo trabajo.

Desconectado jgpeiro06

  • Colaborador
  • PIC18
  • *****
  • Mensajes: 276
Re: AVRPLC: Clon de un PLC de Siemens basado en FreeRTOS
« Respuesta #24 en: 07 de Noviembre de 2010, 20:52:32 »
El programa AWLParser

Los porgramas generados por el STEP 7 necesitan ser "ensamblados" para poder ser utilizados en el AVRPLC.
El AWLparser toma un archivo de entrada .awl y crea un archivo .out con los bytecodes correspondientes. Si encuentra errores de sintaxis, de rango de variables... emite un mensaje por pantalla informando del error.
El programa mas sencillo en KOP es complejo de analizar ya que el AWL generado no solo contiene las intrcciones en AWL, ademas contiene los comentarios, titulo del programa, nombre de las subrutinas...

Aqui se muestra un programa muy sencillo en KOP.

El AWL generado por el STEP 7
Código: C
  1. ORGANIZATION_BLOCK PRINCIPAL:OB1
  2. TITLE=Comentario de UOP
  3. BEGIN
  4. Network 1 // Título de segmento
  5. // Comentario de segmento
  6. LD     I0.0
  7. =      Q0.0
  8. END_ORGANIZATION_BLOCK
  9. SUBROUTINE_BLOCK SBR_0:SBR0
  10. TITLE=Comentario de UOP
  11. BEGIN
  12. Network 1 // Título de segmento
  13. // Comentario de segmento
  14. END_SUBROUTINE_BLOCK
  15. INTERRUPT_BLOCK INT_0:INT0
  16. TITLE=Comentario de UOP
  17. BEGIN
  18. Network 1 // Título de segmento
  19. // Comentario de segmento
  20. END_INTERRUPT_BLOCK

Los bytecodes generados por el AWLParser.
Código: C
  1. 1,0,0,22,10,0,0,74,74


El AWLParser funciona en el PC, ha sido creado con Netbeans + minGW, y esta basado en las librerias LEX-YACC.
Para analizar el programa en AWL, el AWL parser debe "conocer" exactamente toda la sintaxis de este lenguaje.
La sintaxis del lenguaje se ha extraido estudiando varios programas generados por el STEP 7 v4, asi que el AWLparser podria no funcionar si utilizamos otra version del STEP 7.

Para especificar la sintaxis he utilizado un lenguaje preparado para esta tarea llamado EBNF. Este lenguaje permite definir la sintaxis dividiendola en reglas y simbolos. Todo esto tiene grandes ventajas:
   -Facilita la especificacion de la sintaxis ya que permite hacerlo de una manera "natural".
   -Se puede chequear automaticamente la sintaxis definida, para ver si contiene errores o es ambigua, sin necesidad de hacer pruebas.
   -Una vez especificada la sintaxis podemos se puede crear automaticamente un programa en C que analice una entrada utilizando esa sintaxis.

Un ejemplo basico de una calculadora podria ser este:
Código: C
  1. token   number  [0..9]
  2. calc:   expr '='       
  3.         ;
  4. expr :  expr '*' expr
  5.         |       expr '/' expr
  6.         |       expr '+' expr
  7.         |       expr '-' expr
  8.         |       number
  9.         ;

Ademas de definir la sintaxis a analizar, podemos definir las acciones en C que se realizaran cuando la entrada cumpla una determinada regla:
Código: C
  1. token   number  [0..9]  { yylval = atoi(yytext); }
  2. calc:   expr '='                                { printf("%d", $1); }
  3.         ;
  4. expr :  expr '*' expr           { $$ = $1 + $2; }
  5.         |       expr '/' expr           { $$ = $1 + $2; }
  6.         |       expr '+' expr           { $$ = $1 + $2; }
  7.         |       expr '-' expr           { $$ = $1 + $2; }
  8.         |       number                          { $$ = $1; }
  9.         ;
Aquí podéis ver otro proyecto basado en PIC24 de una calculadora más compleja. Es capaz de interpretar un "subset" del lenguaje C y ejecutar sentencias de selección e iteración.
PIC24 Calc

Algunas notas sobre AWLparser
Cuando el AWLParser se encuentra con una instruccion realiza lo siguiente:
Codigo en AWL
Código: C
  1. LD      I0.0    // Carga el valor del bit I0.0

Especificacion de la gramatica en EBNF
//Scanner.l
Código: C
  1. "LD"                                    { return LD;    }
  2. "I"[0-9]+                               { yylval = I0 + atoi(&yytext[1]);       return ADDRESb; } // I0 esta definido en AWLMemoryAreas.h
  3. [".",]                                  { return *yytext; }
  4. [0-9]+                                  { yylval = atoi(yytext);                return INTEGER; }
  5. [ \n\r\t]+                              {;}
//Parser.y
Código: C
  1. LD              ADDRESb '.' INTEGER     { ROM[PC] = OP_LD;      ROM[PC+1] = $2; ROM[PC+2] = $4; PC += 3; } // OP_LD esta definido en AWLOpCodes.h
Pasos internos que se realizan para detectar una instrucción en AWL.
1   Lex devuelve el simbolo LD. Yacc introduce yylval en una pila.
2   Lex devuelve el simbolo ADDRESb, calcula la direccion y la almacena en yylval. Yacc introduce yylval en una pila.
3   Lex devuelve el simbolo '.'. Yacc introduce yylval en una pila.
4   Lex devuelve el simbolo INTEGER, yylval contiene el valor del entero. Yacc introduce yylval en una pila.
5   Yacc acepta la instruccion LD. Asigna OP_LD a ROM[PC], saca los valores de la direccion y el bit de la pila y los almacena en ROM[Pc+1] y ROM[PC+2]. Finalmente incrementa el PC.


Sobre tipos de datos...
Algunas instrucciones aceptan distintos tipos de datos. La instruccion en bytecodes utiliza un operador adicional (OP2) que indica si el valor de ADDRES_INT debe ser tratado como un entero(direccionamiento inmediato) o como una direccion (direccionamiento inmediato).

Código: C
  1. MOVW    IW0, QW0        // Mueve el valor de la direccion IW0 a la direccion QW0.
  2. MOVW    25, QW0         // Mueve el valor 25 a la direccion QW0.
// Parser.y
Código: C
  1. MOVW    ADDRW_INT ',' ADDRESW           { ROM[PC++] = OP_MOVW;  ROM[PC++] = ((($2>>16)&0x01)<<0); ROM[PC++] = $2&0xFF; ROM[PC++] = ($2>>8)&0xFF; ROM[PC++] = $4&0xFF; ROM[PC++] = ($4>>8)&0xFF;}
// Contenido de la instruccion MOV
Código: C
  1. OP_MOVW OP2     ADDRES_INT      ADDRESW

Sobre tipos de acceso...
El acceso a los contadores y temporizadores se puede realizar como bit o como palabra.
Las instrucciones logicas con bits no acceden al valor de los contadores/temporizadores, acceden a un bit de estado. Este bit lo calcula el ensamblador a partir de la direccion del contador/temporizador al que se accede.(ademas necesita conocer el tamaño de la estructura "struct TIMER" y la posicion del primer elemento T0)
Si la instruccion R actua sobre un temporizador/contador, se borra el bit de estado y el valor del temporizador/contador.

Código: C
  1. MOVW    25, QW0 //      Mueve el valor 25 a la direccion QW0.
  2. LD      Q0.0    //      Carga el bit 0 de la direccion Q0
  3.  
  4. MOVW    25, T0  //      Mueve el valor 25 a la direccion T0.
  5. LD      T0      //      Carga el bit de estado del contador T0
  6.  
  7. R       Q0.0, 2 //      Si el RLO es 1, pone a 0 los bit Q0.1:0
  8. R       T0, 2   //      Si el RLO es 1, pone a 0 los bit de estado de T1 y T0 y sus respectivos tiempos.
Parser.y
Código: C
  1. ADDRW_INT
  2.         COUNTER                                         { $$ = 0x00000000|$1;  }
  3.         TIMER                                           { $$ = 0x00000000|$1;  }
  4. bit
  5.         COUNTER                                         { $$ = (($1+(sizeof_COUNTER-1))<<8)|(0);  }
  6.         TIMER                                           { $$ = (($1+(sizeof_TIMER-1))<<8)|(0);  }



Sobre las operaciones de flanco...
EU I0.0      // Carga un 1 si I0.0 pasa de 0 a 1. Carga un 0 en otro caso.

Las operaciones EU y ED requieren almacenar el estado anterior para calcular su resultado. El STEP 7MicroWin no permite especificar un lugar para almacenar este bit en la memoria RAM del interprete. El ensamblador asigna directamente un bit libre del area de memoria Reserved.

Parser.y
Código: C
  1. |       EU                                                      { ROM[PC++] = OP_EU;    ROM[PC++] = R0+(RLOTMP/8);      ROM[PC++] = R0+(RLOTMP%8); if(RLOTMP<RESERVED*8){RLOTMP++;}else{printf( "Error, max EU and ED\n" );} }
  2.         |       ED                                                      { ROM[PC++] = OP_ED;    ROM[PC++] = R0+(RLOTMP/8);      ROM[PC++] = R0+(RLOTMP%8); if(RLOTMP<RESERVED*8){RLOTMP++;}else{printf( "Error, max EU and ED\n" );} }



Sobre Constantes ...
Cuando el ensamblador encuentra una constante en binario o hexadecimal llama a la funcion BINatoi()/HEXatoi() para leer su valor. Si la constante es decimal es leida mediante la funcion estandar atoi(). Esta funcion tambien reconoce numeros negativos.

Código: C
  1. MOVW    25, MW0         // Carga el valor 25 en MW0
  2. MOVW    +25, MW2        // Carga el valor 25 en MW2
  3. MOVW    -25, MW4        // Carga el valor -25 en MW4
  4. MOVW    16#19, MW6      // Carga el valor 0x19 en MW6
  5. MOVW    2#11001, MW8    // Carga el valor 0b11001 en MW8



Sobre las instrucciones FOR-NEXT...
Código: C
  1. FOR             MW0, 1, 5
  2. NEXT

Cuando Yacc encuentra la instruccion FOR, llama a la funcion PUSH_FOR() que almacena el PC en una pila. Esta direccion será usada cuando se encuentre la CORRESPONDIENTE instruccion NEXT.

Cuando Yacc se encuentra la instruccion NEXT, llama a la POP_FOR() que devuelve el valor del PC donde se encuentra el FOR CORRESPONDIENTE necesaria para caulular las direcciones de salto de la instruccion FOR.


A una instruccion FOR no le corresponde necesariamente la siguiente instruccion NEXT ya que los bucles FOR pueden estar anidados.
El ensamblador acepta 64 niveles de anidado e informa si se superan. El interprete no tiene limitaciones en cuanto al numero de bucles FOR anidados.

Sobre el contenido de las instrucciones FOR y NEXT
Código: C
  1. OP_FOR  OP2     ADDRESW ADDRES_INT ADDRES_INT NEXT_PC_ADDRES
  2. OP_NEXT FOR_PC_ADDRES


Instruccion FOR:
   Inicializa o incrementa.
   Comprueba condicion.
      Cierta: Continua en la siguiente instruccion.
      Falsa: Salta a NEXT_PC_ADDRES + 1
Instruccion NEXT:
   Salta a la instruccion FOR_PC_ADDRES

Cuando el AVRPLC ejecuta una instruccion FOR, no puede diferenciar si debe inicializar la variable o incrementarla. Por este motivo, la durante instruccion NEXT se activa un flag que se lee y borra durante la instruccion FRO.




Sobre el calculo de direccion en las instrucciones JUMP-LBL...
El ensamblador acepta 64 etiquetas distintas e informa si se superan. El interprete no tiene limitaciones en cuanto al numero etiquetas.

Sobre el calculo de direccion en las instrucciones CALL...
Scanner.y
Código: C
  1. "CALL"[ _a-zA-Z0-9\t]+  { PUSH_CALL( PC, &yytext[4] ); return CALL;}
  2.  
  3. subroutine
  4.         :       SUB_BLOCK_START BLOCK_NAME INPUT_VARS_DECL OUTPUT_VARS_DECL instruction_list SUB_BLOCK_END      { UPDATE_CALLS(); CLEAR_LABELS(); }
  5.  
  6. |       CALL    ',' ADDRW_INT ',' ADDRW_INT ',' ADDRESW ',' ADDRESW     { ROM[PC++] = OP_CALL; ROM[PC++] = ((($3>>16)&0x01)<<0)|((($5>>16)&0x01)<<1); ROM[PC++] = $3&0xFF; ROM[PC++] = ($3>>8)&0xFF; ROM[PC++] = $5&0xFF; ROM[PC++] = ($5>>8)&0xFF; ROM[PC++] = $7&0xFF; ROM[PC++] = ($7>>8)&0xFF; ROM[PC++] = $9&0xFF; ROM[PC++] = ($9>>8)&0xFF; PC++; PC++; }

El ensamblador acepta 64 subrutinas distintas e informa si se superan. El interprete no tiene limitaciones en cuanto al numero subrutinas, pero si encuanto al numero de llamadas anidadas a subrutinas. Si este numero se supera, el interprete genera un codigo de error indicandolo.

   
Sobre el paso de parametros a las subrutinas

Scanner.y
Código: C
  1. "VAR_INPUT"[ \n\t\r]*"L0:WORD;"[ \n\t\r]*"L2:WORD;"[ \n\t\r]*"END_VAR"  { return INPUT_VARS_DECL; }
  2. "VAR_OUTPUT"[ \n\t\r]*"L4:WORD;"[ \n\t\r]*"L6:WORD;"[ \n\t\r]*"END_VAR" { return OUTPUT_VARS_DECL; }



Por ultimo toda la sintaxis del lenguaje AWL implementado, escrita en EBNF y representada graficamente:

Desconectado jgpeiro06

  • Colaborador
  • PIC18
  • *****
  • Mensajes: 276
Re: AVRPLC: Clon de un PLC de Siemens basado en FreeRTOS
« Respuesta #25 en: 08 de Noviembre de 2010, 14:59:30 »
Los temporizadores y el timer del sistema

Despues de repasar el codigo de los temporizadores me he dado cuenta de que tenian un error, y podian tener un comportamiento que no era el esperado.
Los temporizadores calculan el tiempo transcurrido entre dos eventos basicamente almacenando el valor de xTaskGetTickCount() cuando ocurre el primer evento y leyendo la funcion xTaskGetTickCount() cuando ocurre el segundo.
La funcion xTaskGetTickCount() devuelve un valor de 15 bits, que con una frecuencia de 100 Hz, llega a su valor maximo cada 5 minutos.
Si estamos midiendo un tiempo como TM = ACTUAL - START y el ACTUAL vuelve a empezar de 0, el valor de TM sera erroneo.
La unica manera de evita esto es comprobar antes de calcular:
Código: C
  1. if( ACTUAL > START ){
  2.         TM = ATCUAL - START;
  3. }else{
  4.         TM = MAX - ( START - ATCUAL );
  5. }
Representación del calculo


Implementacion de los temporizadores como retardo a la conexión o TON.
Funcionamiento:
   -Despues de un Reset:
      Bit de temporización = 0.
      TM = 0.
   -Si IN = 0:
      Bit de temporización = 0.
      TM = 0.
   -Si IN = 1:
      TM indica cuenta el tiempo transcurrido.
   Si TM >= PT:
      Bit de temporización = 1.
      TM continúa contando hasta 32.767

Implementacion de la instruccion TON en C y cronograma de un ejemplo basico


Implementacion de los temporizadores como retardo a la conexión con memoria o TONR.
Funcionamiento:
   -Despues de un Reset:
      Bit de temporización = 0.
      TM = 0.
   -Si IN = 0:
      Bit de temporización conserva el ultimo estado.
      TM conserva el ultimo estado.
   -Si IN = 1:
      TM indica cuenta el tiempo transcurrido.
   -Si TM >= PT:
      Bit de temporización = 1.
      TM continúa contando hasta 32.767

Implementacion de la instruccion TONR en C y cronograma de un ejemplo basico


Implementacion de los temporizadores como retardo a la desconexión o TOF.
Funcionamiento:
   -Despues de un Reset:
      Bit de temporización = 0.
      TM = 0.
   -Si IN = 0:
      TM indica cuenta el tiempo transcurrido.
   -Si IN = 1:
      Bit de temporización = 1.
      TM = 0.
   -Si TM >= PT:
      Bit de temporización = 0.
      TM = PT.

Implementacion de la instruccion TONF en C y cronograma de un ejemplo basico


Actualizar el valor de un temporizador
Los temporizadores de 10mS del S7-200 se actualizan al cominezo de cada ciclo.
Los temporizadores de 100mS del S7-200 solo se actualizan al ejecutar la operacion del mismo. Si esta operacion no se ejecuta durante un ciclo o se ejecuta varias veces el valor del temporizador se vera afectado.
Los temporizadores del AVRPLC(todos de 10mS) se actualizan solo al ejecutar la operacion del mismo, pero no alteran su valor si no se ejecutan en todos los ciclos o se ejecutan varias veces en un ciclo.
La opción de actualizar los temporizadores al principio de cada ciclo no es posible en AVRPLC, ya que en ese punto no se conoce de que tipo es cada temporizador, y esto es necesario para actualizarlo.



Desconectado Nocturno

  • Administrador
  • DsPIC33
  • *******
  • Mensajes: 18286
    • MicroPIC
Re: AVRPLC: Clon de un PLC de Siemens basado en FreeRTOS
« Respuesta #26 en: 08 de Noviembre de 2010, 15:41:46 »
¿Y no sigue habiendo error si ocurren dos desbordamientos de ACTUAL, o si ACTUAL supera a START?, con tu solución sólo obtienes el resultado correcto si ha habido un desbordamiento y ACTUAL aún no ha alcanzado el valor de START, o lo que es igual, cuando el ciclo ha durado menos de 5 minutos.
Para ciclos más largos tendrás error, eso creo.

Desconectado jgpeiro06

  • Colaborador
  • PIC18
  • *****
  • Mensajes: 276
Re: AVRPLC: Clon de un PLC de Siemens basado en FreeRTOS
« Respuesta #27 en: 08 de Noviembre de 2010, 16:26:48 »
Citar
¿Y no sigue habiendo error si ocurren dos desbordamientos de ACTUAL, o si ACTUAL supera a START?
No, cuando ACTUAL desborda y va a volver a alcanzar a START ( ACTUAL = START - 1 o TM = 32767 ) se activa un bit interno del temporizador llamado OVER, y el valor de TM se queda fijo en el máximo.
Aunque...yo he asumido que la instrucción del timer se va a ejecutar en todos los ciclos ( quiero decir que TM incrementa de 1 en 1 ). Si esto no es así, y TM pasa de 32766 a 32768 ( o de ACTUAL = START - 1 a ACTUAL = START + 1) sin que OVER se ponga a 1........en ese caso si que falla....

Gracias Nocturno por "buscarle las cosquillas" a mi código. Es la mejor manera de mejorarlo.


Desconectado jgpeiro06

  • Colaborador
  • PIC18
  • *****
  • Mensajes: 276
Re: AVRPLC: Clon de un PLC de Siemens basado en FreeRTOS
« Respuesta #28 en: 08 de Noviembre de 2010, 18:22:23 »
Citar
Aunque...yo he asumido que la instrucción del timer se va a ejecutar en todos los ciclos ( quiero decir que TM incrementa de 1 en 1 ). Si esto no es así, y TM pasa de 32766 a 32768 ( o de ACTUAL = START - 1 a ACTUAL = START + 1) sin que OVER se ponga a 1........en ese caso si que falla...

Creo que ya he encontrado una solucion al problema:
Como el timer es de 15 bits ( 0 a 32767 ) había pensado que era mejor coger solo 15 bits del valor de retorno de la funcion xTaskGetTickCount().
Código: C
  1. unsigned int TMR_ACTUAL = (xTaskGetTickCount()/TMR_RESOLUTION ) & TMR_MAX; // TMR_MAX = 0x7FFF
Si cogemos los 16 bits, ACTUAL - START puede tomar valores entre 0 y 65535. Ahora hay un gran margen para activar el bit OVER cuando TM >= 32767.
El código para las 3 instrucciones de temporización TON, TONR y TOF queda igual, pero sin "& TMR_MAX".
Código: C
  1. unsigned int TMR_ACTUAL = xTaskGetTickCount()/TMR_RESOLUTION;
« Última modificación: 08 de Noviembre de 2010, 18:27:32 por jgpeiro06 »

Desconectado jgpeiro06

  • Colaborador
  • PIC18
  • *****
  • Mensajes: 276
Re: AVRPLC: Clon de un PLC de Siemens basado en FreeRTOS
« Respuesta #29 en: 09 de Noviembre de 2010, 21:26:59 »
Las funciones de depuración de la VM

Aunque originalmente no se iban a implementar, he estado mirando y pueden ser interesantes.
He implementado todas las opciones tipicas que permiten arrancar y parar el programa, poner puntos de ruptura, ejecucion paso a paso y modificar todas las variables de la VM.
Falta hacer un pequeño IDE, que muestre el AWL y la posicion del PC, pero eso se me va de las manos...

Para implementar estas funciones he modificado ligeramente varias partes del proyecto:(todas menos los drivers)
   Task_Xbee.
      Los nuevos comandos permiten la depurar el programa en AWL desde un PC remoto.
   Task_WDT.
      Cuando la tarea Task_AWL se esta depurando se parar en un breakpoint y no soltar el semaforo sWDT durante un tiempo largo. La tarea WDT necesita conocer cuando la tarea AWL esta en modo depuracion.
      El LED WDT, que indicaba si el WDT se habia desbordado, ahora indica que el programa esta en modo depuracion.
   Task_AWL
      En el modo depuracion, antes de ejecutar cada instruccion se comprueba si el PC corresponde con un breakpoint, si es asi, se bloquea la ejecucion.
   AWLparser.
      Como las instrucciones tienen varios tamaños, no todas las direcciones de PC son validas para poner un breakpoint. Es necesario conocer cuales y ademas es importante saber a que linea del AWL original corresponden.


Los comandos de depuracion      
   Disable/Run/Stop
      Cuando la depuracion esta activa (Run o Stop) se comprueba en cada instruccion los breakpoints. En otro caso no se comprueban.
      En el modo Stop se pueden acceder(lectura y escritura) a las memorias de la VM.
      En el modo Run el programa en AWL corre y se para acutomaticamente cuando encuentra un breakpoint.
   Set Breakpoint
      Modifica el valor de uno de los 4 breakpoints. Para eliminar un breakpoint se le asigna la direccion 0xFFFF.
   Read/Write
      Lee/Escribe toda la memoria de la VM, incluyendo PC, RAM, RLO y pila de llamadas... Son unos 300 bytes, que se pueden transmitir en 5 paquetes de 64 como se hace con la ROM.
   Step Into/Over/Return
      Permite ejecutar instrucciones paso a paso y las variaciones tipicas "step-over" y "step-return"
      
Un gráfico con los modos de depuración:

El codigo añadido a la tarea TaskAWL
Código: C
  1. if( debugMode != DEBUG_DISABLE ){
  2.         LEDAWLFreqLowON();
  3.         for( i = 0 ; i < BREAKPOINTS_SIZE ; i++ ){
  4.                 if( PC == breakpoints[i] ){
  5.                         taskENTER_CRITICAL();
  6.                         debugMode = DEBUG_STOP;
  7.                         taskEXIT_CRITICAL();
  8.                 }
  9.         }
  10.         while( debugMode == DEBUG_STOP ){
  11.                 vTaskDelay(1);
  12.         }
  13.         switch( debugStep ){
  14.                 case STEP_INTO:
  15.                         debugStep = STEP_DISABLE;
  16.                         debugMode = DEBUG_STOP;
  17.                         break;
  18.                 case STEP_RETURN:
  19.                         if( ROM[PC] == OP_RET || ROM[PC] == OP_CRET ){
  20.                                 debugStep = STEP_DISABLE;
  21.                                 debugMode = DEBUG_STOP;
  22.                         }
  23.                         break;
  24.                 case STEP_OVER:
  25.                         if( ROM[PC] == OP_CALL ){
  26.                                 debugStep = STEP_RETURN;
  27.                         }else{
  28.                                 debugStep = STEP_DISABLE;
  29.                                 debugMode = DEBUG_STOP;
  30.                         }
  31.                         break;
  32.                 default:
  33.                         break;
  34.         }
  35.                
  36.         LEDAWLFreqLowOFF();
  37. }
   

La FSM para realizar la ejecucion step into/over/return


El codigo añadido al AWLParser
Basicamente cada vez que encuentra una instruccion, añade a un array la linea de AWL actual y el PC actual.
Código: C
  1. instruction_list
  2.         :       instruction instruction_list    { AWLROM[AWLROM_CNT++]=yylineno; AWLROM[AWLROM_CNT++]=PC;}
  3.         |       instruction                             { AWLROM[AWLROM_CNT++]=yylineno; AWLROM[AWLROM_CNT++]=PC;}
  4.         ;
  5.        
  6.         pFilePCL = fopen( filePCLName, "w");
  7.         if( pFilePCL != NULL ){
  8.                 for( i = 0 ; i < AWLROM_CNT ; i++ ){
  9.                         fprintf( pFilePCL, "%u\n", AWLROM[i] );
  10.                 }
  11.                 fclose( pFileTXT );
  12.         }