Está en la página 1de 89

Guia de VideoTutoriales

Programador USBasp V3.0

1
INDICE

_____________________________________________________________________________________

Introducción .................................................................................................................................................. 3
Parpadeo de un LED ...................................................................................................................................... 5
Manejo de los Puertos ................................................................................................................................ 11
Rota-bit ....................................................................................................................................................... 17
Contador de 0-99 con 2 Displays de 7 Segmentos...................................................................................... 23
Matriz de LEDs ............................................................................................................................................ 29
Interrupción Externa ................................................................................................................................... 37
Uso del ADC................................................................................................................................................. 42
Uso del PWM .............................................................................................................................................. 47
Servomotor ................................................................................................................................................. 55
Pantalla LCD ................................................................................................................................................ 62
Sensor ultrasónico SRF08 ............................................................................................................................ 70
Comunicación serial USART ........................................................................................................................ 81

2
Introducción

3
Introducción

Antes de empezar a leer esta guía, es recomendable leer la guía de usuario del programador, en la
que se explica cómo usar el Programador y como instalar los programas necesarios para poder empezar
a programar.

Esta guía es propiedad de HeTPro. Queda expresamente prohibida la reproducción total o parcial por
cualquier medio o soporte de los contenidos de esta publicación sin la autorización expresa del editor.
Todas las marcas y productos aparecidos en esta guía son marca registrada y/o copyright de sus
propietarios.

Cualquier comentario, duda o sugerencia favor de escribir a:

soporte@hetpro.com.mx

4
Parpadeo de un LED

5
Parpadeo
Descripción
En este programa se manejara el puerto para hacer parpadear un LED, en este caso se realizara el
parpadeo a través de dos métodos distintos de programación, utilizando todo el puerto o solo un bit
del mismo, seleccionaremos el pin deseado el cual lo conectaremos a un LED en el cual
visualizaremos el parpadeo.

Diagrama Esquemático

Materiales
1 LED
1 Resistencia de 220 Ohms
1 Microcontrolador ATmega8
Programador USBasp V3.0

6
Introducción

El microcontrolador tiene varios puertos de los cuales podemos hacer uso, estos puertos los
podemos configurar como nosotros queramos, como entrada o como salida, para poder hacer esto
es necesario escribir en los registros del puerto para darle las instrucciones necesarias.

Existen tres principales formas de controlar los puertos, tomamos como ejemplo el puerto B.

DDR
Para obtener que el puerto B se comporte como entrada, como salida o ambos, es necesario
indicarle esto en el DDR, este registro no activara ni desactivara ningún pin del microcontrolador,
simplemente le indicara al puerto si este será entrada o salida.

Para indicarle al DDR si el puerto sea de entrada o salida, el 1 indica salida, y el 0 entrada, se le
puede escribir como hexadecimal, decimal, o binario, por ejemplo, si queremos que todos los bits
del puerto sean salidas lo podemos escribir como sigue:

DDRB=0xFF; //Como Hexadecimal


DDRB=255; //Como Decimal
DDRB=0b11111111;//Como Binario

Si queremos que algunos bits funcionen como entradas:

DDRB=0x8C; //Como Hexadecimal


DDRB=140; //Como Decimal
DDRB=0b10001100;//Como Binario

Se puede escribir en cualquiera de los tres tipos (binario hexadecimal y decimal), en todos los
registros.

PORT
El PORT controla la salida del puerto, este se usa en caso de que el DDR haya sido seleccionado
como salida, un 1 en el PORT indica un nivel alto en el puerto como salida, un 0 indica que el pin
esta en nivel bajo. Varias configuraciones de ejemplo para el PORT:

PORTB=0xFF; //Todos los pines estan activos


PORTB=0x00; //Todos los pines estan desactivados
PORTB=0x03; //Solo los primeros dos bits del puerto estan activos

PIN

7
El PIN es un registro de lectura (notar en la imagen del registro donde dice Read/Write, todos son R),
este registro nos da un 1 si a un pin del microcontrolador se le está alimentando externamente, y un
cero si esta en nivel bajo de voltaje.

En este caso el valor del PIN se le puede asignar a una variable la cual guardara el valor del miso, al
momento de ejecutar la instrucción. ej.

valor=PINB; //El valor de PINB es asignado a la variable "valor"

Programa en C
#include <avr/io.h> //Librería de entradas y salidas
#include <util/delay.h> //Librería de retardos

int main (void){ //Inicio del programa

DDRB=0xFF; //Declarar el puerto como salida

while(1){ //Iniciar bucle infinito

PORTB=0x01; //Puerto B = 00000001


_delay_ms(250); //Espera 250 milisegundos
PORTB=0x00; //Puerto B = 00000000
_delay_ms(250);

PORTB|=_BV(PB0); //Bit 0 del puerto B = 1


_delay_ms(250);
PORTB&=~(_BV(PB0)); //Bit 0 del puerto B = 0
_delay_ms(250);

}
}

8
Detalles del programa
#include <avr/io.h>

Incluimos la librería avr/io que contiene la información de las entradas y salidas del
microcontrolador.

#include <util/delay.h>

Esta librería es necesaria para poder utilizar los retardos de tiempo

int main (void){

El main es la función principal, es donde el programa inicia, siempre es necesario declarar la función
main.

DDRB=0xFF;

Los puertos del micro contienen tres registros los cuales son, el DDR el PORT y el PIN, en este caso
estamos haciendo uso del DDR este es el que determina el uso que se le va a dar al puerto del
microcontrolador, si se le asigna un uno es de salida, y un cero es de entrada.
En este caso 0xFF es la nomenclatura para indicar un valor hexadecimal, si lo pasamos a binario, este se
escribiría 0b11111111, en donde se puede ver claramente que los 8 bits del DDR están siendo activados
como salidas.

while(1){

El ciclo while, es un ciclo que ejecuta todas las instrucciones que se encuentran dentro de sus corchetes,
siempre y cuando lo que este dentro del paréntesis se cumpla, en este caso el 1 es lo mismo que TRUE,
por lo tanto, siempre se cumple y se ejecutaran cíclicamente las instrucciones dentro del while. Esto se
hace con la intención de que el programa nunca se detenga, y siempre repita lo mismo.

PORTB=0x01;
_delay_ms(250);
PORTB=0x00;
_delay_ms(250);

Una manera de hacer parpadear un LED es activando y desactivando la fuente, en este caso, la fuente de
alimentación del mismo viene del bit 0 o el primero pin del puerto B, como podemos ver en el código, se
le asigna un 0x01, se espera 250 milisegundos, se le asigna 0x00 y vuelve a esperar el mismo tiempo.

Estas instrucciones manejan a los 8 bits del puerto, la tabla a continuación describe el código.

9
0 0 0 0 0 0 0 1
RETARDO 250
0 0 0 0 0 0 0 0
RETARDO 250

PORTB|=_BV(PB0);
_delay_ms(250);
PORTB&=~(_BV(PB0));
_delay_ms(250);

En este caso las instrucciones de activar y desactivar el pin del puerto, son algo más complejas, ya
que se separa y se activa o desactiva el bit 0 del puerto B.
Prácticamente el código hace lo mismo, pero la principal diferencia y ventaja, es que al usar este
código, se manejan independiente los bits del puerto, esto es, que al aislar los bits, podemos
trabajar con ellos de manera que si cambiamos uno, no afecte al otro. Esto se observa en la
siguiente tabla.
0
RETARDO 250
1
RETARDO 250

10
Manejo de los Puertos

11
Manejo de los Puertos
Descripción
En este programa se verá un uso práctico de los registros de los puertos, para poder hacer prender
un LED conectado a un pin del microcontrolador, para esto se conectara un Push Button que al
presionarlo hará que prenda el LED. En este ejemplo se verán estructuras de control como el while y
el if.

Diagrama Esquemático

Materiales
1 LED
1 Resistencia de 220 Ohms
1 Push Button
1 Resistencia de 10Kohms
1 Microcontrolador ATmega8
Programador USBasp V3.0

12
Introducción

While

Como se puede ver en el diagrama de flujo, el ciclo while, ejecuta toda instrucción dentro del mismo,
siempre y cuando la condición se cumpla. La nomenclatura del ciclo while es:

while("Condicion"){
instruccion A;
instruccion B;
}

Por lo tanto, siempre y cuando la condición se cumpla, las instrucciones se ejecutaran, en caso de que la
condición no se cumpla, se brinca las instrucciones y el programa continua.

if else

13
En el if/else, también hay una condición y si esta se cumple ejecuta el proceso o las instrucciones
indicadas dentro de los corchetes, en caso de que no se cumpla es opcional escribir el "else" el cual
ejecutara un proceso, en caso de que la condición haya sido falsa.

if ("condicion"){
procesoA;
}
else {
ProcesoB;
}

Programa en C
#include <avr/io.h> //Librería necesaria para las entradas y salidas
#include <util/delay.h> //Librería para usar los retardos de tiempo

int main (void){ //Inicio del programa

DDRB=0x00; //Iniciar el puerto B como entrada


DDRD=0xFF; //Declarar el puerto D como salida

while(1){ //Bucle infinito

if (PINB==0x01){ //"si el PINB es igual a 1


PORTD=0x01; //Prender el LED conectado al bit 0 del puerto D
_delay_ms(1000); //Esperar 1000 mili segundos
PORTD=0x00; //Apagar el LED
}
}
}

Detalles del programa


#include <avr/io.h>

Incluimos la librería avr/io que contiene la informacion de las entradas y salidas del
microcontrolador.

#include <util/delay.h>

14
Esta librería es necesaria para poder utilizar los retardos de tiempo

int main (void){

El main es la función principal, es donde el programa inicia, siempre es necesario declarar la función
main.

DDRB=0x00;
DDRD=0xFF;

Se declara el puerto B como entrada y el puerto D como salida.

while(1){

Ya que el 1 es igual a verdadero, el ciclo while nunca se termina.

if (PINB==0x01){

Al usar el if la condición tiene que ir dentro de paréntesis, y en este caso como se puede ver se usa
doble símbolo de igual para diferenciarlo de cuando se le asigna un valor a una variable,

variable=14; //Se le asigna el valor de 14 a la variabe


variable==14 //Se comparan variable y 14, si es verdadero arroja un 1

Nótese que se está usando PINB, el cual pertenece al puerto B, el mismo que se declaro como salida
en el DDR. Esta condición corresponde al Push Button conectado en el pin del puerto B.

PORTD=0x01;

Se le da al puerto la instrucción de tomar el valor 0x01, previamente el puerto D se declaro como


salida.
Bit 7 6 5 4 3 2 1 0
PORTD 0 0 0 0 0 0 0 1

_delay_ms(1000);

Instrucción que espera 1000 milisegundos, el valor dentro de los paréntesis puede ser ajustado
como se requiera.

15
PORTD=0x00;

Se desactivan todos los pines del puerto D

Bit 7 6 5 4 3 2 1 0
PORTD 0 0 0 0 0 0 0 0

16
Rota-bit

17
Rota-bit
Descripción
Se podrá visualizar el bit del puerto asignado, desplazándose por el puerto de lado a lado a través de
una serie de LEDs conectados al microcontrolador. En este ejemplo se verá el uso de la instrucción para
realizar un corrimiento ya sea a la derecha o a la izquierda.

Diagrama Esquemático:

Materiales
6 LEDs
6 Resistencias de 220 Ohms
1 Microcontrolador ATmega8
Programador USB asp V3.0

Introducción
LED

LED acrónimo de Light Emitting Diode o Diodo Emisor de Luz, es un dispositivo semiconductor que
emite luz al circular a través de él una corriente eléctrica.

18
Los LEDs como los diodos normales tienen un ánodo y un cátodo, los cuales se pueden identificar de
manera fácil, siendo el ánodo la pata más larga como se observa en la figura.

Como se puede apreciar en la figura superior, al LED se le coloca una resistencia en serie para limitar
la corriente del mismo, en este caso que se alimentara con 5v se sugieren usar resistencias de 220
Ohms.

Para calcular las resistencias exactas y obtener un desempeño optimo se realiza a través de la
siguiente fórmula:

𝑽𝒄𝒄 − 𝑽𝑳𝑬𝑫
𝑹=
𝑰𝑳𝑬𝑫

En donde:
R: Resistencia
Vcc: Voltaje de la fuente
VLED: Voltaje del LED
ILED: Corriente del LED

19
Programa en C:

#include <avr/io.h> //Librería de entradas y salidas de los AVR


#include <util/delay.h> //Librería para usar los retardos "_delay_ms()"

int main(void){ //Inicio del programa

DDRD=0xFF; //Declarar el registro del puerto D como salidas


PORTD=0x01; //Asignarle el valor de 0x01 (Hexadecimal) al puerto D

while(1){ //Iniciar un ciclo while infinito


while( PORTD < 0x20) //"Mientras que el Puerto D sea menor a 0x20 continua"
{
PORTD=PORTD<<1; //Recorrer el Puerto D un lugar a la izquierda
_delay_ms(200); //Esperar 200 milisegundos
}

while( PORTD > 0x01 ) //"Mientras que el Puerto D sea mayor a 0x01 continua"
{
PORTD=PORTD>>1; //Recorrer el Puerto D un lugar a la derecha
_delay_ms(200); //Esperar 200 milisegundos
}
} //Terminar el while infinito

} //Fin del main

Detalles del programa:


#include <avr/io.h>

Librería de entradas y salidas de los AVR. Esta contiene la información de los puertos y los pines de
todos los microcontroladores AVR.

#include <util/delay.h>

Librería necesaria para poder utilizar los retardos "_delay_ms( x )" donde "x" es el numero de
milisegundos a esperar.

DDRD=0xFF;

El DDR solamente configura el registro del puerto que le indica si se va a comportar como entrada o
como salida, este se le puede asignar un 1 para indicar una salida o un 0 para entrada, en el caso de
asignarle 0xFF (que es lo mismo en binario que 0b11111111 o en decimal 255), quiere decir que el
puerto actuara como salida.

20
PORTD=0x01;

El PORT maneja el puerto en caso de que lo hayamos asignado como salida, esta instrucción asigna
el nivel lógico a la salida física del microcontrolador, en este caso el puerto D tiene 8 bits a lo que le
estamos asignando un 0x01 el cual en binario es 0b00000001, esto quiere decir que el primer bit del
puerto D estará activo (5v) y los demás se encontraran desactivados (0v) .

while(1){

La estructura de control while, se puede leer como "Mientras se cumpla esta condición, ejecutar las
instrucciones", pero como en este caso el 1 se toma como una situación "True" o verdadera, el ciclo
siempre se cumple y por lo tanto el programa se repite ejecutando todas las instrucciones dentro
del while.

while( PORTD < 0x20)


{
PORTD=PORTD<<1;
_delay_ms(200);
}

Este ciclo while se puede leer como "Mientras que el puerto D sea menor a 0x20 continua
ejecutando las siguientes instrucciones", por lo tanto si continua podemos ver la instrucción
PORTD=PORTD<<1; esto quiere decir que el registro se desplazara una posición a la izquierda, por
ejemplo:

Bit 7 6 5 4 3 2 1 0
PORTD=0x01 0 0 0 0 0 1 0 0
PORTD=PORTD<<1; 0 0 0 0 1 0 0 0
PORTD=PORTD>>1; 0 0 0 0 0 0 1 0

En la tabla previa podemos ver el valor inicial del PORTD representada en binario, en el cual esta
activado el bit 2, si le aplicamos un corrimiento con el operador << o >> este desplazara todo el
registro del puerto por lo que veremos el bit recorrerse. Como se puede observar en la tabla, en
esta se encuentran las instrucciones de corrimiento a la derecha o a la izquierda.
En este caso, por lo tanto, empezara el ciclo con el bit 0 activo (previamente activado con la
instrucción PORTD=0x01), el while revisara que el PORTD sea menor a 0x20, ya que lo es, va a entrar
y desplazar el bit del 0 a la posición 1, si observamos va a seguir en el ciclo hasta que llegue a 0x20 el
cual en binario es 0b00100000, al llegar a ese valor terminara el ciclo y el programa continuara.

21
while( PORTD > 0x01 )
{
PORTD=PORTD>>1;
_delay_ms(200);
}

Este bloque trabaja de igual manera que el anterior, solo con la diferencia de que recorre el puerto
a la derecha hasta que este alcanza de nuevo el valor de 0x01.

Al salir de este while el programa regresara al primer while(1), ya que este se ciclara por lo
comentado previamente, y se repetirán los desplazamientos del bit activo en el puerto, el cual
estará siendo desplazado de un extremo a otro.

22
Contador de 0-99 con 2
Displays de 7
Segmentos

23
Contador de 0-99 con 2 displays de 7
Segmentos
Descripción
El programa incrementara el valor de una variable, la cual se mostrara a través de un par de displays de
7 segmentos, los cuales se controlaran de manera multiplexada, uno a la vez, a una velocidad que el ojo
no alcance a detectar el cambio y perciba ambos displays encendidos a la vez.

Diagrama Esquemático

Materiales
2 Displays 7 segmentos
7 Resistencias de 220 Ohms
2 Resistencias de 10 kOhms
2 Transistores de pequeña señal
1 Microcontrolador ATmega8
Programador USBasp V3.0

24
Introducción

Display 7 segmentos
El display de 7 segmentos es un dispositivo que nos sirve para mostrar números o caracteres, los
cuales se visualizan al activar o desactivar los LEDs que este tiene, esto se hace conectando el común
a la tierra y voltaje en el segmento que deseemos activar (en caso de ser cátodo común).

Para poder representar los números con el display, es necesario generar la tabla que nos dará el
valor que será necesario para generar el numero deseado.

Num PB6/A PB5/B PB4/C PB3/D PB2/E PB1/F PB0/G HEX


0 1 1 1 1 1 1 0 7E
1 0 1 1 0 0 0 0 30
2 1 1 0 1 1 0 1 6D
3 1 1 1 1 0 0 1 79
4 0 1 1 0 0 1 1 33
5 1 0 1 1 0 1 1 5B
6 1 0 1 1 1 1 1 5F
7 1 1 1 0 0 0 0 70
8 1 1 1 1 1 1 1 7F
9 1 1 1 1 0 1 1 7B

Por ejemplo, se puede observar que para hacer un cero se busca que enciendan todos los
segmentos menos el g (pin 10 del display el cual va conectado al PB0 del Micro), con esa
información del numero, tendremos el valor que tomara el puerto para mostrar el numero deseado
a través del display.

Para controlar dos displays a la vez, se hará uso de un par de transistores, en este caso conmutaran
entre uno y otro, mientras los displays están conectados al mismo puerto del micro. Primero se
mostrara las decenas mientras se desactiva el display de las unidades y después de una fracción de
tiempo, se activa el de la unidad y se desactiva el de las decenas y el micro manda el valor de la

25
unidad. Esto se repite determinadas veces a alta velocidad para dar la impresión de que siempre se
encuentran prendidos los dos displays.

Para activar y desactivar los displays se les pondrá un transistor a modo de swhitch el cual se
conectara del colector al común del display, y este controlara el display activándolo o
desactivándolo desde la base.

Para este caso el transistor se está usando como un swhitch, eso es que se está trabajando en las
regiones de corte y saturación, lo que nos dice que al pasar una corriente en la base, el transistor se
comporta como un interruptor que se abre o se cierra.

Programa en C
#include <avr/io.h>
#include <util/delay.h>

int contador=0;
int unidades, decenas,i;
int numeros[10]={0x7E, //Se declara un vector de longitud 10 que contenga los
0x30, //valores obtenidos de la tabla, acomodados en orden
0x6D, //Según su posición del 0 al 9
0x79,
0x33,
0x5B,
0x5F,
0x70,
0x7F,
0x7B};
int mostrar(int numero){ //Creamos una función de tipo entero

unidades=numero%10; //Al dividir un valor entre 10 el residuo nos da las unidades


decenas =numero/10; //Se obtiene la decena dividiendo el numero entre 10
for(i=0;i<20;i++){

PORTD=0x02; //Se activa el transistor de las unidades


PORTB=numeros[unidades]; //Se asigna al PORTB la variable numero en la posición unidades
_delay_ms(5);

PORTD=0x01; //Se activa el transistor de las decenas


PORTB=numeros[decenas]; //Se asigna al PORTB la variable numero en la posición decenas
_delay_ms(5);
} return 0;
}
26
int main(void){ //Inicio del programa

DDRB=0xFF;
DDRD=0x03;

while(1){

mostrar(contador); //Llamar a la función mostrar, mandando la variable contador


contador++; //Incrementar el valor de la variable contador
if (contador > 99) //Si llega a 99, que tome el valor de 0
contador=0;
}
}

Detalles del programa


int numeros[10]={0x7E,
0x30,
0x6D,
0x79,
0x33,
0x5B,
0x5F,
0x70,
0x7F,
0x7B};
Aquí se está declarando un vector de tipo entero, el cual le indicamos la longitud del mismo, el cual
tiene 10 datos, se acomodaron conforme a que la posición del vector corresponde al número que
representa ese valor en el display, en el caso del 3 el cual es el 0x79 (previamente obtenido en la
tabla), podemos ver que se encuentra en la tercera posición.

int mostrar(int numero){

Una función se declara como "tipo de dato" "nombre de la función" ( "tipo de dato" "nombre de
entrada"), en este caso la función obtendrá la unidad y la decena de cualquier numero entre 0-99 y
mandara el dato para mostrarlo en el display.

unidades=numero%10;
decenas =numero/10;

Operación para obtener separar un numero entre su unidad y decena. Por ejemplo el 45, si lo
dividimos entre 10, esto es 45/10 = 4 (recuerde que como son variables de tipo entero es solo 4 y no
4.5), y con el operador mod (de modulo) "%" el resultado es 45%10 = 5. Gracias a esto tenemos en
una variable el numero 4 y en otra el numero 5.

27
PORTB=numeros[unidades];

Al puerto B se le asigna el valor que tenga el vector números en la posición unidades, si tomamos el
ejemplo anterior, unidades valía 5, si vamos al vector números y vemos la quinta posición, veremos
un 0x5B la cual corresponde al número 5 en la tabla, esto es, que al mostrarlo por el puerto B
aparecerá el 5 en el display.

mostrar(contador);

Aquí se manda a llamar a la función mostrar, a la cual se le manda el valor de la variable contador, el
cual puede ser un numero del 0-99, al llamar la función, esta ejecutara todas las instrucciones que se
encuentren dentro de la misma, al terminar regresara y continuara con la siguiente instrucción.

28
Matriz de LEDs

29
Matriz de LEDs
Descripción
Se hará un contador del 0-9, el cual visualizaremos a través de una matriz de LEDs la cual estará
controlada por el microcontrolador, la matriz a utilizar es una matriz de LEDs de 5x7 la cual contiene
un total de 35 LEDs.

Diagrama Esquemático

Materiales
Matriz de LEDs 5x7
7 Resistencias de 220 Ohms
1 Microcontrolador ATmega8
Programador USBasp V3.0

30
Introducción

Matriz de LEDs

La matriz de LEDs no es más que un arreglo de LEDs agrupados dentro de un encapsulado, los cuales
se encuentran agrupados en forma de matriz. Este acomodo nos ayuda para poder generar
cualquier cosa que nosotros queramos siempre y cuando se pueda representar dentro de la matriz.

La matriz de LEDs que se usara en este ejemplo es una como la de la foto superior, esta es de 5
columnas por 7 filas, las columnas son representadas por una C y las filas por una R, en la imagen
inferior podemos ver como se encuentran distribuidos los pines de la matriz a usar.

Para poder formar algo en la matriz, es necesario realizar un barrido en las columnas para
controlarlas de manera independiente, cada columna tendrá su código, por lo que debemos formar
la figura, numero o letra que necesitemos separando la misma en 5 columnas. A continuación
veremos cómo se forma el numero 3 el cual prestando atención al valor de las R's las cuales forman
el código deseado mientras que las C's generan un barrido de las columnas. Es importante destacar
que en las R's el LED prende con un 0 lógico, lo cual está dado ya que la columna correspondiente

31
está habilitada con un 1 lógico (Vcc), el led en las R's prendera con la diferencia de voltaje, por lo
tanto en las R's se usa el 0 como prendido.

Ejemplo de la formación de un numero 3

C bin C hex R bin R hex


00001 0x01 1011101 0x5D
00010 0x02 0111110 0x3E
00100 0x04 0110110 0x36
01000 0x08 0000000 0x00
10000 0x10 1001001 0x49

32
Como se puede ver en la imagen anterior, el numero 3 se formo en base a la combinación de
controlar las C's y las R's, trabajo que le asignaremos al microcontrolador, este proceso se repetirá
varias veces a una velocidad lo suficientemente alta, como para no alcanzar a percibir los cambios, y
tener la idea de que todos los LEDs deseados se encuentran prendidos a la vez.

Después de hacer un proceso similar, se obtuvo una tabla con los valores de cada numero deseados,
en este caso los números se crearon de determinada forma, la cual puede cambiar dependiendo las
necesidades de cada persona, ya sea que se necesite mostrar letras o caracteres distintos.

Numero C1 C2 C3 C4 C5
0 0x41 0x3E 0x3E 0x00 0x41
1 0x7E 0x5E 0x00 0x00 0x7E
2 0x4E 0x3C 0x38 0x02 0x46
3 0x5D 0x3E 0x36 0x00 0x49
4 0x07 0x77 0x77 0x00 0x00
5 0x8C 0x36 0x36 0x30 0x39
6 0x41 0x36 0x36 0x30 0x39
7 0x3F 0x37 0x37 0x00 0x0F
8 0x49 0x36 0x36 0x00 0x49
9 0x4D 0x36 0x36 0x00 0x41

33
Programa en C
#include <avr/io.h>
#include <util/delay.h>

int contador=0;
int i,j;
int numero[10][5]={ {0x41,0x3E,0x3E,0x00,0x41}, //Se declara la matriz de los numeros
{0x7E,0x5E,0x00,0x00,0x7E},
{0x4E,0x3C,0x38,0x02,0x46},
{0x5D,0x3E,0x36,0x00,0x49},
{0x07,0x77,0x77,0x00,0x00},
{0x8C,0x36,0x36,0x30,0x39},
{0x41,0x36,0x36,0x30,0x39},
{0x3F,0x37,0x37,0x00,0x0F},
{0x49,0x36,0x36,0x00,0x49},
{0x4D,0x36,0x36,0x00,0x41}};

int main (void){

DDRD=0xFF;
DDRC=0xFF;
PORTC=0x10; //Inicializar el puerto C para el barrido de las columnas

while(1){

for(j=0;j<25;j++){ //Ciclo de numero de barridos


for(i=0;i<5;i++){ //Ciclo de barrido de columnas

PORTD=numero[contador][i]; //Se le asigna al PORTD el código respecto a la columna


_delay_ms(1);
PORTC=PORTC>>1; //Siguiente columna
}
PORTC=0x10; //Se inicializa a las primera columna
}
contador++; //Incrementar en 1 el contador

if (contador==10) //Si el contador llega a 10


contador=0; //que vuelva a ser 0
}
}

34
Detalles del programa
#include <avr/io.h>
#include <util/delay.h>

Incluir las librerías necesarias para el proyecto.

int numero[10][5]={ {0x41,0x3E,0x3E,0x00,0x41},


{0x7E,0x5E,0x00,0x00,0x7E},
{0x4E,0x3C,0x38,0x02,0x46},
{0x5D,0x3E,0x36,0x00,0x49},
{0x07,0x77,0x77,0x00,0x00},
{0x8C,0x36,0x36,0x30,0x39},
{0x41,0x36,0x36,0x30,0x39},
{0x3F,0x37,0x37,0x00,0x0F},
{0x49,0x36,0x36,0x00,0x49},
{0x4D,0x36,0x36,0x00,0x41}};

Como se puede ver se inicializa una variable de tipo entero, pero en este caso es una matriz, a la
cual, al momento de declararla le añadimos la longitud de las filas y las columnas dentro de los
corchetes, como se puede ver en el código.

La matriz declarada e inicializada con los valores, es la misma que se encuentra representada en la
tabla en la introducción del proyecto, como se puede ver, la relación de la posición de la fila es la
misma que el numero que representa, por ejemplo el numero 3 se encuentra en la 3ra fila, esto nos
ayudara al momento de realizar el código.

DDRD=0xFF;
DDRC=0xFF;
PORTC=0x10;

Se inicializan los puertos como salida, y el puerto C toma el valor de 0x10 el cual activa el bit 4 del
puerto con el que empezara la primera columna, posteriormente se desplazara el bit, para hacer el
barrido.

for(j=0;j<25;j++){

El primer ciclo for del código representa, las veces que se va ha hacer el barrido completo, por lo
tanto será el total de veces que se muestra un numero completo y esto es el tiempo que dura
visualizándose el numero en la matriz. El número 25 en este ciclo for puede cambiarse para obtener
un conteo más rápido o más lento según se requiera.

35
for(i=0;i<5;i++){

El segundo for, es para hacer el barrido, en este caso el 5 es constante y no debe cambiarse ya que
este representa las columnas de la matriz, como se puede ver, este for trabaja en conjunto con el
otro, cuando este termina en el otro for, se incrementa la variable "j" en uno más, por lo tanto este
for se ejecutara completo el número de veces que el for exterior marque.

PORTD=numero[contador][i];
_delay_ms(1);
PORTC=PORTC>>1;

Aquí se puede ver como se asigna el numero, en este caso la variable numero esta con dos
corchetes los cuales corresponden a la filas y a las columnas, se aprecia que la "i" del ciclo está
cambiando dentro del for interior, que representa las columnas mientras que la variable contador
permanece fija durante los dos ciclos for, esta representa la fila, y como se comento anteriormente,
el numero corresponde a la posición del mismo en la fila.

El PORTC está siendo desplazado para recorrer el bit que controla a las columnas de la matriz,
terminando el ciclo for, se inicializa para comenzar de nuevo.

contador++;

El contador incrementa, para asignar otro número a la matriz.

if (contador==10)
contador=0;

Se limita el incremento del contador, con un if, en este caso si llega a 10, un numero del cual no hay
fila en la matriz, se inicializa de nuevo asignándole un cero.

36
Interrupción Externa

37
Interrupción Externa
Descripción
El push button estará conectado al microcontrolador el cual se encuentra configurado para las
interrupciones externas, esto es, que el programa deja de hacer lo que está haciendo para atender
la interrupción y ejecutar las instrucciones dentro esta función, que para este caso es prender el
LED que se encuentra conectado al microcontrolador.

Diagrama Esquemático

Materiales
1 Push Button
1 Resistencia de 220 Ohms
1 Resistencia de 1 kOhms
1 LED
1 Microcontrolador ATmega8
Programador USBasp V3.0

38
Introducción

Interrupción externa

Las interrupciones externas en el ATmega8 son activadas con los pines INT0 y INT1, en caso de que
se habiliten las interrupciones los pines INT siempre activaran alguna interrupción sin importar
como se haya configurado el puerto en el que estos pines se encuentren. Las interrupciones
externan se habilitan cuando la entrada del pin, cambia de estado, se puede configurar si se
requiere que se active cuando cambia de un estado bajo a uno alto o viceversa.

Resistencia de Pull down

Cuando se conecta un pin del microcontrolador a un switch este al presionarlo nos presenta un nivel
alto en el pin, pero cuando este está abierto con la resistencia de pull down aseguramos un cero o
nivel bajo en el microcontrolador.

Resistencia de Pull-down

39
Programa en C
#include <avr/io.h>
#include <avr/interrupt.h> //Libreria necesaria para manejar las interrupciones
#include <util/delay.h>

int main (void) {


DDRB=0xFF;

cli(); //Desactiva las interrupciones globales

MCUCR=0x03;
GIFR =0x40;
GICR=0x40;

sei(); //Activar las interrupciones globales

while(1){

}
}

ISR(INT0_vect) //Vector de interrupción externa del INT0


{
PORTB=0x01;
_delay_ms(2000);
PORTB=0x00;
}

Detalles del programa


#include <avr/interrupt.h>

Cada que se use alguna interrupción es necesario llamar a la librería avr/interrupt.h que es la que
contiene todos los vectores de interrupción de los AVR's.

cli();

Esta instrucción deshabilita las interrupciones.

sei();

Habilita las interrupciones.

40
MCUCR=0x03;

Al asignársele un 0x03 le estamos indicando que active los bits 0 y 1 los cuales para el registro MCUCR
nos indican de que manera se active la interrupción, como se puede ver en la tabla, hay cuatro opciones
que son: El nivel bajo de INT1, Cualquier cambio lógico, El flanco de bajada y El flanco de subida. Con los
bits 0 y 1 en unos, tenemos habilitada la opción del flanco de subida.

GIFR =0x40;

0x40 = 0b01000000, esto es, que estamos seleccionando el bit 6 del registro GIFR el cual nos indica que
al activarlo limpiamos la bandera INTF0.

GICR=0x40;

Al igual que en el registro pasado se está activando el bit 6 del GICR el cual nos indica que se usara el
pin INT0 para la interrupción externa.

41
Uso del ADC

42
Uso del ADC
Descripción
Con este programa podremos visualizar a través de los LEDs, el valor en binario tomado del ADC
conectado a un potenciómetro. El ADC se trabajara a manera de conversión simple y se tomaran
solo 8 de los 10 bits, de los cuales se ajustaran para que la salida vaya de 0 a 63, que será
representada por los LEDs conectados al microcontrolador.

Diagrama Esquemático

Materiales
1 Potenciómetro
6 LEDs
7 Resistencias de 220 Ohms
1 Microcontrolador ATmega8
Programador USBasp V3.0

Introducción

El ADC

El ADC convierte señales continuas a números discretos. El ADC es un dispositivo electrónico que
pasa un nivel de voltaje de entrada a un valor digital proporcional a la magnitud de la entrada, la
salida digital puede estar descrita por diferentes codificaciones.

En este caso, el ADC a utilizar es el del microcontrolador ATega8 el cual es un ADC de 10 bits, de los
cuales solo usaremos 8. Las características principales del ADC del ATmega8 son:

-Resolución de 10 bits
-± 2 bits de precisión
-13 a 260 us de tiempo de conversión

43
-6 Canales de entrada multiplexados
-Rango de voltaje de entrada de 0-Vcc
-Selector de voltaje de referencia de 2.56v
-Tipos de conversión continuo o simple
-Interrupción de conversión
-Ahorro de energía

Potenciómetro

El potenciómetro es un tipo de resistencia variable el cual varia conforme se gira la perilla que tiene,
en este caso el potenciómetro es usado para generar un divisor de voltaje el cual al variar la
resistencia, la salida de voltaje también cambiara proporcionalmente.

El potenciómetro tiene 3 patas las cuales son:

Programa en C
#include<avr/io.h>
#include<util/delay.h>

int main (void)


{
int ADC_val;
DDRD = 0xFF;

ADCSRA = 0xC0; //Configurar el registro ADCSRA


ADMUX = 0x22; //Configurar el registro ADMUX

while(1) {

ADCSRA|=_BV(ADSC); //Activar el bit ADSC del registro ADCSRA inicio de conversion

ADC_val=(ADCH*63)/255; //Ajustar la recta para que vaya de 0 a 63


PORTD = ADC_val;

}
}
Detalles del programa

44
Detalles del programa
ADCSRA = 0xC0;

Para el registro ADCSRA se asigno 0xC0 o 0b11000000, hexadecimal o binario respectivamente. El


bit 7 ADEN habilita el uso del ADC, y el bit 6 ADSC al escribirle un uno inicia la conversión.

ADMUX = 0x22;

Se activan los bits 5 y 2 por lo que el registro nos queda como 0b00100010 (lo que es igual en
hexadecimal a 0x22), al activar el bit 1 le indicamos al ADC que tome la entrada del pin del ADC2

con el bit 5 (ADLAR) del registro ADMUX configuramos la manera en la que nos deposita el valor en
los dos registros, para este caso se configuro de la siguiente manera, en la que como se puede ver se
ignoraron los dos bits más significativos.

ADCSRA|=_BV(ADSC);

Al estar trabajando el ADC en este modo es necesario indicarle cada cuando tiene que realizar la
conversión, con esta instrucción solo entra al bit ADSC del registro y lo habilita, no se modifica
cualquier otro valor del registro ADCSRA.

45
ADC_val=(ADCH*63)/255;

Ya que el valor del ADCH es de 8 bits (como se ve en la imagen al ajustar el ADLAR), se tiene que
ajustar la salida a que sea de 6 bits, ya que se están usando solo 6 LEDs, esto se hace ajustando la
recta, multiplicando por el máximo de nuestra salida ideal y dividiéndolo por el máximo de la salida
obtenida.

46
Uso del PWM

47
Uso del PWM
Descripción
En este ejemplo se hará uso del PWM, o modulación por ancho de pulso, la cual consiste en
modificar el ancho del pulso dejando la frecuencia intacta, el programa aceptara dos entradas,
que son los Push Buttons conectados al microcontrolador, y tendrá una salida, que es el LED, el
cual indicara el nivel de modulación, a más ancho el pulso mas ciclo de trabajo y el LED se
iluminara con mayor intensidad.

Diagrama Esquemático

Materiales
1 LED
1 Resistencia de 220 Ohms
2 Push Button
2 Resistencias de 10Kohms
1 Microcontrolador ATmega8
Programador USBasp V3.0

48
Introducción

PWM
Modulación por ancho de pulso o PWM (Pulse-Width Modulation), de una señal, es cuando se
modifica el ciclo de trabajo o el ancho del pulso de una señal periódica, en este caso representado
por una señal cuadrada, uno de los usos del PWM entre muchos otros, es controlar la cantidad de
energía, en este caso el voltaje promedio es mayor conforme aumenta el ciclo de trabajo.

En la imagen anterior se puede observar, que el periodo de la señal permanece fijo, por lo tanto, la
frecuencia también, solamente cambia el ciclo de trabajo, en la primera se observa que el ciclo de
trabajo es de aproximadamente 50% lo cual nos indica que es el porcentaje de voltaje promedio
entregado a la carga.

El PWM se puede utilizar en varias cosas, como el control de la velocidad de motores de DC, la
posición de un servomotor, fuentes conmutadas, entre otras cosas más.

49
Programa en C
#include <avr/io.h>
#include <util/delay.h>

int main(void){

DDRB=0x02;
DDRD=0x00;

TCCR1A=0b10000011; //Configurar el PWM en modo de fase


TCCR1B=0b00000001; //Sin preescalador
TCNT1 =0b00000000; //No se modifica

OCR1A=0; //Inicializar el TOP en cero

for(;;){ //Ciclo infinito

if ( PIND == 0x01 ) { //Si el boton 1 esta activado


OCR1A++; //Incrementar la modulacion
_delay_us(500);
}

if ( PIND == 0x02 ) { //Si el boton 2 esta activado


OCR1A--; //Decrementar la modulacion
_delay_us(500);
}
}
}

Detalles del programa


#include <avr/io.h>

Incluimos la librería avr/io que contiene la informacion de las entradas y salidas del
microcontrolador.

50
#include <util/delay.h>

Esta librería es necesaria para poder utilizar los retardos de tiempo

int main (void){

El main es la función principal, es donde el programa inicia, siempre es necesario declarar la función
main.

DDRB=0x02;
DDRD=0x00;

Se declaran el bit 1 del puerto B como salida y el puerto D como entrada.

TCCR1A=0b10000011;

Los primeros dos bits del TCCR1A los cuales se observa que tanto en el registro como en la tabla
sonWGM11 y WGM10, y como en este caso ambos están activados, podemos ver que el modo de
operación es el de PWM en fase de 10 bits.

Acerca del bit 7 este depende de qué tipo de PWM se esté trabajando, ya que en este caso se está
trabajando el de corrección de fase, vemos la tabla que le corresponde y vemos que el tenemos el
COM1A1 habilitado el cual nos indica que limpia el OC1A en la comparación al incrementar y ajusta
el OC1A en la comparación al decremento.

51
A continuación se puede ver en la grafica tomada de la hoja de datos el funcionamiento del PWM en
modo de fase:

TCCR1B=0b00000001;

52
Se ajusta el PWM para que no haya preescalador desde el Clk, se puede ver en la tabla de los
últimos tres bits del registro.

OCR1A=0;

Se inicializa el valor al que llega el PWM, como se puede ver en la imagen anterior los picos del
TCNTn dependen del OCRnx, en este caso es el OCR1A el cual se inicializa en cero.

for(;;){

En este caso, es otro manera de generar un loop infinito, esta instrucción es equivalente al while(1){

if ( PIND == 0x01 ) {
OCR1A++;
_delay_us(500);
}

if ( PIND == 0x02 ) {
OCR1A--;
_delay_us(500);
}

En general lo que se hace en esta sección de código, es que primero verifica si el Push Button1 está
siendo presionado al comparar el puerto con un 0x01, en caso de que si este, se incrementa el valor
del OCR1A el cual define el ancho del pulso en la modulación, por consiguiente, como se vio
previamente, a mayor ancho del pulso se entrega un mayor voltaje promedio. El segundo if funciona
de la misma manera, solo que para activarlo hay que presionar el otro botón, y dentro de este el
OCR1A se decrementara.

53
El retardo se usa ya que como el microcontrolador corre a una velocidad de 1Mhz, para limitar la
velocidad del incremento, si no hubiera algún retardo, no podríamos observar que es lo que esta
pasado debido a la velocidad del incremento, ya que con solo presionar algún botón un instante de
tiempo, el código se repetiría cientos de veces y el OCR1A se incrementaría o decrementaria muy
rápido.

54
Servomotor

55
Servomotor
Descripción
Existen varias maneras de generar un PWM capaz de controlar un servomotor, pero esta vez, se
hará a través de un PWM por software, esto es, se generara la rutina para activar y desactivar un pin
del puerto es cual controlara el servomotor, que a su vez, estará siendo controlado con un
potenciómetro conectado al ADC.

Diagrama Esquemático

Materiales
1 Servomotor
1 Resistencia de 220 Ohms
1 Potenciómetro de 10Kohms
1 Microcontrolador ATmega8
Programador USBasp V3.0

56
Introducción

Servomotor
Un servomotor es un dispositivo actuador que tiene la capacidad de ubicarse en cualquier posición
dentro de su rango de operación, y de mantenerse estable en dicha posición. Está formado por un
motor de corriente continua, una caja reductora y un circuito de control, y su margen de
funcionamiento generalmente es de menos de una vuelta completa.

El servomotor usado para el ejemplo es un Hitec HS475HB.

Los servomotores trabajan con PWM, estos tienen tres cables, uno para alimentar el motor, otro de
tierra y el tercero es el de la señal, el cual se conectara al microcontrolador al pin de salida del PWM.

Como se puede ver en la imagen, a mayor ancho de pulso, es mayor el ángulo de trabajo del
servomotor. Este al posicionarse en determinado ángulo, permanecerá en el mismo con torque
siempre y cuando se siga generando la señal.

57
Programa en C
#include<avr/io.h>
#include<util/delay.h>

int servo(int SH){ //Función para generar la señal PWM por software
int i,k;

PORTB|=_BV(PB0); //Se activa el bit 0 del PORTB


for (i = 0; i <= SH; i++) //Ciclo for de 0 hasta el valor introducido por la entrada
{_delay_us(1);} //de la función, con la variable SH, SH veces

PORTB&=~(_BV(PB0)); //Desactiva el bit 0 del PORTB


for (k = 0; k <= (10000-SH); k++) //Ciclo de 0 hasta 10000-SH
{_delay_us(1);} //Se repetirá 10000-SH veces
}
return 0; //Como la función no es void se regresa un 0
}

int main (void) //Inicio del programa


{
int ADC_val;
DDRB = 0xFF;

ADCSRA = 0xC0; //Se configura el ADC


ADMUX = 0x22;

while(1) { //Ciclo infinito

ADCSRA|=_BV(ADSC); //Iniciar conversión


ADC_val=((ADCH*200)/254)+50; //Tomar valor y ajustarlo para el servo
servo(ADC_val); //Llamar la función servo, con la entrada ADC_va, la cual
} //dentro de la función, será SH
}

58
Detalles del programa
int servo(int SH){

Esta función es la que nos servirá para generar los pulsos necesarios para hacer funcionar el
servomotor, nótese que en este caso, a diferencia del ejemplo anterior, se está generando PWM a
través de la programación y no del modulo que el microcontrolador ya trae integrado. Esto con la
finalidad de no depender de el miso.

PORTB|=_BV(PB0);
for (i = 0; i <= SH; i++)
{_delay_us(1);}

Después de activar el primer bit del puerto B, va a mandar un nivel de voltaje alto a través del pin
correspondiente. El ciclo for como se puede ver, va desde 0 hasta que sea mayor o igual a SH, y
dentro del ciclo se está repitiendo la instrucción de retardo de 1 micro segundo, por lo tanto, se
puede interpretar como que se activa el pin de puerto con un tiempo de SH micro segundos.

PORTB&=~(_BV(PB0));
for (k = 0; k <= (10000-SH); k++)
{_delay_us(1);}

En este caso, ocurre algo similar que en el paso anterior, con la principal diferencia de que la señal
va de un nivel alto a un nivel bajo, y espera una cantidad de 10000-SH micro segundos. La resta es
necesaria, ya que no se puede suponer un valor fijo para el tiempo en bajo, ya que si el tiempo fuera
fijo, en caso de que el tiempo en alto variase, también variaría la frecuencia, por lo tanto, se ajusta
restando el valor de SH para que la proporción que SH varia, también lo haga el tiempo en bajo, y así
se logra que la frecuencia sea fija.

59
ADCSRA = 0xC0;
ADMUX = 0x22;

El ADC se configura de la misma manera que se configuro para el ejemplo del ADC

ADCSRA|=_BV(ADSC);

Se le indica al ADC que inicie la conversión.

ADC_val=((ADCH*200)/254)+50;

Al igual que en el ejemplo del ADC se ajustan los valores, pero en este caso se le añade un offset
(+50) ya que el servo no inicia con una modulación del 0%.

servo(ADC_val);

Se manda a llamar la función servo, con el valor del ADC_val como entrada, el cual será el tiempo en
alto de la señal, proporcional al ciclo de trabajo.

60
Como se puede ver en las imágenes, aunque el SH se modifique, el tiempo en bajo siempre se ajusta
para no modificar la frecuencia, ya que el periodo de la señal es constante.

61
Pantalla LCD

62
Pantalla LCD
Descripción
En este ejemplo se trabajara con una pantalla LCD de 16x2, se verán un par de funciones de la misma
que se realizaran como escribir texto o limpiar la pantalla. En este caso para evitar un grado de
complejidad mayor se trabajara la pantalla LCD con una comunicación de 8 bits, la cual es más sencilla
que la de 4 bits. También se hará uso de un potenciómetro como divisor de voltaje para regular el PIN
Vee el cual controla el contraste de la pantalla.

Diagrama Esquemático

Materiales
1 Pantalla LCD 16x2 JHD-162A
1 Resistencia de 220 Ohms
1 Potenciometro de 10 kOhms
1 Microcontrolador ATmega8
Programador USBasp V3.0

63
Introducción

Pantalla LCD

Una pantalla de cristal liquido o LCD es una pantalla delgada y plana formada por un determinado
número de pixeles monocromos (para este caso), colocados delante de una fuente luminosa. Una de las
principales características de las pantallas LCD es su bajo consumo de energía eléctrica.

Pantalla JHD-162A
Configuración de los pines, tamaño de la pantalla y de los caracteres.

Controlador de la pantalla JHD-162A, HD44780

El controlador HD44780 es el circuito que se encuentra en la pantalla JHD-162A y es el que controla el


manejo del LCD, en este es recomendable revisar la hoja de datos del mismo ya que explica los modos
de manejo del LCD y las instrucciones de este. En la siguiente imagen podemos ver una de las páginas de
la hoja de datos del controlador en donde se pueden ver varias de las instrucciones que el LCD maneja.

64
Programa en C
#include <avr/io.h>
#include <util/delay.h>

#define Enable_On PORTC|=_BV(PC2) //Definiciones


#define Enable_Off PORTC&=~_BV(PC2)
#define RS_On PORTC|=_BV(PC0)
#define RS_Off PORTC&=~_BV(PC0)
#define RW_On PORTC|=_BV(PC1)
#define RW_Off PORTC&=~_BV(PC1)

#define Data PORTD


#define DelayL _delay_ms(5);
int i=0;

void PORT_init (void){ //Función para inicializar puertos


DDRC=0x07;
DDRD=0xFF;
PORTC=0x00;
PORTD=0x00;
}

void LCD_init (void){ //Función para inicializar el LCD


Data=0x0F;
Enable_On;
DelayL;
Enable_Off;
Data=0x00;
RS_On;
DelayL;
}

void WriteLCD(char text[15]){ //Función para escribir en el LCD


RS_On;
for (i=0;i<16; i++){
Data=text[i];
Enable_On;
DelayL;
Enable_Off;
DelayL;
}
i=0;
Data=0x00;
RS_Off;
}

65
void ClearLCD(void){ //Función para limpiar la pantalla del LCD
Data=0x01;
Enable_On;
DelayL;
Enable_Off;
}

int main (void){ //Inicio del programa

PORT_init();
LCD_init();

while(1){
WriteLCD("Hola mundo"); //Escribir en el LCD
_delay_ms(1000);

ClearLCD(); //Limpiar la pantalla del LCD

_delay_ms(500);

}
}

Detalles del programa


#define Enable_On PORTC|=_BV(PC2)
#define Enable_Off PORTC&=~_BV(PC2)
#define RS_On PORTC|=_BV(PC0)
#define RS_Off PORTC&=~_BV(PC0)
#define RW_On PORTC|=_BV(PC1)
#define RW_Off PORTC&=~_BV(PC1)

#define Data PORTD


#define DelayL _delay_ms(5);

66
La directiva #define define una macro que proporciona un mecanismo de reemplazo de código por
una secuencia de caracteres, en este caso la sintaxis del #define es:

#define macro_identificador <secuencia-de-tokens>

tomando como referencia uno de las definiciones previamente usadas podemos ver que lo que se
está haciendo con:

#define Enable_On PORTC|=_BV(PC2)

es asignándole a Enable_On la sección de codigo PORTC|=_BV(PC2) la cual activa el bit 2 del registro
PORTC.

void PORT_init (void){


DDRC=0x07;
DDRD=0xFF;
PORTC=0x00;
PORTD=0x00;
}

Esta es una función en la que dentro de ella se configuran los registros de los puertos

void LCD_init (void){


Data=0x0F;
Enable_On;
DelayL;
Enable_Off;
Data=0x00;
RS_On;
DelayL;
}

Para inicializar la pantalla LCD tenemos que seguir una serie de instrucciones las cuales como se
puede apreciar en el código son las siguientes que se encuentran representadas en la tabla.

Instruc \ E R/W RS Data DB0:7


Pin
1 0 0 0 0x0F
2 1 0 0 0
3 0 0 0 0
4 0 0 1 0

Después de inicializar la pantalla un puntero debe aparecer en la misma, y nos indica que esta lista
para recibir los caracteres.

67
void WriteLCD(char text[15]){
RS_On;
for (i=0;i<16; i++){
Data=text[i];
Enable_On;
DelayL;
Enable_Off;
DelayL;
}
i=0;
Data=0x00;
RS_Off;
}

Para escribir en el LCD es necesario seguir una serie de instrucciones, las cuales nos llevaran a
escribir el texto, que queramos ver en el LCD, carácter por carácter, por lo tanto tenemos que
separar nuestro texto en caracteres, esto lo logramos haciendo la función con una entrada de tipo
char (carácter), pero en este caso va a ser un vector, esto es una serie de caracteres o de chars, por
lo tanto cuando llamemos la función y a la entrada de la misma pongamos algún texto este se
guardara en un vector de caracteres, por ejemplo en texto "Hola Mundo":

Posicion 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
Caracter H o l a M u n d o

Para habilitar la entrada de texto, se activa el pin de RS, a continuación el código inicia un ciclo for
de 0 a 15, para lograr desplazarnos por las 16 posiciones que tiene la pantalla. Al iniciar el ciclo se le
asigna a la variable Data el carácter que está en el vector text en la posición j, por ejemplo en el
primer caso inicia con j=0 en caso de haber escrito el texto "Hola Mundo", el vector quedaría como
en la tabla superior y cómo podemos ver la posición 0 tendría el carácter "H", después se le da un
pulso al Enable un retardo y el ciclo for continua. Al terminar se limpian las variables y el RS se
desactiva

Instruc \ E R/W RS Data DB0:7


Pin
1 0 0 1 0
2 0 0 0 Caracter
3 1 0 0 0
4 0 0 0 0

68
void ClearLCD(void){
Data=0x01;
Enable_On;
DelayL;
Enable_Off;
}

Para limpiar la pantalla, se escribió la función ClearLCD la cual inicializa el puntero de la pantalla y
borra todos los caracteres escritos en ella. Para limpiar la pantalla se realizaron las siguientes
instrucciones.

Instruc \ E R/W RS Data DB0:7


Pin
1 0 0 0 0x01
2 1 0 0 0
3 0 0 0 0

int main (void){

PORT_init();
LCD_init();

La función main o principal, a la cual al correr el micro es la primera en ejecutar, en este caso llama a
las funciones inicializadoras de los puertos y del LCD.

while(1){
WriteLCD("Hola mundo"); //Escribir en el LCD
_delay_ms(1000);

ClearLCD(); //Limpiar la pantalla del LCD


_delay_ms(500);
}

Dentro del ciclo while infinito, se llama a la función WriteLCD la cual escribe el texto "Hola Mundo"
en el LCD de la manera que se explico previamente, a continuación damos un retardo de 1000 ms y
posteriormente se limpia la pantalla llamando a la función ClearLCD a lo que esperamos 500 ms,
después de esto el ciclo inicia de nuevo y el texto vuelve a aparecer.

69
Sensor ultrasónico
SRF08

70
Sensor ultrasónico SRF08
Descripción
Para este ejemplo se hará uso de un sensor ultrasónico, este es un modulo independiente, el cual es
controlado vía I2C (Inter-Integrated Circuit) , este protocolo es también conocido como TWI (Two-
Wire-Interface). El sensor nos entrega la distancia directamente en centímetros, la que estaremos
visualizando a través de una serie de LEDs conectados al microcontrolador.

Diagrama Esquemático

Materiales
Sensor ultrasónico SRF08
6 Resistencias de 220 Ohms
1 Microcontrolador ATmega8
Programador USBasp V3.0

71
Introducción

Sensor SRF08

El SRF08 Es un medidor ultrasónico de distancias comercial, el cual hace uso de su transmisor y


receptor para ofrecer una medida de distancia bastante precisa, el sensor también cuenta con un
LDR la cual nos indica la intensidad luminosa del lugar donde lo tengamos.

Algunas de las características del sensor son:


-5v de alimentación
-15mA de consumo de corriente
-Frecuencia 40KHz
-Rango de medición: de 3cm a 6mts
-Conexión I2C
-Sensor de iluminación LDR
-Unidades soportadas: uS, mm, pulgadas.
-Tamaño: 43x20x17 mm

En la imagen superior podemos ver las conexiones del sensor, en este caso como se puede apreciar
el cuarto pin no se conecta, y los pines SDA y SCL son los del protocoló I2C los cuales irán conectados
al microcontrolador como se ve en el diagrama esquemático.

I2C o TWI

I2C es un bus de comunicaciones en serie. Su nombre viene de Inter-Integrated Circuit (Circuitos


Inter-Integrados). La versión 1.0 data del año 1992 y la versión 2.1 del año 2000, su diseñador es
Philips. La velocidad es de 100Kbits por segundo en el modo estándar, aunque también permite
velocidades de 3.4 Mbit/s. Es un bus muy usado en la industria, principalmente para comunicar
microcontroladores y sus periféricos en sistemas integrados (Embedded Systems) y generalizando
más para comunicar circuitos integrados entre si que normalmente residen en un mismo circuito
impreso.

72
La principal característica de I²C es que utiliza dos líneas para transmitir la información: una para los
datos y por otra la señal de reloj. También es necesaria una tercera línea, pero esta sólo es la
referencia GND.

Las líneas se llaman:


SDA: datos
SCL: reloj
GND:

En la imagen se puede ver un ejemplo de conexión de un microcontrolador, en modo maestro y


varios otros dispositivos conectados como esclavos, como ADCs DACs otros microcontroladores,
sensores etc.

Algunas de las instrucciones a utilizar del sensor SRF08

Hexadecimal Decimal Instrucción Lectura/Escritura


0xE0 224 Dirección del sensor -
0x00 0 Inicializar el calculo Escritura
0x51 81 Modo cálculo distancia - Resultado en centímetros Escritura
0x03 3 Fija la ganancia analógica máxima en 103 Lectura

El sensor SRF08 tiene muchas más instrucciones y diferentes modos de operación, esta tabla solo
contiene algunas de las instrucciones que el sensor maneja, esto con la finalidad de entender el
código que va en el micrcontrolador, ya que más adelante se hará uso de estas direcciones.

73
Programa en C
#include<avr/io.h>
#include<util/delay.h>
#include<avr/interrupt.h>
#include<util/twi.h>

#define Clock 1000000 //Frecuencia de trabajo del microcontrolador


#define SRF02 0xE0;

void TWI_config(unsigned long int); //Declarar las funciones


void i2c_transmit(char,char,char);
char i2c_read(char,char);

int main(void) //Inicio del programa


{
DDRD = 0xFF;
TWI_config(100000); //Llamar la funcion para configurar el I2C o TWI
unsigned char valor=0;
while(1)
{
i2c_transmit(0xE0,0,0x51); //Transmitir: direccion 0xE0, registro 0, dato 0x51
_delay_ms(75);
valor = i2c_read(0xE0,3); //leer: direccion 0xE0, registro 3
PORTD = valor; //Mandar el valor leido al puerto D
_delay_ms(100);
}
}

void TWI_config(unsigned long int frecuencia)


{
TWBR = ((Clock/frecuencia) - 16)/8; // Registro para la frecuencia de SCL
TWSR=(0<<TWPS1)+(0<<TWPS0); // Preescala 1

74
char i2c_read(char address, char reg)
{
char read_data = 0;

TWCR = 0xA4; // Mandar el bit de inicio para el bus I2C


while(!(TWCR & 0x80)); // Esperando la confirmación
TWDR = address; // Cargar dirección (adress) en el dispositivo
TWCR = 0x84; // Transmitir
while(!(TWCR & 0x80)); // Esperar confirmación de la transmisión
TWDR = reg; // Enviar número de registro a leer
TWCR = 0x84; // Transmitir
while(!(TWCR & 0x80)); // Esperar confirmación de la transmisión

TWCR = 0xA4; // Volver a mandar un bit de inicio


while(!(TWCR & 0x80)); // Esperar confirmación de la transmisión
TWDR = address+1; // Transmitir dirección por I2C con la lectura habilitada
TWCR = 0xC4; // Borrar la bandera de interrupción de transmisión
while(!(TWCR & 0x80)); // Esperar confirmación de la transmisión
TWCR = 0x84; // Transmitir (Solicitud del último byte)
while(!(TWCR & 0x80)); // Esperar confirmación de la transmisión
read_data = TWDR; // Tomar el dato
TWCR = 0x94; // Mandar un bit de paro al bus I2C
return read_data;
}

void i2c_transmit(char address, char reg, char data)


{
TWCR = 0xA4; // Mandar el bit de inicio para el bus I2C
while(!(TWCR & 0x80)); // Esperar confirmación de la transmisión
TWDR = address; // Cargar dirección (adress) en el dispositivo
TWCR = 0x84; // Transmitir
while(!(TWCR & 0x80)); // Esperar confirmación de la transmisión
TWDR = reg;
TWCR = 0x84; // Transmitir
while(!(TWCR & 0x80)); // Esperar confirmación de la transmisión
TWDR = data;
TWCR = 0x84; // Transmitir
while(!(TWCR & 0x80)); // Esperar confirmación de la transmisión
TWCR = 0x94; // Mandar un bit de paro al bus I2C
}

75
Detalles del programa
#include<avr/io.h>
#include<util/delay.h>
#include<avr/interrupt.h>
#include<util/twi.h>

Se llaman las librerías necesarias para el programa.

void TWI_config(unsigned long int);


void i2c_transmit(char,char,char);
char i2c_read(char,char);

Se declaran las funciones que a continuación se usaran. Es importante notar en caso de escribir las
funciones después del main, es necesario declararlas.

TWI_config(100000);

Se llama a la función TWI_config con la entrada 1000000 que será la frecuencia del
microcontrolador, más adelante se explicara esta función.

i2c_transmit(0xE0,0,0x51);
_delay_ms(75);
valor = i2c_read(0xE0,3);
PORTD = valor;
_delay_ms(100);

Se puede ver en esta sección de código que se transmite por el I2C, y se lee el dato el cual se
muestra a través del puerto D. las funciones de transmitir y de leer se explican a continuación.

void TWI_config(unsigned long int frecuencia)


{
TWBR = ((Clock/frecuencia) - 16)/8;
TWSR=(0<<TWPS1)+(0<<TWPS0);
}

76
El TWBR selecciona el factor de división de la frecuencia del SCL en modo maestro

Al registro de estado TWSR se le está escribiendo la instrucción (0<<TWPS1)+(0<<TWPS0); la cual


nos indica que a los bits TWPS1 y TWPS0 se les está asignando un 0 a cada uno, esto es, para
corroborar que se está usando una preescala de 1.

char i2c_read(char address, char reg)


{
char read_data = 0;

TWCR = 0xA4; // Mandar el bit de inicio para el bus I2C


while(!(TWCR & 0x80)); // Esperando la confirmación
TWDR = address; // Cargar dirección (adress) en el dispositivo
TWCR = 0x84; // Transmitir
while(!(TWCR & 0x80)); // Esperar confirmación de la transmisión
TWDR = reg; // Enviar número de registro a leer
TWCR = 0x84; // Transmitir
while(!(TWCR & 0x80)); // Esperar confirmación de la transmisión

77
TWCR = 0xA4; // Volver a mandar un bit de inicio
while(!(TWCR & 0x80)); // Esperar confirmación de la transmisión
TWDR = address+1; // Transmitir dirección por I2C con la lectura habilitada
TWCR = 0xC4; // Borrar la bandera de interrupción de transmisión
while(!(TWCR & 0x80)); // Esperar confirmación de la transmisión
TWCR = 0x84; // Transmitir, nack (Solicitud del último byte)
while(!(TWCR & 0x80)); // Esperar confirmación de la transmisión
read_data = TWDR; // Tomar el dato
TWCR = 0x94; // Mandar un bit de paro al bus I2C
return read_data;
}

La función i2c_read, lee el dato que manda el sensor, entre las cosas a destacar en esta función son
el uso del registro TWCR y del TWDR:

El TWCR se usa para controlar la operación del TWI. En él se habilita el TWI, se inicializa el acceso
con la condición de inicio, genera una condición de paro, entre otras cosas

En modo de transmisión, el TWDR contiene el siguiente byte para ser transmitido, y en modo de
recepción contiene el ultimo byte en ser recibido.
Dentro del la funcion i2c_read se encuentran varias instrucciones importantes las cuales son:

TWCR = 0xA4;

Esta instrucción nos indica que los bits 2, 5 y 7 los cuales son TWEN, TWSTA y TWINT
respectivamente, del registro TWCR están habilitados.
TWEN: Habilita y activa la comunicación TWI (I2C).
TWSTA: Al habilitarlo, se configura el TWI del microcontrolador como modo maestro.
TWINT: Al escribirle un 1 lógico, se limpia la bandera de interrupción.

78
while(!(TWCR & 0x80));

El 0x80 en el TWCR corresponde a la bandera del bit TWINT, el ciclo espera a la bandera de
confirmación.

TWDR = address;

Se le asigna la variable address, la cual, como se puede ver en el código, tiene el valor de 0xE0. Este
valor es la dirección del dispositivo con el que se está comunicando. En este caso el sensor
ultrasónico responde a la dirección 0xE0

TWCR = 0x84;

Se limpia la bandera y habilita la comunicación.

TWDR = reg;

Envía el registro a leer en el TWDR, en este caso la variable reg es igual a 3 lo que en el sensor indica
el ajuste de la ganancia analógica máxima a 103. La finalidad de poder limitar la ganancia máxima es
permitirle iniciar mediciones a una frecuencia mayor de 65mS.

read_data = TWDR;

Toma el dato y lo coloca en la variable read_data

TWCR = 0x94;

Manda un bit de paro, activando también el TWSTO Stop Condition Bit.

void i2c_transmit(char address, char reg, char data)


{
TWCR = 0xA4; // Mandar el bit de inicio para el bus I2C
while(!(TWCR & 0x80)); // Esperar confirmación de la transmisión
TWDR = address; // Cargar dirección (adress) en el dispositivo
TWCR = 0x84; // Transmitir
while(!(TWCR & 0x80)); // Esperar confirmación de la transmisión
TWDR = reg;
TWCR = 0x84; // Transmitir
while(!(TWCR & 0x80)); // Esperar confirmación de la transmisión
TWDR = data;
TWCR = 0x84; // Transmitir
while(!(TWCR & 0x80)); // Esperar confirmación de la transmisión

79
TWCR = 0x94; // Mandar un bit de paro al bus I2C
}

Las instrucciones de la función para transmitir como podemos ver son casi las mismas que para la
función leer, pero no perdamos de vista que al mandar llamar cada función se le asignan distintos
valores, entre las principales instrucciones a destacar se encuentran:

TWDR = reg;

A diferencia de la función read, el registro en este caso tiene un valor de 0 el cual le indica al sensor
que inicie la sesión de cálculo de la distancia.

TWDR = data;

Se puede ver en el main que a la variable data, se le asigna un 0x51 el cual le indica al sensor que
trabaje en modo de cálculo de distancia y este nos da un resultado en centímetros.

80
Comunicación serial
USART

81
Comunicación Serial USART
Descripción
En este proyecto, se verá una de las maneras de entablar comunicación del microcontrolador con la
computadora, para poder realizar esta práctica se requerirá del uso de varios componentes los
cuales describiremos a continuación, la comunicación serial es una de las maneras más sencillas de
comunicar nuestro microcontrolador hacia el exterior, ya sea con una computadora o con algún otro
microcontrolador. Para este proyecto se configurara el microcontrolador con el oscilador interno a 8
Mhz y se usara del programa Teraterm el cual es el equivalente de la hyperterminal en Windows
Vista. Básicamente el programa realizara un eco del dato mandado, si se manda una "a" el
microcontrolador recibirá esa "a" y la enviara de vuelta. Esto con la finalidad de aislar la
comunicación de cualquier proceso, en su momento es posible mandar un dato, aplicarle algún
proceso o algoritmo y regresar el resultado.

Diagrama Esquemático

Materiales
Max 232
4 capacitores de 10 uF
1 Microcontrolador ATmega8

82
Programador USBasp V3.0
1 Cable USB a Serial en caso de que la computadora no tenga puerto serial

Introducción

Características de la USART del ATmega8

La USART o Universal Synchronous and Asynchronous serial Receiver and Transmitter es un


dispositivo de comunicación serial altamente flexible, sus principales características son:

-Operación Full Dulpex


-Registros de transmisión y recepción independientes
-Operación síncrona o asíncrona
-Generador de Baud Rate de alta resolución
-Detección de error
-Filtro de ruido
-Modo de comunicación multiproceso
-Doble velocidad en modo de comunicación asíncrono

Diagrama a bloques de la USART

El manejo de la comunicación serial presenta muchos beneficios, entre los que destacan, el control
de sistemas a través de la computadora realizando cálculos complejos, visualizando y graficando
datos, entre otros. Es importante destacar que también existen muchos programas aparte de la
hyperterminal los cuales pueden entablar comunicación serial con el microcontrolador, programas
como MatLab, LabVIEW, TeraTerm entre otros.

83
Programa en C
#include<avr/io.h>
#include<util/delay.h>

int dato;

void InitUART( unsigned char baudrate ) { //Configurando la UART


UBRRL = baudrate; //Seleccionando la velocidad
UCSRB = (UCSRB | _BV(RXEN) | _BV(TXEN)); //Habilitando la transmisión y recepción
}

unsigned char ReceiveByte( void ){ //Función para recibir un byte


while ( !(UCSRA & (1<<RXC)) ); //Esperar la recepción
return UDR; //Retornar el dato tomado de la UART
}

void TransmitByte( unsigned char data ) { //Funcion para transmitir dato


while ( !( UCSRA & (1<<UDRE)) ); //Esperar transmision completa
UDR = data; //Depositar el dato para transmitirlo
}

int main (void) {

InitUART( 51 ); //Inicializar la UART


while(1){
dato=ReceiveByte(); //Recibir un dato de la UART
TransmitByte(dato); //Mandar el mismo dato
}
}

Detalles del programa


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

int dato;

Se inicia el programa con las librerías respectivas, y se declara una variable "dato".

84
void InitUART( unsigned char baudrate ) {
UBRRL = baudrate;
UCSRB = (UCSRB | _BV(RXEN) | _BV(TXEN));
}

Se declara la función que configura el USART, recordemos que en este caso se trabajara el
microcontrolador a una frecuencia de 8 Mhz. El primer registro a configurar es el UBRRL

Como se puede ver el UBRR (11:0) contiene 12 bits, en este registro se ajusta la velocidad de
trabajo de la comunicación, el baud rate. Hay que substituir los valores indicados en las siguientes
formulas para obtener el valor que va en el UBRR y él % de error.

Como se usara el modo asíncrono normal, tomamos la primeras formulas para calcular el UBRR y
después los Baudios. Primero resolvemos la del UBRR para obtener el valor calculándolo con 9600
baudios, después calculamos los baudios con el valor del UBRR (Pero el valor entero ya que en los
registros solo se pueden asignar números enteros), y el resultado nos dará los baudios reales.

𝑓𝑜𝑠𝑐 8000000
𝑈𝐵𝑅𝑅 = −1= − 1 = 𝟓𝟏. 𝟎𝟖𝟑𝟑𝟑
16𝐵𝐴𝑈𝐷 16 ∗ 9600

El valor obtenido para 9600 baudios fue de 51.0833 por lo tanto, se tomara el valor de 51
redondeando el valor obtenido, y con el cual calculamos los baudios reales.

𝑓𝑜𝑠𝑐 8000000
𝐵𝐴𝑈𝐷 = = = 𝟗𝟔𝟏𝟓. 𝟑𝟖𝟒𝟔
16 𝑈𝐵𝑅𝑅 + 1 16 51 + 1

85
9615.3846 Sera el valor real de baudios por segundo con los que el microcontrolador trabajara, para
obtener el %de error respecto al valor de 9600 aplicamos la siguiente formula.

9615.3846
𝐸𝑟𝑟𝑜𝑟 % = 9600
− 1 ∗ 100 = 0.16%

Como se puede ver el porcentaje de error es muy pequeño. Estas operaciones previamente
realizadas es para conocer un poco más el funcionamiento del registro y el cómo calcular el valor
respecto a algún baud rate deseado, pero también hay una tabla en la hoja de datos con los valores
más usados.

Como se puede ver, los valores obtenidos para el UBRR son similares a los de la tabla. Volviendo al
registro, solo se le asigna el valor al UBRRL el cual es suficiente modificar ya que el 51 en binario es
0b110011, como se puede ver solo se necesitan los bits del 0 al 6.

Para el UCSRB

Como se observa en el código se habilitan los bits RXEN y TXEN los cuales habilitan el transmisor y el
receptor de la USART además de los pines correspondientes RXD y TXD.

86
unsigned char ReceiveByte( void ){ //Función para recibir un byte
while ( !(UCSRA & (1<<RXC)) ); //Esperar la recepción
return UDR; //Retornar el dato tomado de la UART
}

El uso del registro UCSRA para la función de recibir un byte, es por el bit 7 el RXC el cual indica que
hay algun dato para leer en el registro. Por lo tanto el ciclo while espera hasta que haya un dato,
posteriormente finaliza la función con un return, el cual regresa el valor en el UDR (donde se
encuentra el dato) fuera de la misma.

void TransmitByte( unsigned char data ){


while ( !( UCSRA & (1<<UDRE)) );
UDR = data;
}

Al igual que la función de recibir un byte esta espera el UDRE en el UCSRA el cual indica que el buffer
de transmisión está listo para recibir nuevos datos, una vez que salga del ciclo, se coloca el dato
deseado en el UDR para transmitirlo.

init main (void) {

InitUART( 51 );
while(1){
dato=ReceiveByte();
TransmitByte(dato);
}
}

Como ya se explico, se inicializa la UART con un 51 el valor correspondiente, una vez configurada el
programa entra al ciclo, en donde la función espera recibir un dato, una vez que se recibe el dato,
este mismo sin sufrir modificación alguna es mandado a través de la UART.

Recordemos que se está mandando un byte esto es, que al recibir el microcontrolador una letra en
una variable de tipo entero, esta variable tendrá el valor que corresponde a esa letra (ver la tabla
ASCII), por ejemplo si se manda una variable con el valor 57 en la terminal veremos una "a", pero si

87
mandamos una variable tipo char con una 'a' en la terminal veremos esa misma "a", en la tabla se
muestran varios ejemplos.

Por lo tanto dependiendo del programa que estemos usando, debemos de tener cuidado si este
recibe los bytes y los toma como caracteres o como enteros.

Variable Int Variable char Terminal


Dec Bin Hex
97 0b01100001 0x61 'a' a
65 0b01000001 0x41 'A' A
51 0b00110011 0x33 '3' 3

88
89

También podría gustarte