You are on page 1of 11

Una vez instalado lo que haremos sera crear un nuevo proyecto, ya sea en New Project en

la pagina principal, o en el men Archivo->Nuevo->Projecto.


Aparecer una ventana en la cual deberemos escribir el nombre del proyecto y el tipo de
aplicacin que queramos escribir, en este caso seleccionaremos GCC C Ejecutable
Project con el nombre hola mundo.
Al dar aceptar seguira la ventana donde tendremos que seleccionar el micro que
usaremos, aqu se escogio el Atmega16 de la familia MegaAvr de 8bits, que sera el
utilizado en la mayora de programas escritos en los tutoriales, aunque de vez en cuando
cambiaremos a otros modelos para mostrar como programar otros perifericos y
caracteristicas.
Al dar click en OK, aparecer la ventana principal de nuestro proyecto con un archivo en
extencin .c (.cpp si se eligi la opcion GCC C++) con el nombre de nustro proyecto, en
este archivo se incluye por default la funcin main, el ciclo infinito principal y los includes
necesarios, solo basta escribir nuestra aplicacin, compilar, y probar.




El codigo que escribiremos para nuestra aplicacin sera el
siguiente:
123456789101112131415161718192021222324

/* Copyrigth 2013 Licencia GPL **
** Alfredo Orozco **/

#define F_CPU 8000000L

#include <avr/io.h>
#include <util/delay.h>

int main(void)
{
DDRB = 0xFF;
PORTB = 0;

while(1)
{
PORTB = 0xFF;
_delay_ms(500);
PORTB = 0;
_delay_ms(500);
}

return 0;
}
view rawhola mundo.c hosted with by GitHub
Veamos algunas partes importantes del cdigo, que estan relacionadas con el Hardware
del micro. Primero que nada, la primera linea:
1
#define F_CPU 8000000L
Esta linea lo que hace es definir la frecuencia a la que trabajar el micro, esta definicion
sirve para hacer calculos en algunas librerias, como en la librera delay.h, que requiere
saber la frecuencia del micro para poder calcular los tiempos de espera de sus rutinas.
Las siguientes dos lineas incluyen las librerias necesarias para la programacion. La
libreria io.hcontiene todas las definiciones y declaraciones de los registros internos y
perifericos del micro, con las cuales podemos configurar todos los perifericos del micro.
La libreria delay.h contiene las funciones y macros para realizar retardos del orde de
microsegundos y milsegundos.
1
2
#include <avr/io.h>
#include <util/delay.h>
En el cuerpo del main, se encuentra dos lineas antes del ciclo principal, estas 2 lineas lo
que hacen es configurar el puerto B como salida, y una vez configurado se le manda un
000 a ese puerto, con lo que todas las salidas queda en 0. Para lograr esto, los micros
AVR cuentan con 3 tipos de registros diferentes para controlar sus entradas y salidas, que
son: DDRx, PORTx, y PINx, la x representa el nombre del puerto, que puede ser desde A
hasta F.
El registro DDRx es el Data Direction Register (registro de direccion de datos), y en el se
especifican si el pin correspondienta al puerto ser de entrada o de salida. Escribiendo un
1 en el bit correspondiente al pin, se establecer el puerto como salida, y escribiendo un 0
ser de entrada.
El registro PORTx tiene una doble funcionalidad, cuando el pin del puerto es configurado
como salida (un 1 en DDRx), lo que escribamos en el ser reflejado en los pin del puerto,
si el pin del puerto es configurado como entrada (un 0 en DDRx), el escribir un 1 el bit
correspondiente de la entrada se activara la resistencia de PULL-UP interna
correspondiente al pin, o se desactivar si se escribe un 0.
El registro PINx sirve para leer el estado de los pines del puerto configurados como
entradas, si se desea leer algun boton conectado al puerto, su estado no se debe leer del
registro PORTx, si no desde ste registro.
Lo que hacemos en el programa, es decir que todo el puerto B se configure como salida
(DDRB=0xFF), donde conectaremos nuestros leds, inmediatamente despues de configurar
el puerto, mandamos un 0 a todas sus salidas (PORB=000), lo que apagara los leds
conectados al puerto.
1
2
DDRB=0xFF;
PORTB=0x00;
Lo que sigue es el ciclo principal o ciclo infinito, en el cual escribiremos el cdigo que
queremos que se ejecute infinitamente. Aqui lo que hacemos es, primero apagar todos los
leds, luego esperar 500 milsegundos, despues encender los leds, esperar otros 500
milsegundos y todo el proceso se vuelve a repetir infinitamente, con lo cual haremos
parpader al led cada segundo. lo que hacemos es generar una seal con una frecuencia de
1Hz en las salidas del PORTB.
1
2
3
4
5
6
7
while(1)
{
PORTB=0x00;
_delay_ms(500);
PORTB=0xFF;
_delay_ms(500);
}
Para compilar nuestro cdigo solo basta presionar F7, y todo el proyecto se construir, si
la compilacin fue exitosa, aparecera una ventana que muestra la informacin de la
compilacin y el tamao final del programa ejecutable, si hay errores, se mostraran en la
misma ventana.
Bien, hasta aqu vamos de maravilla (eso quiero pensar), ya tenemos nuestro progama
compilado sin errores, nuestor ejecutable (.hex) y nuestro archivo de depuracin (.elf) que
genero el IDE, pero ahora surge una pregunta cmo pruebo que mi programa
realmente funciona?. pues bien, probemos el programa con un circuito simple:

Si no se tiene la posibilidad de armar el circuito fsicamente (ser menos lindo), podremos
correrlo con el software de Proteus, realizando el diagrama antes presentado y cargando
el .elf para la simulacin (en ota Entrada mostrar como simular circuitos y programas en
proteus.). Recuerden que para grabar el cdigo en el chip se necesita un programador
externo, como AvrispMkII, Avr Dragon,USBASP, algun programador serial o paralelo, etc. y
el software para grabarlo, en este caso sera avr-dude.
Aqui les dejo un gift con la simulacin en proteus:

Con esto damos por concluido este segundo tutorial de programacin en C para mciros
AVR.
En entradas siguientes estaremos mostrando como montar un circuito bsico para probar
todos nuestros programas, hasta incluso una targeta de desarrollo.
Tipos de Datos
Como ya todos sabemos, en C existen variedad de tipos de datos, un tipo de dato no es
mas que una variable con una longitud de bits definida, y que es tratado y definido por el
compilador.
En la programacin de microcontroladores, la definicin de algunos tipos de datos
enteros es significativa, ya que contamos con recursus muy limitados, por lo que el uso
adecuado de la representacin de los datos es importante y debe ser eficiente para la
optimizacin del cdigo.
La definicin de los tipos de datos enteros para micros avr se encuentran en la
libreria stdint.h,contenida en la avr-libc, algunos tipos de datos que define son:
1
2
3
4
typedef signed char int8_t;
typedef unsigned char uint8_t;

typedef short int16_t;
5
6
7
8
9
10
11
typedef unsigned short uint16_t;

typedef long int32_t;
typedef unsigned long uint32_t;

typedef long long int64_t;
typedef unsigned long long uint64_t;
Como se puede observar, se pueden definir variables con tamaos que van desde los 8
bits hasta los 32 bits, ya sea en representacion con o sin signo. A dems de tipos de datos
enteros, el compilador es capaz de manejar otros tipos, como float, double, char etc. en
varias longitudes de bits, y claro, tambien podemos definir nuestros porpios tipos de
datos.
Es de suma importancia conocer la longitud des los distintos tipos de datos en una
aplicacin para un microcontrolador o un sistema embebido, ya que por el limitado
tamao de recursos que contiene (como memoria ram) nuestros programas tiene que
tener mejores tecnias de programacin para reducir en lo posible el uso de variables
inecesarias y redundantes.
Tipos de datos usados en la avrlib:
(signed/unsigned) char ---------- 1 byte
(signed/unsigned) short --------- 2 bytes
(signed/unsigned) int ----------- 2 bytes
(signed/unsigned) long ---------- 4 bytes
(signed/unsigned) long long ----- 8 bytes
float --------------------------- 4 bytes
(floating point)
double -------------------------- alias to
float
Campos de Bits (Bit Fields)
En la programacin de microcontroladores es bastante frecuente el chequeo y cambio de
las variables, ya sea en todos sus bytes, un bit o grupo de bits en especifico. A menudo,
usamos una variable para almacenar el estado lgico de un proceso (0 o 1), por lo que si
usamos una variable del tipo unsigned char, estaremos desperdiciando 7 bits, ya que esta
variable solo toma valos de 0 y 1, alterando nicamente el valor del bit menos
significativo. Pero el lenguaje C tiene una potente y util herramienta que nos permite
definir un nombre para cada bit de una variable, con lo cual, por ejemplo, en una variable
de unsigned char, podremos definir 8 variables que haganr eferencia a cada bit del dato,
incluso un bit puede no tener una referenci asociada, es decir, ese bit no se implementa.
Esta caracterstica del lenguaje C es de gran ayuda para el desarrollo de aplicaciones con
microcontroladores, ya que se tiene un acceso de mas bajo nivel que pudiera estar
orientado a registros del CPU.
La forma de definir campos de bits es similar al de una estructura, en el cual se define el
nombre de la estructura, y los campos que lo componen, con la diferencia de que se le
especifican cuantos bits del tamao total de la estructura se usaran para ese campo, un
ejemplo de una definicin de un campo de bits para manipular 5 banderas y una variable
de 3 bits seria el siguiente:
1
2
3
4
5
6
7
8
typedef struc {
unsigned char Status1:1; //1 bit de longitud
unsigned char Status2:1; //1 bit de longitud
unsigned char Status3:1; //1 bit de longitud
unsigned char Status4:1; //1 bit de longitud
unsigned char Status5:1; //1 bit de longitud
unsigned char Status6:3; //3 bits de longitud
}Flags;
La estructura ocupa en memoria 1 byte, lo que hace es reservar un espacio de memoria
correspondiente al numero de bits usados dentro de la estructura, en el caso anterior se
dice que son 5 variables unsigned char de 1 bit y 1 variable unsigned char de 3 bits, lo
que nos da 1 byte de total. Sin embargo es posible definir campos de mas bits que el tipo
de dato con que se define, o incluso dejar campos de bits vacios y sin usar (lo que serian
bits con condicion de no importa), aqu otro ejemplo:
1
2
3
4
5
6
7
typedef struc {
unsigned char IsLogic :1; //1 bit de longitud
unsigned char IsUnique :1; //1 bit de longitud
unsigned char :1; //no se usa, pero se indica para llenar la
estructura.
unsigned char Status4 :1; //1 bit de longitud
unsigned char :4; //no se usan, pero se indican para llenar la
estructura
}Opcode;
Muy interesante verdad?. Ahora, para ver el uso de esta caracterisitica y aplicndola a los
micros AVR, programemos un pequee ejemplo en C, qu les parece si hacemos una
estructura BitField para acceder y modificar individualmente a cada bit del Registro DDRx,
PORTx y PINx?. Ac abajo les dejo el cdigo propuesto, para que lo chequen, lo compilen y
lo prueben, ya como practica les dejo que hagan algunos BitFields de otros registros
internos del micro, de esos que tienen bits sin implementar ;).
.
123456789101112131415161718192021
222324252627282930313233343536373
839404142434445464748495051525354
555657585960616263646566676869707
/*
* bitfields1-
avr.c
*
* Author:
17273747576777879
Alfredo
*/
#define F_CPU 8000000L
#include
<avr/io.h>
#include
<util/delay.h>

typedef struct
DDBbits_t{

unsig
ned char DD0
:1;
unsig
ned char DD1
:1;
unsig
ned char DD2
:1;
unsig
ned char DD3
:1;
unsig
ned char DD4
:1;
unsig
ned char DD5
:1;
unsig
ned char DD6
:1;
unsig
ned char DD7
:1;

}volatile
*DDBbits;

typedef struct
PBbits_t{

unsig
ned char PO0
:1;
unsig
ned char PO1
:1;
unsig
ned char PO2
:1;
unsig
ned char PO3
:1;
unsig
ned char PO4
:1;
unsig
ned char PO5
:1;
unsig
ned char PO6
:1;
unsig
ned char PO7
:1;

}volatile
*PBbits;

typedef struct
PINBbits_t{

unsig
ned char PI0
:1;
unsig
ned char PI1
:1;
unsig
ned char PI2
:1;
unsig
ned char PI3
:1;
unsig
ned char PI4
:1;
unsig
ned char PI5
:1;
unsig
ned char PI6
:1;
unsig
ned char PI7
:1;

}volatile
*PIBbits;

#define
PORTBbits
(*(PBbits)_SFR
_MEM_ADDR(PORT
B))
#define
DDRBbits
(*(DDBbits)_SF
R_MEM_ADDR(DDR
B))
#DEFINE
PINBbits
(*(PIBbits)_SF
R_MEM_ADDR(PIN
B))

#define INPUT 0
#define OUTPUT 1

#define ON 1
#define OFF 0

#define LED
PORTBbits.PO0
#define BUTTON
PINBbits.PI1

int main(void)
{
DDRBb
its.DD0=OUTPUT
;//Puerto P0
como salida.
DDRBb
its.DD1=INPUT;
//Puerto P1
como entrada.
PORTB
bits.PO1=ON;
//Activamovs
PULL-UP en el
puerto P1.

while
(1)
{
while(BUTTON!=ON) //mientras se presione el boton.
{

LED=O
N;

_dela
y_ms(300);

LED=O
FF;

_dela
y_ms(300);
}
}
retur
n 0;
}
view rawbitfields1-avr.c hosted with by GitHub
Expliquemos un poco el cdigo, que se ve algo diferente a los ejemplos propuestos
anteriormente. Primero que nada se debe saber que los registros de los puertos (y los
demas registros) estan mapeadas en el mapa de direcciones de memoria, es decir, son
una direccin de memoria mas. Lo que se hace es crear una estructura para manipular los
bits individualmente del registro, despues creamos una macro que hace un CAST, que
mapea la direccin del registro en la estructura para que esta pueda manipularlo.
Se usa la macro definina en <sfr_defs.h> _SFR_MEM_ADDR(SFR) para obtener la direccin
de cualquier variable o registro en memoria, en estos casos se obtienen las direcciones
de DDRB, PORTB y PINB, las cuales son mapeadas en la estructura correspondiente. Para
que esto se lleve a cabo correctamente, el tipo de dato al que se hace el CAST deberpa ser
un apuntador con la preposicinvolatile, ya que se trabaja directamente con la memoria
del micro, es por eso que al final de la definicin de cada estructura, se declara
con volatile * y aunque es un apuntador, el CAST de la macro permite acceder a cada
elemento de la estructura sin la necesidad de usar ->.
En otro post veremos como usar esta potente caracterstica, escribiendo una libreria con
las estructuras y macros para hacerla lo ms general posible, pudiendo usar registros y
variables de 8, 16 y 32 bits.


#include "adc.h"

#define ADC_INPUT ADC_MUX_ADC0
#define ADC_VREF ADC_VREF_AVCC

int main(void)
{
// set PORTB as output
DDRB = 0xFF;

// set prescaler and enable ADC
adc_init(ADC_PRESCALER_DIV128);

// output inverted ADC value on PORTB
while (1) {
PORTB = ~(adc_read_8bit(ADC_INPUT, ADC_VREF));
}
}