Curso AVR con CodeVision v8.0

Anuncio
INSTITUTO TECNOLÓGICO DE MORELIA
CURSO BÁSICO-INTERMEDIO DE
PROGRAMACIÓN EN C DE LOS
MICROCONTROLADORES ATMEL
POR DAVID INFANTE SÁNCHEZ
dinfante29@hotmail.com
dinfante@itmorelia.edu.mx
Web site: www.comunidadatmel.com
Revisión 8.0
Septiembre del 2008
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
INFORMACIÓN IMPORTANTE
ESTAS NOTAS FUERON CREADAS POR DAVID INFANTE
SÁNCHEZ, PROFESOR DEL INSTITUTO TECNOLÓGICO DE
MORELIA. SE PROHIBE SU MODIFICACIÓN O SU VENTA,
FUE REALIZADO CON FINES DIDÁCTICOS.
SI SE UTILIZA INFORMACIÓN DE ESTE CURSO EN
TRABAJOS DE TESIS, ARTÍCULOS, ETC. DEBE MOSTRAR
LOS CRÉDITOS DEL AUTOR Y ESTO DEBERÁ SER
REFERENCIADO ASÍ:
Infante S. David. Notas del curso programación en C de los
microcontroladores ATMEL. Instituto Tecnológico de Morelia.
Versión 8.10
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
Acerca del autor
Profesor del Departamento de Ingeniería Electrónica del Instituto desde 1998 a la
fecha, experiencia docente en materias de electrónica digital I y II, analógica I y III,
Instrumentación I y II, microprocesadores I y II, electrónica industrial, física de
semiconductores. Ing. Electrónico por el ITM, Maestría en Ciencias en Ingeniería
Electrónica por el ITEM y Dr. En Ciencias en Ingeniería.
He sido:
• Asesor en más de 70 temas de titulación de licenciatura y 1 de Maestría.
• Sinodal en más de 70 exámenes de titulación de licenciatura y 6 de maestría.
• 17 Artículos publicados en congresos y simposios nacionales de electrónica y
computación.
• 18 Ponencias en congresos y simposios nacionales de electrónica.
• Participación como asesor con proyectos en 19 eventos de creatividad en sus
distintas fases.
• Asistencia en calidad de participante en 15 cursos de robótica,
instrumentación, redes de PLCs, Paneles de Operador, termografía y
microcontroladores COP, HC08, MSP430, AVR.
• Impartición de 12 cursos de microcontroladores PIC, HC08, AVR, DSPs de
Texas Instruments, procesamiento de señales y de PLCs en eventos nacionales
• 7 veces jurado en eventos académicos de electrónica
• Como estudiante participé en los concursos de Ciencias Básicas durante 3
años en sus distintas etapas.
• Obtención de una de las 10 becas nacionales para telecomunicaciones en
CINVESTAV Guadalajara
Experiencia profesional
•
•
•
•
5 años de experiencia profesional en el desarrollo de proyectos de
automatización para empresas embotelladoras y de la transformación, basados
en la integración y programación de PLCs, redes industriales, paneles de
operador, instrumentación y control de procesos.
Amplia experiencia en programación de PLCs de Siemens en lenguaje escalera,
FBDs, y Grafcet. Programación de paneles de operador para diseño de
interfaces hombre-máquina (HMI), experiencia en el campo de la termografía
para análisis de fallas en sistemas eléctricos, mecánicos y estructurales basados
en el análisis de las imágenes térmicas, conocimiento de robots PUMA y
saturno y programación de máquinas de control numérico.
Participación en el diseño de cajeros automáticos para bancos nacionales.
Diseño electrónico de sistemas empotrados con microcontroladores
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
Motivación
El motivo de este curso es para dar a conocer las nuevas tecnologías en microcontroladores,
ya que es lamentable que en las universidades e institutos de educación superior de México
y en Latinoamérica en general, se estén haciendo proyectos y programas demasiados
sencillos en las materias de microcontroladores y peor aún que estén usando los
microcontroladores PIC, ya que universidades europeas y de EUA no los usen desde hace
años por su pobre desempeño, además de que son microcontroladores que no tiene ninguna
aplicación industrial o comercial, sino sólo para efectos didácticos.
Desde que cursaba la carrera de ingeniería electrónica a la fecha he usado los siguientes
microprocesadores, microcontroladores y DSPs:
•
•
•
•
•
•
•
6802, HC11 y HC08 de Motorola
PICs de Microchip
MSP30 de Texas Instruments
COP de National Semiconductors
AVR y ATMEGA de Atmel
8051 de INTEL
DSPs de Texas Instruments y Motorola
Con el conocimiento de estos microcontroladores puedo aportar mi experiencia en el uso de
estos dispositivos y que resumiendo puedo señalar que Motorola es un fabricante con
dispositivos buenos y de amplio uso industrial como PLCs que basan su CPU en sus
microcontroladores. Son usados en ambientes de mucho ruido como el automotriz y el
industrial, sin embargo a últimas fechas ha realizado muchos cambios en el core, en las
herramientas de programación, haciendo más difícil conseguirlos y elevándose su costo.
Los COP son muy poco usados en el ambiente industrial, y resultan caros, además de que
no cuentan con novedades en su diseño, los 8051 de Intel están en plena desaparición y los
que hay son únicamente para reemplazo.
Los microcontroladores MSP de Texas Instrumentes son económicos, son potentes y tiene
herramientas gratuitas, sin embargo son dispositivos para uso portátil por lo que su voltaje
es de 3.3 V y son de montaje superficial lo que dificulta el diseño de prototipos.
Los microcontroladores PIC son los peores microcontroladores que existen, son muy lentos
que trabajan a baja velocidad, tiene un set de instrucciones muy pequeño que obliga a
programas muy extensos y lentos, tiene únicamente un work register cuando un procesador
RISC debe tener al menos 16 registros, de todos los microcontroladores que existen en el
mercado son los peores en el desempeño, algunas empresas comenzaron a hacer sus
equipos con estos procesadores y después al detectárseles fallas como bloqueos o
reinicialización por ruido sacaron del mercado sus productos. En países desarrollados no se
usan ya que no tienen aplicación industrial o comercial, únicamente en escuelas de países
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
de latinoamérica. Estos procesadores únicamente los usé por un año y los dejé de usar hace
una década por ser procesadores obsoletos y de mal desempeño.
Los Microcontroladores AVR de Atmel tiene 4 veces más instrucciones que los PICs, tiene
32 registros de trabajo, el pic sólo 1, el ADC es más potente, el TIMER es mucho más
complejo que el del PIC, y son más económicos. Los AVR de Atmel se usan en teléfonos
celulares, en receptores satelitales, en robótica, etc. Son microcontroladores muy rápidos y
de alto desempeño y son de bajo costo, existen muchos proyectos, tutoriales y herramientas
gratuitas en la red.
A manera de conclusión diría que en orden de desempeño y calidad colocaría los
procesadores así: AVR de Atmel, MSP de Texas Instruments, los 08 de Motorola el 8051
de Intel, COP de Nacional Semiconductor. Un procesador AVR o MSP puede tener un
desempeño de entre 10 y 50 veces al de un PIC con el mismo cristal externo, o en otras
palabras un PIC es entre 10 y 50 veces más lento que un AVR o que un MSP.
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
CAPÍTULO I. PROGRAMACIÓN EN C
1.1 Tipos de Variables
En esta parte únicamente se verá como se manejan las instrucciones de C, quizás al
terminar este capítulo algunas instrucciones no las entienda del todo bien, sin embargo
cuando programemos el microcontrolador usaremos estas instrucciones y las irá
comprendiendo de una mejor manera.
En un programa en C puede colocar comentarios usando //
unsigned char x; // x es una variable
O puede utilizar /*
*/ para poner varias líneas como comentarios
/* Aquí estamos usando estos símbolos para
ilustrar como podemos poner comentarios
en varías líneas */
Tipos de datos
Nuestras variables pueden ser del siguiente tipo:
Tipo
Bit
Char
Unsigned char
signed char
Int
short int
Unsigned int
signed int
long int
Unsigned long int
signed long int
Float
Double
Tamaño en bits
1
8
8
8
16
16
16
16
32
32
32
32
32
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Rango
0o1
-128 a 127
0 a 255
-128 a 127
-32768 a 32767
-32768 a 32767
0 a 65535
-32768 a 32767
-2147483648 a 2147483647
0 a 4294967295
-2147483648 a 2147483647
±1.75 e -38 a ±3.402 e 38
±1.75 e -38 a ±3.402 e 38
Autor: David Infante Sánchez
www.comunidadatmel.com
1.2 Declaración de las variables
Las declaraciones de las variables se hacen de la siguiente forma:
Con signo o sin signo tipo nombre; //Comentarios
Ejemplos
unsigned char x,y,temperatura; //Con esto estoy indicando que son 3 variables sin signo y
//que son tipo char por lo que el rango que pueden manejar son desde 0 a 255
unsigned int var1;
// Es una variable entera sin signo que llamamos var1
También se pueden inicializar las variables dentro de la declaración:
signed char z=20,x=10,m;
la //inicializo a ningún valor
//z inicia con un valor de 20 decimal, x con 10, m no
Importante. Si no se inicializan las variables dentro del programa o en la declaración de las
variables éstas son inicializadas a cero.
El manejo de números los puede hacer en decimal, binario, hexadecimal u octal.
Variable o constante
Decimal
Hexadecimal
Binario
Octal
Formato
Número
0x número hexadecimal
0b número binario
0 número octal
Ejemplos:
x=20;
x=0x14;
x=0b00010100;
x=024;
// x es 20 decimal
//x es 14 hexadecimal que convertido a decimal es 20 decimal
//x se manejado en binario que es equivalente a 20 decimal
//x se maneja en octal que es 20 decimal
1.3 Ubicación de variables y constantes
Las variables deben ser ubicadas en RAM. Solamente se hace declarándolas como
unsigned o signed luego el tipo y finalmente el nombre de las variables;
unsigned long variable3;
signed char x,y,z;
De la manera anterior las variables se guardarán en RAM
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
Las constantes deben ser guardadas en FLASH o memoria de programa, ya que éstas no
van a ser modificadas, para ello se inicia colocando la palabra const o flash ejemplo:
const int k1=60000;
Que es equivalente a:
flash int k1=60000;
Con lo anterior la constante K1 es inicializada a 60000 y se guarda en flash.
Se puede guardar un carácter en flash para ello ponemos el carácter entre comillas simples (
´)
const char x=´a´
1.4 Arreglos
Un arreglo es un conjunto de datos que pueden ser accesado a través de un índice. Su
declaración se hace así:
flash o const nombre del arreglo [número de elementos]={elemento1, elemento2,..
elemento n }
Ejemplo
flash char arreglo1 [3]={0x0a,0x38,0x23};
Es equivalente a la siguiente declaración:
const char arreglo1 [3]={0x0a,0x38,0x23};
flash y const son lo mismo ya que guardan los datos en flash, pero por compatibilidad con
el lenguaje C usaré dentro de este manual la palabra const.
1.4.1 ¿cómo accesar el arreglo?
El primer elemento es el 0, en la declaración anterior del arreglo éste se definió de tres
elementos, siendo el primer elemento el 0 y el último elemento el número 2, vea el
siguiente ejemplo:
x=arreglo1[0];
x=arreglo1[2];
// x vale 0x0a ya que accesó al primer elemento del arreglo
// x vale 0x23 ya que accesó al tercer elemento del arreglo
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
También se puede hacer de la declaración del arreglo como se muestra a continuación
donde omitimos el tamaño del arreglo que es 3 ya que al compilarse el programa verá que
son de tres elementos el arreglo.
flash char arreglo1 []={0x0a,0x38,0x23}; //Note que no se colocó el tamaño del arreglo
Si no se coloca la palabra flash o const se guardará el arreglo en RAM por ejemplo
int arreglo2 [4]={100,200};
Es un arreglo de 4 elementos enteros, el primero vale 100, el segundo 200 y el tercero y
cuarto 0 ya que no fueron inicializados. Como fue un arreglo guardado en RAM se pueden
modificar. En el caso de que se guarden en FLASH no podrán ser modificados ya que serán
considerados como constantes.
También podemos guardar caracteres para posteriormente mandarlos a una pantalla de
cristal líquido (LCD) ejemplo:
flash char arreglostring []=”Ejemplo de caracteres”;
1.4.1.1 Arreglos multidimensionales
Se puede declarar un arreglo de dos dimensiones que se interpretaría como fila y columna
ejemplo:
const char arreglo_multi_dim [2,3]= {1,2,3}, {,4,5,6};
// Es un arreglo de dos filas y
//tres columnas
x=arreglo_multi_dim [1,1]; //x=5 ya que se está accesando el elemento de fila 1 y col 1
1.5. Asignándole dirección a una variable
Cuando declaramos variables el compilador decidirá donde colocar las variables dentro de
RAM, pero también podemos nosotros decirle en qué dirección colocarla usando
@dirección por ejemplo:
unsigned char x=10 @0x80; // x vale 10 y está guardada en la dirección 0x80
Importante. Yo no recomiendo que el programador le indique al compilador donde colocar
las variables, sino que se le deje a éste la decisión de su ubicación, ya que el compilador
colocará la variable en una ubicación que sea la más rápida para su acceso, e inclusive la
puede colocar en alguno los 32 registros de trabajo para hacer más rápido su manejo.
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
1.6 Declaración de bits
Podemos guardar bits de manera individual y esto se hace usando la palabra bit nombre del
bit y su valor ejemplo
bit var1=1;
bit var2=0;
bit var3=1;
//La variable Var1 es de un bit y se guarda en un registro del AVR y vale 1
Se guardan de manera individual esos bits en los registros R2 al R14 del procesador del
AVR. Recuerde que el microcontrolador AVR tiene 32 registros desde R0 hasta R31.
1.7 Conjunto de instrucciones en C
En los siguientes subtemas se verán los distintos operadores aritméticos, lógicos, de
relación, etc. que podemos usar en el compilador, como trabajamos en lenguaje C el diseño
de programas se hace de una manera muy rápida, algo que no podemos lograr con el
lenguaje ensamblador. En la actualidad en el ámbito industrial se usa el término de RAD
que es Rapid Application Development que traducido es Desarrollo Rápido de
Aplicaciones, este concepto indica que para poder ser competitivos se deben desarrollar
proyectos de una manera muy rápida, es por ello que ya no utilizan el lenguaje ensamblador
en las empresas para desarrollar los programas. En la industria automotriz y en el área de
robótica los departamentos que elaboran los programas en los microcontroladores usan
solamente el lenguaje C ya que éste permite elaborar programas complejos muy
rápidamente, y además existe la portabilidad del código, ya que una vez que se ha escrito el
programa en C puede de una manera relativamente fácil cambiar de un microprocesador a
otro.
El lenguaje C reconoce instrucciones como if, for, while, etc; pero el lenguaje C no tiene
instrucciones para el manejo de puertos, de timers, del ADC, de interrupciones, etc.
Entonces para poder manejar los periféricos del microcontrolador se usan librerías donde
están declarados esos registros y su ubicación, aunque esto lo veremos después.
1.7.1 Operadores aritméticos:
Símbolo
+
*
/
%
Operación
Suma
Resta
Multiplicación
División
División Módulo, y el resultado es el
residuo
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
Var1=Var2/12;
Var1=Var2%12;
//Si Var2=40, Var1 será 3;
//Si Var2=40, Var1=4 que es el residuo. Útil para convertir
// números a BCD
1.7.2 Operadores Para manejo de Bits
Símbolo
&
|
^
<<
>>
~
Descripción
And Bit a Bit
OR bit a Bit
Or exclusivo Bit a Bit
Corrimiento a la Izquierda
Corrimiento a la derecha
Complemento
a
unos
(inversión de bits)
Ejemplos:
Var3=Var1&Var2;
Var3=Var1<<(2);
Var3=Var1<<(Var2);
Var3=~Var1;
//Si var1=0xff, var2=0x0A, Var3 será 0x0a
//Var1=0x20; Var3=0x08 ya que se recorre tres bits a la izq;
//Var1=0x01, Var2=0x05 Var3=0x20;
//Var1=0xf1, Var3=0x0e;
1.7.3 Operadores de relación
Los operadores de relación se utilizan con la instrucción if, while, do while.
Importante. No olvide poner los operadores =, | o el & dos veces en la evaluación del if,
while o do while, ya que un soló = significa asignación y dos veces == significa
comparación, un sólo & significa AND bit a bit y dos veces && significa Y TAMBIÉN a
modo de evaluación. Si lo coloca una sóla vez el programa tendrá resultados inesperados.
Operador
>
>=
<
<=
==
!=
&&
||
Descripción
Mayor que
Mayor o igual que
Menor que
Menor Igual que
Igual que
Distinto de
Y también si
O si
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
Ejemplos
if (Var2==1)
{
Var1--;
}
//Se decrementa Var1 si var2=1
Recuerde que variable++ significa incrementar en 1 la variable y que variable-- significa
decrementar en 1 la variable.
Si lleva sólo una instrucción debajo del if no es necesario las llaves: {}
If (Var2!=1)
Var1++;
//Se incrementa Var1 si Var2 es distinto de 1
If (((Var2>12)&&(Var3>=-125)) ||(Var4!=0)) //Var1 se multiplica por -1 si se cumple la
Var1=-Var1;
//Parte de (Var2 y Var3) o (Var4) o ambas
1.8 Estructura if- else
La estructura if else es: si se cumple el if se hace lo que está abajo del if y sino se cumple se
hace lo que está debajo del else
if (Var1==10)
{
Var1=0;
Var2=20;
x=14;
}
else
{
Var1=++;
x=20;
}
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
1.8.1 Estructura else-if
Donde se cumpla la condición rompe las siguientes comparaciones, no evalúa todas, pero si
se cumple hasta abajo habrá evaluado todas las comparaciones.
if (Var1==10)
Var1=0;
else if (Var1==09)
Var1=1;
else if (Var1==08)
Var1=2;
else
Var1=3;
También se puede hacer el programa anterior con varios if, pero aunque se cumpla una
condición sigue comparando las posteriores.
Errores generados por tamaños de las variables
En ocasiones los programadores principiantes generan errores como colocar variables muy
grandes cuando no se ocupan o tener tamaños de variables muy pequeños cuando se ocupan
rangos grandes.
Cuando se tiene una variable que dentro del programa va, digamos, desde 0 hasta 100
decimal, podemos declararla como unsigned char var1, pero si ponemos unsigned int var1,
también funcionará el programa, pero en el último caso estamos apartando dos bytes (int)
cuando solo ocupamos uno sólo, por lo que el programa quedará mas grande y más lento
porque el microcontrolador deberá manipular dos bytes en lugar de uno sólo. Es importante
que vea cuál es el rango de la variable y aparte el tamaño adecuado.
Cuando una variable que ocupa un tamaño grande le apartamos uno más pequeño se puede
crear condiciones que no se cumplen, vea el siguiente ejemplo:
Note el siguiente ejemplo:
signed char var1; // var1 es signada con lo que rango va de -128 a 127
var1++;
If (var1==200)
var2=0;
La condición del if nunca se cumplirá porque var1 es tipo char unsigned, esto es que su
rango es de -128 a 127 decimal y nunca llega a ser mayor a 127.
Si se declara como tipo unsigned char o tipo int si habrá un punto en el que la condición es
válida.
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
1.9 Estructura while
El while evalúa lo que hay entre los paréntesis y mientras se cumpla ejecuta lo que está
debajo del while.
while ((porta==0x01) &&(var1==0))
{
portb++ ;
//Si porta=0 y Var1=0, se incrementa en uno el portb
}
1.10 Estructura do-while
A diferencia del while, esta instrucción ejecuta por lo menos una vez lo que está abajo del
do, después evalúa el while y si es válido ejecuta nuevamente lo que está abajo del do.
do
{
Portb++ ;
//Si porta=0 y Var1=0, se incrementa en uno el portb
}
While ((porta==0x01) &&(Var1==0))
Este programa realiza por lo menos un incremento del portb, sin importar la condición, si la
condición del while se cumple continua en el ciclo incrementando el portb, hasta que no sea
válida la condición en el while.
1.11 Estructura for
La estructura del for es (inicio; condición de paro; incremento del índice)
for (i=0;i<=10000,i++)
{
var1++; //i tendría que ser declarado como int para que cumpla con el rango
}
Puede utilizarse el for para generar un delay
for (i=100;i<=0;i--); //Decrementa i desde 100 hasta 0 y cuando llega a 0 sale del //for,
note el “;” donde se colocó y es porque no hay //instrucciones
que ejecutar abajo del for
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
1.11.1 Ciclos infinitos
for(;;)
{
Se ejecuta infinitamente las instrucciones aquí colocadas
}
Otra forma de generar ciclos infinitos:
while(1)
{
Se ejecuta infinitamente las instrucciones aquí colocadas
}
1.11.2 Ruptura de ciclos con Break
Se puede terminar un ciclo for, while y do while mediante el break sin necesidad de
terminar las iteraciones o de que se vuelva falsa la condición.
for (i=0;i<=10000,i++)
{
var1++;
if (var2==0) //Si var2=0 se rompe ciclo, quedando el i en donde se haya cumplido el var2
break;
}
var3=i;
//Si var2 nunca es verdadera en el ciclo, var3=10000
//Si Var2 se hizo verdadera en i=25 se rompe ciclo y var3=25
1.12 Modificación de bits de manera individual
En ocasiones es necesario poner a 1 o 0 un bit de alguna variable o de algún registro sin
alterar el estado de los otros bits. O también puede ser necesario probar si un bit está en 0 o
en 1. Esa prueba y modificación de bits de manera individual la veremos a continuación en
este apartado.
Primero recordemos que cada bit tiene un peso dentro del byte y este peso es una potencia
de 2. Por ejemplo si la variable es de 8 bits el peso de esos bits es: 128 64 32 16 8 4 2 1, el
bit menos significativo tiene un peso de 1 y el más significativo de 128.
Si escribimos:
variable|=0x08
Es equivalente a: variable=variable|0x08
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
En la siguiente tabla vemos el resultado de variable|=0x08 que se utiliza para poner en 1 el
bit 3; notamos en la tabla que los otros bits de la variable no se modifican y que sólo se
afecta el bit3 (columna azul), si éste estaba en 0 lo pone en 1 y si estaba ya en 1 lo deja en
1.
B7
Variable 1
0x08
0
Resultado 1
De la Or
b6
0
0
0
b5
0
0
0
b4
0
0
0
b3
0
1
1
b2
1
0
1
b1
1
0
1
b0
1
0
1
Entonces para prender bits se debe hacer lo siguiente:
variable|=0x01 para prender el bit0.
variable|=0x02 para prender el bit1.
variable|=0x04 para prender el bit2, etc,
Si deseara prender el bit0 y el bit6 al mismo tiempo sin afectar los otros bits tendría que
escribir:
variable|=0x01|0x40 para prender el bit 0 y el bit 6.
Para apagar bits se usa la expresión:
variable&=~0x01 esta expresión apaga el bit 0 y no modifica los otros bits.
variable&=~(0x02|0x08|0x40);esta expresión apaga el bit 1, el bit 3 y el bit6 y no modifica
el estado de los otros bits.
La expresión variable&=~0x01 es equivalente variable=variable&(~0x01) recordemos que
el símbolo ~ saca el compelemento a 1 que es inversión de bits.
Si suponemos que variable=0b11001011
Variable
~0x01
Resultado
De la AND
b7
1
1
1
b6
1
1
1
b5
0
1
0
b4
0
1
0
b3
1
1
1
b2
0
1
0
b1
1
1
1
b0
1
0
0
Vemos en el ejemplo anterior que apaga el b0 de la variable y los demás bits no los
modifica.
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
1.12.1 Probando el valor de los bits de una variable o registro
Suponga que deseara probar si el bit 0 y el bit 2 de la variable var están en 0, entonces
deberá usar el siguiente esquema:
if ((var &(0x01|0x04)==0) //Se está probando que los bit 0 y 2 estén en 0
Vemos que el resultado no se guarda en ningún lado, sino solamente se prueba si están en 0
esos bits.
Suponga que desea probar si el bit 1 de la variable var está en 1 deberá escribir:
if ((var &(0x02)==0x02) //Se prueba que el bit 1 esté en 1 por eso se compara con 0x02
Para probar los bits 0,1,2,3,4,5,6 y 7 se usan sus pesos que son 0x01, 0x02, 0x04, 0x08,
0x10, 0x20,0x40 y 0x80.
Pero es más fácil si en vez de usar sus pesos usamos definiciones, que es lo que usaremos
en este curso, ejemplo:
#define elbit0 0x01
#define elbit1 0x02
#define elbit2 0x04
#define elbit3 0x08
#define elbit4 0x10
#define elbit5 0x20
#define elbit6 0x40
#define elbit7 0x80
De la manera anterior estamos diciendo que la etiqueta elbit0 =0x01, que el bit1=0x02, etc.
Aunque podemos usar cualquier etiqueta, sólo hay que tener cuidado de no usar las
palabras reservadas para instrucciones, es por ello que no usé la palabra bit que está
reservada, y por eso utilice una etiqueta que llamé elbit1, etc.
En los apartados anteriores usé: variable|=0x01|0x64 para prender el bit 0 y bit 7 de la
variable. Pero si colocamos dentro del programa las definiciones anteriores podemos usar:
variable|=elbit0|elbit6 entonces de esta maneras vemos más fácil que estamos modificando
los bits 0 y 6.
Para probar si están en 0 los bits 0 y 2 usé:
if ((var &(0x01|0x04)==0) //Se está probando que los bit 0 y 2 estén en 0
Pero si utilizamos las definiciones podemos usar:
if ((var &(elbit0|elbit2)==0) //Se está probando que los bit 0 y 2 estén en 0
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
1.12.2 Accesando bits de los registros
Hasta este momento no hemos usado ningún registro del microcontrolador, por ejemplo
uno de estos registros es el del puerto A del microcontrolador, vamos a suponer que ya
configuró el puerto como salida, entonces si escribe: porta=0x0f; está indicando que los 4
primeros pines del microcontrolador los ponga en 1 lógico, es decir, 5 Volts y los 4 más
significativos los ponga en 0 lógico, que es 0 Volts.
Pero si deseará afectar un sólo pin, sin modificar a los otros puede utilizar lo que se vio en
los subtemas anteriores del 1.12.1 y 1.12.2, pero el compilador que usaremos (codevision)
permite modificar de una forma más fácil los bits de los registros y se hace de la siguiente
manera:
Nombre_del_registro.x=valor
Donde: Nombre_del_registro, es el nombre del registro como aparece en la hoja de datos
x, es el número del bit que va desde 0 hasta 7
valor, es el valor lógico que le asignaremos ya sea 1 o 0
Por ejemplo si deseamos poner a 1 el pin 6 del puerto A, sin modificar los otros debemos
escribir:
porta.6=1;
Con lo anterior estamos poniendo a 1 el pin 6 del puerto sin modificar los otros pines, y
esta manera de manejar bits de manera individual lo podemos usar con los primeros 32
registros, cualquier registro mapeado arriba de la dirección 0x1F se hace con las técnicas
mostradas en los subtemas 1.12 y 1.12.1.
La instrucción if (pina.0==0); Prueba que el pin0 del porta esté en 0 (si es que lo
configuramos como entrada).
1.13 Manejo de las funciones
Una función es un conjunto de instrucciones que puede ser llamada en el momento que se
desee. Las funciones son útiles por dos razones:
1. Permiten estructurar el programa, por ejemplo se puede utilizar una función para
inicializar variables, otra función para inicializar el hardware, otra función para
inicializar el timer, etc. Así podemos visualizar de una forma más rápida el
funcionamiento del programa.
2. Permiten ahorrar código, si por ejemplo dentro del programa va hacer
constantemente una multiplicación de dos operandos, en vez de poner
continuamente el código de multiplicación de los operandos, mejor se usa una
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
función que haga la multiplicación y cada vez que se vaya a utilizar se manda
llamar la función, así de esta manera ahorramos código.
Las funciones pueden o no enviar parámetros y puede o no haber retorno de parámetros.
Cuando se va a utilizar una función hay que declararla en la parte superior del programa
(esto se conoce como prototype definition o definición prototipo) para que la reconozca el
compilador y sepa que vamos a utilizar dichas funciones.
Ejemplos de declaración de funciones:
//prototype definition
void ejemplofuncion1 (void)
void ejemplofuncion2 (char, char)
int ejemplofuncion3 (void)
char ejemplofuncion4 (int)
En la parte anterior se definieron 4 funciones, el nombre que les di son: ejemplofuncion1,...,
ejemplofuncion4 aunque el nombre puede ser cualquiera. La palabra void significa que no
recibe nada, por ejemplo void ejemplofuncion (void) significa que la función no recibe nada
y que tampoco se le envía ningún parámetro. La segundo función void ejemplofuncion2
(char, char) indica que no retorna ningún valor la función y que se le envían dos
parámetros tipo char. La tercera función int ejemplofuncion3 (void) indica que retorna un
valor tipo int y que no se le envía ningún parámetro. La última función no retorna ningún
valor y se le envía un parámetro tipo int.
La parte anterior sólo es la definición de las funciones que utilizará el programa. El llamado
de las funciones dentro del programa se hace así:
ejemplofuncion1 ();
ejemplofuncion2 (variable1,variable2);
En el llamado de la función1 no se colocó nada entre los paréntesis porque la función
dentro de la definición le indicamos que no se le iba a enviar ningún parámetro.
En el llamado de la función 2 le colocamos entre paréntesis dos variables que llamamos
variable1 y 2, que previamente debimos definir. Estas dos variables las recibirá la función
para que realice operaciones entre ellas.
variablex=ejemplofuncion3 ();
En el llamado de esta función colocamos variablex=función porque en la definición del
prototipo le indicamos que iba a retornar la función una variable tipo int, entre los
paréntesis no se le coloca nada porque se indicó que no iba a enviar ningún parámetro.
En la función 4 se retorna un valor tipo char y se envía un parámetro tipo int, y su llamado
se haría así:
variabley=elemplofuncion4(variable1);
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
Hasta este punto se ha visto cómo se declaran las funciones y cómo se llaman, falta
únicamente cómo se estructura la función.
En el caso de la primer función:
void ejemplofuncion1(void)
{
//Código de usuario
}
La función lleva void al inicio y entre paréntesis porque esa función no recibe parámetros
ni retorna ningún parámetro o valor.
La segunda función debería colocarse así:
void ejemplofuncion2(char a, char b)
{
//Código de usuario
}
Aquí lleva void al inicio indicando que no retorna parámetro y entre paréntesis se coloca a
y b que indica que son los parámetros que se reciben, cuando se hizo el llamado se hizo así:
ejemplofuncion2 (variable1,variable2); entonces la función envía dos parámetros variable1
y variable2, que recibe la función en a y b, entonces a=variable1 y b=variable2; Se
asignan según su posición.
La tercera función debe escribirse así:
int ejemplofuncion3(void)
{
//Código de usuario
return variable
}
Cuando se hizo el llamado de la función se colocó: variablex=ejemplofuncion3 (); entonces
cuando se ejecute la función se retorna un valor a través de return variable si
variable=0x30 por ejemplo, entonces variablex=0x30.
La función 4 debe escribirse así:
char ejemplofuncion4(int a)
{
//Código de usuario
return variable
}
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
Aquí se indica que la función regresará un char y que recibe un parámetro int en a, ya que
en el llamado de la función se escribió: variabley=elemplofuncion4(variable1);
Entonces a recibirá el valor de variable1 y variabley tomará el valor de variable por el
return variable.
Hemos visto sólo el envío de parámetros que son variables, pero pueden enviarse también
arreglos, o punteros.
Resumen de las funciones
Declaración de la función
void ejemplofuncion1 (void)
Llamado de la función
ejemplofuncion1 ();
void ejemplofuncion2 (char, char)
ejemplofuncion2 (variable1,variable2);
int ejemplofuncion3 (void)
variablex=ejemplofuncion3 ();
char ejemplofuncion4 (int)
variabley=elemplofuncion4(variable1);
Estructura de la función
void ejemplofuncion1(void)
{
//Código de usuario
}
void ejemplofuncion2(char a, char b)
{
//Código de usuario
}
int ejemplofuncion3(void)
{
//Código de usuario
return variable
}
char ejemplofuncion4(int a)
{
//Código de usuario
return variable
}
•
Estos fueron ejemplos de funciones, el nombre se le puede cambiar, se usaron
variables con determinados nombres pero éstos pueden cambiar.
•
Cuando no se envía parámetros se coloca la palabra void, cuando no hay retorno de
valor de la función se coloca la palabra void.
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
•
Cuando se envían parámetros se reciben en las funciones en otras variables según su
posición.
•
El valor que regresará la función se hace con return nombre_de_variable.
•
En la definición del prototipo se le indica al compilador si la función va o no a
enviar parámetros, y si va o no a retornar algún valor la función, en el caso de que
en el prototipo se le indique que va a enviar parámetros y no se envíe parámetros en
el llamado de la función se va a marcar error a la hora de la compilación porque no
concuerda la definición con la forma en que se está manejando la función. Entonces
el manejo de la función debe hacerse como se declaró en la definición del prototipo.
•
En los ejemplos que coloqué envíe como parámetros variables, pero también pueden
ser constantes. ejemplofuncion2(char a, 200); En este ejemplo se envía el valor de
la variable a y se envía la constante 200.
•
El retorno del valor de la función que puse como ejemplo fue el retorno de una
variable, pero también puede retornarse una constante como return 100;
Importante. Dentro de la función cuando se colocaron las variables a y b, éstas NO se
declaran ya que son consideradas variables locales, es decir, que sólo tienen validez
dentro de la función, cuando el programa sale de la función desaparecen. O en otras
palabras esas variables sólo existen dentro de la función. Le puse el nombre de a y b,
pero puede ponerle el nombre que quiera.
Importante. Los PICs y los HC08 sólo tienen un sólo registro de trabajo, el PIC le llama
Work Register y el HC08 le llama acumulador y recomiendan que no use variables
locales en los programas, ya que las variables locales se guardan en la pila a través de
instrucciones de PUSH y PULL, haciendo el manejo de variables locales muy lentas, en
cambio los AVR tienen 32 registros de trabajo, y es ahí donde se guardan las variables
locales haciéndose más rápido el manejo de este tipo de variables. En los programas
que haga con estos microcontroladores AVR trate de usar siempre variables
locales en lugar de variables globales, por las siguientes razones: el programa se ejecuta
más rápido, es más compacto y no usa la RAM. El manejo de variables locales es de 4 a
8 veces más rápido el manejo en los AVR que en los PICs o que en los HC08.
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
CAPITULO 2. PROGRAMACIÓN BÁSICA DEL
MICROCONTROLADOR.
2.1 Introducción
La compañía ATMEL fabrica memorias flash y microcontroladores, es la principal
fabricante de estos dispositivos para uso comercial e industrial. ATMEL nombra a sus
microcontroladores de 8 bits AVR y ATMEGA, los primeros son microcontroladores de
pocos pines y los ATMEGA son microcontroladores basados en el mismo core que los
AVR pero con más pines de entrada/salida y con más periféricos.
El microcontrolador que se utilizará en este curso es el ATMEGA48-20 PU, es un
microcontrolador de la serie ATMEGA. Sin embargo la información mostrada en este curso
sirve para cualquier microcontrolador AVR o ATMEGA de ATMEL.
El compilador que se usará es el CODEVISION AVR que es un compilador que permite al
programador inicializar los periféricos a través de un Wizard (asistente) indicándole solo el
programador a través de clicks cómo quiere que configure el microcontrolador y el
compilador generará todo el código ahorrando tiempo y costo para el desarrollo de
aplicaciones. Además este compilador puede generar el código para el manejo de LCDs o
generar el código para la comunicación I2C para comunicación de dispositivos seriales
como memorias, Real Time Clocks, etc.
El ATMEL es un microcontrolador tipo RISC (Reduced Instruction Set Computer) lo cual
significa que el conjunto de instrucciones es reducido con 131 instrucciones, las cuales se
ejecutan en su mayoría en un sólo ciclo, tiene multiplicador por hardware y tiene 32
registros de trabajo lo cual permite la ejecución de instrucciones muy rápido ya que muchas
instrucciones y operaciones las realiza entre estos registros ejecutándose de manera muy
rápida y eficiente. Si lo comparamos con otro microcontrolador RISC como el PIC vemos
lo siguiente: el PIC tiene sólo 33 instrucciones, lo cual obliga a que los programas sean mas
grandes y lentos debido a lo pobre de sus instrucciones (cosa que no sucede con el AVR ya
que tiene un conjunto de instrucciones 4 veces mas grande), el PIC sólo tiene un solo
registro de trabajo y el AVR tiene 32. El PIC al tener un sólo registro de trabajo obliga que
los operandos se guarden en RAM haciendo mas instrucciones y más lento los programas,
en cambio los ATMEL tienen 32 registros de trabajo con lo cual los operandos pueden
guardarse en ellos haciendo que el programa sea más reducido y más rápido. Los
microcontroladores ATMEL son procesadores optimizados para trabajar con C, el PIC no,
ya que es un core muy viejo, lento, reducido en instrucciones, con pocos registros de
trabajo, en cambio el AVR al tener un conjunto de instrucciones muy grande y con muchos
registros de trabajo puede compilar los programas realizados en C de manera muy eficiente,
ya que si se usan funciones con variables locales éstas son guardadas en los registros de
trabajo, en cambio si se tiene un microprocesador ya sea RISC o CISC con pocos registros
de trabajo obliga a que las variables locales definidas en funciones sean guardadas en pila
resultando en instrucciones adicionales de Push y Pull en stack.
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
Un ATMEL y un PIC trabajando a la misma frecuencia de operación y con el mismo
programa tendría un mejor desempeño el ATMEL pudiendo ocupar entre 6 y 15 veces
menos memoria el ATMEL por el conjunto de instrucciones más amplio, y ejecutando el
programa entre 20 y 60 veces más rápido. Operaciones de punto flotante en el PIC de la
serie 16fxx no se pueden realizar ya que por lo pobre de las instrucciones que tiene no es
posible, además de que no tiene capacidad de memoria para poder realizar este tipo de
operaciones.
Los modos de direccionamiento del AVR es más amplio, pudiendo inclusive manejar
direccionamientos con post y pre incrementos, lo cual eficienta el manejo de tablas y
programas.
De los microcontroladores AVR y ATMEGA de ATMEL existen muchas familias, pero
nosotros nos enfocaremos al ATMEGA48 pero si desea programar cualquier otro AVR o
ATMEGA puede hacerlo con los conocimientos que adquiera con este manual.
Primero veamos en la figura 2.1 cómo está físicamente el microcontrolador ATMEGA48.
Figura 2.1 Pines del Microcontrolador ATMEGA48.
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
2.1 Características del microcontrolador
Es un microcontrolador de 28 pines, con tres puertos B, D de 8 bits y el C de 7 bits, todos
esos pines pueden ser configurados como entradas o salidas de manera individual, note que
todos los pines pueden configurarse con funciones alternas por ejemplo pueden
configurarse como entrada o salida de datos, como entrada del ADC, como fuente de
interrupción, como terminal de comunicaciones, etc.
El microcontrolador tiene un oscilador interno que trabaja a 8 MHz que viene dividido por
8 de fábrica, así que trabaja a 1MHz interno, pero puede colocar un cristal externo en las
terminales 7 y 8, y en ese caso tendría que seleccionar que esos pines no sean entradas o
salidas de datos sino que sean configurados como terminales donde se colocará el cristal
externo que puede ser de hasta 20 MHz, pero para nuestras aplicaciones y para reducir
costos, diseño del PCB, reducción de componentes, etc, se trabajará con el oscilador interno
de fábrica cuya frecuencia interna es de 1 MHz, aunque puede operar hasta 8 Mhz y si se
desea una velocidad más alta deberá colocar un cristal externo que puede ser hasta de 20
MHz. Debe tener en cuenta que a mayor velocidad se incrementa el consumo de energía. Si
se coloca el cristal de 20MHz puede trabajar a 20 MHz internos, recuerde que otros
microcontroladores como los PICs, los HC08 de motorola, etc. la velocidad interna es el del
cristal dividido entre 4. Así que de entrada tenemos que el ATMEL es 4 veces más rápido
que cualquier otro microcontrolador trabajando con el mismo cristal.
2.2 Instalación del software
Deberá instalar estos tres programas (primero descomprima el CVAVRE que abrirá un
SET-UP)
Figura 2.2 Programas a Instalar
En el tema 1.7 se explicó que el lenguaje C no tiene instrucciones para manejar timers,
ADC, puertos de entrada-salida, etc, es decir, los periféricos del microcontrolador, entonces
para poder manejarlos se usan las librerías donde se incluyen las declaraciones de estos
registros y su manejo. Es precisamente el programa Code Vision AVR quien tiene esas
librerías. El primer programa instala el AVR Studio que es el simulador, el segundo
(WinAVR-20071221 ) instala el programador y el tercer SET-UP instala el compilador.
Importante. Use sólo estas versiones, ya que están probadas, versiones más nuevas o viejas
pueden tener problemas con el programador, compilador y/o simulador. En el caso de que
tenga probados versiones más nuevas colocaré el link para su descarga en mi página
www.comunidadatmel.com
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
2.3 Puertos de entrada y salida
A través de los puertos de entrada y salida es como el procesador se comunica con el
mundo exterior. Este microcontrolador en particular tiene tres puertos el B, C y D que
deben ser configurados para decirle cómo va a funcionar los pines del puerto si como
entrada o como salida. Se puede configurar de manera independiente cada pin de un puerto
como salida o como entrada.
Por ejemplo con el registro DDRB especificamos la dirección del pin correspondiente, un 0
es para indicar que el pin es de entrada y un 1 de salida. Por ejemplo si se quiere que los
primeros cuatro pines del puerto A sean entrada y los 4 más significativos sean de salida se
programa así: DDRB=0xf0; aunque recordemos que esta configuración la realiza el
codevision.
Importante. Cuando son configurados como entradas las terminales de un circuito integrado
deben colocarse resistencias de pull-up para evitar que queden flotadas (sin conectarse),
porque de quedarse flotadas el C.I. consume mayor corriente y el estado del pin oscilará
vea el primer caso de la figura 4, lo correcto es poner una resistencia de pull-up (segundo
caso), si el interruptor no se presiona leerá el pin un 1 lógico por la resistencia conectada a
5V, si se presiona el interruptor leerá un 0 lógico, pero este microcontrolador permite al
diseñador ahorrar hardware ya que tiene resistencias de pull-up internas que pueden ser
habilitadas.
Figura 2.3 Conexión de resistencias de Pull-up
.
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
Importante. Si ha manejado otros microcontroladores habrá notado que únicamente tienen
asociados un sólo registro al puerto de entrada/salida, pero en este microcontrolador tiene
asociados dos registros al puerto de entrada/salida, si quiere escribir al puerto B deberá
hacerlo así PORTB=dato; pero si desea leer el estado de los pines de ese puerto deberá
hacer dato=PINB; es decir deberá usar PORTB, PORTC o PORTD para escribir al puerto,
y deberá usar variable=PINB, variable=PINC o variable=PIND para leer el valor del
puerto de entrada.
Cualquier otro microcontrolador de otros fabricante sólo tienen un sólo registro para leer o
escribir al puerto, pero en elcaso de ATMEL se tienen dos dando la ventaja de que de esa
forma el acceso a puertos es más rápido cuando se va usar como entrada un determinado
pin y posteriormente se va usar como salida, es otra ventaja que tenemos con este tipo de
arquitectura.
Importante. Limitantes Físicas de los pines: Máximo pueden dar 40 mA por pin, pero el
Circuito Integrado máximo debe manejar 200mA, es decir que sumando la corriente de los
pines y la que consume el microcontrolador no deben exceder más de 200mA.
Importante. El pin 1, que es la terminal PC6 tiene a la vez la función de RESET, esta
terminal no la podemos utilizar como pin de entrada/salida, ya que a través de ella se hace
la programación del microcontrolador. En otras palabras NO HAGA PROGRAMAS
DONDE UTILICE ESA TERMINAL, YA QUE NO LA PODEMOS USAR. Es posible
deshabilitarle la función de reset y que quede como pin de entrada/salida digital, pero ya no
podremos programar al microcontrolador nuevamente.
Programa 1. Leer el puerto B y escribirlo al puerto D, activando las resistencias de
pull-up en las entradas del puerto B.
Figura 2.4 conexión del P1
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
Vea el video_p1 que se anexa en este CD para que aprecie cómo se inicializa el
microcontrolador y su simulación.
El siguiente listado es lo que generó el codewizard, inicializando el microcontrolador y lo
que está en azul es lo que ud deberá agregar al programa generado para que funcione de
acuerdo a las especificaciones del programa 1.
/*****************************************************
This program was produced by the
CodeWizardAVR V1.25.7a Evaluation
Automatic Program Generator
© Copyright 1998-2007 Pavel Haiduc, HP InfoTech s.r.l.
http://www.hpinfotech.com
Project :
Version :
Date : 13/09/2008
Author : Freeware, for evaluation and non-commercial use only
Company :
Comments:
Chip type
: ATmega48
Clock frequency : 1,000000 MHz
Memory model
: Small
External SRAM size : 0
Data Stack size : 128
*****************************************************/
#include <mega48.h>
// Declare your global variables here
void main(void)
{
// Declare your local variables here
// Crystal Oscillator division factor: 8
#pragma optsizeCLKPR=0x80;
CLKPR=0x03;
#ifdef _OPTIMIZE_SIZE_
#pragma optsize+
#endif
// Input/Output Ports initialization
// Port B initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
// State7=P State6=P State5=P State4=P State3=P State2=P State1=P State0=P
PORTB=0xFF;
DDRB=0x00;
// Port C initialization
// Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
// State6=T State5=T State4=T State3=T State2=T State1=T State0=T
PORTC=0x00;
DDRC=0x00;
// Port D initialization
// Func7=Out Func6=Out Func5=Out Func4=Out Func3=Out Func2=Out Func1=Out Func0=Out
// State7=0 State6=0 State5=0 State4=0 State3=0 State2=0 State1=0 State0=0
PORTD=0x00;
DDRD=0xFF;
// Timer/Counter 0 initialization
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
// Clock source: System Clock
// Clock value: Timer 0 Stopped
// Mode: Normal top=FFh
// OC0A output: Disconnected
// OC0B output: Disconnected
TCCR0A=0x00;
TCCR0B=0x00;
TCNT0=0x00;
OCR0A=0x00;
OCR0B=0x00;
// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: Timer 1 Stopped
// Mode: Normal top=FFFFh
// OC1A output: Discon.
// OC1B output: Discon.
// Noise Canceler: Off
// Input Capture on Falling Edge
// Timer 1 Overflow Interrupt: Off
// Input Capture Interrupt: Off
// Compare A Match Interrupt: Off
// Compare B Match Interrupt: Off
TCCR1A=0x00;
TCCR1B=0x00;
TCNT1H=0x00;
TCNT1L=0x00;
ICR1H=0x00;
ICR1L=0x00;
OCR1AH=0x00;
OCR1AL=0x00;
OCR1BH=0x00;
OCR1BL=0x00;
// Timer/Counter 2 initialization
// Clock source: System Clock
// Clock value: Timer 2 Stopped
// Mode: Normal top=FFh
// OC2A output: Disconnected
// OC2B output: Disconnected
ASSR=0x00;
TCCR2A=0x00;
TCCR2B=0x00;
TCNT2=0x00;
OCR2A=0x00;
OCR2B=0x00;
// External Interrupt(s) initialization
// INT0: Off
// INT1: Off
// Interrupt on any change on pins PCINT0-7: Off
// Interrupt on any change on pins PCINT8-14: Off
// Interrupt on any change on pins PCINT16-23: Off
EICRA=0x00;
EIMSK=0x00;
PCICR=0x00;
// Timer/Counter 0 Interrupt(s) initialization
TIMSK0=0x00;
// Timer/Counter 1 Interrupt(s) initialization
TIMSK1=0x00;
// Timer/Counter 2 Interrupt(s) initialization
TIMSK2=0x00;
// Analog Comparator initialization
// Analog Comparator: Off
// Analog Comparator Input Capture by Timer/Counter 1: Off
ACSR=0x80;
ADCSRB=0x00;
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
while (1)
{
PORTD=PINB;
//Se lee el valor del Puerto A y se escribe en PORTB
//El Puerto B es de entrada por lo que se accesa usando el registro PINB
//El puerto D es de salida por lo que se accesa usando el registro PORTD
// Place your code here
};
}
Programa 2. Haga un contador binario en el puerto B, el cual deberá contar desde 0
hasta 255 y regresar a cero. Para ello coloque un retardo de 1 segundo entre cada
cuenta, ya que de no colocar ningún retardo el conteo sería muy rápido no pudiendo
apreciarse el conteo en el puerto por la velocidad.
Figura 2.5 Conexión del programa 2
Importante. Hay funciones de retardo llamadas delay_us(dato); y delay_ms(dato); que son
funciones en microsegundos y en milisegundos, y el dato es un número o una variable de
hasta 16 bits. Pero para poder utilizar estas funciones deberá colocar la librería
#include<delay.h>, ya que de no hacerlo no reconocerá las funciones el compilador
marcando error.
Importante. El oscilador interno es del tipo RC y su valor aproximado es de 8 MHz/8=1
MHz pero este no es un valor exacto de frecuencia por el tipo de oscilador que tiene.
Cuando se desean frecuencias exactas o más altas es necesario un cristal externo. El Valor
de oscilación con el oscilador interno es dependiente del voltaje de alimentación y de la
temperatura, aunque es cercano a 1 MHz.
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
Vea el video_p2 para que vea la forma en que se hace este programa y vea también su
simulación.
El siguiente listado lo generará el codewizard para inicializar el microcontrolador y lo que
está en azul es lo que ud deberá escribir en el programa para que ejecute lo que se pide en el
programa 2.
/*****************************************************
This program was produced by the
CodeWizardAVR V1.25.7a Evaluation
Automatic Program Generator
© Copyright 1998-2007 Pavel Haiduc, HP InfoTech s.r.l.
http://www.hpinfotech.com
Project :
Version :
Date : 14/09/2008
Author : Freeware, for evaluation and non-commercial use only
Company :
Comments:
Chip type
: ATmega48
Clock frequency : 1,000000 MHz
Memory model
: Small
External SRAM size : 0
Data Stack size : 128
*****************************************************/
#include <mega48.h>
#include <delay.h> //Esta libreria hay que colocarla para poder utilizar las funciones de retardo
// Declare your global variables here
void main(void)
{
// Declare your local variables here
// Crystal Oscillator division factor: 8
#pragma optsizeCLKPR=0x80;
CLKPR=0x03;
#ifdef _OPTIMIZE_SIZE_
#pragma optsize+
#endif
// Input/Output Ports initialization
// Port B initialization
// Func7=Out Func6=Out Func5=Out Func4=Out Func3=Out Func2=Out Func1=Out Func0=Out
// State7=0 State6=0 State5=0 State4=0 State3=0 State2=0 State1=0 State0=0
PORTB=0x00;
DDRB=0xFF;
// Port C initialization
// Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
// State6=T State5=T State4=T State3=T State2=T State1=T State0=T
PORTC=0x00;
DDRC=0x00;
// Port D initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
// State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T
PORTD=0x00;
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
DDRD=0x00;
// Timer/Counter 0 initialization
// Clock source: System Clock
// Clock value: Timer 0 Stopped
// Mode: Normal top=FFh
// OC0A output: Disconnected
// OC0B output: Disconnected
TCCR0A=0x00;
TCCR0B=0x00;
TCNT0=0x00;
OCR0A=0x00;
OCR0B=0x00;
// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: Timer 1 Stopped
// Mode: Normal top=FFFFh
// OC1A output: Discon.
// OC1B output: Discon.
// Noise Canceler: Off
// Input Capture on Falling Edge
// Timer 1 Overflow Interrupt: Off
// Input Capture Interrupt: Off
// Compare A Match Interrupt: Off
// Compare B Match Interrupt: Off
TCCR1A=0x00;
TCCR1B=0x00;
TCNT1H=0x00;
TCNT1L=0x00;
ICR1H=0x00;
ICR1L=0x00;
OCR1AH=0x00;
OCR1AL=0x00;
OCR1BH=0x00;
OCR1BL=0x00;
// Timer/Counter 2 initialization
// Clock source: System Clock
// Clock value: Timer 2 Stopped
// Mode: Normal top=FFh
// OC2A output: Disconnected
// OC2B output: Disconnected
ASSR=0x00;
TCCR2A=0x00;
TCCR2B=0x00;
TCNT2=0x00;
OCR2A=0x00;
OCR2B=0x00;
// External Interrupt(s) initialization
// INT0: Off
// INT1: Off
// Interrupt on any change on pins PCINT0-7: Off
// Interrupt on any change on pins PCINT8-14: Off
// Interrupt on any change on pins PCINT16-23: Off
EICRA=0x00;
EIMSK=0x00;
PCICR=0x00;
// Timer/Counter 0 Interrupt(s) initialization
TIMSK0=0x00;
// Timer/Counter 1 Interrupt(s) initialization
TIMSK1=0x00;
// Timer/Counter 2 Interrupt(s) initialization
TIMSK2=0x00;
// Analog Comparator initialization
// Analog Comparator: Off
// Analog Comparator Input Capture by Timer/Counter 1: Off
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
ACSR=0x80;
ADCSRB=0x00;
while (1)
{
PORTB++;
// Incrementa en una unidad el valor del Puerto B
delay_ms(1000); //Se hace un retardo de 1 segundo
// Place your code here
};
}
Programa 3. Hacer un convertidor de BCD a 7 segmentos. En este programa se desea
que cuando se introduzca un 0 en el puerto B se dibuje un 0 en el display de 7
segmentos que se conectará en el puerto B, si se introduce un 1 se deberá dibujar en el
display de 7 segmentos ese número, etc.
El diagrama de este programa se muestra en la figura 2.6
Figura 2.6 Conexión del programa 3 convertidor de BCD a 7 segmentos
Consideraremos también que el B0 va conectado al segmento a, el B1 a b, B2 a c, B3 a d,
B4 a e, B5 a f y B6 a g. En la tabla 2.1 se aprecia la combinación que deberá ponerse para
dibujarse en el display los números, por ejemplo para el 1 debe encenderse el segmento b y
c. Estas combinaciones son si se trata de un display de cátodo común, en el caso de que sea
de ánodo común sólo deberían invertirse los 1 y los 0.
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
Tabla 2.1 Conexión del convertidor de BCD a 7 Segmentos
Número
Combinación digital
Combinación
g f e d c b a
Hexadecimal
B7 B6 B5 B4 B3 B2 B1 B0
0
0 0 1 1 1 1 1 1
0x3f
1
0 0 0 0 0 1 1 0
0x06
2
0 1 0 1 1 0 1 1
0x5b
3
0 1 0 0 1 1 1 1
0x4f
4
0 1 1 0 0 1 1 0
0x66
5
0 1 1 0 1 1 0 1
0x6d
6
0 1 1 1 1 1 0 0
0x7c
7
0 0 0 0 0 1 1 1
0x07
8
0 1 1 1 1 1 1 1
0x7f
9
0 1 1 0 1 1 1 1
0x6f
Para este programa deberemos configurar todo el Puerto B como salida, y los pines C0, C1,
C2 y C3 como entradas con sus resistencias de pull-up, ya que cuando no se cierre el
interruptor quedará a 1 lógico el pin por la resistencia de pull-up interna, y cuando se cierre
el interruptor leerá 0 el pin.
En los cursos que he dado normalmente los participantes dicen que una forma de hacer el
programa es con if para probar todas las combinaciones y quizás es la forma que se le ha
ocurrido para hacerlo. Es decir algo de esta forma:
if (PINC==0)
PORTB=0x3f;
//Si el puerto C=0 entonces imprime el 0 en el port B
if (PINC==0)
PORTB=0x06;
//Si el puerto C=1 entonces imprime el 1 en el port B
Etc...
if (PINC==9)
PORTB=0x6f;
//Si el puerto C=9 entonces imprime el 9 en el port B
//Así se harían las 8 pruebas restantes
La forma descrita no es incorrecta, pero tiene los siguientes puntos en contra: ocupa
demasiadas instrucciones ya que se deben probar todas las posibilidades que es que el
puerto C valga 0 hasta que valga 9, ocupando mucha memoria y haciendo lento el
programa.
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
La forma que se puede hacer es a través de una tabla, por ejemplo se define una tabla en
flash donde estarán todas las combinaciones anteriores:
const char tabla7segmentos [10]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7c,0x07,0x7f,0x6f};
y dentro del programa principal colocará lo siguiente:
PORTB=tabla7segmentos[PINC];
Y con esa única instrucción ya tenemos un convertidor de BCD a 7 segmentos, si el puerto
C que leemos con PINC es igual a 0 sacará de la tabla el elemento 0 que es el que dibuja el
0 en el display, si vale 9 sacará de la tabla el elemento 9 que es el que dibuja el 9 en el
display y lo mandará al Puerto B.
De la manera anterior logramos que el programa quede más compacto, y mucho más rápido
que usando instrucciones de if.
Vea el video_p3 donde se muestra la configuración del microcontrolador y su simulación.
La parte en azul es la que deberá agregar al programa generado por el codewizard.
/*****************************************************
This program was produced by the
CodeWizardAVR V1.25.7a Evaluation
Automatic Program Generator
© Copyright 1998-2007 Pavel Haiduc, HP InfoTech s.r.l.
http://www.hpinfotech.com
Project :
Version :
Date : 14/09/2008
Author : Freeware, for evaluation and non-commercial use only
Company :
Comments:
Chip type
: ATmega48
Clock frequency : 1,000000 MHz
Memory model
: Small
External SRAM size : 0
Data Stack size : 128
*****************************************************/
#include <mega48.h>
unsigned char variable;
const char tabla7segmentos [10]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7c,0x07,0x7f,0x6f};
// Declare your global variables here
void main(void)
{
// Declare your local variables here
// Crystal Oscillator division factor: 8
#pragma optsizeCLKPR=0x80;
CLKPR=0x03;
#ifdef _OPTIMIZE_SIZE_
#pragma optsize+
#endif
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
// Input/Output Ports initialization
// Port B initialization
// Func7=Out Func6=Out Func5=Out Func4=Out Func3=Out Func2=Out Func1=Out Func0=Out
// State7=0 State6=0 State5=0 State4=0 State3=0 State2=0 State1=0 State0=0
PORTB=0x00;
DDRB=0xFF;
// Port C initialization
// Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
// State6=T State5=T State4=T State3=P State2=P State1=P State0=P
PORTC=0x0F;
DDRC=0x00;
// Port D initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
// State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T
PORTD=0x00;
DDRD=0x00;
// Timer/Counter 0 initialization
// Clock source: System Clock
// Clock value: Timer 0 Stopped
// Mode: Normal top=FFh
// OC0A output: Disconnected
// OC0B output: Disconnected
TCCR0A=0x00;
TCCR0B=0x00;
TCNT0=0x00;
OCR0A=0x00;
OCR0B=0x00;
// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: Timer 1 Stopped
// Mode: Normal top=FFFFh
// OC1A output: Discon.
// OC1B output: Discon.
// Noise Canceler: Off
// Input Capture on Falling Edge
// Timer 1 Overflow Interrupt: Off
// Input Capture Interrupt: Off
// Compare A Match Interrupt: Off
// Compare B Match Interrupt: Off
TCCR1A=0x00;
TCCR1B=0x00;
TCNT1H=0x00;
TCNT1L=0x00;
ICR1H=0x00;
ICR1L=0x00;
OCR1AH=0x00;
OCR1AL=0x00;
OCR1BH=0x00;
OCR1BL=0x00;
// Timer/Counter 2 initialization
// Clock source: System Clock
// Clock value: Timer 2 Stopped
// Mode: Normal top=FFh
// OC2A output: Disconnected
// OC2B output: Disconnected
ASSR=0x00;
TCCR2A=0x00;
TCCR2B=0x00;
TCNT2=0x00;
OCR2A=0x00;
OCR2B=0x00;
// External Interrupt(s) initialization
// INT0: Off
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
// INT1: Off
// Interrupt on any change on pins PCINT0-7: Off
// Interrupt on any change on pins PCINT8-14: Off
// Interrupt on any change on pins PCINT16-23: Off
EICRA=0x00;
EIMSK=0x00;
PCICR=0x00;
// Timer/Counter 0 Interrupt(s) initialization
TIMSK0=0x00;
// Timer/Counter 1 Interrupt(s) initialization
TIMSK1=0x00;
// Timer/Counter 2 Interrupt(s) initialization
TIMSK2=0x00;
// Analog Comparator initialization
// Analog Comparator: Off
// Analog Comparator Input Capture by Timer/Counter 1: Off
ACSR=0x80;
ADCSRB=0x00;
while (1)
{
variable=PINC&0x0f;
//Enmascaramos los 4 bits menos significativos
//del puerto A ya que los demás no interesan.
if (variable<10)
PORTB=tabla7segmentos[variable];
if (variable>=10)
PORTB=0x3f;
// Place your code here
};
//Si lo que leemos es mayor o igual de 10 que dibuje en el display un 0
}
Explicación del Programa
Explicación de la instrucción variable=PINC&0x0f; suponga que en el puerto C (que es de
7 bits) existen estos datos C=110,0110 pero a nosostros nos interesan solo los 4 menos
significativos, esto es el 0110 y los otros no, por lo que debemos quitarlos así que con el
and de 110,0110 & 0000,1111 nos da el resultado de 0000,0110, es decir ya sólo nos
asignó a la variable el valor de los 4 bits que nos interesan. Entonces donde se desea que
haya un 0 como es el caso de los bits superiores hacemos el and colocando 0 en esas
posiciones, y donde queremos que no se modifiquen los bits como en los 4 bits menos
significativos hacemos el and con 1 lógico en esa posición para que nos dé como resultado
un 1 donde había un 1 y un 0 donde había un 0, es decir, lo mismo que había se queda sin
alterarse.
Se agregó una instrucción que prueba si el Puerto C tiene un número mayor o igual a 10 y
en caso de que así sea va a dibujar un 0 en el display. Ya que con 4 bits se pueden
representar números desde 0 hasta 15, pero solamente nos interesan los números del 0 al 9
ya que es el tamaño de la tabla, en caso de no haberse puesto esas instrucciones, entonces
mostraría basura cuando variable>9, ya que esos elementos no están definidos en la tabla.
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
Programa 4 Cuando se presione el botón conectado al pin C0 se incrementará el
número mostrado en el display que será conectado al puerto B.
Figura 2.7 Conexiones para los programas 4, 5 y 6
Configuración: C0 como entrada con resistencia de pull-up activada, los pines B0 a B7
como salida que es donde se conectará el display.
/*****************************************************
This program was produced by the
CodeWizardAVR V1.25.7a Evaluation
Automatic Program Generator
© Copyright 1998-2007 Pavel Haiduc, HP InfoTech s.r.l.
http://www.hpinfotech.com
Project :
Version :
Date : 14/09/2008
Author : Freeware, for evaluation and non-commercial use only
Company :
Comments:
Chip type
: ATmega48
Clock frequency : 1,000000 MHz
Memory model
: Small
External SRAM size : 0
Data Stack size : 128
*****************************************************/
#include <mega48.h>
#define boton PINC.0
const char tabla7segmentos [10]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7c,0x07,0x7f,0x6f};
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
unsigned char var1;
// Declare your global variables here
void main(void)
{
// Declare your local variables here
// Crystal Oscillator division factor: 8
#pragma optsizeCLKPR=0x80;
CLKPR=0x03;
#ifdef _OPTIMIZE_SIZE_
#pragma optsize+
#endif
// Input/Output Ports initialization
// Port B initialization
// Func7=Out Func6=Out Func5=Out Func4=Out Func3=Out Func2=Out Func1=Out Func0=Out
// State7=0 State6=0 State5=0 State4=0 State3=0 State2=0 State1=0 State0=0
PORTB=0x00;
DDRB=0xFF;
// Port C initialization
// Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
// State6=T State5=T State4=T State3=T State2=T State1=T State0=P
PORTC=0x01;
DDRC=0x00;
// Port D initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
// State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T
PORTD=0x00;
DDRD=0x00;
// Timer/Counter 0 initialization
// Clock source: System Clock
// Clock value: Timer 0 Stopped
// Mode: Normal top=FFh
// OC0A output: Disconnected
// OC0B output: Disconnected
TCCR0A=0x00;
TCCR0B=0x00;
TCNT0=0x00;
OCR0A=0x00;
OCR0B=0x00;
// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: Timer 1 Stopped
// Mode: Normal top=FFFFh
// OC1A output: Discon.
// OC1B output: Discon.
// Noise Canceler: Off
// Input Capture on Falling Edge
// Timer 1 Overflow Interrupt: Off
// Input Capture Interrupt: Off
// Compare A Match Interrupt: Off
// Compare B Match Interrupt: Off
TCCR1A=0x00;
TCCR1B=0x00;
TCNT1H=0x00;
TCNT1L=0x00;
ICR1H=0x00;
ICR1L=0x00;
OCR1AH=0x00;
OCR1AL=0x00;
OCR1BH=0x00;
OCR1BL=0x00;
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
// Timer/Counter 2 initialization
// Clock source: System Clock
// Clock value: Timer 2 Stopped
// Mode: Normal top=FFh
// OC2A output: Disconnected
// OC2B output: Disconnected
ASSR=0x00;
TCCR2A=0x00;
TCCR2B=0x00;
TCNT2=0x00;
OCR2A=0x00;
OCR2B=0x00;
// External Interrupt(s) initialization
// INT0: Off
// INT1: Off
// Interrupt on any change on pins PCINT0-7: Off
// Interrupt on any change on pins PCINT8-14: Off
// Interrupt on any change on pins PCINT16-23: Off
EICRA=0x00;
EIMSK=0x00;
PCICR=0x00;
// Timer/Counter 0 Interrupt(s) initialization
TIMSK0=0x00;
// Timer/Counter 1 Interrupt(s) initialization
TIMSK1=0x00;
// Timer/Counter 2 Interrupt(s) initialization
TIMSK2=0x00;
// Analog Comparator initialization
// Analog Comparator: Off
// Analog Comparator Input Capture by Timer/Counter 1: Off
ACSR=0x80;
ADCSRB=0x00;
while (1)
{
if (boton==0)
var1++;
if (var1==10)
var1=0;
PORTB=tabla7segmentos [var1];
// Place your code here
};
}
Explicación del programa:
Un solo bit de un registro se puede accesar nombre_de_registro.número_de_bit
El registro del Puerto C es PINC y el bit que deseamos accesar es el 0, entonces PINC.0 es
accesar el bit 0 del PINC, que es la terminal C0
Se desea que cuando se presione el botón conectado al pin C0 se incremente el contador,
entonces podríamos escribir así:
if (PINC.0==0) //Cuando se presiona vale 0
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
var++;
//Se incrementa la variable en 1
Pero es más fácil escribir, interpretar un programa con nombres más claros, por ejemplo el
C0 es donde está conectado el botón así que en se definió en la parte superior una etiqueta
donde iguala boton a PINC.0, para que sea más fácil interpretar el programa.
#define boton PINC.0
Con lo anterior boton=PINC.0 así que si utilizo en el programa PINC.0 o boton el
compilador lo interpreta igual, y es más fácil ubicar la palabra boton que PINC.0. Suponga
que tenga conectados leds, botones, etc. podría hacer esa definición de
#define led_rojo PORTB.1
#define led_verde PORTB.2
#define borrado PINC.1
etc.
Cuando el botón se presiona, es decir que vale 0, ya que cuando no está presionado lee 1
lógico debido a la resistencia de pull-up, incrementa en 1 la variable var1
if (boton==0)
var1++;
Se utiliza una variable que se nombró var1, aunque puede ser cualquier nombre. Como se
está utilizando la tabla que convierte a 7segmentos el número, sólo se pueden desplegar
números de 0 hasta 9, así que si la var1 es igual a 10 se regresa a 0, porque números
superiores al 10 desplegarían basura ya que no existen en la tabla accesando localidades no
inicializadas.
if (var1==10)
var1=0;
Y finalmente el valor de var1 que va desde 0 hasta 9 va indexando la tabla con la
instrucción PORTB=tabla7segmentos [var1];
Cuando programe el microcontrolador y arme el circuito verá que cuando presione el botón
se incrementará muy rápidamente el display y la razón de eso es porque el programa
detecta el nivel de 0, es decir, cuando valga 0 el pin se incrementará el valor a mostrar en el
display; aunque nosotros presionemos y soltemos el botón muy rápido el programa leerá en
es pequeño lapso de tiempo miles de veces un nivel lógico de 0.
Si deseáramos que se modificara el valor sólo una vez cuando se presione el botón deberá
hacerse que funcione por flancos, es decir cuando cambie el pin de 1 a 0. Vea el diagrama
de la figura 2.8.
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
Figura 2.8 Detección de flancos
Para poder trabajar por flancos es necesario tener el valor del botón anterior y del botón
actual, por ejemplo en 1 y 2 el botón pasado vale 1 y el botón actual vale 1, con eso
sabemos que no ha sido presionado el botón; ahora en el punto 2 y 3 el punto 2 es botón
pasado y el 3 es botón actual y ambos valen 1, así que no ha sido presionado el botón; en el
punto 3 y 4 el botón pasado vale 1 y el botón actual vale 0 eso significa que se presionó el
botón y que hubo un flanco de bajada de 1 a 0 ese cambio nos sirve, por ejemplo, para
incrementar la variable. Después el punto 4 vale 0 y el punto 5 vale 0 que son botón pasado
y actual respectivamente eso significa que no hay cambio de flanco, cuando esté en los
puntos 7 y 8 tanto botón pasado como actual valen 0 por lo que no hay cambio, en el punto
8 y 9 el botón pasado y actual valen 0 y 1 ese es un cambio de flanco de subida; en el punto
9 y 10 los valores de botón pasado y actual valen 1 por lo que no hay cambio de flanco.
Ahora en el programa se hará que el valor en el display se incremente solamente en 1 sin
importar cuánto dure presionado el botón, es decir, que funcionará por flanco, y lo haremos
que se incremente en 1 cuando cambia de 1 a 0 el valor del pin, es por ello que debemos
tener dos valores: botona y botonp que guardarán los valores que tuvo el botón actual y el
botón pasado.
Programa 5. Programa que incrementa el valor del contador BCD conectado al
puerto B cada vez que se presiona el interruptor conectado al pin C0. Este funcionará
por nivel de bajada, es decir, solo cuando se presione el interruptor se incrementará el
conteo.
Configuración. Puerto B de salidas, pin C0 de entrada con resistencia de pull-up activada.
El circuito a aramar es el mismo de la figura 2.7.
La inicialización se hará igual que el programa anterior, vea el video_p5 donde está cómo
se inicializa el microcontrolador.
/*****************************************************
This program was produced by the
CodeWizardAVR V1.25.7a Evaluation
Automatic Program Generator
© Copyright 1998-2007 Pavel Haiduc, HP InfoTech s.r.l.
http://www.hpinfotech.com
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
Project :
Version :
Date : 14/09/2008
Author : Freeware, for evaluation and non-commercial use only
Company :
Comments:
Chip type
: ATmega48
Clock frequency : 1,000000 MHz
Memory model
: Small
External SRAM size : 0
Data Stack size : 128
*****************************************************/
#include <mega48.h>
// Declare your global variables here
#define boton PINC.0
bit botonp;
bit botona;
unsigned char var;
const char tabla7segmentos [10]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7c,0x07,0x7f,0x6f};
void main(void)
{
// Declare your local variables here
// Crystal Oscillator division factor: 8
#pragma optsizeCLKPR=0x80;
CLKPR=0x03;
#ifdef _OPTIMIZE_SIZE_
#pragma optsize+
#endif
// Input/Output Ports initialization
// Port B initialization
// Func7=Out Func6=Out Func5=Out Func4=Out Func3=Out Func2=Out Func1=Out Func0=Out
// State7=0 State6=0 State5=0 State4=0 State3=0 State2=0 State1=0 State0=0
PORTB=0x00;
DDRB=0xFF;
// Port C initialization
// Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
// State6=T State5=T State4=T State3=T State2=T State1=T State0=P
PORTC=0x01;
DDRC=0x00;
// Port D initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
// State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T
PORTD=0x00;
DDRD=0x00;
// Timer/Counter 0 initialization
// Clock source: System Clock
// Clock value: Timer 0 Stopped
// Mode: Normal top=FFh
// OC0A output: Disconnected
// OC0B output: Disconnected
TCCR0A=0x00;
TCCR0B=0x00;
TCNT0=0x00;
OCR0A=0x00;
OCR0B=0x00;
// Timer/Counter 1 initialization
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
// Clock source: System Clock
// Clock value: Timer 1 Stopped
// Mode: Normal top=FFFFh
// OC1A output: Discon.
// OC1B output: Discon.
// Noise Canceler: Off
// Input Capture on Falling Edge
// Timer 1 Overflow Interrupt: Off
// Input Capture Interrupt: Off
// Compare A Match Interrupt: Off
// Compare B Match Interrupt: Off
TCCR1A=0x00;
TCCR1B=0x00;
TCNT1H=0x00;
TCNT1L=0x00;
ICR1H=0x00;
ICR1L=0x00;
OCR1AH=0x00;
OCR1AL=0x00;
OCR1BH=0x00;
OCR1BL=0x00;
// Timer/Counter 2 initialization
// Clock source: System Clock
// Clock value: Timer 2 Stopped
// Mode: Normal top=FFh
// OC2A output: Disconnected
// OC2B output: Disconnected
ASSR=0x00;
TCCR2A=0x00;
TCCR2B=0x00;
TCNT2=0x00;
OCR2A=0x00;
OCR2B=0x00;
// External Interrupt(s) initialization
// INT0: Off
// INT1: Off
// Interrupt on any change on pins PCINT0-7: Off
// Interrupt on any change on pins PCINT8-14: Off
// Interrupt on any change on pins PCINT16-23: Off
EICRA=0x00;
EIMSK=0x00;
PCICR=0x00;
// Timer/Counter 0 Interrupt(s) initialization
TIMSK0=0x00;
// Timer/Counter 1 Interrupt(s) initialization
TIMSK1=0x00;
// Timer/Counter 2 Interrupt(s) initialization
TIMSK2=0x00;
// Analog Comparator initialization
// Analog Comparator: Off
// Analog Comparator Input Capture by Timer/Counter 1: Off
ACSR=0x80;
ADCSRB=0x00;
while (1)
{
// Place your code here
if (boton==0)
botona=0;
else
botona=1;
if ((botona==0)&&(botonp==1)) //hubo cambio de flanco de 1 a 0
var++;
//Se incrementa la variable
if (var==10)
var=0;
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
PORTB=tabla7segmentos [var];
botonp=botona;
};
}
Explicación del Programa.
Cuando el boton (que es el pin C0 por el define) sea 0 se hará la variable tipo bit botona=0,
si boton es 1 entonces botona=1. Es decir sólo copia el valor del pin y se asigna a esa
variable tipo bit
if (boton==0)
botona=0;
else
botona=1;
En la parte más baja del programa se tiene
botonp=botona;
Esto es porque el valor del botona (botón actual) se convierte en el valor del botonp (botón
pasado), cuando regrese a la parte de arriba del programa se leerá el valor del botona (boton
actual) entonces de esta manera tenemos el valor actual (botona), así como el valor pasado
inmediato (botonp). Cuando llegue a la parte de abajo el botona (botón actual) será el valor
pasado, porque después se leerá el valor del botón actual, y así consecutivamente.
Ya que se tiene el valor actual y pasado del botón se tienen las instrucciones de y con el
primer if se prueba si hubo cambio de flanco de 1 a 0 ya que se prueba si botonp==1 y que
botona==0, con estas dos pruebas sabemos que hubo cambio de flanco de 1 a 0, y si lo
hubo se incrementa la variable, entonces solo cuando se presiona el botón se incrementará
la variable, sin importar cuánto dure en 0, ya que funciona el programa por flanco de bajada
y no de subida.
if ((botona==0)&&(botonp==1)) //hubo cambio de flanco de 1 a 0
var++;
//Se incrementa la variable
if (var==10)
var=0;
Cuando programe el microcontrolador y arme el circuito verá que en ocasiones cuando
presiona el interruptor o cuando lo suelta se incrementa en varias unidades el display, y es
porque el interruptor genera rebotes, ya que el cerrado y la apertura de interruptores
mecánicos no es instantáneo generándose rebotes (vea la figura 2.9). A continuación
explicaré como eliminar los rebotes.
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
Eliminación de rebotes en un interruptor
vea el siguiente diagrama donde se aprecia que cuando se presiona un interruptor, éste se
abre y cierra varias veces por que el cerrado no es instántaneo; de la misma forma cuando
se abre el interruptor se generan rebotes y éstos se pueden generar durante periodos de 40
mS.
Figura 2.9 Rebotes en un interruptor mecánico
Si hiciéramos un programa que cada vez que se presiona una tecla incrementara un valor y
no quitáramos los rebotes y se generará los rebotes del diagrama anterior veríamos que se
incrementaría la variable 5 veces en lugar de 1 vez debido a que los rebotes generan varios
cambios de nivel de 1 a 0 y de 0 a 1 tanto al presionar el interruptor como al soltarlo.
En ocasiones los diseñadores y programadores inexpertos colocan circuitos externos (filtros
pasabajas o circuitos integrados) para eliminar los rebotes, pero esto no es necesario ya que
se puede hacer por software. La forma de eliminar los rebotes es detectar cuando hubo
cambio de flanco tanto de bajada como de subida y poner un retardo de 40 mS para evitar
que detecte los otros flancos.
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
Programa 6. Cada vez que se presione el botón conectado al pin C0 deberá
incrementarse el valor que se muestra en el display que se conectará en el Puerto B,
eliminándose los rebotes que se generan en el cierre y en la apertura del interruptor.
Configuración: Pin C0 de entrada y con resistencia de pull-up activada, B0 a B6 de salidas.
Vea el video_p6 donde se muestra la configuración del microcontrolador. A continuación
está el listado del programa completo, lo que está en azul es el código que deberá agregar al
código generado con el codewizard.
/*****************************************************
This program was produced by the
CodeWizardAVR V1.25.7a Evaluation
Automatic Program Generator
© Copyright 1998-2007 Pavel Haiduc, HP InfoTech s.r.l.
http://www.hpinfotech.com
Project :
Version :
Date : 14/09/2008
Author : Freeware, for evaluation and non-commercial use only
Company :
Comments:
Chip type
: ATmega48
Clock frequency : 1,000000 MHz
Memory model
: Small
External SRAM size : 0
Data Stack size : 128
*****************************************************/
#include <mega48.h>
#include <delay.h>
// Declare your global variables here
#define boton PINC.0
bit botonp;
bit botona;
unsigned char var;
const char tabla7segmentos [10]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7c,0x07,0x7f,0x6f};
void main(void)
{
// Declare your local variables here
// Crystal Oscillator division factor: 8
#pragma optsizeCLKPR=0x80;
CLKPR=0x03;
#ifdef _OPTIMIZE_SIZE_
#pragma optsize+
#endif
// Input/Output Ports initialization
// Port B initialization
// Func7=Out Func6=Out Func5=Out Func4=Out Func3=Out Func2=Out Func1=Out Func0=Out
// State7=0 State6=0 State5=0 State4=0 State3=0 State2=0 State1=0 State0=0
PORTB=0x00;
DDRB=0xFF;
// Port C initialization
// Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
// State6=T State5=T State4=T State3=T State2=T State1=T State0=P
PORTC=0x01;
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
DDRC=0x00;
// Port D initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
// State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T
PORTD=0x00;
DDRD=0x00;
// Timer/Counter 0 initialization
// Clock source: System Clock
// Clock value: Timer 0 Stopped
// Mode: Normal top=FFh
// OC0A output: Disconnected
// OC0B output: Disconnected
TCCR0A=0x00;
TCCR0B=0x00;
TCNT0=0x00;
OCR0A=0x00;
OCR0B=0x00;
// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: Timer 1 Stopped
// Mode: Normal top=FFFFh
// OC1A output: Discon.
// OC1B output: Discon.
// Noise Canceler: Off
// Input Capture on Falling Edge
// Timer 1 Overflow Interrupt: Off
// Input Capture Interrupt: Off
// Compare A Match Interrupt: Off
// Compare B Match Interrupt: Off
TCCR1A=0x00;
TCCR1B=0x00;
TCNT1H=0x00;
TCNT1L=0x00;
ICR1H=0x00;
ICR1L=0x00;
OCR1AH=0x00;
OCR1AL=0x00;
OCR1BH=0x00;
OCR1BL=0x00;
// Timer/Counter 2 initialization
// Clock source: System Clock
// Clock value: Timer 2 Stopped
// Mode: Normal top=FFh
// OC2A output: Disconnected
// OC2B output: Disconnected
ASSR=0x00;
TCCR2A=0x00;
TCCR2B=0x00;
TCNT2=0x00;
OCR2A=0x00;
OCR2B=0x00;
// External Interrupt(s) initialization
// INT0: Off
// INT1: Off
// Interrupt on any change on pins PCINT0-7: Off
// Interrupt on any change on pins PCINT8-14: Off
// Interrupt on any change on pins PCINT16-23: Off
EICRA=0x00;
EIMSK=0x00;
PCICR=0x00;
// Timer/Counter 0 Interrupt(s) initialization
TIMSK0=0x00;
// Timer/Counter 1 Interrupt(s) initialization
TIMSK1=0x00;
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
// Timer/Counter 2 Interrupt(s) initialization
TIMSK2=0x00;
// Analog Comparator initialization
// Analog Comparator: Off
// Analog Comparator Input Capture by Timer/Counter 1: Off
ACSR=0x80;
ADCSRB=0x00;
while (1)
{
if (boton==0)
botona=0;
else
botona=1;
if ((botonp==1)&&(botona==0))
//hubo cambio de flanco de 1 a 0
{
var++;
//Se incrementa la variable
if (var==10)
var=0;
delay_ms(40); //Se coloca retardo de 40mS para eliminar rebotes
}
if ((botonp==0)&&(botona==1))
//hubo cambio de flanco de 0 a 1
delay_ms(40); //Se coloca retardo de 40mS para eliminar rebotes
PORTB=tabla7segmentos [var];
botonp=botona;
};
}
Explicación del programa
La diferencia con respecto al programa 5 es que en las instrucciones donde detecta el flanco
de 1 a 0 se colocó la instrucción de delay_ms(40); esa instrucción dura 40 mS y ahí se
queda ese tiempo el programa, entonces cuando vuelva a checar el valor del botón ya los
rebotes ya no existen porque ya pasaron 40mS
if ((botonp==1)&&(botona==0))
//hubo cambio de flanco de 1 a 0
{
var++;
//Se incrementa la variable
if (var==10)
var=0;
delay_ms(40); //Se coloca retardo de 40mS para eliminar rebotes
}
También se agregó las siguientes instrucciones:
if ((botonp==0)&&(botona==1))
//hubo cambio de flanco de 0 a 1
delay_ms(40); //Se coloca retardo de 40mS para eliminar rebotes
Recuerde que cuando se abre el interruptor (en el flanco de 0 a 1) también se pueden
generar rebotes, por lo tanto cuando se detecta que se abrió el interruptor se coloca un
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
retardo de 40mS para que se quede ahí ciclado el programa y cuando salga de ahí, ya los
rebotes no existirán porque ya pasaron 40 mS.
Teclado Matricial
Si deseara manejar digamos 16 botones y lo hiciera poniendo un botón en cada pin gastaría
16 pines del microcontrolador, en vez de esto se puede hacer un teclado matricial, el cual
ahorra pines. Por ejemplo una matriz de 4*4 puede manejar 16 teclas, una matriz de 5*5
puede manejar 25 teclas. En el primer caso se ocuparían 4 pines de salida y 4 de entrada, en
el segundo se ocupan 5 pines de salida y 5 de entrada. Aquí vemos el ahorro, para 16 teclas
se ocupan 8 pines en lugar de 16 pines si se hiciera el teclado conectando cada pin a un
botón, y el ahorro es mayor cuando se ocupan 25 teclas, ya que se ocupan 10 pines en total
en lugar de 25 pines si se conecta cada botón con cada pin.
Diagrama de un teclado matricial
Figura 2.10 Diagrama de un teclado matricial de 3*3
En la figura 2.10 se muestra un teclado matricial de 9 teclas, aquí se ocupan 3 pines de
salida que son los 3 pines superiores, los 3 pines inferiores se ocupan como entradas, es
importante señalar que a los pines de entrada SE LES CONFIGURA LA RESISTENCIA
DE PULL-UP INTERNA.
Si no se presiona ningún botón marcado de B1 a B9 los 3 pines de entrada leerán “111” ya
que leen el 1 por la Resistencia de pull-up interna.
El funcionamiento del teclado matricial se basa en “escaneos”, es decir, se va probando
columna por columa. Por ejemplo si en los 3 pines de salida se envía 011 se va a probar la
primera columna donde están B1,B2 y B3, si se presiona B1 en los pines de entrada se leerá
“011”, si se presiona B3 se leerá “110”, si no se presiona ningún botón se leerá “111”. Note
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
que si en los pines de salida se envía “011” y se presiona un botón de B4 a B9 no se va a
leer ningún cero en los 3 pines de entrada porque se está probando la columna 1.
Para probar la segunda columna se envía en los pines de salida “101” y si no se presiona
ninguna tecla se leerá en la entrada “111”, pero si se presiona el boton 5 se leerá “101”, si
se presiona el botón 6 se leerá “110”. Si se presiona los botones B1 a B3 o B7 a B9 se
leerán en los 3 pines de entrada “111” debido a que se está probando la segunda columna.
Las resistencias de 100 ohms que se ponen a la salida de los 3 pines es para prevenir cortos
circuitos y dañar el puerto del microcontrolador, suponga que no se pusiera esas
resistencias y que se envía el dato “101” y se presiona al mismo tiempo el botón B1 y B4
vemos que en B1 hay 1 (5 volts) y en B4 hay 0 (0 Volts) entonces se generaría un corto
circuito. Pero al poner esa resistencia de 100 ohms no sucede el corto ya que se limita la
corriente por ese resistor.
Resumen del teclado matricial:
Para probar la primera columna (botones B1 a B3) se envía en los pines de salida “011”
Para probar la segunda columna (botones B4 a B6) se envía en los pines de salida “101”
Para probar la tercera columna (botones B7 a B9) se envía en los pines de salida “110”
Si se envió el código “011” y se lee en los pines de entrada “011” entonces se presionó el
botón B1.
Si se envió el código “011” y se lee en los pines de entrada “101” entonces se presionó el
botón B2.
Si se envió el código “011” y se lee en los pines de entrada “110” entonces se presionó el
botón B3.
Si se envió el código “101” y se lee en los pines de entrada “011” entonces se presionó el
botón B4.
Si se envió el código “101” y se lee en los pines de entrada “101” entonces se presionó el
botón B5.
Si se envió el código “101” y se lee en los pines de entrada “110” entonces se presionó el
botón B6.
Si se envió el código “110” y se lee en los pines de entrada “011” entonces se presionó el
botón B7.
Si se envió el código “110” y se lee en los pines de entrada “101” entonces se presionó el
botón B8.
Si se envió el código “110” y se lee en los pines de entrada “110” entonces se presionó el
botón B9.
Si cuando se envían los códigos “011”, “101” y “110” no se presiona ningún botón se leerá
en los pines de entrada “111”
En el teclado matricial a los pines de entrada hay que activarles la resistencia de pull-up
para que cuando no se presione ningún botón se lea 1 lógico en cada pin y no quede flotado
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
el pin, que recordando cuando un pin queda flotado comienza a oscilar leyendo 0 y 1 de
manera aleatoria debido al ruido.
Las resistencias de 100 ohms a la salida de los pines que son los que envían el código son
para proteger contra corto circuitos, por ejemplo si se enviara en la primera columna un 0 y
en la segunda un 1 y se presionará el botón 1 y 4 habría un corto ya que en una línea hay 5
volts y en el otro 0, así que al tener la resistencia de 100 Ohms la corriente se ve limitada y
no sucede nada al presionar dos teclas al mismo tiempo.
Como son botones los que están conmutando a tierra hay que quitarle los rebotes, tal y
como lo hicimos en el programa 6. Aunque en este programa que se hará no se quitarán los
rebotes.
Este ejemplo de 3*3 se puede usar como base para diseñar cualquier teclado matricial 5*5,
6*6, etc.
Programa 7. Diseño de un teclado matricial de 3*3, con despliegue a display a 7
segmentos. Los pines C0, C1 y C2 serán los pines de salida por donde se enviarán los
códigos de “saceneo”, los pines C3, C4 y C5 serán los pines de entrada donde se leerán
los botones presionados.
Configuración: C0, C1 y C2 de salida, C3, C4 y C5 de entrada con resistencia de pull-up
interna activada. B0 a B7 de salida que es donde se conectará el display.
Figura 2.11 Conexiones para el programa 7
/*****************************************************
This program was produced by the
CodeWizardAVR V1.25.7a Evaluation
Automatic Program Generator
© Copyright 1998-2007 Pavel Haiduc, HP InfoTech s.r.l.
http://www.hpinfotech.com
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
Project :
Version :
Date : 14/09/2008
Author : Freeware, for evaluation and non-commercial use only
Company :
Comments:
Chip type
: ATmega48
Clock frequency : 1,000000 MHz
Memory model
: Small
External SRAM size : 0
Data Stack size : 128
*****************************************************/
#include <mega48.h>
// Declare your global variables here
unsigned char tecla,lectura;
const char tabla7segmentos [10]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7c,0x07,0x7f,0x6f};
void main(void)
{
// Declare your local variables here
// Crystal Oscillator division factor: 8
#pragma optsizeCLKPR=0x80;
CLKPR=0x03;
#ifdef _OPTIMIZE_SIZE_
#pragma optsize+
#endif
// Input/Output Ports initialization
// Port B initialization
// Func7=Out Func6=Out Func5=Out Func4=Out Func3=Out Func2=Out Func1=Out Func0=Out
// State7=0 State6=0 State5=0 State4=0 State3=0 State2=0 State1=0 State0=0
PORTB=0x00;
DDRB=0xFF;
// Port C initialization
// Func6=In Func5=In Func4=In Func3=In Func2=Out Func1=Out Func0=Out
// State6=T State5=P State4=P State3=P State2=1 State1=1 State0=1
PORTC=0x3F;
DDRC=0x07;
// Port D initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
// State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T
PORTD=0x00;
DDRD=0x00;
// Timer/Counter 0 initialization
// Clock source: System Clock
// Clock value: Timer 0 Stopped
// Mode: Normal top=FFh
// OC0A output: Disconnected
// OC0B output: Disconnected
TCCR0A=0x00;
TCCR0B=0x00;
TCNT0=0x00;
OCR0A=0x00;
OCR0B=0x00;
// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: Timer 1 Stopped
// Mode: Normal top=FFFFh
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
// OC1A output: Discon.
// OC1B output: Discon.
// Noise Canceler: Off
// Input Capture on Falling Edge
// Timer 1 Overflow Interrupt: Off
// Input Capture Interrupt: Off
// Compare A Match Interrupt: Off
// Compare B Match Interrupt: Off
TCCR1A=0x00;
TCCR1B=0x00;
TCNT1H=0x00;
TCNT1L=0x00;
ICR1H=0x00;
ICR1L=0x00;
OCR1AH=0x00;
OCR1AL=0x00;
OCR1BH=0x00;
OCR1BL=0x00;
// Timer/Counter 2 initialization
// Clock source: System Clock
// Clock value: Timer 2 Stopped
// Mode: Normal top=FFh
// OC2A output: Disconnected
// OC2B output: Disconnected
ASSR=0x00;
TCCR2A=0x00;
TCCR2B=0x00;
TCNT2=0x00;
OCR2A=0x00;
OCR2B=0x00;
// External Interrupt(s) initialization
// INT0: Off
// INT1: Off
// Interrupt on any change on pins PCINT0-7: Off
// Interrupt on any change on pins PCINT8-14: Off
// Interrupt on any change on pins PCINT16-23: Off
EICRA=0x00;
EIMSK=0x00;
PCICR=0x00;
// Timer/Counter 0 Interrupt(s) initialization
TIMSK0=0x00;
// Timer/Counter 1 Interrupt(s) initialization
TIMSK1=0x00;
// Timer/Counter 2 Interrupt(s) initialization
TIMSK2=0x00;
// Analog Comparator initialization
// Analog Comparator: Off
// Analog Comparator Input Capture by Timer/Counter 1: Off
ACSR=0x80;
ADCSRB=0x00;
while (1)
{
//C0 A C2 son de salida y son para
//Probar las 3 columnas
//Se prueba la primera columna se envía 110
PORTC=0b00111110;
//C3, C4 y C5 son las entradas del teclado
//Por eso se enmascaran con 00111000
lectura=PINC&0b00111000;
if (lectura==0b00110000)
tecla=7;
if (lectura==0b00101000)
tecla=8;
if (lectura==0b00011000)
tecla=9;
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
//se prueba segunda columna se envía 101
PORTC=0b00111101;
//C3, C4 y C5 son las entradas del teclado
//Por eso se enmascaran con 00111000
lectura=PINC&0b00111000;
if (lectura==0b00110000)
tecla=4;
if (lectura==0b00101000)
tecla=5;
if (lectura==0b00011000)
tecla=6;
//se prueba tercera columna se envía 011
PORTC=0b00111011;
//C3, C4 y C5 son las entradas del teclado
//Por eso se enmascaran con 00111000
lectura=PINC&0b00111000;
if (lectura==0b00110000)
tecla=1;
if (lectura==0b00101000)
tecla=2;
if (lectura==0b00011000)
tecla=3;
PORTB=tabla7segmentos [tecla];
// Place your code here
};
}
Importante. Un puerto tiene asociado 2 registros el PINX y el PORTX donde X es el puerto
A, B, C, etc según sea el caso del microcontrolador que se está usando, y además de de ser
los registros para escribir y leer datos al puerto, sirven para ACTIVAR y DESACTIVAR
LAS RESISTENCIAS DE PULL-UP. Ejemplo suponga que configura los primeros 4 bits
del puerto B como entrada y los 4 bits siguientes como salidas. Cuando desee escribir
información al Puerto B (veámoslo en binario) puede hacer lo siguiente:
1. PORTB=0b11001111;
2. PORTB=0b11001100;
3. PORTB=0b11000011;
En los 3 casos al puerto B le escribiría 1100 en la parte alta, la parta baja aparentemente no
se modificaría porque están configurados como entradas, pero sucede lo siguiente:
Cuando un pin es configurado como entrada y se le escribe a través del PORTX un 1 en ese
bit le activará la resistencia de pull-up.
Cuando un pin es configurado como entrada y se le escribe a través del PORTX un 0 en ese
bit le desactivará la resistencia de pull-up.
Entonces en el caso 1: le está activando la resistencia de pull-up interna a los 4 bits menos
significativos. En el caso 2 le está desactivando la resistencia de pull-up a los 2 bits menos
significativos y le está activando la resistencia de pull-up interna a los 2 pines siguientes.
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
En el caso 3 le está activando la resistencia de pull-up a los 2 primeros pines y se la está
desactivando a los 2 pines siguientes.
Por ello en el programa cuando se deseaba que C2,C1 y C0 fuera 110 se escribió
PORTC=0b00111110; para no desactivar las resistencia de pull-up en los pines C3,C4 y C5.
Entonces recuerde que si un puerto lo tiene configurado unos pines como salida y otros
como entrada, y además a los de entrada a unos pines les activó la resistencia de pull-up y a
otro no, deberá escribir a través del PORTX un 1 en la posición de los pines que les activó
la resistencia de pull-up, ya que si se le pone 0 la va deshabilitar, ahora en el caso de que
esos pines sean configurados como entrada y no les activó la resistencia de pull-up deberá
escribir en PORTX un 0 en los pines que no están activadas las resistencia de pull-up, ya
que de escribirles un 1 las activará.
Ejemplo: B3, B2, B1 y B0 como salidas, B4 y B5 como entradas con resistencia de pull-up
interna y B6 y B7 también de entrada pero sin resistencia interna de pull-up activada.
Y desea escribir de B3 a B0 “1101” deberá usar PORTB así:
PORTB=0b00111101; //B7 y B6 0 para no habilitar la R de pull-up, B5 y B4 1 para
mantener la R de pull-up y en B3 a B0 1101 que es el dato que desea escribir.
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
2.5 MEMORIA EEPROM
Este microcontrolador ATMEGA48 tiene 256 Bytes de memoria no volátil EEPROM, el
manejo de la EEPROM se hace a través de varios registros: EEARH, EEARL, EEDR, y
EECR. Donde hay que especificar la dirección, escribir el dato a guardar, y unos bits de
control, etc. Pero no es necesario conocer y manejar esos registros, ya que el codevision nos
permite manejar los datos en eeprom como variables y él se encarga de manejar los
registros, así que solamente hay que escribir la variable o arreglo como lo hemos hecho en
temas anteriores pero antecediendo la palabra eeprom y de esa manera el compilador
guardará ese dato en la memoria eeprom
Ejemplos:
eeprom unsigned char var1;
eeprom unsigned int var1, var2, calibración, hora;
eeprom unsigned char tabla [5]={0x01,0x02,0x04,0x03,0x10};
Programa 8. Haga un programa en el cual con un botón conectado al pin C0
incrementará el valor en el display conectado en el puerto B y cuando se presione el
botón C1 lo guardará. Después de descargar el programa desconecte el
microcontrolador de la energía eléctrica y vuélvalo a conectar para que observe que el
dato se quedó guardado.
Figura 2.12 Conexiones para el programa 8
Configuración: C0 y C1 de entrada con resistencia de pull-up activada y es donde se
conectarán los botones; B0 a B6 de salida para conectar el display.
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
/*****************************************************
This program was produced by the
CodeWizardAVR V1.25.7a Evaluation
Automatic Program Generator
© Copyright 1998-2007 Pavel Haiduc, HP InfoTech s.r.l.
http://www.hpinfotech.com
Project :
Version :
Date : 14/09/2008
Author : Freeware, for evaluation and non-commercial use only
Company :
Comments:
Chip type
: ATmega48
Clock frequency : 1,000000 MHz
Memory model
: Small
External SRAM size : 0
Data Stack size : 128
*****************************************************/
#include <mega48.h>
#include <delay.h>
#define boton PINC.0
#define boton_guarda PINC.1
bit botonp;
bit botona;
unsigned char var; //Ahora la variable se guarda en eeprom
const char tabla7segmentos [10]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7c,0x07,0x7f,0x6f};
eeprom char datoaguardar;
void checa_boton (void); // Aquí se declaran todas las funciones que se van usar
// Declare your global variables here
void main(void)
{
// Declare your local variables here
// Crystal Oscillator division factor: 8
#pragma optsizeCLKPR=0x80;
CLKPR=0x03;
#ifdef _OPTIMIZE_SIZE_
#pragma optsize+
#endif
// Input/Output Ports initialization
// Port B initialization
// Func7=Out Func6=Out Func5=Out Func4=Out Func3=Out Func2=Out Func1=Out Func0=Out
// State7=0 State6=0 State5=0 State4=0 State3=0 State2=0 State1=0 State0=0
PORTB=0x00;
DDRB=0xFF;
// Port C initialization
// Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
// State6=T State5=T State4=T State3=T State2=T State1=P State0=P
PORTC=0x03;
DDRC=0x00;
// Port D initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
// State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T
PORTD=0x00;
DDRD=0x00;
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
// Timer/Counter 0 initialization
// Clock source: System Clock
// Clock value: Timer 0 Stopped
// Mode: Normal top=FFh
// OC0A output: Disconnected
// OC0B output: Disconnected
TCCR0A=0x00;
TCCR0B=0x00;
TCNT0=0x00;
OCR0A=0x00;
OCR0B=0x00;
// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: Timer 1 Stopped
// Mode: Normal top=FFFFh
// OC1A output: Discon.
// OC1B output: Discon.
// Noise Canceler: Off
// Input Capture on Falling Edge
// Timer 1 Overflow Interrupt: Off
// Input Capture Interrupt: Off
// Compare A Match Interrupt: Off
// Compare B Match Interrupt: Off
TCCR1A=0x00;
TCCR1B=0x00;
TCNT1H=0x00;
TCNT1L=0x00;
ICR1H=0x00;
ICR1L=0x00;
OCR1AH=0x00;
OCR1AL=0x00;
OCR1BH=0x00;
OCR1BL=0x00;
// Timer/Counter 2 initialization
// Clock source: System Clock
// Clock value: Timer 2 Stopped
// Mode: Normal top=FFh
// OC2A output: Disconnected
// OC2B output: Disconnected
ASSR=0x00;
TCCR2A=0x00;
TCCR2B=0x00;
TCNT2=0x00;
OCR2A=0x00;
OCR2B=0x00;
// External Interrupt(s) initialization
// INT0: Off
// INT1: Off
// Interrupt on any change on pins PCINT0-7: Off
// Interrupt on any change on pins PCINT8-14: Off
// Interrupt on any change on pins PCINT16-23: Off
EICRA=0x00;
EIMSK=0x00;
PCICR=0x00;
// Timer/Counter 0 Interrupt(s) initialization
TIMSK0=0x00;
// Timer/Counter 1 Interrupt(s) initialization
TIMSK1=0x00;
// Timer/Counter 2 Interrupt(s) initialization
TIMSK2=0x00;
// Analog Comparator initialization
// Analog Comparator: Off
// Analog Comparator Input Capture by Timer/Counter 1: Off
ACSR=0x80;
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
ADCSRB=0x00;
if (datoaguardar>10)
datoaguardar=0;
var=datoaguardar;
while (1)
{
checa_boton();
PORTB=tabla7segmentos [var];
if (boton_guarda==0) //Si se presiona el botón de guardar
datoaguardar=var; //se grabara la eeprom con el valor de var
// Place your code here
};
}
void checa_boton (void)
{
if (boton==0)
botona=0;
else
botona=1;
if ((botonp==1)&&(botona==0))
//hubo cambio de flanco de 1 a 0
{
var++;
//Se incrementa la variable
if (var>=10)
var=0;
delay_ms(40); //Se coloca retardo de 40mS para eliminar rebotes
}
if ((botonp==0)&&(botona==1))
//hubo cambio de flanco de 0 a 1
delay_ms(40); //Se coloca retardo de 40mS para eliminar rebotes
botonp=botona;
}
Explicación del Programa
En este programa se está utilizando una función que llamé checa_boton, pero esta función
se debe declarar en la parte de arriba, esta función no regresa, ni envía parámetros por lo
que se coloca void checa_boton(void); Para entender mejor esto regrese a la sección del
primer capítulo donde se explica las funciones.
void checa_boton (void); // Aquí se declaran todas las funciones que se van usar
Después en el programa se llama la función y se coloca:
checa_boton();
Cuando la función se ejecuta, regresa a la instrucción que está debajo de checa_boton();
que es PORTB=tabla7segmentos [var];
En una parte del programa se tiene:
if (datoaguardar>10)
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
datoaguardar=0;
La razón de ello es que si el datoaguardar que es una variable eeprom tiene un valor inicial
mayor a 10 la hacemos 0, y esto sucedería una sola vez, ya que cuando se guarde un dato el
dato estará entre 0 y 9. Pero un microcontrolador nuevo tiene en la memoria eeprom puros
0xff en todas las localidades, entonces la primera vez hará que ese dato de 0xff se haga 0.
Importante. Si dentro de un programa utiliza una variable eeprom como una variable RAM
puede suceder que dañe esa localidad de memoria eeprom, ya que una eeprom tiene una
vida útil de 100,000 ciclos de escritura y lectura, pero si usa esa variable eeprom como una
variable RAM puede suceder que en cuestión de segundos sucedan esos 100,000 ciclos de
escritura y lectura porque un programa en el microcontrolador ejecuta el código miles de
veces en un segundo, entonces al reescribir esa variable en el programa miles de veces en
un segundo se va a cabar la vida útil de la localidad de memoria. En el programa vemos que
la eeprom sólo se escribe cuando se presiona un botón.
2.6 Configuración del Convertidor de Analógico a Digital (ADC)
Las características del ADC que tiene el microcontrolador AVR son:
•
•
•
•
•
Tiene 6 canales con 10 bits de resolución
Capaz de obtener 15,000 muestras por segundo a la máxima resolución
Voltaje de referencia internos de 1.1V
Voltaje de referencia externo de 0 a Vcc del ADC
Capacidad de detener el CPU para minimizar el ruido y mejorar el resultado de la
conversión
2.6.1 Funcionamiento de un ADC
Un ADC convierte una señal analógica a un dato digital de manera lineal, para ello es
necesario identificar tres aspectos importantes y éstos son: el voltaje de referencia (Vref), el
número de bits y si es unipolar o bipolar el ADC.
2.6.1.1 Conversión unipolar
En la conversión unipolar el voltaje que se introduce a los canales del convertidor es
solamente positivo, y pudiendo ser la conversión del ADC de 10 bits o de 8 bits, si es de 10
bits significa que identifica hasta 210 combinaciones que son 1024; si es de 8 bits tiene 28
combinaciones esto es 256 combinaciones. Obviamente entre más bits sean es mejor el
ADC aunque en ocasiones no es necesario una resolución tan elevada. El voltaje de
referencia es el valor con el que se comparará el voltaje de entrada analógico y con base en
ello se determina la combinación de salida. La forma de obtener la combinación digital si
está configurado el ADC en 10 bits es:
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
conversión=(Vin*1024)/Vref .....................................................................................ec(2.1)
Donde Vin es el voltaje que se aplica, vref es el voltaje de referencia y conversión es el
valor digital.
Por ejemplo si Vref=5 y el vin que va a medir es de 2.5 dará como salida 512 según la
ecuación 2.1; si ahora el vin es de 1 volt la conversión que dará será de 204.
En el caso de que se seleccione una conversión de 8 bits, la fórmula a utilizar es:
conversión=(Vin*256)/Vref ......................................................................................ec(2.2)
Sustituyendo en la ec 2.2 los valores de vin=2.5 el resultado de la conversión es 128, si el
vin=1el resultado de la conversión será 51
En el circuito integrado identifiquemos las terminales asociadas al ADC
Figura 2.13 Pines del microcontrolador ATMEGA48
En la figura 2.13 vemos que en las terminales 22 y 20 dicen GND y AVCC, estás son las
términales de tierra y alimentación del periférico del convertidor de analógico a digital.
Normalmente los microcontroladores de otros fabricantes el ADC se alimenta con el mismo
voltaje del microcontrolador, pero en los de ATMEL son alimentados por separado para
disminuir los efectos del ruido. Aunque en nuestro caso y para los programas que vamos a
realizar el AVCC y VCC los conectaremos juntos y los GND también serán conectados
juntos. Pero recuerde que si en su aplicación requiere de una conversión más exacta puede
alimentar el microcontrolador y el periférico del ADC por separado. Solamente tenga en
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
cuenta que si los va alimentar por separado el voltaje del periférico del ADC no debe ser
mayor al del microcontrolador, por ejemplo si el microcontrolador lo va alimentar con dos
pilas y el voltaje es de 3 Volts, el ADC debe ser alimentado con 3 volts como máximo
también.
El vref que se encuentra en las ecuaciones 2.1 y 2.2 puede ser de tres tipos:
1. Vref=Vcc del microcontrolador, en este caso Aref no se conecta (pin 21), vea la
figura 2.14 el circuito de en medio.
2. Vref=Aref (pin 21). En este caso se tiene una referencia externa y puede ser
cualquier voltaje, siempre y cuando no sea superior al Vcc del microcontrolador.
Este se ejemplifica en el primer diagrama de la figura 2.14.
3. Vref=1.1 Volts interno, se debe conectar un capacitor externo de 0.1uf del tipo gota
de tantalo y se muestra en el tercer circuito de la figura 2.14.
Figura 2.14 conexión de las terminales del ADC según la selección del Vref
Todos los casos anteriores se configuran con el codevision con clicks.
En las ecuaciones 2.1 y 2.2 se tiene un término llamado Vin que es el voltaje de entrada, es
decir, el voltaje que será convertido a una combinación digital, según su proporción con
Vref. Este Vin se aplica en los canales del ADC que se numeran desde ADC0 hasta ADC5
(pines del 23 al 28), entonces tenemos que esos pines tienen varias funciones ya sean como
pines de entrada o salida, o como entradas del ADC. Entonces si el pin se configura como
entrada del ADC ya no puede funcionar como entrada/salida digital, esto es que las
funciones son excluyentes.
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
Resumen del ADC
En este periférico podemos tener:
•
•
•
•
•
Conversión unipolar donde se aplica a la entrada analógica un valor de voltaje
positivo que puede ir desde 0 hasta Vref.
o Puede ser la conversión de 8 bits donde el resultado está dado por:
conversión=(Vin*256)/Vref
o Puede ser la conversión de 10 bits donde el resultado está dado por:
conversión=(Vin*1024)/Vref
El Vref puede ser el voltaje del microcontrolador, pueder ser uno externo conectado
a la terminal Aref o uno interno de 1.1 Volts.
El Vin es el voltaje analógico que será convertido a un combinación digital, y ese
vin se aplica en cualquiera de los canales ADC0 hasta ADC5
El perifércio del ADC se alimenta por separado, pero no puede ser mayor al Vcc del
microcontrolador. Aunque para estos ejemplos conecatremos las terminales de GND
y el Vcc lo conectaremos al Avcc.
Las configuraciones descritas anteriormente se realizan con clicks en el codevision,
por lo que no es necesario aprender ningún registro o bit de éstos para su
configuración.
Importante. Si va a utilizar un canal para hacer la conversión tenga la precaución de
configurar ese pin como entrada. Ya que en ese canal va aplicar voltaje y no puede estar
configurado el pin como salida, ya que de ser así se podría dañar el pin. Por ejemplo si va
utilizar el canal 0 del adc que es el pin A0 deberá configurar ese pin como entrada.
Importante El Avcc no puede ser mayor al Vcc, por eso conéctelos juntos
Importante El Aref no puede ser mayor al Vcc del microcontrolador.
Importante No aplique Vin mayores a Aref o a Vcc para evitar daños en el pin del ADC.
Programa 9. Haga una conversión en 8 bits sobre el canal 0 del ADC y muestre el
resultado en leds que se conectarán en el Puerto B. Configure como Vref el AVcc del
microcontrolador.
Tabla 2.1 Canal y pin del ADC
Canal
Pin
0 ADC0
23 C0
1 ADC1
24 C1
2 ADC2
25 C2
3 ADC3
26 C3
4 ADC4
27 C4
5 ADC5
28 C5
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
Figura 2.15 Conexión para el programa 9
Con el codewizard se hace la configuración del ADC a través de simples clicks, lo único
que debemos hacer en el programa es leer el valor de la conversión del canal que estemos
usando a través de la siguiente función:
variable=read_adc (número_de_canal);
Por ejemplo si deseará utilizar el canal 3, que es el pin 26 deberá colocar :
variable=read_adc(3);
Recuerde también que el ADC puede entregar la conversión en 8 o 10 bits, si selecciona
conversión en 8 bits, entonces la variable deberá definirse como unsigned char variable; es
decir tipo char de 8 bits, pero si se selecciona una conversión en 10 bits deberá declarar la
variable como unsigned int variable, ya que el valor de la conversión ocupa 10 bits.
El nombre que le di a la variable donde se guardará el resultado fue variable, pero puede
darle cualquier nombre.
Importante. La función read_adc() sólo puede utilizarse si dentro del codevision se habilitó
el ADC, ya que si lo usa en cualquiera de los otros programas que se han realizado y que no
han unicializado el ADC marcará error.
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
Vea el video_p9 donde se muestra la forma de inicializar el ADC.
/*****************************************************
This program was produced by the
CodeWizardAVR V1.25.7a Evaluation
Automatic Program Generator
© Copyright 1998-2007 Pavel Haiduc, HP InfoTech s.r.l.
http://www.hpinfotech.com
Project :
Version :
Date : 15/09/2008
Author : Freeware, for evaluation and non-commercial use only
Company :
Comments:
Chip type
: ATmega48
Clock frequency : 1,000000 MHz
Memory model
: Small
External SRAM size : 0
Data Stack size : 128
*****************************************************/
#include <mega48.h>
#include <delay.h>
unsigned char x;
#define ADC_VREF_TYPE 0x60
// Read the 8 most significant bits
// of the AD conversion result
unsigned char read_adc(unsigned char adc_input)
{
ADMUX=adc_input | (ADC_VREF_TYPE & 0xff);
// Delay needed for the stabilization of the ADC input voltage
delay_us(10);
// Start the AD conversion
ADCSRA|=0x40;
// Wait for the AD conversion to complete
while ((ADCSRA & 0x10)==0);
ADCSRA|=0x10;
return ADCH;
}
// Declare your global variables here
void main(void)
{
// Declare your local variables here
// Crystal Oscillator division factor: 8
#pragma optsizeCLKPR=0x80;
CLKPR=0x03;
#ifdef _OPTIMIZE_SIZE_
#pragma optsize+
#endif
// Input/Output Ports initialization
// Port B initialization
// Func7=Out Func6=Out Func5=Out Func4=Out Func3=Out Func2=Out Func1=Out Func0=Out
// State7=0 State6=0 State5=0 State4=0 State3=0 State2=0 State1=0 State0=0
PORTB=0x00;
DDRB=0xFF;
// Port C initialization
// Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
// State6=T State5=T State4=T State3=T State2=T State1=T State0=T
PORTC=0x00;
DDRC=0x00;
// Port D initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
// State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T
PORTD=0x00;
DDRD=0x00;
// Timer/Counter 0 initialization
// Clock source: System Clock
// Clock value: Timer 0 Stopped
// Mode: Normal top=FFh
// OC0A output: Disconnected
// OC0B output: Disconnected
TCCR0A=0x00;
TCCR0B=0x00;
TCNT0=0x00;
OCR0A=0x00;
OCR0B=0x00;
// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: Timer 1 Stopped
// Mode: Normal top=FFFFh
// OC1A output: Discon.
// OC1B output: Discon.
// Noise Canceler: Off
// Input Capture on Falling Edge
// Timer 1 Overflow Interrupt: Off
// Input Capture Interrupt: Off
// Compare A Match Interrupt: Off
// Compare B Match Interrupt: Off
TCCR1A=0x00;
TCCR1B=0x00;
TCNT1H=0x00;
TCNT1L=0x00;
ICR1H=0x00;
ICR1L=0x00;
OCR1AH=0x00;
OCR1AL=0x00;
OCR1BH=0x00;
OCR1BL=0x00;
// Timer/Counter 2 initialization
// Clock source: System Clock
// Clock value: Timer 2 Stopped
// Mode: Normal top=FFh
// OC2A output: Disconnected
// OC2B output: Disconnected
ASSR=0x00;
TCCR2A=0x00;
TCCR2B=0x00;
TCNT2=0x00;
OCR2A=0x00;
OCR2B=0x00;
// External Interrupt(s) initialization
// INT0: Off
// INT1: Off
// Interrupt on any change on pins PCINT0-7: Off
// Interrupt on any change on pins PCINT8-14: Off
// Interrupt on any change on pins PCINT16-23: Off
EICRA=0x00;
EIMSK=0x00;
PCICR=0x00;
// Timer/Counter 0 Interrupt(s) initialization
TIMSK0=0x00;
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
// Timer/Counter 1 Interrupt(s) initialization
TIMSK1=0x00;
// Timer/Counter 2 Interrupt(s) initialization
TIMSK2=0x00;
// Analog Comparator initialization
// Analog Comparator: Off
// Analog Comparator Input Capture by Timer/Counter 1: Off
ACSR=0x80;
ADCSRB=0x00;
// ADC initialization
// ADC Clock frequency: 500,000 kHz
// ADC Voltage Reference: AVCC pin
// ADC Auto Trigger Source: None
// Only the 8 most significant bits of
// the AD conversion result are used
// Digital input buffers on ADC0: On, ADC1: On, ADC2: On, ADC3: On
// ADC4: On, ADC5: On
DIDR0=0x00;
ADMUX=ADC_VREF_TYPE & 0xff;
ADCSRA=0x81;
while (1)
{
x=read_adc(0); //Se hace la conversión sobre el canal 0 del ADC
PORTB=x;
//Se despliega en el puerto B el valor digital
// Place your code here
};
}
El programa 9 hace una conversión sobre el canal 0, pero el resultado que da, no es en
voltaje sino en un código binario. Si Vin=0 volts la conversión da 0b0000,0000; si vin=2.5
volts la conversión da 0b1000,0000 (128 decimal); si es 5 Volts=0b1111,1111 (255
decimal). Es decir nos convierte a una proporción binaria que luego debemos interpretar,
pero ese resultado lo podemos convertir a voltaje a través de una regla de 3.
Si Vin=5 da como resultado 0xff, es decir 255, Pero deseamos que muestre un código
nuevo que sea 50 (que sería 5.0 Volts). Entonces se resuleve así:
Código nuevo=Conversión*50/conversión máxima
Note, si Vin=5 Volts, la conversión dará 0b1111,1111 que es 255, que sustituyendo en la ec
1.3 da:
Código nuevo=255*50/255=50 que podemos interpretar como 5.0 Volts.
Si vin=2.5 Volts, la conversión dará 0b1000,0000 que es 128
Código nuevo=128*50/255 =25 que podemos interpretar como 2.5 Volts.
Entonces la ecuación 1.3 queda:
código nuevo=conversión del ADC * código que deseamos.....................................ec(2.3)
conversión_Máxima.
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
Ejemplo suponga que conectamos un sensor de temperatura que da 5 volts cuando hay
100°C, entonces ahora deseamos mostrar el resultado en grados centígrados, no en voltaje,
así que sustituyendo en la ecuación 1.3 queda:
Código nuevo=Conversión del ADC*100/Conversión Máxima (255 para 8 bits)
Si conversión=255, el código nuevo=100°C
Si la conversión del ADC=128, el código nuevo será 128*100/255=50, el código
nuevo=50°C.
Pero ese código nuevo debemos separarlo en digitos para poderlo desplegar en el display de
7 segmentos o en una LCD. Esto se realiza en el programa 10.
Programa 10. Haga una conversión de 8 bits en el canal 1 y muestre el resultado del
volatje en dos displays que se conectarán al Puerto B. El Vref=Vcc=5 volts. El pin Co
controlará el display de unidades y el C1 el de decenas. Se usará el canal 2 del ADC2
por donde se aplicará un voltaje variable de entre 0 y 5 volts, el cual será desplegado
en los dos displays desde 0.0 hasta 5.0
Usando la ecuación 2.3 tenemos que la conversión máxima =255, el código que deseamos
con esa conversión máxima=50 para mostrar 5.0 Volts.
código nuevo= conversión del ADC * código que deseamos
Conversión Máxima
Debemos hacer lo siguiente en el programa:
Leer la conversión del canal
x=read_adc(1);
codigonuevo=x*50/255;
Importante. x es tipo char, se multiplica por 50 pero ese resultado temporal ocupa más de 8
bits. Para evitar esto se hace el casting, es decir, se le dice al compilador el resultado de
x*50/255 da como resultado un char, ya que no sobrepasa a 255, pero lo obliga a que en las
operaciones intermedias (sobre todo en la multiplicación) se guarde el resultado en algún
lugar ya que ocupa más de 8 bits. El casting se hace codigonuevo=(char)x*50/255
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
EL USO DEL CASTING
Ejemplo 1.
Suponga que tiene x=y+w+z; y todas son tipo char, en ese caso no existe problema ya que
si y+w+z da como resultado un número menor a 255 se podrá guardar en x.
Ejemplo 2.
x es tipo int; y,w y z son tipo char. Y se escribe x=y+w+z; Pero se sabe que y+w+z van a
dar mas de 255, y que caben en x ya que esta es tipo int. Pero lo de la derecha y+w+z está
declarado como char, entonces cuando la suma dé más de 255 ¿dónde quedan esas
operaciones? Entonces aquí si va existir un problema porque x=int y y+w+z son tipo char
cuya suma da mas de 8 bits y que cabe en x, pero en las sumas parciales las variables son
char y estas no pueden guardar mas de 8 bits, entonces hay que hacer un casting, obligando
al compilador a decirle todas son tipo char, pero el resultado es tipo int para que lo guarde
temporalmente en algún lugar para después asignarlo a x.
x=(int) y+w+z; //Esto ya da un resultado correcto
x=y+w+z
//Daría un resultado erróneo ya que y+w+z da mas de 8 bits y estas
variables no pueden manejar más de 8 bits
Importante. Podemos evitar hacer cálculos de las operaciones intermedias para ver si
hacemos o no el casting, y esto dejarlo al compilador. Esta opción se selecciona en el
codevision en project-configure-C Compiler y finalmente dando click en promote char to
int como se muestra en la figura 2.15. Con la selección de la casilla “promote char to int” le
estamos indicando al compilador que cuando ocupe hacer que la variable la haga más
grande lo haga él mismo para evitarnos problemas de casting.
Figura 2.15 Habilitación de cambio detamaño de variables para evitar el casting
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
Con lo anterior le estamos indicando al compilador si la operación intermedia ocupa el
char un espacio de un int que lo genere para evitar errores y así ya nos olvidamos del
casting.
Con las dos instrucciones anteriores ya tenemos representado en voltaje la conversión, pero
debemos separarlo en dos digitos, esto es calcularle la cantidad de decenas y las unidades.
Continuando con la explicación del programa, si ya se calculó el código nuevo, ahora
debemos obtener los dos digitos a desplegar ya que el 50 no se puede desplegar en un
display, debemos obtener los dos digitos, así que:
decenas=codigonuevo/10;
unidades=codigonuevo%10;
Recuerde que % es la operación módulo y que da como resultado el residuo. Suponga que
el códigonuevo=48, es decir 4.8 Volts
Entonces decenas=48/10=4
Y unidades=48%10=8 (que es el residuo)
Y las decenas y unidades los mandamos desplegar en dos displays de 7 segmentos.
Importante. En cada proyecto nuevo que hagamos debemos seleccionar la casilla de
promote char to int.
Configuración: PORTB de salidas para conectar los displays. C0 y C1 salidas que serán
usados para manejar los dos transistores: el que controla el display de unidades, y el que
controla el display de decenas. El pin C2 como entrada que es también el canal 2 del ADC
(ADC2).
Vea el video_p10 donde se muestra la inicialización del microcontrolador junto con el
ADC.
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
El siguiente listado es el programa que generó el codewizard y lo que está en azul es lo que
deberá agregar al programa que usted haga.
/*****************************************************
This program was produced by the
CodeWizardAVR V2.03.6 Evaluation
Automatic Program Generator
© Copyright 1998-2008 Pavel Haiduc, HP InfoTech s.r.l.
http://www.hpinfotech.com
Project :
Version :
Date : 05/10/2008
Author : Freeware, for evaluation and non-commercial use only
Company :
Comments:
Chip type
: ATmega48
Clock frequency : 1,000000 MHz
Memory model
: Small
External RAM size : 0
Data Stack size : 128
*****************************************************/
#include <mega48.h>
#include <delay.h>
unsigned char var,codigonuevo,unidades,decenas;
const char tabla7segmentos [10]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7c,0x07,0x7f,0x6f};
void despliega(void); //Se va a utilizar una función que se llama despliega sin parámetros
#define ADC_VREF_TYPE 0x60
#define tru PINC.0
#define trd PINC.1
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
// Read the 8 most significant bits
// of the AD conversion result
unsigned char read_adc(unsigned char adc_input)
{
ADMUX=adc_input | (ADC_VREF_TYPE & 0xff);
// Delay needed for the stabilization of the ADC input voltage
delay_us(10);
// Start the AD conversion
ADCSRA|=0x40;
// Wait for the AD conversion to complete
while ((ADCSRA & 0x10)==0);
ADCSRA|=0x10;
return ADCH;
}
// Declare your global variables here
void main(void)
{
// Declare your local variables here
// Crystal Oscillator division factor: 8
#pragma optsizeCLKPR=0x80;
CLKPR=0x03;
#ifdef _OPTIMIZE_SIZE_
#pragma optsize+
#endif
// Input/Output Ports initialization
// Port B initialization
// Func7=Out Func6=Out Func5=Out Func4=Out Func3=Out Func2=Out Func1=Out Func0=Out
// State7=0 State6=0 State5=0 State4=0 State3=0 State2=0 State1=0 State0=0
PORTB=0x00;
DDRB=0xFF;
// Port C initialization
// Func6=In Func5=In Func4=In Func3=In Func2=In Func1=Out Func0=Out
// State6=T State5=T State4=T State3=T State2=T State1=0 State0=0
PORTC=0x00;
DDRC=0x03;
// Port D initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
// State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T
PORTD=0x00;
DDRD=0x00;
// Timer/Counter 0 initialization
// Clock source: System Clock
// Clock value: Timer 0 Stopped
// Mode: Normal top=FFh
// OC0A output: Disconnected
// OC0B output: Disconnected
TCCR0A=0x00;
TCCR0B=0x00;
TCNT0=0x00;
OCR0A=0x00;
OCR0B=0x00;
// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: Timer 1 Stopped
// Mode: Normal top=FFFFh
// OC1A output: Discon.
// OC1B output: Discon.
// Noise Canceler: Off
// Input Capture on Falling Edge
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
// Timer 1 Overflow Interrupt: Off
// Input Capture Interrupt: Off
// Compare A Match Interrupt: Off
// Compare B Match Interrupt: Off
TCCR1A=0x00;
TCCR1B=0x00;
TCNT1H=0x00;
TCNT1L=0x00;
ICR1H=0x00;
ICR1L=0x00;
OCR1AH=0x00;
OCR1AL=0x00;
OCR1BH=0x00;
OCR1BL=0x00;
// Timer/Counter 2 initialization
// Clock source: System Clock
// Clock value: Timer 2 Stopped
// Mode: Normal top=FFh
// OC2A output: Disconnected
// OC2B output: Disconnected
ASSR=0x00;
TCCR2A=0x00;
TCCR2B=0x00;
TCNT2=0x00;
OCR2A=0x00;
OCR2B=0x00;
// External Interrupt(s) initialization
// INT0: Off
// INT1: Off
// Interrupt on any change on pins PCINT0-7: Off
// Interrupt on any change on pins PCINT8-14: Off
// Interrupt on any change on pins PCINT16-23: Off
EICRA=0x00;
EIMSK=0x00;
PCICR=0x00;
// Timer/Counter 0 Interrupt(s) initialization
TIMSK0=0x00;
// Timer/Counter 1 Interrupt(s) initialization
TIMSK1=0x00;
// Timer/Counter 2 Interrupt(s) initialization
TIMSK2=0x00;
// Analog Comparator initialization
// Analog Comparator: Off
// Analog Comparator Input Capture by Timer/Counter 1: Off
ACSR=0x80;
ADCSRB=0x00;
// ADC initialization
// ADC Clock frequency: 500,000 kHz
// ADC Voltage Reference: AVCC pin
// ADC Auto Trigger Source: None
// Only the 8 most significant bits of
// the AD conversion result are used
// Digital input buffers on ADC0: On, ADC1: On, ADC2: On, ADC3: On
// ADC4: On, ADC5: On
DIDR0=0x00;
ADMUX=ADC_VREF_TYPE & 0xff;
ADCSRA=0x81;
while (1)
{
var=read_adc(2);
//Se lee el valor del Canal 2 del ADC (PIN C2)
codigonuevo=50*255/var; //Convierte el valor del ADC a un código nuevo en voltaje
decenas=codigonuevo/10;
//Calcula las decenas
unidades=codigonuevo%10;
//Calcula las unidades
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
despliega();
//Manda desplegar los dos digitos
// Place your code here
};
}
void despliega(void)
{
PORTB=tabla7segmentos [unidades];
tru=1;
//Prende display de unidades
delay_ms(4); //Deja prendido display de unidades 4mS
tru=0;
//Apaga display de unidades
PORTB=tabla7segmentos[decenas];
trd=1;
//Prende display de decenas
delay_ms(4); //Deja prendido display de decenas 4mS
trd=0;
//Apaga display de decenas
}
Explicación del programa:
Se inicializó el valor de la tabla que nos dibuja los números en los 7 segmentos del display
y declaramos una función que nombré despliega y que utilizaré en el programa y que no
envía, ni recibe parámetros.
unsigned char var,codigonuevo,unidades,decenas;
const char tabla7segmentos [10]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7c,0x07,0x7f,0x6f};
void despliega(void); //Se va a utilizar una función que se llama despliega sin parámetros
Después se agregó las siguientes igualdades
#define tru PINC.0
#define trd PINC.1
El tru es transistor de unidades y el trd es el transistor de decenas, es más fácil escribir el
programa haciendo por ejemplo tru=1; con ello estamos haciendo que el PINC.0=1; ya que
es una igualdad, pero tru resulta más fácil recordar y sabiendo que es donde está conectado
el transistor del displya de unidades
Recuerde que la función para leer el valor de conversión del ADC es read_adc(canal); que
es la primera línea y dicho valor de la conversión se guarda en una variable llamada var que
es de 8 bits, ya que la conversión que escojimos a la hora de inicializar el ADC fue de 8
bits.
Si el voltaje que se le aplica al canal 2 es de 2.5 Volts, el valor de la conversión será de 127,
pero nosotros queremos representarlo en voltaje, así que con la fórmula de 50*255/var y
sustituyendo var=127 nos dará como resultado 25, que ahora si podemos representar como
2.5 Volts. Pero el 25 hay que convertirlo a dos digitos para desplegarlos en el display, por
lo que 25/10=2, y 25%10=5 (recuerde que % es la funcióm módulo hace la división pero el
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
resultado es el residuo) y esos valores se guardan en decenas y unidades para desplegarlos
en la función despliega(); Todo lo anterior es el siguiente código agregado al programa:
var=read_adc(2);
//Se lee el valor del Canal 2 del ADC (PIN C2)
codigonuevo=50*255/var; //Convierte el valor del ADC a un código nuevo en voltaje
decenas=codigonuevo/10;
//Calcula las decenas
unidades=codigonuevo%10;
//Calcula las unidades
Posteriormente se manda llamar una función para desplegar los dos digitos del voltaje:
despliega();
//Manda desplegar los dos digitos
La función de despliega manda imprimir en el puerto B el digito correspondiente de
unidades, después activa el transistor de unidades, lo deja prendido 4mS y lo apaga y
después manda imprimir el digito de decenas lo prende y apaga, pero lo hace tan rápido que
aparenta que están los dos digitos al mismo tiempo.
void despliega(void)
{
PORTB=tabla7segmentos [unidades];
tru=1;
//Prende display de unidades
delay_ms(4); //Deja prendido display de unidades 4mS
tru=0;
//Apaga display de unidades
PORTB=tabla7segmentos[decenas];
trd=1;
//Prende display de decenas
delay_ms(4); //Deja prendido display de decenas 4mS
trd=0;
//Apaga display de decenas
}
Importante. La función read_adc(canal) funciona únicamente si a la hora de inicializar el
microcontrolador con el codewizard inicializa el ADC, así que si utiliza esta función en los
programas anteriores no funcionará e inclusive marcará erros porque no está inicializado el
periférico del ADC.
2.7 Manejo de las pantallas de LCD
Las pantallas de cristal líquido LCD alfanuméricas sin importar el fabricante se basan en un
circuito integrado de Hitachi que es el HD44780, esto significa que cualquier pantalla que
se utilice funciona de la misma manera, por lo que la programación de la pantalla es
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
idéntica sin importar la marca de la LCD. La única diferencia es en la velocidad, ya que
algunas responden más rápidamente a los comandos que se le mandan.
El asistente que tiene el codevision solicita que se le indique de cuántos caracteres es cada
línea de la LCD. Así mismo se indica en el mismo asistente cómo conectar la LCD y a que
pines. Las pantallas se pueden conectar en un bus de datos de 8 bits o de 4, el asistente lo
coloca en un bus de 4 bits para ahorrar pines. El asistente del codevision genera todo el
código necesario para inicializar la LCD y el programador sólo debe preocuparse por
manejar las siguientes funciones:
Los comandos que acepta la LCD son:
lcd_clear(); Se utiliza para borrar todos los carácteres en la LCD
lcd_gotoxy(unsigned char x,unsigned char y); Coloca el cursor en la columna x y fila y,
puede ser o una variable x y y o una constante. Cuando se posiciona el cursor en algún
punto determinado y se manda escribir una letra el cursor se mueve hacía la derecha un
lugar.
lcd_putchar(char c); Coloca un carácter en la posición donde haya quedado el cursor, de
acuerdo a la función anterior.
_lcd_write_data();
_lcd_write_byte(direccion,dato);
Lo primero que debe hacerse es ubicar la posición del cursor, es decirle en cuál fila y en
cual columna. Por ejemplo las LCDs de 2x16 son 2 filas por 16 caracteres. Entonces su se
desea escribir en la segunda fi
_lcd_ready();
Hasta este punto ya sabe como manejar puertos, desplegar datos en displays de 7 segmentos
y LCds, guardar datos en eeprom y además sabe utilizar el ADC. Con este conocimiento
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
que ha adquirido está en la posibilidad de hacer controles, sistemas de adquisición y
monitoreo de datos y su procesamiento.
Con esta parte se concluye la programación básica-intermedia del microcontrolador. En el
siguiente capitulo veremos temás avanzados del microcontrolador como interrupciones,
protocolos de comunicación seriales y timers.
Programación en C de los microcontroladores ATMEL
e-mail: dinfante29@hotmail.com
Autor: David Infante Sánchez
www.comunidadatmel.com
Descargar