sábado, 28 de abril de 2012

Programacion C con CM-510: vectores (arrays), puertos y LEDs

Programanción C con Bioloid CM-510: "arrays", puertos y LEDs

Cadenas y Vectores ("Strings y Arrays)


Una de las formas más habituales y fáciles de manejar un conjunto de datos en C, y en muchos otros lenguajes como C++, Java y C#,  es mediante cadenas y vectores.

Cadenas

Una cadena consiste en un conjunto de caracteres, como las que utilizamos en el programa "holaMundo.c" del anterior artículo:

[sourcecode language="c"]

int main(void)
{
int ax12Id=6;

init();

printf("rn Un sencillo ejemplo");

...
}
[/sourcecode]

A la función printf le pasamos como parámetro la cadena "rn Un sencillo ejemplo"
printf("/r/n Un sencillo ejemplo");

En este caso no hemos utilizado ninguna variable para almacenarla, sino que se la hemos pasado directamente, como un literal. Si quisiéramos utilizar dicha cadena en distintas partes la hubiéramos almacenado en una variable, aunque también existen otras razones para utilizar no utilizar literales en el código, como agruparlas en un único punto para facilitar su localización o su traducción).
char titulo[]="Un sencillo ejemplo";

printf("rn %s", titulo);

Unas breves explicaciones sobre printf: La función printf es mucho más potente de lo que parece a primera vista, admitiendo bastantes parámetros que nos permiten mostrar una gran cantidad de tipos de datos y formatos diferentes. En este caso %s indica que en dicha posición del mensaje se incluirá una cadena que le pasaremos como parámetro a continuación, "titulo" en este ejemplo.

Cada carácter de la cadena se almacena en un posición de memoria [U][n][ ][s][e][n]... siendo las cadenas un caso especial de vector o, en inglés, "array".

Descárgate aquí una presentación en PDF con explicaciones muy detalladas sobre cadenas en C, de la Universidad del País Vasco.

Vectores

Un vector es probablemente la forma más sencilla de manejar o almacenar un conjunto de datos. En el ejemplo que viene a continuación utilizaremos el siguiente vector de enteros:

int pin[]={1,2,4,8,16,32,64};

Aunque quizás sea más claro si utilizamos la forma "completa", en lugar de la abreviada, de cargar datos:
int pin[7] //  creamos un vector con 7 posiciones de memoria para almacenar un entero (int)

int[0]=1; // vamos asignando cada valor... ¡Ojo, que el primero es la posición 0 (cero)!

int[1]=2;

int[2]=4;

int[3]=8;

int[4]=16;

int[5]=32;

int[6]=64;

Descárgate aquí una presentación en PDF con explicaciones muy detalladas sobre vectores en C, también de la Universidad del País Vasco.

Los puertos de los microcontroladores

Probablemente el elemento más característico de los microcontroladores son los puertos de Entrada/Salida E/S (Input/Output, I/O en inglés), con los cuales podemos enviar y recibir información para controlar distintos elementos electrónicos como sensores, actuadores y LEDs.

El controlador de Robotis CM-510 nos ofrece 6 conexiones donde podremos utilizar los puertos de entrada salida que el microcontrolador ATMega 2561 (página en inglés del fabricante ATMEL) incorpora.

[caption id="attachment_449" align="alignnone" width="300"]Controlador CM-510 Controlador CM-510[/caption]

Cada puerto se controla mediante tres registros (un registro es básicamente una zona de memoria):

  • DDRx: donde se indica si se enviarán o recibirán datos

  • PINx: aquí se reciben los datos

  • PORTx: y desde aquí se envían los datos al exterior del microcontrolador


Para manejar los puertos se han de utilizar los operadores de C a nivel de bits para activar y desactivar cada uno de los bits que representan los puntos de conexión (PIN) que componen los puertos. Estas operaciones utilizan los mismos operadores booleanos que las puertas lógicas y tablas de verdad

[caption id="attachment_461" align="alignnone" width="165"]tabla y puerta or tabla y puerta or[/caption]

[caption id="attachment_460" align="alignnone" width="167"]tabla y puerta and tabla y puerta and[/caption]

Mediante los puertos podremos controlar los LEDs y podremos comprobar si está pulsado alguno de los botones. Por ejemplo:

// El puerto se inicializa con los valores a 1, 1111 1111 ó 0x7F en hexadecimal. OJO 1 (uno) es LED apagado, 0 (cero) es encendido.

// Al ejecutar:
PORTC &= ~0x01; // el complementario de 0000 0001 es 1111 1110

/* la operación AND entre
1111 1111       el puerto C
1111 1110       y el valor complementario de 0000 0001
--—- —---
1111 1110       el resultado es que sólo el LED 1 estará encencido

Son operaciones sencillas pero en las cuales es fácil equivocarse. Las funciones son uno de los elementos fundamentales de C para encapsular los detalles y, una vez comprobado que funcionan perfectamente, “olvidarnos” de ellos:

[sourcecode language="c"]
int pin[]={1,2,4,8,16,32,64};

void encenderYapagarLEDs()
{
for (int i=0;i<=6;i++)
{
ledOn(pin[i]); //enciende LED
_delay_ms(500); // una pausa de medio segundo
ledOff(pin[i]); // apaga LED
}
}
[/sourcecode]

teniendo definido previamente, claro:

[sourcecode language="c"]
void ledOn(unsigned char ledId)
{
PORTC &= ~ledId;
}

void ledOff(unsigned char ledId)
{
PORTC |= ledId;
}
[/sourcecode]

Habitualmente en lugar de funciones se utiliza #define, pero creo que ahora es mucho más claro utilizar funciones. Por cierto, ¿por qué utilizo al array "int pin[]={1,2,4,8,16,32,64};"?, ¿de qué otras formas se puede hacer?

No hay comentarios:

Publicar un comentario