jueves, 29 de diciembre de 2011

555 Astable

El circuito 555 es un circuito muy utilizado en temporizadores y aquí os traigo un ejemplo del circuito 555 funcionando como Astable, es una captura de pantalla donde podéis ver cómo hay que configurarlo y el resultado que produce:

martes, 27 de diciembre de 2011

Punteros y Variables Dinámicas

Términos muy importantes a la hora de programar son los punteros y las variables dinámicas , son fundamentales, por ello en este post vamos a tratar de explicarlos para que podamos entenderlos de la mejor manera posible.

La estructura secuencia:

          La estructura secuencia puede definirse como un esquema de datos del tipo iterativo, pero con un número variable de componentes. La estructura secuencia resulta parecida a una formación con número variable de elementos. Las operaciones de construcción pueden incluir: añadir o retirar componentes al principio de la secuencia, añadir o retirar componentes al final de la secuencia, añadir o retirar componentes en posiciones intermedias de la secuencia, etc. Las operaciones de acceso pueden ser: acceso secuencial las componentes deben tratarse una por una, en el orden en que aparecen en la secuencia, acceso directo se puede acceder a cualquier componente directamente indicando su posición, como en una formación o vector.

          En muchos casos, y en particular cuando el acceso es secuencial, el tratamiento de una secuencia se realiza empleando un cursor. El cursor es una variable que señala a un elemento de la secuencia. El acceso, inserción o eliminación de componentes de la secuencia se hace actuando sobre el elemento señalado por el cursor. Dicho elemento lo representaremos simbólicamente como cursor empleando una flecha como símbolo gráfico para designar el elemento de información señalado por otro. Para actuar sobre el cursos se suelen plantear las siguientes operaciones: iniciar que pone el cursos señalando el primer elemento, avanzar que pasa el cursor al siguiente elemento y fin que es una función que indica si el cursor ha llegado al final de la secuencia.

Variables dinámicas:

          Una manera de realizar estructuras de datos ilimitadas en C+- es mediante el empleo de variables dinámicas. Una variable dinámica no se declara como tal, sino que se crea en el momento necesario, y se destruye cuando ya no se necesita. Las variables dinámicas no tienen nombre, sino que se designan mediante otras variables llamadas punteros o referencias.

Punteros:

          En C+- los punteros o referencias son variables simples cuyo contenido es precisamente una referencia a otra variable. El valor de un puntero no es representable como número o texto. Para poder detectar si un puntero señala realmente o no a otra variable, existe en C+- el valor especial NULL. Este valor es compatible con cualquier tipo de puntero, e indica que el puntero no señala a ninguna parte, por lo tanto debería ser asignado a cualquier puntero que sepamos que no señala a ninguna variable. Normalmente se usará para inicializar las variables de tipo puntero al comienzo del programa. La inicialización no es automática, sino que debe ser realizada expresamente por el programador. Esta sentencia garantiza que sólo se usa la variable apuntada cuando realmente existe.

Uso de variables dinámicas:

          Una variable de un programa se corresponde, en general, con una zona concreta de la memoria que el compilador reserva para almacenaren ella el valor de la variable. La forma mas sencilla de crear una variable dinámica es mediante el operador new. La variable dinámica se crea a base de reservarle el espacio necesario en una zona general de memoria gestionada dinámicamente. La sentencia delete, permite destruir la variable dinámica que señala un puntero. Una variable dinámica puede estar referenciada por más de un puntero. Esto ocurre cuando se copia un puntero en otro. Un problema delicado al manejar variables dinámicas es que pueden quedar perdidas, sin posibilidad de hacer referencia a ellas. Una variable perdida es totalmente inútil y representa una pérdida de la capacidad de memoria disponible.

Una definición recursiva es aquella en que se hace referencia a sí misma. Sería deseable que una secuencia ilimitada se pudiese definir de manera recursiva, sin necesidad de punteros.

Punteros y paso de argumentos:

          Un puntero puede pasarse como argumento a un subprograma. En general el valor de un puntero en sí mismo no es significativo, sino que el puntero es sólo un medio para designar la variable apuntada. Desde un punto de vista conceptual el paso de un puntero como argumento puede ser considerado equivalente a pasar como argumento la variable apuntada.

jueves, 8 de diciembre de 2011

Circuito codificador para controlar ALU 74181

La ALU 74181 es una Unidad Aritmético Lógica que está implementada con un circuito integrado TTL de la serie 7400 y cómo dato anecdótico fue la primera ALU completa en un chip.

En este post os traigo una captura de pantalla de un circuito codificador que tuve que hacer para una asignatura para controlar una ALU 74181 dependiendo de si la palabra que entrara era: A>B, si A=B y si A<B

Circuito codificador ALU 74181
Circuito Codificador para ALU 74181

domingo, 4 de diciembre de 2011

Tipos en C+-

Pequeño esquema resumen con los tipos más importantes en el lenguajes de programación C + -:

Tipos definidos:

          Tipos predefinidos: int, char y float.

          La definición de tipos supone crear un nuevo nivel de abstracción dentro del programa.

          En C+- la declaración de cada nuevo tipo siempre se inicia con la palabra clave typedef. Ejemplo:

                    typedef int TipoEdad;

Tipo enumerado:

          Una manera sencilla de definir un nuevo tipo de dato es enumerar todos los posibles valores que pueden tomar. En C+- el nuevo tipo enumerado se define det´ras de la palabra clave enum mediante un identificador del tipo y a continuación se detalla la lista con los valores separados por comas (,) y encerrados entre llaves {…}. Cada posible valor también se describe mediante un idenfiticador. Estos identificadores al mismo tiempo quedan declarados como valores constantes. Ejemplo:

                    typedef enum TipoDia {

                              Lunes, Martes, Miercoles, Jueves,

                              Viernes, Sabado, Domingo

                    };

          Los tipos de datos enumerados forman parte de una clase de tipos de C+- denominados tipos ordinales, a la cual pertenecen también los tipos int y char, pero no el tipo float.

Uso de tipos enumerados:

          Los tipos enumerados se emplean de manera similar a los tipos predefinidos. El identificador de tipo se puede emplear para definir variables de este tipo, y los identificadores de los valores enumerados se emplean como las constantes con nombre. Al igual que para el resto de los tipos ordinales, con los tipos enumerados se puede utilizar la notación int(e) para obtener la posición de un valor en la lista de valores del tipo. La operación inversa, que permita conocer qué valor enumerado ocupa una determinada posición, se consigue mediante la notación inversa que hace uso del identificador del tipo enumerado y que se invoca de la siguiente forma: TipoEnumerado(N) que devuelve el valor que ocupa la posición N en la colección de valores del tipo TipoEnumerado.

El tipo predefinido bool:

          Bool responde a la siguiente definición, análoga a la de un tipo enumerado: typedef enum boll { false, true};. El nombre bool es el identificador del tipo y las constante simbólicas false y true corresponde a los valores de verdad falso y cierto, respectivamente. Es posible realizar operaciones entre ellas. En concreto, entre operandos booleanos (variables o no) es posible realizar las operaciones lógicas para formas expresiones lógicas cuyos operadores son: && para y, || para o y ! para no.

          Es frecuente definir funciones cuyo resultado es un valor booleano cuando se quiere realizar un test sobre los argumentos de la función. Este tipo de funciones se denominan predicados.

Tipos estructurados:

          Un tipo estructurado de datos, o estructura de datos, es un tipo cuyos valores se construyen agrupando datos de otros tipos más sencillas. Los elementos de información que integran un valor estructurado se denominan componentes. Todos los tipos estructurados se definen, en último término, a partir de tipos simples combinados.

Tipo formación:

          Las estructuras de datos de tipo formación son quizá las más básicas, o al menos las que se introdujeron primero en los lenguajes de programción imperativos. Estas estructuras se denominan genéricamente formaciones y permiten la generalización de la declaración, referencia y manipulación de colecciones de datos todos del mismo tipo.

Tipo vector:

          Un vector está constituido por una serie de valores, todos ellos del mismo tipo, a los que se les da un nombre común que identifica a toda la estructura globalmente. Cada valor concreto dentro de la estructura se distingue por su índice o número de orden que ocupa en la serie.

Declaración de vectores:

          typedef TipoElemento TipoVector[NumeroElementos];

Para poder utilizar los tipos declarados es necesario declarar a su vez, posteriormente, las correspondientes variables, pues sino no podremos utilizarlas.

Es obligatorio que todas las variables se declaren precedidas del identificador de un tipo predefinido del lenguaje o bien definido previamente en el programa. Tanto en la sintaxis de C+- como en el Manual de Estilo está expresamente prohibido la declaración de variables de tipo anónimo.

Inicialización de un vector:

          En C+- cuando se declara una única variable de cualquier tipo simper es posible darle un valor inicial. En el caso de un vector la inicialización afecta a todos sus elementos y por tanto la notación es algo especial y en ella se indica el valor inicial de todos los elementos agrupándolos entre llaves {…} y separándolos por comas (,).

Operaciones con elementos de vectores:

          La mayoría de las operaciones interesantes con vectores hay que realizarlas operando con sus elementos uno por uno. La referencia a un elemento concreto de un vector se hace mediante el nombre del vector seguido, entre corchetes, del índice del elemento referenciado.

Como índice para designar un elemento de un vector se puede utilizar una variable o expresión, siempre que sea de tipo entero.

La comprobación de que el índice para acceder a un elemnto de vector está dentro del rango permitido es responsabilidad del programador. Muchos ataques informáticos aprovechan la falta de previsión de esta comprobación para alterar el funcionamiento normal de un programa suministrándole datos de mayor tamaño que el previsto y provocar lo que se denomina en inglés buffer overrum.

Operaciones globales con vectores: para realizar una operación global con vectores la operación se tiene que programar explícitamente mediante un bucle que realice la operación elemento a elemento.

Paso de argumentos de tipo vector: en C+- el modo por defecto de paso de argumentos de tipo formación, y más concretamente de tipo vector, es el paso por referencia. En C+- cuando se utilizan argumentos de tipo formación y no se quiere que se modifiquen los parámetros reales en la llamada al procedimiento, los argumentos formales deben ir precedidos de la palabra clave const.

Vectores de caracteres: Cadena (String)

          En C+- cualquier tipo vector cuya declaración sea de la forma: typedef char Nombre[N] se considera una cadena o string, con independencia de su longitud particular, esto es, del valor de N

Una cadena de caracters (en inglés string) es un vector en el que se pueden almacenar textos de diferentes longitudes (si caben). Para distinguir la longitud útil en cada momento se reserva siempre espacio para un carácter más, y se hace que toda cadena termine con un carácter nulo ‘\0’ situado al final. Por tanto, para declarar una cadena de un máximo de veinte caracteres se debe hacer de la siguiente forma: typedef char Cadena20[21]

Tipo tupla: otra forma de construir un dato estructurado consiste en agrupar elementos de información usando el esquema de tupla o agregado. En este esquema el dato estructurado está formado por una colección de componentes, cada uno de los cuales puede ser de un tipo diferente. Podemos definir tupla como colección de elementos componentes, de diferentes tipos, cada uno de los cuales se identifica por un nombre.

Tipo registro (struct):

          Los esquemas de tupla pueden usarse en programs en C+- definiéndolos como estructuras del tipo registro o struct. Un resgistro o struct es una estructura de datos formada por una colección de leemtnos de información llamados campos.

Definición de registros: la declaración de un tipo registro en C+- se hace utilizando la palabra clave struct de la siguiente forma:

                    Typedef struct TipoFecha {

                              int dia;

                              tipoMes mes;

                              int anno;

Variables de tipo registro y su inicialización: para declarar variables de tipo registro es necesario haber realizado previamente la definición del tipo del registro. También estas variables se pueden inicializar en la declaración de una manera semejante a las formaciones agrupando los valores iniciales entre llaves {…} y separándolos por una coma (,), pero teniendo en cuenta el tipo de dato de cada campo.

Uso de registros: al manejar datos estructurados de tipo registro se dispone de dos posibilidades: operar con el dato completo, o bien operar con cada campo por separado. Las posibilidades de operar con el dato completo son bastante limitadas. La única operación admisible es la de asignación. La forma de hacer referencia a un campo es mediante la notación: registro.campo

jueves, 1 de diciembre de 2011

Desarrollo de Programas 3/3

Aquí está el último apartado del resumen de desarrollo de programas, aquí tenéis el anterior por si queréis verlo (Desarrollo de Programas 2/3)

Usar subprogramas como técnica de abstracción

Operaciones abstractas: los subprogramas constituyen un primer paso hacia la metodología de programación basada en abstracciones. Los subprogramas permiten definir operaciones abstractas. Una abstracción es una visión simplificada de una cierta entidad, de la que sólo consideramos sus elementos esenciales, prescindiendo de los detalles. Las entidades que podemos abstraer para materializarlas como subprogramas son, en general, operaciones. Con la palabra operación englobamos tanto la idea de acción como la de función.

La visión abstracta es la que permite usar dicha operación sin más que conocer que hace dicha operación. La visión detallada es la que define cómo se hace dicha operación y permite que el procesador la ejecute.

La visión abstracta es la interfaz de la operación y la visión detallada es la implementación.

En programación la idea de función surge al aplicar el concepto de abstracción a las expresiones aritméticas. Una expresión representa un nuevo valor obtenido por cálculo a partir de ciertos valores ya conocidos que se usan como operandos.

Los operandos que intervienen en el cálculo del valor de la función y que pueden cambiar de una vez a otra se especifican como argumentos de dicha función. La función aparece así como una expresión parametrizada.

La cualidad más deseable al utilizar funciones es conseguir su transparencia referencial, que significa que la función devolverá siempre el mismo resultado cada vez que se la invoque con los mismos argumentos.

La transparencia referencial se garantiza si la realización de la función no utiliza datos exteriores a ella, es decir, si no emplea variables externas al subprograma, datos procedentes del exterior, llamadas a otras funciones o procedimientos que no posean transparencia referencial.

Las funciones que cumplen la cualidad de transparencia referencial y que no producen efectos laterales o secundarios se denominan funciones puras.

Los procedimientos pueden ser considerados como acciones abstractas, igualmente parametrizadas. Como acciones abstractas, podemos tener dos visiones de un procedimiento, la primera es la visión abstrata o especificación, formada por la cabecera del procedimiento y una descripción de qué hace dicho procedimiento y la segunda es la realización, en que se detalla codificada en el lenguaje de programación elegido, cómo se hace la acción definida como procedimiento.

Podemos recomendar que los procedimientos se escriban siempre como procedimientos puros, entendiendo por ello que no produzcan efectos laterales o secundarios. Se garantiza que un procedimiento cumple con esta cualidad si en su realización no utiliza variables externas al subprograma y llamadas a otros subprogramas que no sean procedimientos o funciones puras.

Desarrollo usando abstracciones:

          Desarrollo descendente: la estrategia de desarrollo descendente, es simplemente el desarrollo por refinamientos sucesivos, teniendo en cuenta además la posibilidad de definir operaciones abstractas. En cada etapa de refinamiento de una operación habrá que optar por una de las alternativas siguientes: considerar la operación como operación terminal, considerarla como operación compleja o considerar la operación como operación abstracta.

En general resultará ventajoso refinar una operación como operación abstracta si se consigue alguna de las ventajas siguientes: mezclar operaciones con un nivel de detalle muy diferente y evitar escribir repetidamente fragmentos de código que realicen operaciones análogas.

          Reutilización: la realización de ciertas operaciones como subprogramas independientes facilita lo que se llama reutilización de software. Si la operación identificada como operación abstracta tiene un cierto sentido en sí misma, es muy posible que resulta de utilidad en otros programas.

          Desarrollo para reutilización: para aplicara de manera eficaz las técnicas de reutilización de software es preciso pensar en las posibles aplicaciones de un cierto subprograma en el momento de especificarlo, con independencia de las necesidades particulares del programa que se está desarrollando en ese momento. La principal ventaja es que se amplía el conjunto de aplicaciones en que se podrá reutilizar más adelante el subprograma que se está desarrollando ahora. Su principal inconveniente es que será más costoso hacer el desarrollo del subprograma planteado como operación de uso general, que planteando como operación particular, hecha a medida del programa que lo utiliza en este momento.

          Desarrollo ascendente: la metodología de desarrollo ascendente consiste en ir creando subprogramas que realicen operaciones significativas de utilidad para el programa que se intenta construir, hasta que finalmente sea posible escribir el programa principal, de manera relativamente sencilla, apoyándose en los subprogramas desarrollados hasta ese momento. En este caso se trata de que la identificación de las operaciones no surja de un proceso de descomposición o refinamiento de alguna acción en particular, sino simplemente pensando en el programa que se desarrolla, casi como una más de las posibles aplicaciones futuras.

Programas robustos:

Un programa se dice que es un programa robusto si su operación se mantiene en condiciones controladas aunque se le suministren datos erróneos.

          Programación a la defensiva: la postura más cómoda desde el punto de vista del programador es declinar toda responsabilidad  en el caso de que los datos no sean válidos. Si los datos de entrada con cumplen con los requisitos previstos, el programa puede entonces hacer cualquier cosa. La llamada programación a la defensiva consiste en que cada programa o subprograma esté escrito de manera que desconfíe sistemáticamente de los datos o argumentos con que se le invoca, y devuelva siempre como resultado el resultado correcto o una indicación precisa de error.

          Tratamiento de excepciones: ante la posibilidad de errores en los datos con que se opera hay que considerar dos actividades diferentes: detección de la situación de error y corrección de la situación de error. Existen varios esquemas de programación posibles para tratamiento de errores. Un modelo recomendado es el modelo de terminación. En este modelo, si se detecta un error en una sección o bloque del programa, la acción de tratamiento del error reemplaza al resto de las acciones pendientes de dicha sección, con lo cual tras la acción correctora se da por terminado el bloque. La sentencia throw provoca la terminación del subprograma de manera semejante a una sentencia return. Sin embargo, ambas terminaciones son distintas: con return se realiza una terminación normal y con throw se realiza una terminación por excepción. La sentencia throw puede devolver cualquier tipo de resultado en excepción. Además, la sentencia throw es la encargada de indicar que se ha detectado una situación de error y lanzar el mecanismo de tratamiento de excepciones. La sentencia try agrupa el bloque de código en el que se programa el algoritmo del problema a resolver sin tener en cuenta las posibles excepciones que se pudieran producir. A continuación, la sentencia catch agrupa el código para el tratamiento de la excepción que se declara entre paréntesis.