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

Comunicaciones puerto serie (UART)

Vamos a dedicar un par de entradas a examinar con cierto detalle el funcionamiento de las comunicaciones asíncronas (UART, o lo que habitualmente se conoce como el puerto serie) de la familia PIC18.

Nos centraremos en el software, dejando de lado el aspecto hardware, sobre todo la conversión de niveles si la comunicación es entre un PIC y el PC o entre dos microcontroladores con diferentes voltajes (p.e. 3.3V y 5V). En mis pruebas he usado el conversor de niveles integrado en la placa EasyPIC6. En general habrá que usar un conversor de niveles basado en el Max232 o similar. Una búsqueda de RS232 MAX232 TTL en ebay nos mostrará varias posibilidades a partir de unos $5.

En primer lugar presentaremos las funciones básicas que la mayoría de los compiladores tienen para abrir el puerto serie, enviar/recibir, etc. Luego escribiremos nuestras propias rutinas. El objetivo no es prescindir de las rutinas del compilador, sino entender los fundamentos para ser capaces (cuando sea necesario) de saltarnos las limitaciones de un compilador en particular.

También escribiremos una función que nos permita configurar el puerto serie a partir de la velocidad en baudios, sin tener que calcular y programar los registros asociados.

Código asociado a esta entrada:  uart0.c  uart1.c 

Uso de temporizadores (timers)

La mayoría de los microcontroladores tienen uno o varios timers. Serán muy útiles para medir el tiempo que ha pasado entre dos eventos, establecer tareas para ejecutarse a intervalos regulares, etc. Dependendo del modelo los PICs cuentan con un número variable de timers.

Vamos a describir con cierto detalle el timer 0 (TMR0). Los demás son muy similares, aunque siempre conviene mirarse el manual correspondiente, sobre todo porque algunos de ellos están asociados a otras tareas y puede que no estén disponibles para su uso general si se están usando ciertos periféricos.

Describiremos los registros asociados para configurar los temporizadores. El compilador C18 tiene disponibles varias rutinas para configurar y operar con los timers de una forma bastante cómoda. Sin embargo, la idea es ser capaces por nuestra cuenta de reproducir dichas rutinas lo que puede ser importante si lo que queremos hacer no se puede hacer exactamente con las rutinas suministradas. Como siempre es una cuestión de usar lo más conveniente en cada caso (pero para ello hay que conocer las posibilidades).

La configuración de los timers está basada en ciclos del oscilador.  Escribiremos alguna rutina que nos permita especificar un tiempo en usec o msec conocida la frecuencia de oscilador usado.

Código asociado a esta entrada: tipos.h  timer_1.c timer_2.c

Niveles de prioridad en interrupciones

En cualquier aplicación tendremos que manejar varias fuentes de interrupciones.  El problema es que, en principio, una interrupción no puede ser a su vez interrumpida por otra. En este primer ejemplo, mientras se este procesando una interrupción, aunque se produzca otra, tendrá que esperar. Esta es una de las razones por las que un código de interrupción debe ser lo más reducido posible.

No es difícil pensar una situación donde esto pueda suponer un problema. Imaginad que una de las interrupciones es critica e impredecible (p.e. contar el número de pulsos que llegan a un pin), mientras que la otra, que se ejecuta cada cierto tiempo, está asociada a una tarea menos importante  (p.e. refrescar una pantalla con el número de pulsos recibidos). El problema es que si los pulsos llegan muy rápidamente es posible que algunos lleguen mientras estamos en la segunda tarea. Como dicha tarea no se interrumpirá (ella misma es una interrupción) algunos pulsos no serán contados y perderemos dicha información.  La solución será usar prioridades en las interrupciones.

La familia PIC18 introdujo una importante novedad respecto a los PIC16 en el sentido de que es posible definir 2 niveles de prioridad en las interrupciones. Una interrupción de alto nivel puede interrumpir a otra de bajo nivel, pero lo contrario no sucederá. Presentaremos como configurar un PIC en este modo de prioridades y usaremos lo aprendido para escribir un sencillo programa que solucione el problema anterior.

Archivos de código asociado a esta entrada: test_int2.c  test_low_high_ok.c

Interrupciones (conceptos básicos)

Una interrupción es un aviso provocado por un módulo del PIC, por un cambio en el estado de un pin o un recordatorio de que ha pasado un cierto tiempo. Como su nombre indica este aviso interrumpirá la tarea que se este haciendo en ese momento y pasaremos a ejecutar una rutina de servicio o gestión de la interrupción.

Veremos un repaso de los bits y registros de control asociados a las diferentes interrupciones, como habilitarlas y como escribir rutinas de servicio (ISR). Crearemos definiciones (#define) que nos permitirán operar con las interrupciones sin tener que recordar los bits/registros asociados, a la vez que facilitarán la tarea de portar nuestro programa a otro compilador y/o microcontrolador. 

Es importante familiarizarse con el manejo de interrupciones, ya que nos evita poder manejar muchos tipos de eventos sin estar pendientes de ello. En sucesivos tutoriales veremos como el uso de interrupciones nos permite aprovechar de forma mucho más eficiente los recursos del PIC.

Archivos de código asociado a esta entrada:  int_defs.h   test_int.c 

jueves, 21 de junio de 2012

Introducción al BLOG

A raíz de la preparación de un curso, he ido acumulando cierto material sobre programación de microcontroladores PIC. Mi idea ahora es pulir dicho material y publicarlo, en forma de tutoriales, en este blog.  En el material asumiremos un conocimiento básico de los microcontroladores (no veremos como encender y apagar un LED) y está enfocado a programación en C.

Los compiladores que he usado al preparar el material han sido el C18 de Microchip y el MikroC Pro de MikroElektronika. De cara a estos tutoriales me he centrado en el C18 dado su caracter gratuito (en su versión de evaluación), aunque la práctica totalidad de los programas ejemplos presentados pueden compilarse dentro del límite de 2K words de la versión demo de MikroC Pro.

De todas formas el compilador usado no es tan importante ya que la idea de los tutoriales es entender que está pasando por debajo, de forma que no tengamos que depender de las rutinas de un compilador y que podamos reproducirlas por nuestra cuenta si decidimos migrar a otro.

En cuanto al hardware he usado fundamentalmente la placa de desarrollo EasyPIC6 de Mikroelektronica. Esencialmente consiste en un programador a través de USB + varios periféricos ya conectados (o conectables). Además de poder ver el estado de todos los pines a través de LEDs, contamos con un conversor de niveles MAX232, conector para LCD, un duplicador de puertos y alguna cosa más. Nada que no se pueda montar por separado gastándonos 10 euros. 

Los tutoriales alternarán entre descripciones de los diferentes módulos del PIC, explicaciones de protocolos o formatos de posibles periféricos y aplicaciones más o menos interesantes.