Translate

martes, 17 de julio de 2012

ADC con interrupciones


En la entrada dedicada al conversor AD vimos como optimizar el código ejemplo del compilador de C18. Aprovechando las características especificas de cada controlador, usando macros en vez de llamadas a funciones, etc. conseguimos que el tiempo necesario para una conversión bajase desde unos 50 usec a 25 usec. Eso equivalía a duplicar el posible ritmo de muestreo de 20000 a 40000 muestras por segundo.

En esta entrada vamos a explorar el uso de interrupciones junto con el conversor AD.

En primer lugar veremos como usar la interrupción de un timer para asegurar un ritmo de muestreo exacto. Posteriormente veremos como usar la interrupción asociada al ADC para no tener que esperar mientras el conversor realiza su tarea. Esto nos permitirá liberar gran parte de los recursos del micro, lo que será especialmente útil si estamos cerca del ritmo de muestreo máximo.

Codigo asociado a esta entrada:  adc0_timer.c, adc1_timer.c

viernes, 13 de julio de 2012

Proyecto: brújula electrónica

En este proyecto vamos a ver una simple aplicación del uso del conversor AD del  PIC. Se trata de escribir el código base de una brújula electrónica. La idea es disponer de dos sensores magnéticos alineados en dos ejes (A y B) perpendiculares en el plano horizontal, cada uno de ellos midiendo la correspondiente componente del campo magnético terrestre.


Con el conversor AD tomaremos dos medidas (ma y mb) "simultáneas" (separadas por unos pocos usec) de ambos sensores. A partir de ellas hallaremos el ángulo relativo (theta) entre la orientación del eje A de nuestra brújula y el norte magnético.  Además de usar el conversor AD, el proyecto nos servirá para introducir el tema de optimización de rutinas matemáticas cuando estamos usando microcontroladores. En particular en este proyecto tenemos que usar la función arco tangente para obtener el ángulo a partir de las medidas ma y mb. Veremos una implementación aproximada de dicha función, adecuada para la precisión requerida en este proyecto y con bastante menos carga (tanto de código como computacional) que la rutina matemática (atan2) suministrada con el compilador.

El siguiente cutrevideo trata de dar una idea del resultado final.  


Código asociado a esta entrada:  brujula1.c, brujula2.c

Conversor ADC


En otro tutorial vimos como el módulo PWM funcionaba como una especie de conversor digital -> analógico (DAC). El conversor analógico-digital (ADC) que equipa a muchos microcontroladores hace justo lo contrario, convirtiendo un voltaje analógico externo en un número, con el que podremos operar.

Como siempre empezaremos con una introducción sobre los fundamentos de un conversor AD y los registros usados para su manejo en los PIC. Tras entender el proceso, presentaremos las rutinas de C18 para el manejo del ADC y escribiremos nuestras propias funciones, que nos permitirán optimizar el rendimiento del ADC y no depender de un compilador en particular.

En la siguiente entrada veremos como al igual que pasaba con el puerto serie, usando interrupciones podemos optimizar el rendimiento del PIC mientras estamos usando el conversor AD.

Codigo asociado a esta entrada:  adc1.c

miércoles, 27 de junio de 2012

Aplicación: Audio con PWM

Una aplicación inmediata (y más interesante que los LED de colores del programa anterior) de la modulación PWM es intentar reproducir un archivo de audio digital tipo WAV usando el módulo PWM del PIC como un DAC (Digital-Analog Converter) incorporado.

Obviamente el principal problema es dónde guarda el PIC el audio a reproducir.  En la memoria de un PIC pueden caber unos 1000/2000 bytes, lo que a un ritmo de típico de 8 o 10 KHz solo da para una fracción de segundo. Lo que vamos a hacer en esta aplicación es recibir los datos a través del puerto serie. Si ajustamos la velocidad del puerto serie a 115200, teniendo en cuenta que para cada byte necesitamos mandar 10 bits (datos + start + stop) esto se traduce a un ritmo de unos 11500 bytes/segundo, muy cercano a 11025 que es una típica frecuencia de muestreo de archivos de audio. Según lleguen los datos por el puerto serie los usaremos para modificar el ciclo de trabajo del módulo PWM que previamente habremos configurado.

A la izquierda tenemos una foto con el hardware usado. A la derecha, un video donde pueden oírse el resultado,  junto con una captura de la señal PWM en el osciloscopio:

                         

Código asociado a esta entrada:  PWM_audio_serial.c
Proyecto C18 completo:  proyecto.rar

Modulación PWM (Pulse Width Modulation)

Nuestro controlador (digital) eventualmente tendrá que interactuar con el mundo exterior (analógico). Al margen de usar dispositivos externos (convertidor digital analógico o DAC) muchos microcontroladores cuentan con un módulo PWM (Pulse Width Modulation) que puede usarse (con ciertas limitaciones) para mandar "ordenes" analógicas.

En este tutorial describiremos en que consiste la modulación por ancho de pulsos (PWM), veremos las rutinas disponibles en C18 y, como hacemos habitualmente, describiremos los registros asociados y su funcionalidad.

Aprovecharemos los conocimientos adquiridos para escribir una rutina para inicializar el módulo PWM especificando la frecuencia deseada y escribiremos un pequeño programa para usar los dos módulos PWM de un PIC para crear transiciones de colores en un LED bicolor.

En una entrada posterior aplicaremos lo que hemos aprendido a una aplicación más interesante usando el módulo PWM como conversor DAC para un archivo de audio.

Archivos de código asociados a esta entrada: pwm1.c y pwm2.c

--------------------------------------------------------------------------------------------

viernes, 22 de junio de 2012

Decodificador de un mando a distancia


Vamos a escribir una aplicación que junta los tres temas sobre los que hemos hablado hasta ahora: timers, interrupciones y UART.  El objetivo es detectar los pulsos y medir sus tiempos de un mando a distancia por infrarrojos, obteniendo un volcado por el puerto serie similar a este:


Esta información la podremos usar para determinar qué protocolo se está usando. Posteriormente, una vez que sabemos que protocolo usa nuestro mando será simple escribir otro programa que decodifique o emule los pulsos. Esto puede ser muy útil en muchos proyectos para usar un mando por infrarrojos para enviar órdenes a nuestro sistema.

Codigo asociado a esta entrada: IR_timing.c

Comunicaciones UART con paridad

En muchos compiladores las rutinas del puerto serie asumen los valores por defecto de comunicación con 8 bits de datos y sin paridad.

Vamos a ver como, con los conocimientos adquiridos, es muy sencillo establecer una comunicación serie usando por ejemplo paridad. esto puede ser muy útil si tenemos que interactuar con un periférico que sólo soporte este tipo de comunicaciones.

Código asociado a esta entrada: uart_paridad.c

Comunicaciones UART con interrupciones

En el tutorial anterior hemos visto que lo que un compilador hace al escribir/leer de la UART no es muy complicado. Esencialmente se reduce a leer/escribir de un par de registros (TXREG, RCREG) verificando previamente que están libres (caso de TXREG) o que sus contenidos se han renovado (caso de RCREG).

Aprovecharemos estos conocimientos para implementar un programa de transmisión/recepción de datos más eficaz, usando las interrupciones asociadas a la UART, tanto de recepción (RX, activada al recibir un byte) como de transmisión (TX, activada cuando la UART está libre para mandar otro byte).

Asimismo usaremos dos buffers de mayor capacidad, uno de recepción (donde la interrupción RX depositará los datos recibidos) y otro de transmisión (de donde la interrupción de TX cogerá los datos a transmitir).

Veremos como la implementación de estas técnicas puede llevarnos a drásticas reducciones de la ocupación del PIC, liberándolo para otras tareas. 

También veremos como nuestra implementación con buffers e interrupciones puede configurarse como la salida por defecto stdout, pudiendo ser usada directamente por rutinas como printf, puts, etc. de forma transparente para el usuariuo.

Código asociado a esta entrada:  uart3_int.c