#include <18F4550.h>
#device adc=8
#FUSES NOWDT //No Watch Dog Timer
#FUSES HS //High speed Osc (> 4mhz for PCM/PCH) (>10mhz for PCD)
#FUSES NOPUT //No Power Up Timer
#FUSES NOPROTECT //Code not protected from reading
#FUSES NODEBUG //No Debug mode for ICD
#FUSES NOBROWNOUT //No brownout reset
#FUSES NOLVP //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOCPD //No EE protection
#FUSES NOWRT //Program memory not write protected
#use delay(clock=8000000)
#include <LCD421.C>
#ZERO_RAM
//Inicio de variables
int a, j, k, z, temper, fuel, shift, stick=1, alt, fail[6], vacio=0, dis, unit, decs, cent, mils, degs, km0, km1, km2, km3, km4, km5, od0, od1, od2, od3;
int16 timer=0, speed, rpm, volt, oil, div=0, cal;
byte valor, pos=0b11111111, posb, value[4];
byte const bitMask[8]={1,2,4,8,16,32,64,128};
//Arreglos generan los caracteres de los dígitos de los displays multilplexados
unsigned char segunit[10] = {0b00000001,0b10000001,0b00010001,0b10010001,0b00100001,0b10100001,0b00110001,0b10110001,0b01000001,0b11000001};
unsigned char segdecs[10] = {0b00000010,0b10000010,0b00010010,0b10010010,0b00100010,0b10100010,0b00110010,0b10110010,0b01000010,0b11000010};
unsigned char segcent[10] = {0b00000100,0b10000011,0b00010011,0b10010011,0b00100111,0b10100011,0b00110011,0b10110011,0b01000011,0b11000011};
unsigned char segmils[10] = {0b00000100,0b10000100,0b00010100,0b10011100,0b00100100,0b10100100,0b00110100,0b10110100,0b01000100,0b11000100};
unsigned char degmils[10] = {0b00000101,0b10000101,0b00010101,0b10011101,0b00100101,0b10100101,0b00110101,0b10110101,0b01000101,0b11000101};
void main(){
//Inhabilitamos funciones del micro que no necesitamos
setup_spi(SPI_SS_DISABLED);
setup_comparator(NC_NC_NC_NC);
setup_vref(FALSE);
//Leemos los datos guardados en memoria para comenzar.
km0 = read_eeprom (0x01);
km1 = read_eeprom (0x02);
km2 = read_eeprom (0x03);
km3 = read_eeprom (0x04);
km4 = read_eeprom (0x05);
km5 = read_eeprom (0x06);
od0 = read_eeprom (0x07);
od1 = read_eeprom (0x08);
od2 = read_eeprom (0x09);
od3 = read_eeprom (0x10);
//Configuramos los puertos analógicos
setup_adc_ports(AN0_TO_AN7);
setup_adc(ADC_CLOCK_DIV_64);
//Inicio de los displays LCD
lcd_init1();
lcd_init2();
//Iniciamos los puertos de salida en 0
output_c(0x00);
output_d(0x00);
shift = stick;
for(k=0;k<8;k++){
output_low(PIN_C4);
output_bit(PIN_C5,pos&bitMask[k]);
output_high(PIN_C4);
}
//Iniciamos el sistema
output_high(PIN_C2); //LCD Backlight.
//Presentación
lcd_gotoxy1(1,1);
printf(lcd_putc1 " RatNET ");
lcd_gotoxy1(1,2);
printf(lcd_putc1 " ElectroGears ");
delay_ms(3000);
lcd_putc1("\f");
for(;;){
//Mostramos los datos cargados del odómetro.
lcd_gotoxy2(1,1);
printf(lcd_putc2 "%d%d%d%d%d%dKm",km5,km4,km3,km2,km1,km0);
lcd_gotoxy2(1,2);
printf(lcd_putc2 "Tp %d%d%d.%d",od3,od2,od1,od0);
//Reinicia contador (Reset).
if(input(PIN_C1)){
od0 = 0; od1 = 0; od2 = 0; od3 = 0;
}
//Reinicio general de los contadores (Hard reset).
else if(input(PIN_C0) && input(PIN_C1)){
if((timer % 10) == 0){
km0 = 0; km1 = 0; km2 = 0; km3 = 0; km4 = 0; km5 = 0; od0 = 0; od1 = 0; od2 = 0; od3 = 0;
lcd_putc2("\f");
}
}
//La variable z funciona como anti-rebotes.
if(input(PIN_A4) && z==0){
div=div+1;
z=1;
}
else if(!input(PIN_A4) && z==1){
z=0;
}
//Este condicional, permite calibrar la relación de recorrido enviado por el sensor de la caja
if(div > read_eeprom (0xA0)){
//Terminada la división, aumentamos el valor del odómetro
od0=od0+1;
//Terminado el conteo, se guardan los datos en la memoria interna del micro.
write_eeprom (0x01, km0);
write_eeprom (0x02, km1);
write_eeprom (0x03, km2);
write_eeprom (0x04, km3);
write_eeprom (0x05, km4);
write_eeprom (0x06, km5);
write_eeprom (0x07, od0);
write_eeprom (0x08, od1);
write_eeprom (0x09, od2);
write_eeprom (0x10, od3);
div=0;
}
//Manejamos cada cifra por separado para no sobrecargar el PIC, además mejora la presentación.
if(od0>9){od0=0; od1=od1+1;}
if(od1>9){od1=0; od2=od2+1;}
if(od2>9){od2=0; od3=od3+1;}
if(od3>9){od3=0; km0=km0+1;}
if(km0>9){km0=0; km1=km1+1;}
if(km1>9){km1=0; km2=km2+1;}
if(km2>9){km2=0; km3=km3+1;}
if(km3>9){km3=0; km4=km4+1;}
if(km4>9){km4=0; km5=km5+1;}
if(km5>9){km5=0;}
//Los valores de los timer se actualizan cada 100mS
if((timer % 100) == 0){
//Lectura de ADCs
set_adc_channel(0);
delay_ms(1);
temper = (1.97 * read_adc());
set_adc_channel(1);
delay_ms(1);
volt = read_adc();
set_adc_channel(2);
delay_ms(1);
oil = (1.76 * read_adc());
set_adc_channel(3);
delay_ms(1);
shift = read_adc();
set_adc_channel(4);
delay_ms(1);
speed = read_adc();
set_adc_channel(5);
delay_ms(1);
rpm = (3.1416 * read_adc());
set_adc_channel(6);
delay_ms(1);
fuel = (1.67 * read_adc());
set_adc_channel(7);
delay_ms(1);
alt = read_adc();
}
//Detección de fallas, si se detecta algún problema, se guarda en una posición del Array.
if(temper > 100){fail[0]=1;}else{fail[0]=0;}
if(oil < 11){fail[1]=1;}else{fail[1]=0;}
if(fuel < 16){fail[2]=1;}else{fail[2]=0;}
if(volt > 200){fail[3]=1;}else{fail[3]=0;}
if(volt < 100){fail[4]=1;}else{fail[4]=0;}
if(alt > 100 || alt < 10){fail[5]=1;}else{fail[5]=0;}
//Menú interno de calibración del odómetro
if(input(PIN_D3)){
/*El pin D3 dehabilita la lectura de los ADC según el timer
por eso llamamos al ADC 3 sólo para esta función */
set_adc_channel(3);
delay_ms(1);
shift = read_adc();
//Cargamos último valor guardado en memoria
cal = read_eeprom (0xA0);
//Aumenta el número divisor y guarda en memoria
if(shift > 86){
if(cal < 1000){
cal = cal + 1;
delay_ms(500);
write_eeprom (0xA0, cal);
}
}
//Disminuye el número divisor y guarda en memoria
else if(shift > 29 && shift < 32){
if(cal > 1){
cal = cal - 1;
delay_ms(500);
write_eeprom (0xA0, cal);
}
}
//Muestra la división en pantalla
lcd_gotoxy1(1,1);
printf(lcd_putc1 " Calibration ");
lcd_gotoxy1(1,2);
printf(lcd_putc1 "Div: %lu In: %lu ",cal,div);
}
//Detecta si el motor está encendido
else if(rpm < 50){
lcd_gotoxy1(1,1);
printf(lcd_putc1 " ");
lcd_gotoxy1(1,2);
printf(lcd_putc1 " Stop Engine ");
valor = 64; //Cinturones de seguridad.
for(j=0;j<8;j++){
output_low(PIN_C7);
output_bit(PIN_C6,valor&bitMask[j]);
output_high(PIN_C7);
}
vacio=0;
}
//Activa el directorio de fallas en caso de haber alguna
else if((fail[0] == 1 || fail[1] == 1 || fail[2] == 1 || fail[3] == 1 || fail[4] == 1 || fail[5] == 1)){
switch(a){
case 0:
if(fail[0]==1){
lcd_gotoxy1(1,1);
printf(lcd_putc1 " %d%CC ",temper,0xDF);
lcd_gotoxy1(1,2);
printf(lcd_putc1 "HIGH TEMPERATURE");
value[3] = 8;
}
break;
case 1:
if(fail[1]==1){
lcd_gotoxy1(1,1);
printf(lcd_putc1 " %lu PSI ",oil);
lcd_gotoxy1(1,2);
printf(lcd_putc1 "LOW OIL PRESSURE");
value[0] = 1;
value[3] = 8;
}
else{value[0] = 0;}
break;
case 2:
if(fail[2]==1){
lcd_gotoxy1(1,1);
printf(lcd_putc1 " %d%C ",fuel,0x25);
lcd_gotoxy1(1,2);
printf(lcd_putc1 " LOW LEVEL FUEL ");
value[1] = 2;
}
else{value[1] = 0;}
break;
case 3:
if(fail[3]==1){
lcd_gotoxy1(1,1);
printf(lcd_putc1 " %lu%lu.%luV ",(volt/100),((volt % 100) / 10),(volt % 10));
lcd_gotoxy1(1,2);
printf(lcd_putc1 " OVER TENSION ");
}
break;
case 4:
if(fail[4]==1){
lcd_gotoxy1(1,1);
printf(lcd_putc1 " %lu%lu.%luV ",(volt/100),((volt % 100) / 10),(volt % 10));
lcd_gotoxy1(1,2);
printf(lcd_putc1 " OVER LOAD ");
}
break;
case 5:
if(fail[5]==1){
lcd_gotoxy1(1,1);
printf(lcd_putc1 " %d%d.%dA ",(alt/100),((alt % 100) / 10),(alt % 10));
lcd_gotoxy1(1,2);
printf(lcd_putc1 " Alt Failure ");
value[2] = 4;
value[3] = 8;
}
else{value[2] = 0;}
break;
}
//Envía información al registro de desplazamiento para los indicadores externos
valor = value[0]+value[1]+value[2]+value[3];
//El condicional evita el sobre envío de datos al registro de desplazamiento
if(valor != vacio){
for(j=0;j<8;j++){
output_low(PIN_C7);
output_bit(PIN_C6,valor&bitMask[j]);
output_high(PIN_C7);
}
}
//Recorre el directorio de fallas
if((timer % 200)==0){if(a>6){a=0;}else{a=a+1;}}
//Hace parpadear el backlight de la LCD para captar la atención del conductor
if((timer % 100)==0){output_toggle(PIN_C2);}
//Se igualan las variables para que el registro deje de actualizarce innecesariamente
vacio = valor;
}
//Activa el modo de indicador de posición de la palanca de cambios
else if(shift != stick){
lcd_gotoxy1(1,1);
if(shift >= 10 && shift <= 35){
printf(lcd_putc1 " Reverse ");
pos = 0b00110010;
}
else if(shift >= 36 && shift <= 61){
printf(lcd_putc1 " Neutral ");
pos = 0b00100010;
}
else if(shift >= 62 && shift <= 87){
printf(lcd_putc1 " Drive ");
pos = 0b00000101;
}
else if(shift >= 88 && shift <= 113){
printf(lcd_putc1 " Lower 2 ");
pos = 0b00010001;
}
else if(shift > 114 && shift <= 139){
printf(lcd_putc1 " Lower 1 ");
pos = 0b01100111;
}
else{
printf(lcd_putc1 " Parking ");
pos = 0b00110000;
}
lcd_gotoxy1(1,2);
printf(lcd_putc1 " ");
//Envía información al registro de desplazamiento para el display de posición de la palanca de cambios
if(pos != posb){
for(k=0;k<8;k++){
output_low(PIN_C4);
output_bit(PIN_C5,pos&bitMask[k]);
output_high(PIN_C4);
}
}
//Compara las variables de cambio de velocidad hasta igualarlas, solo para que aparezcan unos segundos en el display
posb = pos;
if((timer % 256) == 0){stick = shift;}
}
//Modo de descanso (Funcionamiento normal del automovil)
else{
if(timer < 1024){
lcd_gotoxy1(1,1);
printf(lcd_putc1 "%d%CC %lu%lu.%luV %lu ",temper,0xDF,(volt/100),((volt % 100) / 10),(volt % 10),oil);
lcd_gotoxy1(1,2);
printf(lcd_putc1 "Temp Volts Oil");
}
else{
lcd_gotoxy1(1,1);
printf(lcd_putc1 "%lu0 %d%d.%d %d%C ",rpm,(alt/100),((alt % 100) / 10),(alt % 10),fuel,0x25);
lcd_gotoxy1(1,2);
printf(lcd_putc1 "RPM Amps Fuel");
}
output_high(PIN_C7);
//Vacía la variable de errores.
value[0] = 0;
value[1] = 0;
value[2] = 0;
value[3] = 0;
valor = 0;
for(j=0;j<8;j++){
output_low(PIN_C7);
output_bit(PIN_C6,valor&bitMask[j]);
output_high(PIN_C7);
}
}
if(!input(PIN_D3)){
//Velocímetro-Tacómetro / Multiplexación de displays LED
dis = dis+1;
//Usando un switch para asignar cada display, controlamos mejor la multiplexación y evitamos sobre cargar el micro
switch(dis){
case 1:
if(speed > 99){
cent = speed / 100;
output_d(segcent[cent]);
}
break;
case 2:
if(speed > 9){
decs = (speed % 100) / 10;
output_d(segdecs[decs]);
}
break;
case 3:
unit = speed % 10;
output_d(segunit[unit]);
break;
case 4:
mils = rpm / 100;
output_d(segmils[mils]);
break;
case 5:
degs = (rpm % 100) / 10;
output_d(degmils[degs]);
break;
}
if(dis>4){dis=0;}
/* Contador interno para sincronizar el código, así evitamos el uso de delays
que alteren el correcto funcionamiento de los displays multiplexados */
if(timer>2048){timer=0;}else{timer=timer+1;}
}
else{
//Mientras esté activo el menú de calibración, bloqueamos los puertos de salida
timer = 0;
output_d(0x00);
}
}
}