Matriz de leds con Arduino 
La matriz de leds es un mecanismo de salida muy utilizado en el ámbito de los microcontroladores ya que permite controlar una gran cantidad de leds con relativamente pocos pines. En este post describiré cómo he aplicado la multiplexación para incluir una matriz de leds en el Arduino. En un segundo post describiré cómo implementar un teclado mediante una matriz de pulsadores.

Para realizar la matriz de leds he distribuido 9 leds en una configuración de 3x3 como se indica en la figura:

Como se puede apreciar los cátodos de los 9 diodos están conectados a las líneas verticales, que llamaremos columnas, mientras que los ánodos de los 9 diodos están conectados a las líneas horizontales, que llamaremos filas. Cada columna está conectada directamente a una salida digital mientras que cada fila está conectada mediante una resistencia a una salida digital. En total necesitamos 6 salidas digitales (3 + 3) para controlar los 9 leds.

Si asumimos que la información de iluminación la tenemos almacenada en una matriz booleana:
#define  APAGADO    false
#define  ENCENDIDO  true

bool leds[3][3];   // [0..2][0..2]

El procedimiento de multiplexación es muy sencillo y puede definirse, en pseudocódigo, como sigue:
inicialización:
columna := 0
cargarContadorDescendenteTimer(10ms)

ejecución:
si (contadorTimer = 0) entonces
activarColumna(columna)
para fila := 0 hasta 2 hacer
v := leds[fila][columna]
activarFila(fila, v)
fin para
columna := ((columna + 1) % 3)
cargarContadorDescendenteTimer(10ms)
fin si

inicialización se ejecutará al principio del programa mientras que ejecución deberá ejecutarse continuamente en el bucle principal de la aplicación.

activarColumna(c) debe encargarse de poner a 0 (0 voltios) la columna “c” y de poner a 1 (5 voltios) el resto de columnas. Poner a 0 voltios la columna “c” la “activa” en el sentido que los tres leds colocados a lo largo de ella son susceptibles de ser polarizados y, por lo tanto de encenderse.

activarFila(f, v) debe encargarse de poner a 1 (5 voltios) la fila “f” si “v” es “true” y de ponerla a 0 (0 voltios) si “v” es “false”. Poner a 5 voltios la fila “f” enciende el led localizado en esa columa “f” y en la columna previamente activada.

Con una adecuada temporización, la sensación es de que no hay parpadeo. Ya tenemos a nuestra disposición un pequeño display de 9 luces utilizando tan solo 6 salidas digitales. Este tipo de multiplexación puede ser ampliado, obviamente, a cualquier configuración (por ejemplo 3 x 4 = 12 luces con 3 + 4 = 7 salidas digitales).

En mi caso particular, he mejorado las características de la matriz incluyendo un tercer estado de “parpadeo”. Para este caso, partiendo del pseudocódigo anterior, tan solo hay que hacer algunos pequeños cambios:

Definimos la matriz de leds como multivaluada:
#define  APAGADO      0
#define  PARPADEANTE  1
#define  ENCENDIDO    2

uint8_t leds[3][3];   // [0..2][0..2]

Y utilizamos un timer adicional más lento para generar el parpadeo:
inicialización:
columna := 0
cargarContadorDescendenteTimer1(10ms)
cargarContadorDescendenteTimer2(500ms)
parpadeoEncendido := true

ejecución:
si (contadorTimer1 = 0) entonces
activarColuma(columna)
para fila := 0 hasta 2 hacer
aux := leds[fila][columna]
v := (aux == 2) OR ((aux == 1) AND parpadeoEncendido)
activarFila(fila, v)
fin para
columna := ((columna + 1) % 3)
cargarContadorDescendenteTimer1(10ms)
fin si
si (contadorTimer2 = 0) entonces
parpadeoEncendido := no parpadeoEncendido
cargarContadorDescendenteTimer2(500ms)
fin si

Ahora tenemos que cada led puede estar en tres estados: apagado, parpadeando o encendido.

Existen otras técnicas de multiplexado, como el Charlieplexing (http://en.wikipedia.org/wiki/Charlieplexing) con las que se consiguen controlar una mayor cantidad de leds con menos pines pero a costa de unos mayores requerimientos de hardware (el charlieplexing requiere más circuitería y que las salidas sean triestadas).

[ añadir comentario ] ( 1347 visualizaciones )   |  [ 0 trackbacks ]   |  enlace permanente  |   ( 3 / 1615 )
Programar el Arduino en C++ 
Programar el microcontrolador AVR el Arduino con el lenguaje Processing está muy bien y es una forma muy rápida de desarrollar aplicaciones sencillas. Sin embargo cualquiera que quiera hacer algo medianamente estructurado o complejo echará rápidamente de menos el C o el C++.

Lo cierto es que el toolkit que se instala con Arduino viene ya con una completa toolchain GNU de C y C++: El propio lenguaje Processing es una especie de "subconjunto" de C y lo que hace el IDE del Arduino no es otra cosa que traducir a C los sketchs que hacemos en lenguaje Processing.

Para hacer nuestro primer programita en C++ para el Arduino vamos a probar hacer una versión OOP del famoso parpadeo (blink.cc):
#include <avr/io.h>
#include <util/delay.h>

using namespace std;

class Led {
    public:
        Led();
        void on();
        void off();
};

Led::Led() {
    // bit 7 el puerto C en modo salida
    DDRC |= 0x80;
}

void Led::on() {
    // bit 7 del puerto C a 1
    PORTC |= 0x80;
}

void Led::off() {
    // bit 7 del puerto C a 0
    PORTC &= 0x7F;
}

int main() {
    Led led;
    while (1) {
        led.on();
        _delay_ms(250);
        led.off();
        _delay_ms(250);
    }
}

Hacer parpadear un led en C++ con OOP no deja de ser como matar moscas a cañonazos pero bueno, se trata de una prueba de concepto :-)

Para compilar el programita utilizamos la toolchain de GNU que ya viene con el software del Arduino:
PREFIJO_RUTA_BIN/avr-g++ -DF_CPU=16000000UL -mmcu=atmega32u4 -Os -c -o blink.o blink.cc
PREFIJO_RUTA_BIN/avr-g++ -DF_CPU=16000000UL -mmcu=atmega32u4 -Os -o blink.elf blink.o
PREFIJO_RUTA_BIN/avr-objcopy -O ihex blink.elf blink.hex

Utilizo la opción -mmcu=atmega32u4 porque en mi caso concreto estoy utilizando un Arduino Leonardo, que posee un procesador ATmega32u4.

Ahora sólo falta subir el fichero hex al Arduino mediante la utilidad avrdude incluida en la toolchain de GNU. El Arduino cuando arranca permanece unos pocos segundos en modo "boot" a la espera de ver si desde un ordenador conectado por USB se le envía algún fichero hex para instalar y ejecutar. En caso de que no se le envíe nada en este modo, el procesador del Arduino pasa a ejecutar el código que contenga ahora mismo la flash interna. Si estando en modo "boot" le llega algún nuevo programa, lo escribe en la flash interna y a continuación lo ejecuta.

Para poder cargar un fichero hex en el Arduino bastará con lanzar el siguiente comando inmediatamente después de hacer un reset:
PREFIJO_RUTA_BIN/avrdude -patmega32u4 -cavr109 -P/dev/ttyUSBXXX -b57600 -D -Uflash:w:blink.hex:i -C PREFIJO_RUTA_ETC/avrdude.conf

Algunos de los parámetros que le paso al avrdude son específicos para Arduino Leonardo, en caso de usar otro modelo de Arduino hay que mirar qué parámetros son los adecuados. Nótese que el ejecutable avrdude no se encuentra en la misma carpeta que el fichero avrdude.conf. Hay que asegurarse de que se usan las rutas correctas y acordes a las carpetas de instalación del software del Arduino.

Si en el Arduino se encuentra previamente cargado un sketch realizado con lenguaje Processing es posible cargar un nuevo fichero hex sin necesidad de reiniciar el Arduino: Basta con abrir el puerto serie USB y configurarlo a 1200 bps (sin necesidad de enviar ni recibir ningún byte) para que el Arduino pase a modo "boot", como si lo hubiésemos reiniciado (gracias a Nicholas Kell por esta info).

He aquí un programa de ejemplo, compilable en cualquier *nix, que realiza este "reseteo soft":
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <termios.h>
#include <string.h>

int main(int argc, char *argv[]) {
    struct termios tio;
    int fd;
    if (argc < 2)
        return 1;
    fd = open(argv[1], O_RDONLY | O_NONBLOCK);
    tcgetattr(fd, &tio);
    tio.c_cflag = CS8;
    cfsetspeed(&tio, B1200);
    tcsetattr(fd, TCSANOW, &tio);
    close(fd);
    return 0;
}

De esta manera y, de forma genérica, el script upload.sh podríamos dejarlo como sigue:
#!/bin/sh
./reset $2
sleep 2
PREFIJO_RUTA_BIN/avrdude -patmega32u4 -cavr109 -P$2 -b57600 -D -Uflash:w:$1:i -C PREFIJO_RUTA_ETC/avrdude.conf

Invocándolo de la siguiente manera:
./upload.sh blink.hex /dev/ttyUSBXXX

Y voilà, ya tenemos nuestro primer led parpadeante hecho en C++ :-)

[ añadir comentario ] ( 2126 visualizaciones )   |  [ 0 trackbacks ]   |  enlace permanente  |   ( 3 / 1663 )
Minisintetizador basado en Arduino 
Versión iniciar y muy básica de un minisintetizador mononfónico de onda cuadrada con entrada MIDI y basado en Arduino. Por ahora sólo reconoce mensajes MIDI "NOTE ON" y "NOTE OFF".

El procesador del Arduino se encarga simplemente de parsear los mensajes MIDI: Genera los tonos y los silencios ante las tramas NOTE ON y NOTE OFF que detecta por la entrada MIDI.

#define  MIDI_NOTE_LOW   16
#define MIDI_NOTE_HIGH 107

// midi frequencies from C0 to B7
int freq[] = {
21, 22, 23, 24, 26, 28, 29, 31,
33, 35, 37, 39, 41, 44, 46, 48, 52, 55, 58, 62,
65, 69, 73, 78, 82, 87, 92, 98, 104, 110, 117, 123,
131, 139, 147, 156, 165, 175, 185, 196, 208, 220, 233, 247,
262, 277, 294, 311, 329, 349, 370, 392, 415, 440, 466, 494,
523, 554, 587, 622, 659, 698, 740, 784, 831, 880, 932, 988,
1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1864, 1976,
2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951
};

#define MIDI_STATUS_WAIT_STATUS 0
#define MIDI_STATUS_WAIT_NOTE 1
#define MIDI_STATUS_WAIT_VELOCITY 2
#define MIDI_STATUS_WAIT_NOTE_OR_STATUS 3

#define SPEAKER_PIN 13

int midiStatus = MIDI_STATUS_WAIT_STATUS;
int midiNote = 0;
int midiVelocity = 0;

void setup() {
Serial1.begin(31250);
}

void parseMidi(int b) {
if (midiStatus == MIDI_STATUS_WAIT_STATUS) {
if ((b & 0xF0) == 0x90)
midiStatus = MIDI_STATUS_WAIT_NOTE;
}
else if (midiStatus == MIDI_STATUS_WAIT_NOTE) {
midiNote = b;
midiStatus = MIDI_STATUS_WAIT_VELOCITY;
}
else if (midiStatus == MIDI_STATUS_WAIT_VELOCITY) {
midiVelocity = b;
midiStatus = MIDI_STATUS_WAIT_STATUS;
if (midiVelocity == 0)
noTone(SPEAKER_PIN);
else {
if ((midiNote >= MIDI_NOTE_LOW) && (midiNote <= MIDI_NOTE_HIGH))
tone(SPEAKER_PIN, freq[midiNote - MIDI_NOTE_LOW]);
}
midiStatus = MIDI_STATUS_WAIT_NOTE_OR_STATUS;
}
else if (midiStatus == MIDI_STATUS_WAIT_NOTE_OR_STATUS) {
if (b < 0x80) {
midiNote = b;
midiStatus = MIDI_STATUS_WAIT_VELOCITY;
}
else if ((b & 0xF0) == 0x90)
midiStatus = MIDI_STATUS_WAIT_NOTE;
else
midiStatus = MIDI_STATUS_WAIT_STATUS;
}
}

void loop() {
while (Serial1.available() > 0) {
int b = Serial1.read();
parseMidi(b);
}
}

Como se puede ver, el parseado de las tramas MIDI se realiza mediante un sencillo autómata finito (DFA) de 4 estados.



[ añadir comentario ] ( 1331 visualizaciones )   |  [ 0 trackbacks ]   |  enlace permanente  |   ( 3 / 3483 )
Luces del belén controladas por una placa Arduino en función de la luz ambiente 
He construido un sencillo circuito utilizando una placa Arduino para controlar las luces de un belén. El procesador genera destellos aleatorios en función de la luz ambiente que mide a través de una de las entradas analógicas.

A continuación el código fuente para Arduino:
#define  NUM_ESTRELLAS  5
#define MS_BUCLE 500

int pines[NUM_ESTRELLAS] = {
2, 3, 4, 5, 6
};


void setup() {
int n;
randomSeed(analogRead(0));
for (n = 0; n < NUM_ESTRELLAS; n++)
pinMode(pines[n], OUTPUT);
}


void loop() {
int n;
int v = analogRead(0);
for (n = 0; n < NUM_ESTRELLAS; n++) {
int r = random(0, 1023);
if (r < v)
digitalWrite(pines[n], HIGH);
else
digitalWrite(pines[n], LOW);
}
delay(MS_BUCLE);
}



[ añadir comentario ] ( 5959 visualizaciones )   |  [ 0 trackbacks ]   |  enlace permanente  |   ( 3 / 1800 )

<< <Anterior | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |