Mostrando entradas con la etiqueta errores de programación. Mostrar todas las entradas
Mostrando entradas con la etiqueta errores de programación. Mostrar todas las entradas

19 de abril de 2011

Access violation..Error al depurar un programa en Keil

Cuando ejecutas en Keil paso a paso un programa para un microcontrolador y no has incluido el bucle "while(1){..}" tu programa se ejecutará sólo una vez y al llegar al final, continuará buscando instrucciones, produciendo un error. Supongamos que tenemos un programa así:


Debes añadir un bucle "while(1)" para que no termine su ejecución. Quedaría así:



Descarga el programa aquí.

11 de febrero de 2011

SimSeny no se lleva bien con el tratamiento de errores..

Un alumno me envía un problema que tiene al intentar activar una salida. Parece que la tarjeta de inicializa bien, pero al hacer clic en el botón "Activar calefactor" se muestra el siguiente error:


La solución es sencilla, debes ir a la función que genera el error (en este caso "proceso_escribir_calefactor()" y comentar la línea correspondiente a la llamada de la función "proceso_error" (resaltada en color rojo en la imagen de abajo):


Una vez hecho, al activar la salida, podrás ver en el simulador como cambian los niveles y así comprobar que lo estás haciendo bien (que se activa sólo una salida y no todo el puerto por ejemplo):

Con el simulador abierto puedes ir haciendo clic en los botones activar/desactivar calefactor/válvula de tu programa y comprobar si cambian los niveles en las salidas (H/L). Es una manera magnífica de ver si lo estás haciendo bien.

Recuerda que estamos en un entorno de simulación que nos permite trabajar sin tener el "HW". En situaciones reales, deberás tener muy presente el tratamiento de errores para poder determinar, en caso de error, donde puede estar el fallo.
..
..

Al iniciar el programa..los temporizadores deberían estar apagados

En los proyectos se debe incluir uno o varios temporizadores para controlar la frecuencia de refresco de las entradas/salidas. Un alumno me envía un correo comentando los errores que se muestran al iniciar el programa:


Son dos mensajes, uno en el fondo con el título "SimSenyII" y otro delante "ssDAQmxReadAnalogScalarF64()..". Esta claro que el primero es del simulador, que intenta arrancar (cuando se llama a alguna función de la tarjeta) y el segundo corresponde a una función de la tarjeta de adquisición..Estos errores aparecen sin intervención del usuario, nada más arrancar, ésto sugiere que es debido a un temporizador que debería estar parado.

Lo que sucede aquí es bastante frecuente, arrancar la aplicación con los temporizadores ENCENDIDOS. Como el timer se dispara antes de poder inicializar el simulador y la tarjeta, todo salta por los aires, no se puede empezar a usar una función de la tarjeta si antes no se ha inicializado..

Solución: Busca el temporizador en el formulario y desactívalo cambiando su propiedad "Enabled=false", de este modo, al arrancar el programa, el timer estará apagado:


De esta manera el sistema estará inicializado pero detenido hasta que el usuario determine los valores deseados y el modo (manual o automático) .

Si se usa el modo automático, habrá que activar el temporizador:
"Form1->Timer1->Enabled=1" (el timer es el "encargado" de controlar el sistema)

Si se usa el modo manual, habrá que detener el temporizador:
"Form1->Timer1->Enabled=0" (el usuario controla las salidas, el timer no hace falta)

Por último, yo añadiría un botón nuevo con el que puedas inicializar la tarjeta, añade uno al formulario, cámbiale el nombre y haz doble clic en para escribir el código que deberá ejecutarse al pulsarlo:
"proceso_inicializar_proceso();" (inicia la tarjeta y el simulador)

De este modo, cuando arranques la aplicación, pulsa primero en el botón y tendrás las tarjeta lista para usar. A partir de este momento ya puedes llamar a las funciones que quieras.

Esto es una parte de la solución.
Ahora el programa permite inicializar la tarjeta y elegir el modo de funcionamiento, sin errores.
..

10 de febrero de 2011

Funciones mal borradas..funciones desaparecidas

Durante la creación de un programa creas y borras funciones. Cuando borras, como es tan fácil hacerlo, puede ser un arma de doble filo si no lo haces correctamente. Si borras una función también deberás borrar su definición en la cabecera del módulo.

Si no lo haces, el compilador encontrará la definición de una función que le dirá "aquí se ha creado una función con estos argumentos y que puedes encontrar aquí al lado.." y...cuando se va a mirar el código de la función, resulta que no está porque la hemos borrado, así de golpe..

El error es parecido al que sale cuando no se encuentra alguna función de la tarjeta.
Podría ser algo así:


Aparecen un par de errores "[Linker Error] Unresolved external__" que hacen referencia a funciones que fueron creadas por el usuario: "CalentadorClick" y "Button2Click". Estas funciones se crean automáticamente cuando el usuario hace doble clic en algún botón. Si más adelante las borras, porque has eliminado el botón o por cualquier otro motivo DEBES BORRARLAS BIEN.

Para hacerlo correctamente, debes ir al módulo que indica el error y mostrar la cabecera del mismo. Haz clic con el botón derecho sobre el título de la unidad y selecciona "Open Source/Header File":




Una vez hecho, podrás ver el contenido de la cabecera, con todas las definiciones de las funciones usadas en el programa. Podría ser algo así:



Recordando un poco, en la cabecera aparecen todas las definiciones de las funciones creadas. Aquí sólo están sus nombres, argumentos que usarán (void, int, double..) y los datos que devolverán. Para cada función creada en ".cpp" aquí habrá una definición única.

Si borras una función de ".cpp" debes borrarla también de la cabecera ".h". Si no lo haces, el compilador encontrará una definición de la función en la cabecera pero no encontrará la función en el cuerpo del programa y se quejará..con un error tipo "[Link Error]"

La solución pasa por borrar de la cabecera aquellas funciones que el usuario creyó haber borrado (aquellas que aparecen referenciadas en el error).

El error también podría generarse al olvidar algún "include" que impediría encontrar las funciones buscadas, pero lo normal es que sea por el motivo anterior, de hecho si en la cabecera está definida la función y no está en el cuerpo, es casi seguro que la borraste así "de golpe".

Si olvidaras un "include" verías un error de este tipo:


Recuerda: una vez hayas eliminado las definiciones de la cabecera, no olvides volver al cuerpo de la unidad con la misma operación (botón derecho sobre el nombre de la unidad).
.

Cannot convert 'int (*)() to 'int'

Cuando accedemos a las variables del módulo de datos, por ejemplo durante la inicialización del sistema, usamos funciones creadas por el usario. Recuerda que para cada variable asociada a un sensor o actuador, has creado funciones de lectura y escritura. Nos encontramos este error:



En la figura anterior, aparece un error de conversión. Esto puede suceder cuando al llamar a una función, ésta no entiende muy bien los datos que se le pasan o se le devuelve un dato que no coincide con lo esperado. Por ejemplo podría darse el caso de esperar un entero (int) y le ha venido de golpe un doble (double) u otro tipo.

En la instrucción marcada en rojo de la figura, lo que se trata de hacer es actúar sobre la válvula, abriéndola o cerrándola físicamente. El valor que se le pasará será aquel que el usuario haya decidido, haciendo clic en un botón o de cualquier otro modo en el interfaz gráfico. Cuando lo haga, este valor se guardará en el módulo de datos para mantener la imagen del proceso.

Aquí es donde está el problema, en la llamada a la función, se le pasa como parámetro "VerVálvula" pero debería ser "VerVálvula()" ya que es la función del módulo de datos que consulta la variable.

Al olvidar los paréntesis, el compilador busca una variable llamada "VerVálvula" y genera el error porque lo que encuentra es una función.

Recuerda: aunque la función no tenga argumentos - void VerVálvula(void) - al llamarla siempre hay que incluirlos.

No encuentra las funciones de la tarjeta DAQ

Cuando se compila un programa, pueden aparecer muchos errores relacionado con las funciones de la tarjeta de adquisición. Los errores que aparecen pueden parecerse a los siguientes:


Los errores tipo: "[C++ Warnings]", son advertencias pero no detienen la ejecución. La compilación se va a parar en seco cuando encuentre errores de tipo: "[Link Error]". En una de las líneas con este error, dice que no puede encontrar una función externa "DAQmxReadAnalogScalar64..".

Aunque hayamos copiado la librería (fichero.lib) de la tarjeta DAQ en la carpeta del proyecto, quizá no le hayamos indicado al compilador que está allí. El compilador es un poco perezoso y no va a mirar a menos que se lo indiques.

Para resolverlo, activa la opción "View/Project manager", verás la estructura del proyecto:


Lo que ves en la figura anterior es una lista de los ficheros que "mirará" el compilador. Si algo no está en esta "lista", es como si no existiera (aunque físicamente esté copiado en la carpeta del proyecto). Puedes ver todas las unidades del proyecto (ficheros.cpp) y la cabecera de la librería (NIDAQmx.h), pero NO la librería (NIDAQmx.lib). Búscala y añadela.

Selecciona la opción "Project/Add to project", localiza la librería y selecciónala:


Ahora comprueba de nuevo desde "View/Project manager" que la librería ya está en la lista. De este modo, el compilador ya sabe donde se encuentran todas las funciones que necesita para compilar tu programa.


Cuando compiles de nuevo, los errores generados por las funciones de la tarjeta habrán desaparecido, pero nos quedarán otros asociados con las funciones que ha creado el usuario (eso en otro artículo).

29 de enero de 2011

Pregunta de examen

Pregunta de examen (Test Enero 2010):
7. Observa detenidamente el siguiente código en C y determina el valor de la variable “z” una vez ejecutadas todas las sentencias.

int x,y,z;
x=8;
y=4;
if(x=y){z=x*y;}
else{z=x+y;}
a) 0x12
b) 0x0C
c) 0x10

Respuesta:
En la sentencia de comparación "if(x=y) {z=x*y}" es donde está la clave.
Como sabes, una sentencia "if" compara un valor con otro y si se cumple la condición, ejecuta la sentencia que le sigue. Pero la sentencia "if" puede funcionar de manera diferente...si nos olvidamos algo..

En este problema, hay un fallo, la sentencia correcta sería "if(x==y)".
Recuerda que en una comparación siempre deben incluirse dos signos "=".

Por lo tanto al incluir sólo un signo "=", la sentencia NO COMPARA, ASIGNA:

if (x=y) >> ASIGNA EL VALOR DE "y" A "x" (ambas valen ahora 4).
A continuación, se ejecuta la sentencia "{z=x*y}"  z=4*4=16=0x10.

Si tienes curiosidad y quieres comprobarlo, puedes abrir un proyecto en Builder y probarlo.
Añade un objeto tipo "Label" y asígnale el valor de z. Comprobarás que al poner sólo un signo "=" el la etiqueta aparece 16, pero si lo haces correctamente con "==" en la etiqueta aparecerá 12.

..

26 de octubre de 2010

Quiero cargar otro formulario en Borland C++

"He creado un proyecto en Borland C++, he guardado correctamente y al compilar, mi programa abre un formulario que no es el que deseaba. ¿Qué está ocurriendo? ¿Cómo puedo hacer que mi programa abra el formulario que deseo si tengo varios?" - Ana.

Primera Opción:

Cuando compilamos un programa, el primer formulario que se muestra en pantalla está determinado por las instrucciones del bloque principal "Project1.cpp". Selecciona la opción "View/Project manager" para abrir el gestor de proyectos y haz doble clic en "Project1.cpp":


"Project1.cpp" podría tener otro nombre si lo has renombrado al crear tu proyecto. Una vez abierto, verás algunas instrucciones dentro. Puedes ver una muestra en la figura siguiente:


En la primera línea aparece: "Useform("Unit1.cpp",Form1)".
En esta instrucción se declara el formulario creado en la unidad "Unit1.cpp". Para cada formulario en el proyecto, aquí habrá una línea. Recuerda que un formulario está vinculado a las unidades.

Más adelante aparece: "Application..CreateForm".
Dicha instrucción crea los formularios. También habrá una instrucción para cada formulario. El formulario al que se haga referencia en la primera línea será el primero en cargarse (el que verá el usuario). Veamos un ejemplo:


En este proyecto tienes el bloque principal "Project1.cpp" y las dos unidades "Unit1.cpp" y "Unit3.cpp", con sus correspondientes formularios "Form1" y "Form3". Vamos a echar un vistazo al código del bloque principal "Project1.cpp" que es el encargado de cargar el formulario inicial:


En la figura anterior se puede ver que se han definido los dos formularios en las primeras líneas con la instrucción  "Useform". Más adelante se crean con la instrucción "Application..CreateForm". Es importante fíjarse en el orden, primero se crea el formulario "Form1" y después "Form3", por lo tanto el que aparecerá al ejecutar la aplicación será el primero: "Form1".

Si se quisiera mostrar inicialmente el segundo, sólo se tendría que cambiar el orden de las instrucciones.

Segunda Opción:

Esta operación puedes realizarla también configurando las opciones del proyecto. Activa la opción del menú "Project/Options":


Se muestra la ventana de configuración de tu proyecto. Haz clic en la pestaña "Forms":


En la figura anterior puedes ver que en la zona de la izquierda aparecen los formularios usados por la aplicación (en este caso hay dos). Justo arriba en el apartado "Main form" puedes definir el formulario principal de tu aplicación. Aparece seleccionado "Form1", éste será el que se abrirá primero. Si quieres cambiarlo, sólo tienes que abrir la lista desplegable y seleccionar otro.

Esta operación lo que hace es cambiar el orden de las instrucciones que se acaban de explicar unos párrafos más arriba. Puedes comprobarlo cambiando el formulario por defecto aquí y abriendo el bloque principal de tu programa "Project1.cpp", busca las instrucciones "Application..CreateForm" y comprueba que se ha cambiado el orden de las instrucciones.

----
tiempo edición: 2'30h.

Elementos de un proyecto nuevo en Borland C++

"No tengo muy claro cual es la estructura de un proyecto, ¿por favor podrías explicármelo un poco? Gracias." - José.

Cuando empiezas un proyecto nuevo en Borland, se crean una serie de archivos. Para ver los elementos que componen dicho proyecto, puedes activar la opción del menú "View/Project manager":



















"Project1.cpp" es el bloque de código que "arranca" tu programa (contiene el "main"). Normalmente no escribirás tu código aquí.

"Unit1.cpp" es un bloque o segmento de código donde escribirás tu programa. También se llama "unidad" y puedes tener las unidades que desees, pero cuando se crea un proyecto nuevo, sólo se crea una por defecto. Un buen programa tendrá varias unidades y cada una incluirá funciones diferentes, de modo que el código esté bien organizado.

"Form1" es el formulario. Cuando se crea una unidad, Borland crea también un formulario vinculado a ella. Puedes ver en la figura anterior que justo debajo de "Unit1.cpp" aparece "Form1". Un formulario te permitirá incluir elementos gráficos como botones, etiquetas y otros objetos que dotarán a tu aplicación de la necesaria interactividad.

En el siguiente diagrama puedes ver la estructura general de un proyecto. Del fichero "Project1.cpp" dependen las diferentes unidades "Unit1.cpp", "Unit2.cpp", etc. Observa que cada formulario está vinculado a una unidad.

Importante: Si eliminas de tu proyecto una unidad, también estarás borrando su formulario asociado.


Lo verás mejor si activas el modo "Pantalla completa".
(icono situado en la esquina inferior derecha del video)


----
t.edición del post 1'30h
grabación y publicación en HD
insertado en ventana, no enlace

11 de mayo de 2010

Definir puertos como entradas o salidas

Cuando vamos a utilizar los puertos para leer sensores o manejar actuadores debemos especificar si estos puertos serán entradas o salidas, es decir, definir la dirección de los datos.

Para definir todas las líneas del puerto "P0" como entradas:
P0 = 0xFF; // todos los bits del puerto cero como entradas

Para definir un puerto como salidas, no hace falta hacer nada, en este micro, por defecto están configurados como salidas.

Puedes ver un ejemplo en el siguiente código.
Este programa lee un bit del puerto "P0" y activa una salida en el puerto "P1":

sfr P0=0x80;
sfr P1=0x90;
sbit P0_0=P0^0;
sbit P1_7=P1^7;

void main(void){

P0=0xFF; // definimos todo el puerto P0 como entradas
// el puerto P1 configurado como salida por defecto

while (1){

if (P0_0==0) P1_7=1; // comprueba nivel bit P0.0
else P1_7=0;

}}

Nota: Si no tienes en cuenta este paso definiendo la dirección de los datos, cuando simules en el compilador Keil, tendrás un error y en ocasiones el programa podrá terminar inesperadamente.

Prueba a compilar y simular este programa sin definir la dirección de los puertos y observa como al cambiar el estado de los bits en los puertos, el programa se bloquea (trabajando con la versión 2 de Keil).

26 de marzo de 2010

Error "Access violation at"...al depurar

Diego (curso 2009-2010) escribe un programa, lo compila con Keil y comienza la depuración. Empieza a usar "F11" para ejecutar paso a paso y encuentra el siguiente error:








El código es el siguiente:

sfr P1=0x90;
sfr P2=0xA0;
void main(void)
{int i;
i=P1&0x07;
switch(i){
case 0:P2=0x3f; break;
case 1:P2=0x06; break;
case 2:P2=0x5b; break; }
}

Cuando se graba un programa en el micro, empieza a ejecutarse inmediatamente desde el punto de entrada (main). Si te fijas, en el programa del ejemplo, sólo se ejecuta una vez ya que no está dentro de ningún bucle.

En un microcontrolador hay que 'encerrar' al programa en un bucle infinito (dentro de un "while"). El motivo es que al grabar el programa en la memoria y comienza la ejecución si no tiene un bucle rápidamente llega al final, pero no se detiene y sigue buscando instrucciones en las posiciones de memoria siguientes, pero como no encuentra nada 'coherente' se genera un error de ejecución. Por lo tanto el programa escrito correctamente sería:

sfr P1=0x90;
sfr P2=0xA0;
void main(void)
{
int i;
while(1) {
i=P1&0x07;
switch(i){
case 0:P2=0x3f; break;
case 1:P2=0x06; break;
case 2:P2=0x5b; break; }
}
}

23 de marzo de 2010

No olvides el punto de entrada al programa

Intenta compilar este programa utilizando Keil:

sfr P1 = 0x90;
sbit e0 = P1^0;
sbit e1 = P1^1;
sbit s0 = P1^4;
sbit s1 = P1^5;
while (1) {
if (e0==0 && e1==0){s0=1;s1=1;}
}

En un programa escrito en Keil, tenemos dos bloques:
1.- La definición de los registros a utilizar (sfr) y
2.- Punto de entrada del programa (main).

En el código anterior, no se ha incluido el segundo bloque, por lo tanto al intentar compilar el programa se produce un error. Recuerda que todo programa debe tener un punto de arranque, por donde empieza la ejecución. Si no incluyes el "main", nuestro programa no funcionará. La versión correcta del programa sería:

// definición de registros
sfr P1 = 0x90;
sbit e0 = P1^0;
sbit e1 = P1^1;
sbit s0 = P1^4;
sbit s1 = P1^5;

// punto de entrada del programa
void main(void) {
while (1) {

if (e0==0 && e1==0)
{s0=1;s1=1;}

}}

9 de marzo de 2010

Inicializar las variables dentro del main

Tienes el siguiente programa escrito en C. El objeto del mismo es complementar todos los bits de el registro P0...

sfr P0 = 0x80; //define el puerto P0
P0 = 0x00; // intentamos asignar un valor al puerto P0...
void main (void)
{while (1)
{P0=~P0;}
}

...pero al compilarlo en Keil obtienes el siguiente error: "error C231 : 'P0' : redefinition".
¿Qué está pasando? Al asignar un valor al puerto en la instrucción 'P0 = 0x00;' lo hemos hecho fuera del programa principal (fuera del main) y el compilador interpreta que la instrucción es una definición de una variable (a la que asigna un valor directamente), pero como esta variable ha sido definida en la instrucción anterior 'sfr P0 = 0x80;' el compilador no lo permite.

La solución es inicializar las variables dentro del main.
Recuerda: Define todas las variables que vayas a necesitar antes del 'main' e inicialízalas dentro.