Éste es el primer
módulo del hexápodo, el "prototipo" del módulo LCD
con 3 pulsadores y un potenciometro que se usará para
comunicarse con él de manera autónoma. Ha sido el primero
de los distintos módulos que se ha hecho, se utilizará
para construir el resto y comprobar su correcto funcionamiento.
Sólo lleva cargado un programa que permite seleccionar
entre varios "programas/funciones" para que ejecute el micro, de esta
forma podemos tener todos los programas que hagamos para probar la
distinta electrónica del hexápodo cargados en el micro y
ordenados, así no tenemos que estar cargando el programa cada
vez que queramos probar un componente. El único programa que
lleva de momento es el del anterior ejemplo de leer el potenciometro.
Respecto al programa sólo anotar el uso de las variables
compartidas del main/funciones junto con la ISR, cuando usemos
una variable global en el main() y accedamos a ella en las ISRs,
debemos declarar esta variable como "volatile", indicandole al
compilador que almacene la variable siempre y sólo en el mismo
sitio (que no ponga copia en los registros) de tal forma que siempre la
ISR y las funciones que las usen lean el valor actualizado de la
variable (que la ISR no la modifique en un registro y la función
la lea en otro no actualizado), bueno para explicar bien esto tengo que
mirarme como se distribuye y usa la memoria para saber donde colocar
correctamente las variables.
Otro punto a tener en cuenta es cuando operamos con variables mayores
de 8 bits en nuestra función y en la ISR (variables que hemos
declarado como volatile según lo anterior), al ser un micro de 8
bits leemos y escribimos en pasos de 8 bits, si accedemos a una
variable de mas de 8 bits y antes de terminar de operar con ella la ISR
la modifica pues tendremos un valor desconocido de la variable con
bytes actualizados por la ISR y los que hayamos leído antes de
la ISR sin actualizar. Por lo que cuando tengamos que operar con una
variable mayor de 8 bits que pueda ser modificada por la ISR hay que
deshabilitar las interrupciones, hacer la operación y
después volver a habilitarlas. La mejor forma de hacerlo es
guardar el registro SREG, hacer un cli(); y restaurar el registro SREG,
de esta manera si las interrupciones no estaban habilitadas antes
pues no las activaremos que sería el caso si
usasemos cli(); y sei();. Con ciertas operaciones ("test and set")
y variables de 8 bits también hay que hacer lo anterior.
Recomiendo los foros de AVRfreaks.net, están llenos de
tutoriales donde explican estas cosas.
He tenido que limpiar los flags de la interrupción a mano,
tenía el problema de que algunas veces al salir de la
interrupción se volvía a entrar en ésta (lo
hacía una de cada 4 veces mas o menos) como si de un rebote se
tratase. Por lo que tenía entendido al salir de la ISR el
compilador limpiaba el flag y así lo hacía la
mayoría de las veces.., la solución ha sido limpiarlo a
mano al final de cada ISR y desaparecer el problema de pulsar una tecla
y saltar dos espacios.
Por último tengo que leerme algo de como ordenar el
código ya que el programa del hexápodo va a ser muy largo
y tendré que crear varios .c y .h, así que tengo que ver
cuál es la mejor forma de gestionar programas grandes que
desconozco totalmente.
Me dejo aquí el código del programa para tenerlo a mano y
el video del funcionamiento, el programa voy escribiendo y probando y
si funciona pues todo bien, según vaya conociendo los AVR ya
intentaré hacer las cosas bien, de momento estos micros me
están pareciendo una maravilla y es muy fácil empezar con
ellos con sólo leer lo que hay por internet.
Esto es lo que llevo usado de memoria:
AVR Memory Usage
----------------
Device: atmega1280
Program: 5126 bytes (3.9% Full)
(.text + .data + .bootloader)
Data: 361 bytes (4.4% Full)
(.data + .bss + .noinit)
Build succeeded with 0 Warnings...
Aquí cabe mucho código...
//Módulo LCD Hexápodo.
#include <avr/io.h>
#define F_CPU 16000000UL
#include <util/delay.h>
#include <avr/interrupt.h>
#define Bus_Datos PORTK
#define Bus_Control PORTF
#define RS PORTF5
#define RW PORTF6
#define E PORTF7
//Comandos situar el cursor inicio línea.
#define DDRA_LINEA1 0x80 //1000 0000
#define DDRA_LINEA2 0xC0 //1100 0000
#define DDRA_LINEA3 0x94 //1001 0100
#define DDRA_LINEA4 0xD4 //1101 0100
//Botones del LCD, interrupciones externas.
//#define boton1 PORTD2 //INT2
//#define boton2 PORTD3 //INT3
//#define boton3 PORTE4 //INT4
//#define Potenciometro PORTF0 //ADC0
void lcd_comando(unsigned char comando);
void lcd_escribir_c(char caracter);
void lcd_escribir(char *cadena);
void posicionar_cursor(unsigned char x, unsigned char y) ;
void escribir_valor_decimal(unsigned char registro);
void escribir_registro(unsigned char registro);
void valor_real(unsigned char registro);
void lcd_inicializar();
//****************** Programas *******************************/
void programa0();
void programa1();
void programa2();
void programa3();
void programa4();
void programa5();
void programa6();
void programa7();
void programa8();
void programa9();
//****************** Variables Globales *********************//
volatile unsigned char estado;
unsigned char copia_estado;
volatile unsigned char cuenta_tecla;
unsigned char copia_cuenta_tecla;
unsigned char ultima_cuenta_tecla;
volatile unsigned char pantalla0=1;
volatile unsigned char pantalla1=1;
volatile unsigned char pantalla2=1;
unsigned char copia_pantalla0=1;
unsigned char copia_pantalla1=1;
unsigned char copia_pantalla2=1;
//**********************************************/
int main(void)
{
lcd_inicializar(); //Inicializar módulo lcd.
char linea1[]="Hexapodo FireFly";
char linea2[]="www.jmnlab.com";
char linea3[]="Pulse el boton1 para";
char linea4[]="comenzar";
posicionar_cursor(1,3);
lcd_escribir(linea1);
posicionar_cursor(2,4);
lcd_escribir(linea2);
posicionar_cursor(3,1);
lcd_escribir(linea3);
posicionar_cursor(4,7);
lcd_escribir(linea4);
while(1)
{
switch(estado)
{
case 0:
{
programa0();
}break;
case 1:
{
programa1();
}break;
case 2:
{
programa2();
}break;
case 3:
{
programa3();
}break;
case 4:
{
programa4();
}break;
case 5:
{
programa5();
}break;
case 6:
{
programa6();
}break;
case 7:
{
programa7();
}break;
case 8:
{
programa8();
}break;
case 9:
{
programa9();
}break;
default:break;
}
}
}
/**********************************************************************************************************************/
/********************************** Configurar y escribir en el LCD ***************************************************/
/**********************************************************************************************************************/
void lcd_comando(unsigned char comando)
{
Bus_Control &= ~(1<<RS); //RS0
Bus_Control &= ~(1<<RW); //RW0
Bus_Datos = comando; //Comando
Bus_Control |= (1<<E);
_delay_ms(5);
Bus_Control &= ~(1<<E);
_delay_ms(5);
}
void lcd_escribir_c(char caracter)
{
Bus_Control |= (1<<RS); //RS1
Bus_Control &= ~(1<<RW); //RS0
Bus_Datos = caracter;
Bus_Control |= (1<<E);
_delay_ms(5);
Bus_Control &= ~(1<<E);
_delay_ms(5);
}
void lcd_escribir(char *cadena)
{
char *inicio = cadena;
unsigned char i=0;
for(i=0;((inicio[i]!='\0')&&(i<20));i++)
{
Bus_Control |= (1<<RS); //RS0 RW0 caracter
Bus_Control &= ~(1<<RW);
Bus_Datos = inicio[i];
Bus_Control |= (1<<E);
_delay_ms(5);
Bus_Control &= ~(1<<E);
_delay_ms(5);
}
}
void posicionar_cursor(unsigned char x, unsigned char y) //línea 1 a 4, columna de 1 a 20.
{
unsigned char comando=DDRA_LINEA1;
switch(x)
{
case 1:
{
comando=DDRA_LINEA1+y-1;
}break;
case 2:
{
comando=DDRA_LINEA2+y-1;
}break;
case 3:
{
comando=DDRA_LINEA3+y-1;
}break;
case 4:
{
comando=DDRA_LINEA4+y-1;
}break;
default:break;
}
lcd_comando(comando);
}
/**********************************************************************************************************************/
/****************************************** ISR's *********************************************************************/
/**********************************************************************************************************************/
ISR(INT2_vect)
{
switch(estado)
{
case 0:
{
cuenta_tecla++;
if(cuenta_tecla>9)
cuenta_tecla=9;
}break;
default:break;
}
_delay_ms(200); //Rebotes
EIFR |=(1<<INT2);
}
ISR(INT3_vect)
{
switch(estado)
{
case 0:
{
cuenta_tecla--;
if(cuenta_tecla<1)
cuenta_tecla=1;
}
default:break;
}
_delay_ms(200); //Rebotes
EIFR |=(1<<INT3);
}
ISR(INT4_vect)
{
pantalla0=1;
pantalla1=1;
pantalla2=1;
if (estado==0)
{
estado=cuenta_tecla;
ultima_cuenta_tecla=cuenta_tecla;
}
else
{
estado=0;
cuenta_tecla=ultima_cuenta_tecla;
}
_delay_ms(200); //Rebotes
EIFR |=(1<<INT4);
}
ISR(ADC_vect)
{
}
/**********************************************************************************************************************/
/*************************************** Funciones Manipular Registros LCD ********************************************/
/**********************************************************************************************************************/
void escribir_registro(unsigned char registro) //Escribir registro LCD.
{
unsigned char mascara = 0b10000000;
unsigned char cont=0;
for(cont=0;cont<8;cont++)
{
if((registro & mascara)==0)
lcd_escribir_c('0');
else
lcd_escribir_c('1');
// mascara>>1;
mascara = mascara/2;
}
}
void escribir_valor_decimal(unsigned char registro) //Escribir binario a decimal.
{
char centenas = 0;
char decenas = 0;
char unidades = 0;
while(registro>=100)
{
registro= registro -100;
centenas++;
}
while(registro>=10)
{
registro=registro -10;
decenas++;
}
while(registro>0)
{
registro=registro-1;
unidades++;
}
lcd_escribir_c(centenas | 0b00110000);
lcd_escribir_c(decenas | 0b00110000);
lcd_escribir_c(unidades | 0b00110000);
}
void valor_real(unsigned char registro) //Escribir valor binario a analógico LCD, Vref 5V, 8 bits.
{
unsigned int v_real = (int)registro;
char unidades = 0;
char decimas = 0;
char centesimas = 0;
v_real=v_real*195;
while(v_real>10000)
{
v_real=v_real - 10000;
unidades++;
}
while(v_real>1000)
{
v_real=v_real - 1000;
decimas++;
}
while(v_real>100)
{
v_real=v_real - 100;
centesimas++;
}
lcd_escribir_c(unidades | 0b00110000);
lcd_escribir_c('.');
lcd_escribir_c(decimas | 0b00110000);
lcd_escribir_c(centesimas | 0b00110000);
lcd_escribir_c('V');
}
/**********************************************************************************************************************/
/**************************** Inicializar el Módulo LCD ***************************************************************/
/**********************************************************************************************************************/
void lcd_inicializar()
{
/************************************************************///LCD
DDRK=0xFF; //1111 1111
PORTK=0x00;
DDRF|=((1<<RS)|(1<<RW)|(1<<E)); //1110 0000
PORTF &= ~(1<<E);
_delay_ms(100);
lcd_comando(0x3C); //Function Set RS0 RW0 0011 NF** 0011 1100 C
lcd_comando(0x3C); //Function Set
lcd_comando(0x0C); //Display ON/OFF Control RS0 RW0 00001DCB 0000 1100 c
lcd_comando(0x01); //Display Clear RS0 RW0 00000001
lcd_comando(0x06); //Entry Mode Set RS0 RW0 000001I/DS 0000 0111
/************************************************************///ADC
ADMUX |=(1<<REFS0); //Tensión de referencia AVCC 5 voltios.
ADMUX |=(1<<ADLAR); //8 bits de resolución.
//Canal ADC
por defecto ADC0, no tenemos que cambiarlo.
ADCSRA |=(1<<ADATE);//Habilitamos el Free Running Mode, por defecto seleccionado
ADCSRA |=((1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)); //Prescaler 128, Fadc 125 KHz @ 16MHz.
DIDR0 |=(1<<ADC0D); //Apagamos la parte digital del puerto.
ADCSRA |= (1<<ADIE);//Enable ADC Interrupt
ADCSRA |= (1<<ADEN);//Enable ADC
/************************************************************///Botones
//configurar registros interrupciones.
//Tipo de evento que dispará la interrupción, 10, flanco de bajada.
EICRA &= ~(1<<ISC20); //INT2
EICRA |= (1<<ISC21);
EICRA &= ~(1<<ISC30); //INT3
EICRA |= (1<<ISC31);
EICRB &= ~(1<<ISC40); //INT4
EICRB |= (1<<ISC41);
//Habilitar interrupciones externas
EIMSK |= ((1<<INT2)|(1<<INT3)|(1<<INT4));
//Habilitar todas las interrupciones
sei();
}
/**********************************************************************************************************************/
void programa0()
{
//Selección de programa.
char linea5[]="Seleccione Programa:";
char linea6[]="Leer Potenciometro";
char linea7[]="Servomotor";
char linea8[]="Sensor U. I2C";
char linea9[]="S. Infrarrojo";
char linea10[]="PWM Led RGB";
char linea11[]="Comunicacion PC";
char linea12[]="Comunicacion SPI";
char linea13[]="Controladora 24 S.";
char linea14[]="Control Bateria";
ADCSRA &= ~(1<<ADEN); //Apagar ADC.
ADCSRA &= ~(1<<ADSC);
while(estado==0)
{
cli();
copia_cuenta_tecla=cuenta_tecla;
copia_pantalla0=pantalla0;
copia_pantalla1=pantalla1;
copia_pantalla2=pantalla2;
sei();
if((copia_cuenta_tecla>0)&&(copia_cuenta_tecla<4)&&(copia_pantalla0==1))
{
lcd_comando(0x01);
lcd_escribir(linea5);
posicionar_cursor(2,2);
lcd_escribir(linea6);
posicionar_cursor(3,2);
lcd_escribir(linea7);
posicionar_cursor(4,2);
lcd_escribir(linea8);
pantalla0=0;
pantalla1=1;
pantalla2=1;
}
if((copia_cuenta_tecla>3)&&(copia_cuenta_tecla<7)&&(copia_pantalla1==1))
{
lcd_comando(0x01);
lcd_escribir(linea5);
posicionar_cursor(2,2);
lcd_escribir(linea9);
posicionar_cursor(3,2);
lcd_escribir(linea10);
posicionar_cursor(4,2);
lcd_escribir(linea11);
pantalla0=1;
pantalla1=0;
pantalla2=1;
}
if((copia_cuenta_tecla>6)&&(copia_cuenta_tecla<10)&&(copia_pantalla2==1))
{
lcd_comando(0x01);
lcd_escribir(linea5);
posicionar_cursor(2,2);
lcd_escribir(linea12);
posicionar_cursor(3,2);
lcd_escribir(linea13);
posicionar_cursor(4,2);
lcd_escribir(linea14);
pantalla0=1;
pantalla1=1;
pantalla2=0;
}
switch(copia_cuenta_tecla)
{
case 1:
{
posicionar_cursor(2,1);
lcd_escribir_c(0b01111110);
posicionar_cursor(3,1);
lcd_escribir_c(' ');
posicionar_cursor(4,1);
lcd_escribir_c(' ');
}break;
case 2:
{
posicionar_cursor(2,1);
lcd_escribir_c(' ');
posicionar_cursor(3,1);
lcd_escribir_c(0b01111110);
posicionar_cursor(4,1);
lcd_escribir_c(' ');
}break;
case 3:
{
posicionar_cursor(2,1);
lcd_escribir_c(' ');
posicionar_cursor(3,1);
lcd_escribir_c(' ');
posicionar_cursor(4,1);
lcd_escribir_c(0b01111110);
}break;
case 4:
{
posicionar_cursor(2,1);
lcd_escribir_c(0b01111110);
posicionar_cursor(3,1);
lcd_escribir_c(' ');
posicionar_cursor(4,1);
lcd_escribir_c(' ');
}break;
case 5:
{
posicionar_cursor(2,1);
lcd_escribir_c(' ');
posicionar_cursor(3,1);
lcd_escribir_c(0b01111110);
posicionar_cursor(4,1);
lcd_escribir_c(' ');
}break;
case 6:
{
posicionar_cursor(2,1);
lcd_escribir_c(' ');
posicionar_cursor(3,1);
lcd_escribir_c(' ');
posicionar_cursor(4,1);
lcd_escribir_c(0b01111110);
}break;
case 7:
{
posicionar_cursor(2,1);
lcd_escribir_c(0b01111110);
posicionar_cursor(3,1);
lcd_escribir_c(' ');
posicionar_cursor(4,1);
lcd_escribir_c(' ');
}break;
case 8:
{
posicionar_cursor(2,1);
lcd_escribir_c(' ');
posicionar_cursor(3,1);
lcd_escribir_c(0b01111110);
posicionar_cursor(4,1);
lcd_escribir_c(' ');
}break;
case 9:
{
posicionar_cursor(2,1);
lcd_escribir_c(' ');
posicionar_cursor(3,1);
lcd_escribir_c(' ');
posicionar_cursor(4,1);
lcd_escribir_c(0b01111110);
}break;
}
}
}
/******************************************************************************************************/
/********************** Programa para Leer el Potenciometro del LCD **********************************/
/******************************************************************************************************/
void programa1()
{
char linea1[]="Reg. ADCH:";
char linea2[]="Caracter LCD:";
char linea3[]="Valor Tension:";
char linea4[]="V. decimal ADCH:";
char linea5[]="Programa para leer";
char linea6[]="un Potenciometro";
char linea7[]="Registro usado ADCH";
unsigned char contador_anim=0;
unsigned char copia_ADCH=ADCH;
cli();
copia_pantalla0=pantalla0;
copia_estado=estado;
sei();
lcd_comando(0x01);
posicionar_cursor(1,2);
lcd_escribir(linea5);
posicionar_cursor(2,3);
lcd_escribir(linea6);
posicionar_cursor(3,1);
lcd_escribir(linea7);
posicionar_cursor(4,1);
for(contador_anim=0;contador_anim<20;contador_anim++)
{
lcd_escribir_c('.');
_delay_ms(100);
}
ADCSRA &= ~(1<<ADIE); //Sin interrupción
ADCSRA|= (1<<ADEN); //Comenzar conversiones.
ADCSRA|= (1<<ADSC); //Comenzar conversiones.
lcd_comando(0x01);
posicionar_cursor(1,1);
lcd_escribir(linea1);
posicionar_cursor(3,1);
lcd_escribir(linea2);
posicionar_cursor(2,1);
lcd_escribir(linea4);
posicionar_cursor(4,1);
lcd_escribir(linea3);
while(estado==1)
{
copia_ADCH=ADCH;
posicionar_cursor(1,12);
escribir_registro(copia_ADCH);
posicionar_cursor(3,15);
lcd_escribir_c(copia_ADCH);
posicionar_cursor(2,18);
escribir_valor_decimal(copia_ADCH);
posicionar_cursor(4,16);
valor_real(copia_ADCH);
}
}
/******************************************************************************************************/
/***************************** Futuro Programa ********************************************************/
/******************************************************************************************************/
void programa2()
{
char linea[]="Servomotor";
char linea1[]="No implementado";
cli();
copia_pantalla0=pantalla0;
copia_estado=estado;
sei();
if ((copia_pantalla0==1)&&(copia_estado!=0))
{
lcd_comando(0x01);
posicionar_cursor(1,6);
lcd_escribir(linea);
posicionar_cursor(3,3);
lcd_escribir(linea1);
if(copia_estado==2)
pantalla0=0;
}
}
/******************************************************************************************************/
/***************************** Futuro Programa ********************************************************/
/******************************************************************************************************/
void programa3()
{
char linea[]="Sensor U. I2C";
char linea1[]="No implementado";
cli();
copia_pantalla0=pantalla0;
copia_estado=estado;
sei();
if ((copia_pantalla0==1)&&(copia_estado!=0))
{
lcd_comando(0x01);
posicionar_cursor(1,4);
lcd_escribir(linea);
posicionar_cursor(3,3);
lcd_escribir(linea1);
if(copia_estado==3)
pantalla0=0;
}
}
/******************************************************************************************************/
/***************************** Futuro Programa ********************************************************/
/******************************************************************************************************/
void programa4()
{
char linea[]="S. Infrarrojo";
char linea1[]="No implementado";
cli();
copia_pantalla0=pantalla0;
copia_estado=estado;
sei();
if ((copia_pantalla0==1)&&(copia_estado!=0))
{
lcd_comando(0x01);
posicionar_cursor(1,4);
lcd_escribir(linea);
posicionar_cursor(3,3);
lcd_escribir(linea1);
if(copia_estado==4)
pantalla0=0;
}
}
/******************************************************************************************************/
/***************************** Futuro Programa ********************************************************/
/******************************************************************************************************/
void programa5()
{
char linea[]="PWM Led RGB";
char linea1[]="No implementado";
cli();
copia_pantalla0=pantalla0;
copia_estado=estado;
sei();
if ((copia_pantalla0==1)&&(copia_estado!=0))
{
lcd_comando(0x01);
posicionar_cursor(1,5);
lcd_escribir(linea);
posicionar_cursor(3,3);
lcd_escribir(linea1);
if(copia_estado==5)
pantalla0=0;
}
}
/******************************************************************************************************/
/***************************** Futuro Programa ********************************************************/
/******************************************************************************************************/
void programa6()
{
char linea[]="Comunicacion PC";
char linea1[]="No implementado";
cli();
copia_pantalla0=pantalla0;
copia_estado=estado;
sei();
if ((copia_pantalla0==1)&&(copia_estado!=0))
{
lcd_comando(0x01);
posicionar_cursor(1,3);
lcd_escribir(linea);
posicionar_cursor(3,3);
lcd_escribir(linea1);
if(copia_estado==6)
pantalla0=0;
}
}
/******************************************************************************************************/
/***************************** Futuro Programa ********************************************************/
/******************************************************************************************************/
void programa7()
{
char linea[]="Comunicacion SPI";
char linea1[]="No implementado";
cli();
copia_pantalla0=pantalla0;
copia_estado=estado;
sei();
if ((copia_pantalla0==1)&&(copia_estado!=0))
{
lcd_comando(0x01);
posicionar_cursor(1,3);
lcd_escribir(linea);
posicionar_cursor(3,3);
lcd_escribir(linea1);
if(copia_estado==7)
pantalla0=0;
}
}
/******************************************************************************************************/
/***************************** Futuro Programa ********************************************************/
/******************************************************************************************************/
void programa8()
{
char linea[]="Controladora 24 S.";
char linea1[]="No implementado";
cli();
copia_pantalla0=pantalla0;
copia_estado=estado;
sei();
if ((copia_pantalla0==1)&&(copia_estado!=0))
{
lcd_comando(0x01);
posicionar_cursor(1,2);
lcd_escribir(linea);
posicionar_cursor(3,3);
lcd_escribir(linea1);
if(copia_estado==8)
pantalla0=0;
}
}
/******************************************************************************************************/
/***************************** Futuro Programa ********************************************************/
/******************************************************************************************************/
void programa9()
{
char linea[]="Control Bateria";
char linea1[]="No implementado";
cli();
copia_pantalla0=pantalla0;
copia_estado=estado;
sei();
if ((copia_pantalla0==1)&&(copia_estado!=0))
{
lcd_comando(0x01);
posicionar_cursor(1,3);
lcd_escribir(linea);
posicionar_cursor(3,3);
lcd_escribir(linea1);
if(copia_estado==9)
pantalla0=0;
}
}
/******************************************************************************************************/
/******************************************************************************************************/
/******************************************************************************************************/