sábado, 12 de marzo de 2016

Generar código C++ embebido de Simulink (Matlab) e importar en QT5

En este tutorial se va explicar como poder generar código de c++ usando las librerías de simulink. El código generado será utilizado en QT5 para poderlo controlar a través de consola o de una Interfaz gráfica.

Se recomienda revisar los siguientes tutoriales antes de empezar.

Compilar QT5.
Configurar QT5.
Ejemplo Básico QT5.
Instalar X11VNC.

Este paso es opcional.
El tener instalado un compilador es importante para depurar el código generado por Matlab-Simulink y detectar errores.

En la ventana de comandos de Matlab, escribir "mex -setup". Este paso es necesario para poder detectar que compiladores compatibles con Matlab-Simulink están instalados. Si se tiene más de dos compiladores compatibles con Matlab, se debe escoger el que se quiera usar.

Para poder saber que compiladores son compatibles con Matlab-Simulink, revisar el siguiente link:

Matlab R2015a


En este caso se usa Matlab R2015a con compilador Microsoft Visual Studio 2012 Profesional.

Nota: La versión de Visual Studio 2012 está registrada con licencia profesional, mientras que en la versión de Visual Studio 2013 está con licencia Community; y es detectado por matlab pero no compila el código. Para que funcione todo correctamente Visual Studio debe tener la versión Profesional.

A continuación se crearán algunos ejemplos para poder comprender como se puede generar código C++ embebido y entender como funciona el código obtenido, para luego importarlo en QT5.

Pasos Opcionales:

Hay pasos que son opcionales y de no hacerlos no tendrán gran impacto en el código generado, estos son:


Si se deja la configuración en Generic y Unespecified, no habrá problema con la generación del código, pero se recomienda configurar de acuerdo al dispositivo que se va a usar. En este caso se usará la Beaglebone Black para ejecutar los programas creados en QT5. Por lo tanto se debe configurar con procesador ARM Cortex A8.


Con eso en la configuración de Interface ya se podrá utilizar la opción GCC ARM Cortex-A8. Si se realiza este paso se evitarán algunas advertencias que genera el reporte en Simulink.



Habilitar el reporte ayuda a poder visualizar el código c++ embebido que se ha generado, es muy útil para poder leer detalladamente todas las variables y códigos.


La configuración de comentarios, ayuda bastante al momento de analizar el código. En este caso se utilizará sola las marcadas, ya que las demás opciones son útiles cuando se crean comentarios o descripciones en simulink, y en este caso no se realizará eso.


El estilo de código es muy útil para generar código más eficiente.


Si no se ha instalado ningún compilador compatible con Matlab-Simulink, se debe deshabilitar esta opción.


Para poder detectar advertencias, es necesario configurar en "ON (stop for warnings)". Luego hay que pulsar en "Check Model", una véz que se haya terminado de corregir las advertencias, hay que volver a desactivarlo "off" para poder generar el código.



Está parte es importante para priorizar los procesos, esta configuración puede cambiar de acuerdo al tipo de parámetros que se use, el siguiente link explica como funciona.
Optimization Pane: Signals and Parameters.


Es opcional pero importante tener la opción de "Generate an example main program", con esto se genera un archivo main que explica como utilizar las funciones.

Ejemplos:
 
1)  Suma -> Generando el código con Argumentos Individuales.

Configurar la ruta donde se desea trabajar con Matlab-Simulink, esto se lo realiza en Preferences -> General. Al terminar reiniciar Matlab.



Para comodidad crear un nueva carpeta dentro de la carpeta MATLAB, en este caso se la llamará ExportCPP. Ingresar a la carpeta y escribir en la ventana de comandos "simulink".



En el modelo de Simulink se utilizaron:

2 Constant : Simulink->Sources
1 Add : Simulink->Math Operations
1 Display : Simulink->Sinks

Nombrar los bloques utilizados, para facilitar la identificación de las variables en el código generado por Simulink.


El sigueinte paso es de gran importancia, ya que nos permite poder generar el código del modelo realizado. Siempre hay que convertir los bloques en subsistemas y activar la opción de




Al aplicar los cambios, se activa en la pestaña Code Generation el tipo de función que queremos usar. En todos los ejemplos se va usar la opción de "Reusable function".


Aplicar los cambios y aceptar.

Ahora renombrar el subsistema con un nombre "Calc_Suma".

Por el momento no se realizarán más cambios en el modelo, estos se explicarán en los siguientes ejemplos.

Para configurar los parámetos del modelo, hay que ir a Simulation->Model Configuration Parameters o dar click en la figura que parece un engrane o presionar Ctrl+E.


Configuración

Aplicar cada cambio que se vaya haciendo.


Para generar código de c++ es necesario configurar en la pestaña Solver con Type->Fixed-step y Solver->discrete.

Lo siguiente es configurar la pestaña de Code Generation, donde se debe escoger la opción ert.tlc para crear código c/c++ para sistemas embebidos.


Aplicar cambios y aceptar.


Lo útimo es configurar las opciones de Code Generation->Interface y Optimization->Signals and Parameters.



Ir a Code Generation y activar la opción On (stop for warnings), para poder visualizar las advertencias y corregirlas.


Saldrá una advertencia que dice que no se ha configurado los objetivos. Para eso dar click en Set Objectives y configurarlos de la siguiente manera.



Al terminar saldrá una ventana que indica los avisos para la generación de código.


Ahora se tratará de solucionar las advertencias.


Al modificar los parámetros de la advertencia, solo queda chequear que se ha eliminado.


Realizar este paso para las demás advertencias que se crea conveniente eliminar, algunas advertencias como la de "Check the hardware implementation" se soluciona al hacer los Pasos Opcionales de Hardware Implementation. Por el momento dejar todo como está y generar el código.

Nota: El solucionar advertencias puede desconfigurar los parámetros realizados en los pasos anteriores.

Desactivar la opción "Check model before generating code", para poder generar el código y Generate makefile. Esto se lo va a ser en este ejemplo ya que al ser simple no es necesario compilar con Visual Studio Profesional.




Se ha obtenido el siguiente error que dice que hay que activar la opción de  non-finite numbers. Esto se podía haber evitado si se corregían las advertencias.


Volver a realizar el paso anterior para generar el código.


Ahora si se ha completado la generación de código.
 
Si se desea se pueden configurar más cosas, las cuáles se han mencionado en Pasos Opcionales.

Ir a la carpeta de Matlab->ExportCPP->suma_ert_rtw para verificar el código generado.

Los archivos señalados en el recuadro azul, son los que se van a utilizar en QT5. Los archivos más importantes son los siguientes; si no se ha obtenido los siguientes resultados debe ser porque se ha desconfigurado lo siguiente:

Debe estar en Individual arguments y desactivado Inline parameters, aplicar los cambios y volver a generar el código. Es recomendable aplicar los cambios cada que se realice alguna modificación.



Las dos configuraciones son importantes que esten correctamente, ya que si no esta en Individual arguments no generará las funciones del bloque (suma) y si no está en Auto no generará los headers necesarios para configurar el tipo de variables. Se puede usar la opción "Share location" si se usa Visual Studio Profesional y los headers de crearán en la carpeta que se genera para correr los archivos en Visual Studio.

//
// File: suma.h
//
// Code generated for Simulink model 'suma'.
//
// Model version                  : 1.4
// Simulink Coder version         : 8.8 (R2015a) 09-Feb-2015
// C/C++ source code generated on : Thu Mar 10 02:52:06 2016
//
// Target selection: ert.tlc
// Embedded hardware selection: 32-bit Generic
// Code generation objectives:
//    1. Execution efficiency
//    2. ROM efficiency
//    3. RAM efficiency
//    4. Traceability
//    5. Safety precaution
// Validation result: Not run
//
#ifndef RTW_HEADER_suma_h_
#define RTW_HEADER_suma_h_
#ifndef suma_COMMON_INCLUDES_
# define suma_COMMON_INCLUDES_
#include "rtwtypes.h"
#endif                                 // suma_COMMON_INCLUDES_


#include "suma_types.h"
#include "rt_defines.h"


// Macros for accessing real-time model data structure


// Parameters (auto storage)
struct P_suma_T_ {
  real_T Valor1_Value;                 // Expression: 1
                                       //  Referenced by: '<Root>/Valor1'


  real_T Valor2_Value;                 // Expression: 8
                                       //  Referenced by: '<Root>/Valor2'


};


// Real-time Model Data Structure
struct tag_RTM_suma_T {
  //
  //  ModelData:
  //  The following substructure contains information regarding
  //  the data used in the model.


  struct {
    P_suma_T *defaultParam;
  } ModelData;
};


#ifdef __cplusplus


extern "C" {


#endif


#ifdef __cplusplus


}
#endif


#ifdef __cplusplus


extern "C" {


#endif


  // Model entry point functions
  extern void suma_initialize(RT_MODEL_suma_T *const suma_M);
  extern void suma_step(RT_MODEL_suma_T *const suma_M);


#ifdef __cplusplus


}
#endif


//-
//  These blocks were eliminated from the model due to optimizations:
//
//  Block '<Root>/Display' : Unused code path elimination




//-
//  The generated code includes comments that allow you to trace directly
//  back to the appropriate location in the model.  The basic format
//  is <system>/block_name, where system is the system number (uniquely
//  assigned by Simulink) and block_name is the name of the block.
//
//  Use the MATLAB hilite_system command to trace the generated code back
//  to the model.  For example,
//
//  hilite_system('<S3>')    - opens system 3
//  hilite_system('<S3>/Kp') - opens and selects block Kp which resides in S3
//
//  Here is the system hierarchy for this model
//
//  '<Root>' : 'suma'
//  '<S1>'   : 'suma/Calc_Suma'




//-
//  Requirements for '<Root>': suma


#endif                                 // RTW_HEADER_suma_h_


//
// File trailer for generated code.
//
// [EOF]
//

//
// File: suma.cpp
//
// Code generated for Simulink model 'suma'.
//
// Model version                  : 1.4
// Simulink Coder version         : 8.8 (R2015a) 09-Feb-2015
// C/C++ source code generated on : Thu Mar 10 02:52:06 2016
//
// Target selection: ert.tlc
// Embedded hardware selection: 32-bit Generic
// Code generation objectives:
//    1. Execution efficiency
//    2. ROM efficiency
//    3. RAM efficiency
//    4. Traceability
//    5. Safety precaution
// Validation result: Not run
//
#include "suma.h"
#include "suma_private.h"


// Output and update for atomic system: '<Root>/Calc_Suma'
void suma_Calc_Suma(real_T rtu_In1, real_T rtu_In2, real_T *rty_Out1)
{
  // Sum: '<S1>/Suma'
  *rty_Out1 = rtu_In1 + rtu_In2;
}


// Model step function
void suma_step(RT_MODEL_suma_T *const suma_M)
{
  P_suma_T *suma_P = ((P_suma_T *) suma_M->ModelData.defaultParam);
  real_T rtb_Suma;


  // Outputs for Atomic SubSystem: '<Root>/Calc_Suma'


  // Constant: '<Root>/Valor1' incorporates:
  //   Constant: '<Root>/Valor2'


  suma_Calc_Suma(suma_P->Valor1_Value, suma_P->Valor2_Value, &rtb_Suma);


  // End of Outputs for SubSystem: '<Root>/Calc_Suma'
}


// Model initialize function
void suma_initialize(RT_MODEL_suma_T *const suma_M)
{
  // (no initialization code required)
  UNUSED_PARAMETER(suma_M);
}


//
// File trailer for generated code.
//
// [EOF]
//

//
// File: ert_main.cpp
//
// Code generated for Simulink model 'suma'.
//
// Model version                  : 1.4
// Simulink Coder version         : 8.8 (R2015a) 09-Feb-2015
// C/C++ source code generated on : Thu Mar 10 02:52:06 2016
//
// Target selection: ert.tlc
// Embedded hardware selection: 32-bit Generic
// Code generation objectives:
//    1. Execution efficiency
//    2. ROM efficiency
//    3. RAM efficiency
//    4. Traceability
//    5. Safety precaution
// Validation result: Not run
//
#include <stddef.h>
#include <stdio.h>                     // This ert_main.c example uses printf/fflush 
#include "suma.h"                      // Model's header file
#include "rtwtypes.h"


static RT_MODEL_suma_T suma_M_;
static RT_MODEL_suma_T *const suma_M = &suma_M_;// Real-time model
static P_suma_T suma_P = {
  1.0,                                 // Expression: 1
                                       //  Referenced by: '<Root>/Valor1'


  8.0                                  // Expression: 8
                                       //  Referenced by: '<Root>/Valor2'


};                                     // Modifiable parameters


//
// Associating rt_OneStep with a real-time clock or interrupt service routine
// is what makes the generated code "real-time".  The function rt_OneStep is
// always associated with the base rate of the model.  Subrates are managed
// by the base rate from inside the generated code.  Enabling/disabling
// interrupts and floating point context switches are target specific.  This
// example code indicates where these should take place relative to executing
// the generated code step function.  Overrun behavior should be tailored to
// your application needs.  This example simply sets an error status in the
// real-time model and returns from rt_OneStep.
//
void rt_OneStep(RT_MODEL_suma_T *const suma_M);
void rt_OneStep(RT_MODEL_suma_T *const suma_M)
{
  static boolean_T OverrunFlag = false;


  // Disable interrupts here


  // Check for overrun
  if (OverrunFlag) {
    return;
  }


  OverrunFlag = true;


  // Save FPU context here (if necessary)
  // Re-enable timer or interrupt here
  // Set model inputs here


  // Step the model
  suma_step(suma_M);


  // Get model outputs here


  // Indicate task complete
  OverrunFlag = false;


  // Disable interrupts here
  // Restore FPU context here (if necessary)
  // Enable interrupts here
}


//
// The example "main" function illustrates what is required by your
// application code to initialize, execute, and terminate the generated code.
// Attaching rt_OneStep to a real-time clock is target specific.  This example
// illustates how you do this relative to initializing the model.
//
int_T main(int_T argc, const char *argv[])
{
  // Unused arguments
  (void)(argc);
  (void)(argv);


  // Pack model data into RTM
  suma_M->ModelData.defaultParam = &suma_P;


  // Initialize model
  suma_initialize(suma_M);


  // Attach rt_OneStep to a timer or interrupt service routine with
  //  period 0.2 seconds (the model's base sample time) here.  The
  //  call syntax for rt_OneStep is
  //
  //   rt_OneStep(suma_M);


  printf("Warning: The simulation will run forever. "
         "Generated ERT main won't simulate model step behavior. "
         "To change this behavior select the 'MAT-file logging' option.\n");
  fflush((NULL));
  while (((void*) 0) == (NULL)) {
    //  Perform other application tasks here
  }


  // The option 'Suppress error status in real-time model data structure'
  //  is selected, therefore the following code does not need to execute.


#if 0


  // Disable rt_OneStep() here
#endif


  return 0;
}


//
// File trailer for generated code.
//
// [EOF]
//

Esos 3 archivos son los más importantes, ya que tienen los procesos del modelo creado. El archivo ert_main.cpp no se usará en QT5, este solo sirve de referencia para poder saber como llamar las funciones y que cabeceras colocar en el archivo que controla las librerías del código generado.

Configuración QT5.

File->New File or Project


El proyecto va ha tener el nombre de suma. Escoger la ruta donde se desea guardar el proyecto.


Por el momento se va a trabajar con el compilador por defecto de QT5.


Si no ha parece la barra que muestra los archivos del código, ir a Window->Show Sidebar.



Agregar todos los archivos .h y .cpp generados por simulink.






Para poder visualizar el valor de las variables, se va usar "Qdebug" y "cout".

Ir al archivo suma.cpp y agregar lo siguiente.



Del archivo  ert_main.cpp, se deben agregar las cabeceras, prototipos y valores de variables. En este ejemplo se explicará como depurar el código si se obtiene un error por lo cuál, se realizará el siguiente procedemiento para ver que esta fallando en el código.

#include <stddef.h>
#include <stdio.h>                     // This ert_main.c example uses printf/fflush 
#include "suma.h"                      // Model's header file
#include "rtwtypes.h"


static RT_MODEL_suma_T suma_M_;
static RT_MODEL_suma_T *const suma_M = &suma_M_;// Real-time model
static P_suma_T suma_P = {
  1.0,                                 // Expression: 1
                                       //  Referenced by: '<Root>/Valor1'


  8.0                                  // Expression: 8
                                       //  Referenced by: '<Root>/Valor2'


};                                     // Modifiable parameters 

  // Step the model
  suma_step(suma_M); 

Agregar esto al archivo main.cpp de QT.



Al correr la aplicación se obtiene el siguiente error.


Para saber que esta ocurriendo se va ha usar el depurador de código.


Ese error ocurre porque suma_M es un puntero que necesita recibir los datos que se han guardado en forma de estructura en P_suma_T, los valores que se almacenan en P_suma_T son adquiridos de suma_P.

//Parte de código extraido del archivo suma.h

   struct {
    P_suma_T *defaultParam;
  } ModelData;
//Parte de código extraido del archivo main.cpp
static P_suma_T suma_P = {
  1.0,                                 // Expression: 1
                                       //  Referenced by: '<Root>/Valor1'




  8.0                                  // Expression: 8
                                       //  Referenced by: '<Root>/Valor2'




};                                     // Modifiable parameters


Para solucionar el problema, revisar nuevamente el archivo ert_main.cpp y se encontrará con una parte donde manda a llamar a los datos almacenados y el proceso de inicialización del modelo.

int_T main(int_T argc, const char *argv[])
{
  // Unused arguments
  (void)(argc);
  (void)(argv);


  // Pack model data into RTM
  suma_M->ModelData.defaultParam = &suma_P;


  // Initialize model
  suma_initialize(suma_M);


  // Attach rt_OneStep to a timer or interrupt service routine with
  //  period 0.2 seconds (the model's base sample time) here.  The
  //  call syntax for rt_OneStep is
  //
  //   rt_OneStep(suma_M);


  printf("Warning: The simulation will run forever. "
         "Generated ERT main won't simulate model step behavior. "
         "To change this behavior select the 'MAT-file logging' option.\n");
  fflush((NULL));
  while (((void*) 0) == (NULL)) {
    //  Perform other application tasks here
  }


  // The option 'Suppress error status in real-time model data structure'
  //  is selected, therefore the following code does not need to execute.


#if 0


  // Disable rt_OneStep() here
#endif


  return 0;
}

La parte que interesa es la resaltada con color amarrilo, copiar eso al archivo main.cpp.

 
Correr la aplicación.

 
2)  Suma -> Generando el código con Estructuras.

Para poder generar el código c++ de simulink con estructruras, es necesario hacer algunos cambios. El más importante es proporcionar una salida al modelo de simulink.

Out1: Simulink->sinks 





Aplicar los cambios y generar código.


Realizar los pasos de configuración de QT mencionados en el ejemplo anterior.

- Copiar los archivos generados por Simulink.
- Importarlos en QT
- Modificar los archivos "suma.cpp" y "main.cpp" para imprimir en consola. El recuadro gris indica como ha cambiado el código de Individual Arguments a Structures.

Nota: Se recomienda realizar la acción de limpiar el código generado "Clean" o si es necesario borrar la carpeta build generada por QT. Esto se lo realiza cuando se obtiene errores .exe o .obj.





3)  Suma -> Generando el código con Clases C++.

Para este ejemplo se va a modificar los parámetros de generación de código del subsistema. Se dará un nombre a la función del subsistema para poder crear cabeceras por separado.

Al igual que la generación de código "structure", es necesario poner una salida al modelo.

In1 : Simulink->Sources


En los parametros del subsistema "Calc_Suma", realizar los siguientes cambios.


Configurar los parámetros como "C++ class".



Si se obtiene un error es necesario activar una opción de la pestaña Optimiztion.


Para este ejemplo se va activar el reporte.


Aplicar cambios y generar código.


El reporte de código generado, indica que se han creado dos funciones adicionales a las que se tenían. Esto demuestra que del archivo suma.cpp se separo de la función de suma que se encarga de realizar la operación matemática. El cambio se genera por la modificación del modelo (subsistema), al asignar un nombre a la función.



Realizar los pasos de configuración de QT mencionados en el ejemplo anterior.

- Copiar los archivos generados por Simulink.
- Importarlos en QT.
- Modificar los archivos "suma.cpp" y "main.cpp" para imprimir en consola.



Al ser la función "step" real_T(double),  puede retornar el resultado de la suma. Con esto se logra poder imprimir el resultado desde el archivo main.cpp.

Se puede dar buen uso de la librería stdio.h para imprimir en consola.

    printf("Ingrese el valor1: ");
    scanf_s("%lf", &arg_Valor1);



4)  Matriz Determinante, Operaciones Matemáticas e Interfaz gráfica en QT5.

En este ejemplo se va ha usar todos los pasos anteriores y se va ha configurar con los parámetros mencionados en la parte de Pasos Opcionales. El método que se usará para generar el código es el de "C++ class".

1 Create 3x3 Matrix : Aerospace Blockset -> Utilities -> Math Operations.
1 Determinant of 3x3 Matrix : Aerospace Blockset -> Utilities -> Math Operations.
2 MATLAB Function : Simulink -> User-Defined Functions
10 In1 : Simulink -> Sources
3 Out1 : Simulink -> Sinks
3 Bus Creator :  Simulink -> Signal Routing
3 Bus Selector : Simulink -> Signal Routing
1 Display : Simulink -> Sinks


Poner nombres a las entradas y salidas de las señales del puerto. Se puede poner de dos formas.





La otra es dar doble click sobre la línea.



Al ir a los parámetros del Bus Creator, ya se tendrán las señales con el nombre asignado a las entradas.


Para los parámetros del Bus Selector se deben escoger las señales que se quieren utilizar.



Al finalizar de asignar los nombres a los bloques y señales de entradas-salidas, se configuran los bloques de MATLAB Function. Dar doble click y programar cada uno de la siguiente manera.







Al finalizar, se creará un script en matlab para poder importar las variables del workspace a simulink y simular el modelo realizado.

V_A11 = [0,4];
V_A12 = [0,7];
V_A13 = [0,9];
V_A21 = [0,3];
V_A22 = [0,6];
V_A23 = [0,4];
V_A31 = [0,1];
V_A32 = [0,8];
V_A33 = [0,5];
Input_Val = [0,2];


Es necesario que las variables sean un array, caso contrario no se podrá importar la variable como señal. Para más información revizar el siguiente link Matlab map data using root inport mapping tool.





Aplicar los cambios realizados y correr la simulación.



Si se quiere poner nombre a la función del subsistema, la configuración va ha ser la de los Pasos Opcionales y del Ejemplo "C++ class".

Verificar si el subsistema tiene asiganadas correctamente las señales.


Configuración.












Aplicar cambios y Generar código.


include <stddef.h>
//
// File: DeterminanteOP.cpp
//
// Code generated for Simulink model 'DeterminanteOP'.
//
// Model version                  : 1.9
// Simulink Coder version         : 8.8 (R2015a) 09-Feb-2015
// C/C++ source code generated on : Sat Mar 12 15:24:11 2016
//
// Target selection: ert.tlc
// Embedded hardware selection: ARM Compatible->ARM Cortex
// Code generation objectives:
//    1. Execution efficiency
//    2. ROM efficiency
//    3. RAM efficiency
//    4. Traceability
//    5. Safety precaution
// Validation result: Not run
//
#include "DeterminanteOP.h"
#include "DeterminanteOP_private.h"

// Output and update for atomic system: '<Root>/DeterminanteOP'
void DeterminanteOP_DeterminanteOP(real_T rtu_In1, real_T rtu_In2, real_T
  rtu_In3, real_T rtu_In4, real_T rtu_In5, real_T rtu_Input_Val, real_T rtu_In7,
  real_T rtu_In8, real_T rtu_In9, real_T rtu_In10,
  B_DeterminanteOP_Determinante_T *localB)
{
  // Sum: '<S3>/Sum' incorporates:
  //   Product: '<S3>/Product'
  //   Product: '<S3>/Product1'
  //   Product: '<S3>/Product2'
  //   Product: '<S3>/Product3'
  //   Product: '<S3>/Product4'
  //   Product: '<S3>/Product5'
  //   SignalConversion: '<S2>/ConcatBufferAtVector ConcatenateIn1'
  //   SignalConversion: '<S2>/ConcatBufferAtVector ConcatenateIn2'
  //   SignalConversion: '<S2>/ConcatBufferAtVector ConcatenateIn3'
  //   SignalConversion: '<S2>/ConcatBufferAtVector ConcatenateIn4'
  //   SignalConversion: '<S2>/ConcatBufferAtVector ConcatenateIn5'
  //   SignalConversion: '<S2>/ConcatBufferAtVector ConcatenateIn6'
  //   SignalConversion: '<S2>/ConcatBufferAtVector ConcatenateIn7'
  //   SignalConversion: '<S2>/ConcatBufferAtVector ConcatenateIn8'
  //   SignalConversion: '<S2>/ConcatBufferAtVector ConcatenateIn9'

  localB->detMatrix = ((((rtu_In1 * rtu_In5 * rtu_In10 - rtu_In1 * rtu_In9 *
    rtu_In7) - rtu_In4 * rtu_In2 * rtu_In10) + rtu_In8 * rtu_In2 * rtu_In7) +
                       rtu_In4 * rtu_In9 * rtu_In3) - rtu_In8 * rtu_In5 *
    rtu_In3;

  // MATLAB Function: '<S1>/func_producto' incorporates:
  //   Inport: '<S1>/Input_Val'

  // MATLAB Function 'DeterminanteOP/func_producto': '<S4>:1'
  // '<S4>:1:4' producto = R_determinante*Input_Val;
  localB->producto = localB->detMatrix * rtu_Input_Val;

  // MATLAB Function: '<S1>/func_suma' incorporates:
  //   Inport: '<S1>/Input_Val'

  // MATLAB Function 'DeterminanteOP/func_suma': '<S5>:1'
  // '<S5>:1:4' suma = R_determinante+Input_Val;
  localB->suma = localB->detMatrix + rtu_Input_Val;
}

// Model step function
void DeterminanteOPModelClass::step(real_T *arg_V_A11, real_T *arg_V_A12, real_T
  *arg_V_A13, real_T *arg_V_A21, real_T *arg_V_A22, real_T *arg_V_A23, real_T
  *arg_V_A31, real_T *arg_V_A32, real_T *arg_V_A33, real_T *arg_Input_Val,
  real_T *arg_R_determinante, real_T *arg_Suma, real_T *arg_Producto)
{
  // Outputs for Atomic SubSystem: '<Root>/DeterminanteOP'

  // Inport: '<Root>/V_A11' incorporates:
  //   Inport: '<Root>/Input_Val'
  //   Inport: '<Root>/V_A12'
  //   Inport: '<Root>/V_A13'
  //   Inport: '<Root>/V_A21'
  //   Inport: '<Root>/V_A22'
  //   Inport: '<Root>/V_A23'
  //   Inport: '<Root>/V_A31'
  //   Inport: '<Root>/V_A32'
  //   Inport: '<Root>/V_A33'

  DeterminanteOP_DeterminanteOP(*arg_V_A11, *arg_V_A12, *arg_V_A13, *arg_V_A21, *
    arg_V_A22, *arg_Input_Val, *arg_V_A23, *arg_V_A31, *arg_V_A32, *arg_V_A33,
    &DeterminanteOP_B.DeterminanteOP_f);

  // End of Outputs for SubSystem: '<Root>/DeterminanteOP'

  // Outport: '<Root>/R_determinante'
  *arg_R_determinante = DeterminanteOP_B.DeterminanteOP_f.detMatrix;

  // Outport: '<Root>/Suma'
  *arg_Suma = DeterminanteOP_B.DeterminanteOP_f.suma;

  // Outport: '<Root>/Producto'
  *arg_Producto = DeterminanteOP_B.DeterminanteOP_f.producto;
}

// Model initialize function
void DeterminanteOPModelClass::initialize()
{
  // Registration code

  // initialize error status
  rtmSetErrorStatus((&DeterminanteOP_M), (NULL));

  // block I/O
  (void) memset(((void *) &DeterminanteOP_B), 0,
                sizeof(B_DeterminanteOP_T));
}

// Model terminate function
void DeterminanteOPModelClass::terminate()
{
  // (no terminate code required)
}

// Constructor
DeterminanteOPModelClass::DeterminanteOPModelClass()
{
}

// Destructor
DeterminanteOPModelClass::~DeterminanteOPModelClass()
{
  // Currently there is no destructor body generated.
}

// Real-Time Model get method
RT_MODEL_DeterminanteOP_T * DeterminanteOPModelClass::getRTM()
{
  return (&DeterminanteOP_M);
}

//
// File trailer for generated code.
//
// [EOF]
//

Descargar archivos:

DeterminanteOP

Configuración QT.





Copiar y pegar los archivos generados por simulink a la carpeta creada por QT e importar los archivos.



Crear la Interfaz gráfica y asignar nombres a los objetos.



Para probar la Interfaz gráfica, correr QT con el compilador que viene por defecto.





Crear el slot -> clicked() en el botón calcular, copiar lo siguiente en el archivo determinanteop_gui.cpp. En este archivo ira todo lo que antes iba en main.cpp, ya que ahora en main.cpp se manda a llamar a la Interfaz gráfica.

#include "determinanteop_gui.h"
#include "ui_determinanteop_gui.h"
#include <stddef.h>
#include <stdio.h>                     // This ert_main.c example uses printf/fflush
#include "DeterminanteOP.h"            // Model's header file
#include "rtwtypes.h"
#include <QLabel>                     //Utiliza Qstring 


static DeterminanteOPModelClass DeterminanteOP_Obj;// Instance of model class


// '<Root>/V_A11'
static real_T arg_V_A11 = 0.0;


// '<Root>/V_A12'
static real_T arg_V_A12 = 0.0;


// '<Root>/V_A13'
static real_T arg_V_A13 = 0.0;


// '<Root>/V_A21'
static real_T arg_V_A21 = 0.0;


// '<Root>/V_A22'
static real_T arg_V_A22 = 0.0;


// '<Root>/V_A23'
static real_T arg_V_A23 = 0.0;


// '<Root>/V_A31'
static real_T arg_V_A31 = 0.0;


// '<Root>/V_A32'
static real_T arg_V_A32 = 0.0;


// '<Root>/V_A33'
static real_T arg_V_A33 = 0.0;


// '<Root>/Input_Val'
static real_T arg_Input_Val = 0.0;


// '<Root>/R_determinante'
static real_T arg_R_determinante;


// '<Root>/Suma'
static real_T arg_Suma;


// '<Root>/Producto'
static real_T arg_Producto;


DeterminanteOP_GUI::DeterminanteOP_GUI(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::DeterminanteOP_GUI)
{
    ui->setupUi(this);
}


DeterminanteOP_GUI::~DeterminanteOP_GUI()
{
    delete ui;
}


void DeterminanteOP_GUI::on_Calcular_btn_clicked()
{

    //Asigna los valores ingresados y los convierte de string a entero.
    arg_V_A11=ui->V_A11->text().toInt();
    arg_V_A12=ui->V_A12->text().toInt();
    arg_V_A13=ui->V_A13->text().toInt();
    arg_V_A21=ui->V_A21->text().toInt();
    arg_V_A22=ui->V_A22->text().toInt();
    arg_V_A23=ui->V_A23->text().toInt();
    arg_V_A31=ui->V_A31->text().toInt();
    arg_V_A32=ui->V_A32->text().toInt();
    arg_V_A33=ui->V_A33->text().toInt();
    arg_Input_Val=ui->InVal->text().toInt();
    // Step the model
    DeterminanteOP_Obj.step(&arg_V_A11, &arg_V_A12, &arg_V_A13, &arg_V_A21,
      &arg_V_A22, &arg_V_A23, &arg_V_A31, &arg_V_A32, &arg_V_A33, &arg_Input_Val,
      &arg_R_determinante, &arg_Suma, &arg_Producto);
    //Imprime el valor Calculado en las etiquetas.
    QString R_determinante = QString::number(arg_R_determinante);
    ui->DetVal->setText(R_determinante);
    QString suma = QString::number(arg_Suma);
    ui->Suma->setText(suma);
    QString producto = QString::number(arg_Producto);
    ui->Producto->setText(producto);
}




Ahora solo queda correr la aplicación en la Beaglebone Black, en este caso la aplicación correra por conexión remota.

No olvidar configurar el archivo .pro para que corra la aplicación en la Beaglebone Black



Descargar Archivos:

DeterminanteOP.

Referencia: http://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/20120014980.pdf.

3 comentarios:

  1. Muy buenos aportes Leonardo! Soy fan de tu blog XD

    ResponderEliminar
  2. Muchas gracias, pronto seguiré subiendo más tutoriales.

    ResponderEliminar
  3. excelente trabajo Leonardo Noguera Armas, muy agradecido por su gentil aporte.

    ResponderEliminar