# Ejecución de varias rutinas a la vez en un PIC



## mecatronix89 (Ene 10, 2012)

Hola. ¿Qué tal? No encontré algo así en el foro y por ello abrí el tema.

El problema que tengo es que estoy programando un PIC16F887 a 20MHz con CCS y necesito que una rutina se ejecute sin importar que otra rutina se ejecute en el PIC.
O sea, como si estuviera ejecutándose en un segundo plano.

Lo que tengo es lo siguiente, el PIC controla un LCD el cual despliega un menú que falta terminar pero el punto principal es que me muestra la temperatura en tiempo real utilizando un LM35.
Luego tengo otra rutina que de acuerdo a un LDR me enciende 1 ó 2 relevadores según esté más oscuro para las luces del auto.

Pero pues o me muestra cosas en el LCD o me controla los relevadores, y quiero que todo funcione al mismo tiempo.
Aparentemente claro, entendiendo que el PIC realiza un proceso a la vez a alta velocidad. 


```
#include<16F887.h>
#device ADC=10
#fuses HS,NOWDT,NOLVP,MCLR,NOPROTECT
#use delay(clock=20000000)
#define use_portb_lcd TRUE
#include<LCD.C>
#define M PIN_D7 // 1 MENU
#define T PIN_D6 // 2 TEMPERATURA
//--------------------------------------------------------------------
int x;
long int ADC;
float temp,aux;
//--------------------------------------------------------------------
void config(void){
    set_tris_d(0xF0);
    set_tris_a(0x0F);
    setup_adc(ADC_CLOCK_INTERNAL|VSS_VDD);
    setup_adc_ports(sAN0);
            set_adc_channel(0);
    setup_comparator(NC_NC_NC_NC);
}
void config2(void){
    set_tris_d(0xF0);
    set_tris_a(0x0F);
    setup_adc(ADC_CLOCK_INTERNAL|VSS_VDD);
    setup_adc_ports(sAN1);
            set_adc_channel(1);
    setup_comparator(NC_NC_NC_NC);
}
void main(void){

    config();
    lcd_init();
            delay_ms(1000);
            lcd_gotoxy(1,1);
            lcd_putc("   BIENVENIDO");
                delay_ms(1000);
        for (x=0;x<=2;++x){
            lcd_gotoxy(1,2);
            lcd_putc("CARGANDO SISTEMA");
                delay_ms(400);
            lcd_gotoxy(1,2);
            lcd_putc("----------------");
                delay_ms(400);
        }goto MENU;
//-------------------------- --RELEVADORES-- ----------------------------//
INICIO:
config2();
    if(!input(PIN_C6)){
        delay_us(20);
LUCES:
        do{
            ADC=read_adc();
                delay_ms(800);
            if(ADC<=600){
                output_d(0b0000001);
            }else{
                    output_d(0x00);
                }
            if(ADC<=300){
                output_d(0b00000011);
            }else{
                    output_d(0x01);
                }
            if(ADC>600){
                output_d(0x00);
            }
                if(input(PIN_C6)){
                    output_d(0x00);
                    goto INICIO;
                }else{
                    goto LUCES;
                }

        }while(TRUE);
    }else{goto INICIO;}
//-----------------------------LCD MENU-------------------------------//
MENU:
        do{
            lcd_gotoxy(1,1);
            lcd_putc("      MENU      ");
            lcd_gotoxy(1,2);
            lcd_putc("   TEMP   C/C   ");
    if (input(T)){
        goto TEMPERATURA;
    }
        }while(TRUE);


TEMPERATURA:
        lcd_gotoxy(1,1);
        lcd_putc("    TEMP EXT");
            delay_ms(300);
        lcd_gotoxy(1,2);
        printf(lcd_putc,"INICIANDO SENSOR");
        do{
    if (input(M)){
        goto MENU;
    }
            delay_ms(1000);
            temp=read_adc();
            temp=(temp*300)/614;
            if(temp!=aux){
                lcd_gotoxy(1,2);
                printf(lcd_putc," < < < %0.0f'C > > >",temp);
                    delay_ms(400);
                lcd_gotoxy(1,2);
                printf(lcd_putc,"  - -  %0.0f'C  - -  ",temp);
                aux=temp;
            }
    if (input(M)){
        goto MENU;
    }
        }while(TRUE);
}
```
Ahí el código con las configuraciones de los ADC separada para que no se confunda el LCD ni el código de el LDR

Espero me puedan ayudar. Gracias.


----------



## reyvilla (Ene 10, 2012)

Hola un microcontrolador lamentablemente tiene como desventaja que ejecuta una tarea a la vez, a diferencia de un microprocesador que puede ejecutar varias tareas al mismo tiempo, lo que puedes hacer es utilizar interrupciones y de igual manera va a dejar lo que esta haciendo por atender la interrupción, pero no seria notable si trabajas en useg o mlseg...saludoss


----------



## mecatronix89 (Ene 10, 2012)

reyvilla dijo:


> Hola un microcontrolador lamentablemente tiene como desventaja que ejecuta una tarea a la vez, a diferencia de un microprocesador que puede ejecutar varias tareas al mismo tiempo, lo que puedes hacer es utilizar interrupciones y de igual manera va a dejar lo que esta haciendo por atender la interrupción, pero no seria notable si trabajas en useg o mlseg...saludoss



Que caray... buscaré eso de las interrupciones porque se muy poco jeje... Gracias


----------



## jaimepsantos (Ene 10, 2012)

Lo unico que puede hacer es muestrear la temperatura ya sea con un timer o metiendo dentro de un ciclo infinito que haga el muestreo, te cheque asi tambien la LDR, y te muestre en el LCD lo que desees, asi parecera que lo hace al mismo tiempo, por que es inperceptible para el usuario, ademas que no sera critico por que los cambios de temperatura y los cambios de luz no necesitan una respuesta ultrarapida(rango nS), bueno saludos!


----------



## mecatronix89 (Ene 10, 2012)

jaimepsantos dijo:


> Lo unico que puede hacer es muestrear la temperatura ya sea con un timer o metiendo dentro de un ciclo infinito que haga el muestreo, te cheque asi tambien la LDR, y te muestre en el LCD lo que desees, asi parecera que lo hace al mismo tiempo, por que es inperceptible para el usuario, ademas que no sera critico por que los cambios de temperatura y los cambios de luz no necesitan una respuesta ultrarapida(rango nS), bueno saludos!



También es buena idea... llamando la configuracion del adc antes de cada muestreo de cada sensor... creo que trataré esto antas de las interrupciones, me parece más sencillo jeje


----------



## Scooter (Ene 11, 2012)

Lo que hay que hacer siempre, es procurar usar interrupciones y nunca usar bucles vacíos como temporizadores, aprovechar ese tiempo haciendo algo.


----------



## Sebastian1989 (Ene 11, 2012)

Estoy de acuerdo con los de arriba, utiliza una interrupción para realizar la segunda rutina cada unos pocos milisegundos, la otra opción es utilizar un microcontrolador propeller que es capaz de realizar 8 rutinas de forma simultanea, lógicamente para tu proyecto te recomiendo seguir con los PIC implementando la interrupción.


----------



## aguevara (Ene 11, 2012)

usa un pic de gama mas alta y usa RTOS de CCS


----------



## karlitos3000 (Ene 11, 2012)

Hola... el problema q tienes se me presento hace tiempo, sucedía q para visualizar datos por lcd requieres darle un tiempo (delay); y mientras hacía esto perdía informacion en la recoleccion. Lo único q me aconsejaron era el de estructurar mejor el programa, utilizar las interrupciones de forma más adecuada o por último migrar al pic 18, ya que como dicen los pic gama media no pueden hacer dos cosas al mismo tiempo; el RTOS no es mala idea pero se requiere conocer bien esta herramienta... 
SALUDOS


----------



## fernandob (Ene 11, 2012)

no comprendo, porla velocidad que usa el ck y las aplicaciones que va a hacer no veo problema.

me parece que no comprendes bien lso recursos o mas bien como funciona el pic.
yo nunca me meti a usar un LCD , mencionas que hay tiempso de espera.
pues que si haces el programa bien no le veo problemas.

siempre que hay tiempso de espera como por ejemplo cuando usaba un conversor A/D hay flags, y hoy dia debe incluso haber pedidos de interrupcion.

y mencionas la activacion de reles.

no estas hablando de cosas que realmente requieran velocidad.

hay algo en la estructura del programa que falllas.





Sebastian1989 dijo:


> Estoy de acuerdo con los de arriba, utiliza una interrupción para realizar la segunda rutina cada unos pocos milisegundos, la otra opción es utilizar un microcontrolador propeller que es capaz de realizar 8 rutinas de forma simultanea, lógicamente para tu proyecto te recomiendo seguir con los PIC implementando la interrupción.


 
solo de curioso  me puse a buscar que es eso ??

que increible la mercaderia que hay disponible hoy dia en el mercado.
yo creo que hoy dia un Ingeniero apasionado por este rubro tiene para entretenerse , y no le alcanzarian las 24 hs de el dia con la tecnologia que hay disponible.


----------



## mnicolau (Ene 11, 2012)

Hola, a todo lo que comentaron, te agregaría evitar el uso de los "goto" (tal vez vengas de ASM y por eso los utilizás, a mi también me pasaba al principio). En C podrías utilizar funciones para hacer el direccionamiento de manera adecuada y más eficiente.

Estructurando mejor el programa y utilizando funciones, podés lograr sin problemas lo que estás planteando, sin ninguna necesidad del RTOS, incluso sin utilizar interrupciones en tu caso... recordá que si bien el código se ejecuta en forma secuencial, sin poder hacer rutinas simultáneas, la ejecución del código es tan rápido que podés tomarlas como prácticamente simultáneas.

Saludos


----------



## mecatronix89 (Ene 11, 2012)

mnicolau dijo:


> Hola, a todo lo que comentaron, te agregaría evitar el uso de los "goto" (tal vez vengas de ASM y por eso los utilizás, a mi también me pasaba al principio). En C podrías utilizar funciones para hacer el direccionamiento de manera adecuada y más eficiente.
> 
> Estructurando mejor el programa y utilizando funciones, podés lograr sin problemas lo que estás planteando, sin ninguna necesidad del RTOS, incluso sin utilizar interrupciones en tu caso... recordá que si bien el código se ejecuta en forma secuencial, sin poder hacer rutinas simultáneas, la ejecución del código es tan rápido que podés tomarlas como prácticamente simultáneas.
> 
> Saludos



Hola gracias por responder, ciertamente jamás programé en ASM jeje entré con Basicprogramando unos PICAXE y creo se me quedó de ahí jeje... y que diferencia tienen las funciones al "GOTO"?

sería algo como esto?

luces() {
  instruccion;
  instruccion;
  .
  .
  instruccion; }

para crear el bloque de la función? y luego llamarla como: "  luces();  "


----------



## mnicolau (Ene 11, 2012)

Ahh yo desconozco Basic, comencé en ASM y es muy común el uso de los goto, así que cuando migré a C costó al principio reemplazarlos.

Las funciones serían tal cual lo comentaste... y si mal no recuerdo, en el libro "Compilador C Ccs Y Simulador Proteus Para Microcontroladores Pic" hay un ejemplo similar al que estás realizando (LCD + control de temperatura), sería cuestión de agregar en un par de líneas la lógica de los relevadores.

De paso te recomiendo ese libro... muy bueno para aprender el tema.

Saludos


----------



## biker2k3 (Ene 11, 2012)

Lamentablemente no programo en CSS pero el problema para mi es que esta mal el programa, el LCD una ves ke le mandas la informacion, la mantiene solo no hace falta estar refrescandolo todo el tiempo. Igualmente yo use un LCD para recibir una señal de control remoto y mostrarla y me anda sin problemas, tambien hice un velocimetro con un pic16f819 y mide, voltaje de bateria, temperatura ambiente, velocidad, kilometros recorridos y todo eso mientras muestra la informacion en 3 displays 7segmentos multiplexados, asique tu problema esta en el programa, tenes recursos de sobra. No puedo ayudarte porque programo en proton.


----------



## mecatronix89 (Ene 11, 2012)

biker2k3 dijo:


> Lamentablemente no programo en CSS pero el problema para mi es que esta mal el programa, el LCD una ves ke le mandas la informacion, la mantiene solo no hace falta estar refrescandolo todo el tiempo. Igualmente yo use un LCD para recibir una señal de control remoto y mostrarla y me anda sin problemas, tambien hice un velocimetro con un pic16f819 y mide, voltaje de bateria, temperatura ambiente, velocidad, kilometros recorridos y todo eso mientras muestra la informacion en 3 displays 7segmentos multiplexados, asique tu problema esta en el programa, tenes recursos de sobra. No puedo ayudarte porque programo en proton.



Gracias amigo... efectivamente está en el programa, cuestión de afinar detalles y algunos cambios... pero ya con algunas ideas iré reestructurandolo

SALUDOS


----------



## biker2k3 (Ene 11, 2012)

Creo que tenes muchos delays, igual explicanos un poco mejor que hace el programa, porque el lcd no puede apagarse solo, a no ser que le mandes la instruccion de borrado. Que es lo que hace, tarda en refrescar la informacion o se cuelga o se apaga el lcd o que hace?


----------



## mecatronix89 (Ene 12, 2012)

HOla otra vez, reestructuré un poco el código... construí bloques de funciones. pero al compilarlo me dice que debo de definir cada funcion, puse #define pero me salen muchos otros errores... espero puedan ayudarme en esto...

#include<16F887.h>
#device ADC=10
#fuses HS,NOWDT,NOLVP,MCLR,NOPROTECT
#use delay(clock=20000000)
#define use_portb_lcd TRUE
#include<LCD.C>
#define M PIN_D7 // 1 MENU
#define T PIN_D6 // 2 TEMPERATURA
//--------------------------------------------------------------------
int x;
long int ADC;
float temp,aux;
//-------------------BLOQUES DE FUNCIONES-----------------------------

//CONFIGURACIONES

                        //ADC LDR//
void adcldr(void){
	set_tris_d(0xF0);
	set_tris_a(0x0F);
	setup_adc(ADC_CLOCK_INTERNAL|VSS_VDD);
	setup_adc_ports(sAN1);
			set_adc_channel(1);
	setup_comparator(NC_NC_NC_NC);
}

                       //ADC SENSOR//
void adcsen(void){
	set_tris_d(0xF0);
	set_tris_a(0x0F);
	setup_adc(ADC_CLOCK_INTERNAL|VSS_VDD);
	setup_adc_ports(sAN0);
			set_adc_channel(0);
	setup_comparator(NC_NC_NC_NC);
}

//FUNCIONES DE EJECUCIÓN

					//FUNCION CONTROL//
void CONTROL(void){
	do{
		ldr();
			delay_ms(20);
		sensor();
			delay_ms(20;
	}while(TRUE);
}

                       //FUNCION MAIN//
void main(void){
		CONTROL();
}

					   //FUNCION LDR//
void ldr(void){
	adcldr();
void INICIO(void){
	if(!input(PIN_C6)){
		delay_us(20);
void LUCES(void){
		do{
			ADC=read_adc();
				delay_ms(800);
			if(ADC<=600){
				output_d(0b0000001);
			}else{
					output_d(0x00);
				}
			if(ADC<=300){
				output_d(0b00000011);
			}else{
					output_d(0x01);
				}
			if(ADC>600){
				output_d(0x00);
			}
				if(input(PIN_C6)){
					output_d(0x00);
					INICIO();
				}else{
					LUCES();
				}

		}while(TRUE);
}
	}else{INICIO();}
}
}

					 //FUNCION SENSOR//
void sensor(void){
	adcsen();
	lcd_init();
			delay_ms(1000);
			lcd_gotoxy(1,1);
			lcd_putc("   BIENVENIDO");
				delay_ms(1000);
		for (x=0;x<=2;++x){
			lcd_gotoxy(1,2);
			lcd_putc("CARGANDO SISTEMA");
				delay_ms(400);
			lcd_gotoxy(1,2);
			lcd_putc("----------------");
				delay_ms(400);
		}MENU();

//-----------------------------LCD MENU-------------------------------//
void MENU(void){
		do{
			lcd_gotoxy(1,1);
			lcd_putc("      MENU      ");
			lcd_gotoxy(1,2);
			lcd_putc("      TEMP      ");
	if (input(T)){
		TEMPERATURA();
	}
		}while(TRUE);

}
void TEMPERATURA(void){
		lcd_gotoxy(1,1);
		lcd_putc("    TEMP EXT");
			delay_ms(300);
		lcd_gotoxy(1,2);
		printf(lcd_putc,"INICIANDO SENSOR");
		do{
	if (input(M)){
			MENU();
	}
			delay_ms(1000);
			temp=read_adc();
			temp=(temp*300)/614;
			if(temp!=aux){
				lcd_gotoxy(1,2);
				printf(lcd_putc," < < < %0.0f'C > > >",temp);
					delay_ms(400);
				lcd_gotoxy(1,2);
				printf(lcd_putc,"  - -  %0.0f'C  - -  ",temp);
				aux=temp;
			}
	if (input(M)){
			MENU();
	}
		}while(TRUE);
}

}





biker2k3 dijo:


> Creo que tenes muchos delays, igual explicanos un poco mejor que hace el programa, porque el lcd no puede apagarse solo, a no ser que le mandes la instruccion de borrado. Que es lo que hace, tarda en refrescar la informacion o se cuelga o se apaga el lcd o que hace?



hola que tal, no se apaga el LCD, hay 2 fuentes separadas una para pic sensores y lcd y otra para los relevadores, ya que solo tengo de 5vcd y para que no interfieran los relevadores con el control.

el problema es que me muestra la temperatura pero deja de ejecutar la rutina de los relevadores, pero es necesario ejecutarla ya que es el encendido automatico de las luces en caso de que se oscurezca el entorno.


----------



## mecatronix89 (Ene 12, 2012)

según acabo de leer, para llamar una función, debo hacer un archivo aparte con extensión ".h" y ahí guardar los bloques de funciones; luego #include nombredelarchivo.h y despues solo llamar alguna de las funciones guardadas.

Es correcto?


----------



## arrivaellobo (Ene 12, 2012)

No es necesario, pero es recomendable separar el proyecto en varios archivos para organizarlo mejor.
Una cosa, para llamar a una función desde otra, ya sea el _main_ o cualquier otra, tiene que estar definida *antes* de la función desde donde la llamas.
Un ejemplo, si tienes dos funciones, por ejemplo RecojeDatos() y EscribeDatos(), y quieres llamarlas dentro del _main_, tienes que declararlas antes de éste, no se si me entiendes...

Un abrazo


----------



## mecatronix89 (Ene 12, 2012)

arrivaellobo dijo:


> No es necesario, pero es recomendable separar el proyecto en varios archivos para organizarlo mejor.
> Una cosa, para llamar a una función desde otra, ya sea el _main_ o cualquier otra, tiene que estar definida *antes* de la función desde donde la llamas.
> Un ejemplo, si tienes dos funciones, por ejemplo RecojeDatos() y EscribeDatos(), y quieres llamarlas dentro del _main_, tienes que declararlas antes de éste, no se si me entiendes...
> 
> Un abrazo



si ya logré entender como se hace, lo que pasa es que nunca había trabajado con tantos bloques de funciones jeje... pero con ese archivo ".h" con los prototipos de las funciones, ya se compila el código, solo falta reacomodar todo... 

GRACIAS


----------



## arrivaellobo (Ene 12, 2012)

Exacto.
Yo en mis programas separo un archivo .h para las configuraciones, otro para las funciones, otro con el método _main_, etc.


----------



## mecatronix89 (Ene 12, 2012)

Aquí el código, solo que no se como saltar del main a los otros bloques, porque si llamo la función de CONTROL que es la que me va estar monitoreando los dos ADC me dice que 

*** Error 69 "E:\Users\**\Documents\PROYECTOS MPLAB\FOLDER*\NOMBRE*.C" Line 120(1,2): Recursion not permitted  [CONTROL]


```
#include<16F887.h>
#device ADC=10
#fuses HS,NOWDT,NOLVP,MCLR,NOPROTECT
#use delay(clock=20000000)
#define use_portb_lcd TRUE
#include<LCD.C>
#include<FUNCIONES_*****.h>
//--------------------------------------------------------------------
long int ADC;
float temp,aux;
//-------------------BLOQUES DE FUNCIONES-----------------------------

//CONFIGURACIONES

                        //ADC LDR//
void adcldr(void){
    set_tris_d(0xF0);
    set_tris_a(0x0F);
    setup_adc(ADC_CLOCK_INTERNAL|VSS_VDD);
    setup_adc_ports(sAN1);
            set_adc_channel(1);
    setup_comparator(NC_NC_NC_NC);
}

                       //ADC SENSOR//
void adcsen(void){
    set_tris_d(0xF0);
    set_tris_a(0x0F);
    setup_adc(ADC_CLOCK_INTERNAL|VSS_VDD);
    setup_adc_ports(sAN0);
            set_adc_channel(0);
    setup_comparator(NC_NC_NC_NC);
}

//FUNCIONES DE EJECUCIÓN

                       //FUNCION MAIN//
void main(void){

}
                    //FUNCION CONTROL//
void CONTROL(void){

    do{
        sensor();
            delay_ms(20);
        ldr();
            delay_ms(20);
    }while(TRUE);
}

                       //FUNCION LDR//
void ldr(void){
        adcldr();
        LUCES();
}
void LUCES(void){
        if(!input(PIN_C6)){
    delay_us(20);
        do{
            ADC=read_adc();
                delay_ms(800);
            if(ADC<=600){
                output_d(0b0000001);
            }else{
                    output_d(0x00);
                }
            if(ADC<=300){
                output_d(0b00000011);
            }else{
                    output_d(0x01);
                }
            if(ADC>600){
                output_d(0x00);
            }
                if(input(PIN_C6)){
                    output_d(0x00);
                    CONTROL();
                }
        CONTROL();

        }while(TRUE);
        }else{CONTROL();}
}                                    //>>>>>>>>>>>>>>>>>>>>>>OK

                     //FUNCION SENSOR//
void sensor(void){
    lcd_init();
            delay_ms(1000);
            lcd_gotoxy(1,1);
            lcd_putc("TEMP. & LIGHTING");
            lcd_gotoxy(1,2);
            lcd_putc(" SYSTEM  <1.0v> ");
                delay_ms(800);
            lcd_gotoxy(1,1);
            lcd_putc("    STARTING    ");
            lcd_gotoxy(1,2);
            lcd_putc("====>+~][~+<====");
                delay_ms(400);
    adcsen();
        lcd_gotoxy(1,1);
        lcd_putc("    TEMP EXT");
            delay_ms(20);
        lcd_gotoxy(1,2);
        printf(lcd_putc,"INICIANDO SENSOR");
            delay_ms(100);
            temp=read_adc();
            temp=(temp*300)/614;
            if(temp!=aux){
                lcd_gotoxy(1,2);
                printf(lcd_putc," < < < %0.0f'C > > >",temp);
                    delay_ms(100);
                lcd_gotoxy(1,2);
                printf(lcd_putc,"  - -  %0.0f'C  - -  ",temp);
                aux=temp;
            }
        CONTROL();
}
```


----------



## ingdirson (Mar 19, 2015)

Hola. ¿Qué tal? Estoy trabajando con tiempo real en CCS usando un PIC18F452.
La idea es hacer una aplicación con unas cuantas tareas que me permitan activar tres tareas  con pulsadores, y luego desactivarlas con un comando enviado desde el hiperterminal de windows o el de isis proteus en simulación.

Hasta ahora he logrado hacerlo pero para que se ejecute una tarea se necesita de una orden desde el hiperterminal y la idea es que la activación sea independiente de la desactivación de las tareas.

Serían de gran ayuda sus aportes.


----------



## Saint_ (Mar 20, 2015)

Hola ingdirson todo depende la las tareas que quieras ejecutar, segun ellas en algun caso se opuede trabajar en rutina principal, en otros casos con las interrupciones y otros sera mejor usar RTOS.
por lo general basta y sobra unsar las interrupciones y en casos mas complejos serán RTOS mas interrupciones.
para tu caso puedo recomendarte que te interiorices en el RTOS de CCS me imagino que con eso sera suficiente.


----------



## Scooter (Mar 20, 2015)

Antes de matar moscas a cañonazos... Mira de usar interrupciones, polling etc.
Lo del "a la vez" es un tanto subjetivo.


----------



## ingdirson (Mar 21, 2015)

Saint_ dijo:


> Hola ingdirson todo depende la las tareas que quieras ejecutar, segun ellas en algun caso se opuede trabajar en rutina principal, en otros casos con las interrupciones y otros sera mejor usar RTOS.
> por lo general basta y sobra unsar las interrupciones y en casos mas complejos serán RTOS mas interrupciones.
> para tu caso puedo recomendarte que te interiorices en el RTOS de CCS me imagino que con eso sera suficiente.



Hola. De hecho estoy trabajando con RTOS y también usé #INT_RDA, que según lo que sé es la activación de las interrupciones RDA y global, pero tal vez por que soy nuevo en este tema no las haya usado bien.

Adjunto mi proyecto. En el archivo están el código en C con PIC C Compiler de CCS, un archivo de ISIS Proteus y unas pequeñas instrucciones de uso.

PD: En éste no estoy haciendo uso de las interrupciones.

Esperaré tu comentario al respecto.


----------



## Saint_ (Mar 23, 2015)

Hola  ingdirson, te comento que no hay una buena conceptualización de las tareas que tienes.
En este fragmento de código hay un problema:


> void leer()
> { int a,b,c;
> dato=getc();   //****** esta instrucción "traba" a la tarea y se queda en bucle infinito****
> lcd_init();
> ...


Adjunto un ejemplo del como podria plantearse algo mejor las tareas.

Los pulsadores activan las tareas individuales y estas tareas se desactivas mediante comandos revividos por el puerto serie: 'r' desactiva la tarea 1, 'v' desactiva la tarea 2, 'a' desactiva la tarea 3 y 'c' desactiva todas las tareas.


```
#include <18f452.h>
#fuses HS,NOWDT
#use delay(clock=20000000)
#use rs232(baud=19200,xmit=PIN_c6,rcv=PIN_c7,bits=8,parity=N)
#use RTOS(timer=0, minor_cycle=10ms)
#include <lcd.c>

#task (rate=10ms,max=1ms)
void puerto_serie();
#task (rate=100ms,max=1ms)
void leer_pulsadores();
/******************************************************************************
Tareas a ejectar
******************************************************************************/
#task (rate=100ms,max=1ms,enabled=false)  //la terea no inicia automatocamente
void tarea1();
#task (rate=250ms,max=1ms,enabled=false)  //la terea no inicia automatocamente
void tarea2();
#task (rate=350ms,max=1ms,enabled=false)  //la terea no inicia automatocamente
void tarea3();
/*****************************************************************************/
void main()
{ 
   lcd_init();
   lcd_putc("T: | 1 | 2 | 3 |\n");
   lcd_putc("   |off|off|off|");
   setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
   rtos_run();
}
/******************************************************************************
Lee el estado del puerto serie
******************************************************************************/
void puerto_serie()
{
   char  dato_rx;
   rtos_await(kbhit());
   dato_rx=getc();
   switch(dato_rx)
   {
      case 'r':  
      {
         rtos_disable(tarea1);
         output_low(pin_b0);
         lcd_gotoxy(5,2);
         lcd_putc("off");
         putc(dato_rx);
         break;
      }
      case 'v':
      {
         rtos_disable(tarea2);
         output_low(pin_b1);
         lcd_gotoxy(9,2);
         lcd_putc("off");
         putc(dato_rx);
         break;
      }
      case 'a':
      {
         rtos_disable(tarea3);
         output_low(pin_b2);
         lcd_gotoxy(13,2);
         lcd_putc("off");
         putc(dato_rx);
         break;
      }
      case 'c':
      {
         rtos_disable(tarea1);
         rtos_disable(tarea2);
         rtos_disable(tarea3);
         output_low(pin_b0);
         output_low(pin_b1);
         output_low(pin_b2);
         lcd_gotoxy(1,2);
         lcd_putc("   |off|off|off|");
         putc(dato_rx);
         break;
      }
   }
}
/******************************************************************************
Lee los pulsadores y segun el caso habilita las tareas
******************************************************************************/
void leer_pulsadores()
{
   if(input(pin_b4))
   {
      rtos_enable(tarea1);
      lcd_gotoxy(5,2);
      lcd_putc("on ");
   }
   if(input(pin_b5))
   {
      rtos_enable(tarea2);
      lcd_gotoxy(9,2);
      lcd_putc("on ");
   }
   if(input(pin_b6))
   {
      rtos_enable(tarea3);
      lcd_gotoxy(13,2);
      lcd_putc("on ");
   }
}
void tarea1()
{
   output_toggle(PIN_b0); 
}
void tarea2()
{
   output_toggle(PIN_b1);
}
void tarea3()
{
   output_toggle(PIN_b2);
}
```


----------



## ingdirson (Mar 23, 2015)

Saint_ dijo:


> ```
> ******************************************************************************/
> #task (rate=100ms,max=1ms,enabled=false)  //la terea no inicia automatocamente
> void tarea1();
> ...


 


Hola saint_. Te cuento que el archivo de isis funciona muy bien, pero al intentar compilar el archivo de ccs me presenta un error en las líneas que cito.
No sé si es mi versión del compilador, la que uso es 4.104.
Adjunto imagen del error.

Por otro lado, me gustaría, si puedes, que me expliques lo que hace este comando:
rtos_await(kbhit()); , la parte del await(kbhit());  

Saludos.
Gracias.


----------



## Saint_ (Mar 24, 2015)

hola ingdirson, el error que se presenta quizá podría ser como tu dices por la version, yo hice el programa en CCS v5.xxx lo mas seguro es que el hecho de declarar una tarea sin que inicie automáticamente sea de otro modo, en el Help de CCS seguro que encuentras las solución.
Respecto de _rtos_await(kbhit());_

la función _kbhit();_ retorna _true_ si existe un dato recibido por el puerto serie, para leer ese dato se usa la función _getc();_, también _getc();_ espera en bucle infinito hasta que haya un dato en el puerto serie. Para evitar que el PIC se quede esperando "toda la vida" hasta que le llegue un dato se usa primero _kbhit();_ para verificar si existe algún dato, si existe se lo lee con _getc();_.
la funcion _await(xxx); _ espera a que _xxx_ sea verdadero, mientras "espera" deja el control de la cpu para que otras tareas puedan ejecutarse "si es que les toca su turno".
_rtos_await(kbhit());_, espera a que este disponible un dato en el buffer del puerto serie, mientras tanto deja que se ejecuten otras tareas.


----------



## ingdirson (Mar 24, 2015)

Hola Saint, gracias por tu ayuda me ha sido muy útil, intentare descargar o conseguir la versión del compilador que usaste  para trabajar con ella. De nuevo  gracias.

Hasta la próxima...
Saludos...


----------



## Berto75 (Ago 30, 2015)

De todo lo que he leído, esto es lo que me interesa:

"Exacto. Yo en mis programas separo un archivo .h para las configuraciones, otro para las funciones, otro con el método main, etc."

Pero no sé hacerlo. Si realizo un código donde sólo hay funciones el compilador no lo admite.
Este programa es el más sencillo que he podido hacer para explicar todo lo que quiero.

Lo que me gustaría es que la función FUERA(); No estuviese fuera del programa principal, otro módulo, otro código escrito aparte y que el programa principal pueda acceder a éste.

Agradecería un ejemplo visual de estos dos códigos escritos aparte y que a la vez puedan comunicarse.


```
#include <16f877.h>
#FUSES NOWDT, XT, PUT, NOPROTECT, BROWNOUT, NOLVP, NOCPD, NOWRT, NODEBUG 
#use delay(clock=4M)
#DEFINE use_portd_lcd TRUE
//#DEFINE use_portb_kbd TRUE  (declarado en kbd.c)
#include <lcd.c>
#include <kbd.c>
#include <stdio.h>
#BYTE PORTC=0x07  
#BYTE TRISC=0XFF 
#BYTE PORTD=0X08 
#BYTE TRISD=0X88 

int  FUERA(int cont); ///DONDE TIENE QUE ESTAR ESTAR DECLARADA?
int glooball; ///ESTA VARIABLE DEBE RECONOCERLA TANTO programa-principal Y TODOS LOS PROGRAMAS A LOS QUE LLAME
   void main(){///programa-principal
int con,recogerdefuera;///
lcd_init();
kbd_init();
port_b_pullups(false);
   while(true){///while true

recogerdefuera=FUERA(15);

for (con=0;10>con;con++){
printf(lcd_putc,"\fse recogio de FUERA(); es %d",recogerdefuera);
 delay_ms(300);
} 
if (glooball<100) { glooball++; } else { glooball=0; }
}///while true
}///programa-principal
  
////QUIERO SABER COMO ESCRIBIR ESTA FUNCION EN OTRO PROGRAMA Y QUE progama-principal PUEDA ////HACER USO DE ELLA LA RECONOZCA
FUERA(int cont) {
int co;
for (co=0;cont>co;co++){
lcd_putc("\fEsto codigo tiene que estar fuera del"); lcd_gotoxy(1,2);
printf(lcd_putc,"programa principal llamado PRUEBA %d",glooball);
 delay_ms(300);
}
co=Input_c ();
return co;
}
////QUIERO SABER COMO ESCRIBIR ESTA FUNCION EN OTRO PROGRAMA Y QUE progama-principal PUEDA ////HACER USO DE ELLA
```



Perdón. "funcion FUERA(); No estuviese fuera del programa-principal"
Quise decir, "funcion FUERA(); Estuviese fuera del programa-principal"


----------



## Scooter (Ago 30, 2015)

Pero esto que preguntas es cuestión del compilador que uses. De que permite y que no permite hacer.
Puedes compilar con un main casi vacío para ves si van y luego quitarlo y linkarlo a otro programa, aunque no se como se hace eso, pero lo he visto hacer.
En ensamblador yo lo hacia pero depende del compilador que se haga de una forma u otra.


----------



## Dr. Zoidberg (Ago 30, 2015)

Tenes que empezar por estudiar el lenguaje C. Luego vas a entender como hacer un desarrollo correcto.


----------



## Berto75 (Ago 30, 2015)

Ya. Pero yo nunca he hecho esto en C. ¿Qué es lo que tengo que estudiar para hacer esto?
Si esto es general en todo C, Genial.
¿Podrías ponerme un ejemplo visual de dos pequeños programas hechos en C que puedan acceder a sus 
funciones y variables respectivamente? 

¿CCS PIC C compiler 5.0?
No me acuerdo exactamente. No me lo dice cuando pincho en propiedades.


----------



## ingdirson (Ago 31, 2015)

Hola, Albererto. Saludos desde Colombia.
Creo entender que es lo que quieres hacer.
Primero, te recomiendo el libro "Compilador CCS y simulador Proteus"
Allí puedes encontrar ejemplos y teoría sobre funciones y características de CCS.

```
#include <18f452.h>
#fuses XT,NOWDT,NOPROTECT,WDT128,NOBROWNOUT  
#use delay(clock=4000000)
#use rs232(baud=9600,xmit=PIN_c6,rcv=PIN_c7,bits=8, parity=N)   // declaracion de uso de protocolo rs232
#include <lcd.c>

#use standard_io(B)

char dato=0;
int val=0;                         // las variables y funciones que se declaran de forma GLOBAL podran ser accedidas desde 
void recibe(char datos);           // cualquier parte del codigo, siempre que esten llamadas por debajo de la declaracion.

#INT_RDA
void  RDA_isr(void) 
{
dato= getc();                // interrupcion por recepcion de datos por rs232
    recibe(dato);
   
}

void main()
{
 lcd_init();
 enable_interrupts(INT_RDA);
 enable_interrupts(GLOBAL);
 lcd_gotoxy(2,1);                               // programa principal
 printf(lcd_putc,"BIENVENIDO ");
 delay_ms(600);
 while(true)
{ 
 if(val==1)
  {
   printf(" esto es lo que quieres?\r\r");    // aqui se verifica la condicion del caso "1"
   val=0;
  }

 }
 
}

 void recibe(datos)
 {
 if (datos!='1' && datos!='a')
    {
    printf(" Dato no valido...\r");                // funcion.
    printf(lcd_putc,"\f");
    printf(lcd_putc,"Dato invalido!");
    
    }
 
 switch(datos)
 {
  case '1':
        lcd_gotoxy(1,1);
        printf(lcd_putc,"\f");
        printf(lcd_putc,"recivido: %c",datos);
        output_high(PIN_B0);
        printf("\rled encendido!!\r");
        val=1;              // Esta es la variable que se prodra llamar desde cualquier lugar del programa.
        break;
 case 'a':
       lcd_gotoxy(1,1);
       printf(lcd_putc,"\f");
       printf(lcd_putc,"recivido: %c",datos);
       output_low(PIN_B0);
       printf("\rled apagado!!\r");
 }
 }
```
Nota: El programa está diseñado para usar el protocolo RS-232, pero en las líneas comentadas puedes ver lo que necesitas.
Para simular sólo montas el circuito en ISIS y conectas el terminal virtual de forma cruzada Rx a Tx y Tx a Rx, cargas el código y simulas.

PD: Espero esta sea la solución a tu duda.

Saludos.


----------



## Berto75 (Ago 31, 2015)

No estoy seguro, ingdirson.
Al parecer lo que he leído del protocolo RS-232, se refiere a una comunicación del PIC con interrupciones.

 Yo también quiero ésto.


```
while(true)  {/// whil
if(val==1)
{   printf(" esto es lo que quieres?\r\r");    val=0;    }      // aqui se verifica la condicion del caso "1"
}  //whil
```
No debería funcionar porque el bucle está encerrado donde sólo pregunta por la variable "vall", nunca la modifica.
Por lo poco que he leído, tú pareces hacerlo de una manera física, mientras que yo quiero cambiar la variable "vall" desde otro código.

Y ahora lo primero que se me ocurre:


```
#INT_RDA
void RDA_isr(void)
{
otroprograma(); // Me serviria esto si "otroprograma()" Tratase la variable vall
}
```


----------



## ingdirson (Ago 31, 2015)

Hola, AlberertO. ¿Cuando dices, "otro código", te refieres a?

Lo del protocolo es sólo porque era lo que tenía a mano.
Lo que quería mostrarte es la parte que has citado.
#INT_RDA, sólo es para interrupciones por datos en el búfer de recepción y transmisión en el protocolo RS-232

Si lo que quieres decir, es que si tuvieses otra función y en ella modificases la variable "VAL", ya sea por un dato externo, es decir, por una entrada en pin o un dato recibido o por software, es decir, a partir de una condición del programa, podrías conocer ese cambio desde el main y/o otra función en el mismo código.

Te digo que si es posible, primero declarando val de forma global, segundo usando banderas, es decir, variables que cambien un estado a partir de una condición.

Éxitos.


----------



## ingdirson (Sep 3, 2015)

hola alberto, te recomiendo limpiar las variables cada que termines un proceso, en ocaciones esto genera que el codigo se sature y sobrecargue la memoria de tu microcontrolador. lo que te digo es reiniciar a 0 cualquier variable que hayas usado, siempre y cuando esta informacion no sea necesaria mas adelante en el proceso.
Exitos..


----------



## Berto75 (Sep 3, 2015)

El problema está desde el inicio.
Me he fijado que hay bastantes variables que sólo necesitaban ser booleanas.
Sustituyendo int por int1, consigo reducir la RAM pero no la ROM como yo esperaba
Ésta, al parecer depende de la cantidad de código escrito.
Dos microcontroladores es lo que me conviene la próxima vez, con sus .hex correspondientes.

El éxito es A.C. Actualmente no pasa a D.C.

Saludos.


----------



## ingdirson (Sep 6, 2015)

Podrías estimar la posibilidad de un PIC con mayor memoria*.*
*T*am*bién *podrías opti*m*izar código evitando desbordes de memoria, revisar los ciclos que no se hagan infinitos, es decir*, *que nunca se están rompiendo.

*P*or otro lado*,* imagino que debes estar implementando algo bien complejo para que requiera de tantas líeneas.
*P*odrías también usar las librerías*,* ya que éstas traen dentro el código necesario para algunos dispositivos.

Saludos...


----------



## Berto75 (Sep 7, 2015)

Sí. Realmente por eso empecé a utilizar el 16F877
De todos los proyectos que traté, vi que era el que tenía mayor número de pines, pero a llegado un momento que no es suficiente.
Parece que la solución es aprender a tratar con varios micros a la vez.
Esto soluciona, tanto el desbordamiento, cómo la limitación de pines.
Es lo que he empezado a hacer.

Ahora tengo este problema:

```
///////////////////////MASTER///////////////////////
........................
set_tris_c(16);    // 0001 0110       // Configuración del puerto C para trabajar el spi
  setup_spi(spi_master | spi_l_to_h | spi_clk_div_16);
............................
lcd_putc("\f"); cu=1;
for (co=0;5>co;co++){
spi_write(fclee[co]); delay_ms(2000);
 lcd_gotoxy(cu,1); lcd_gotoxy(cu,1); printf(lcd_putc,"fl[%d]=%d ",co,fclee[co]);  cu+=8; 
}
   delay_ms(1000);
...................
///////////////////////ESCLAVO/////////////////////////
......................................
set_tris_c(223);///1101 1111 B3_E B4_E B5_S
setup_spi(spi_slave | spi_l_to_h | spi_clk_div_16| SPI_XMIT_L_TO_H); //);// | SPI_XMIT_L_TO_H);
...................................................
if(spi_data_is_in()){

lcd_putc("\f"); cu=1;
for (co=0;5>co;co++){//for

fclee[co]=spi_read(); 
lcd_gotoxy(cu,1); printf(lcd_putc,"fl[%d]=%d ",co,fclee[co]);  cu+=8;
}//for
delay_ms(1000);
}
```
Funciona, pero el problema es que tiene que ser al instante, de hecho hay más arrays y variables que el "slave" debe saber del "master".
Si quito delays no sincronizan cada LCD de su respectivo micro, muestran algo distinto.
No es fiable con delays-cortos (y a mi me convienen que ni siquiera existan)

El spi_i_data_is_in() no es suficiente o no sé utilizarlo.
He pensado utilizar un par de pines de cada micro, tipo -enviar-recibido para sincronizar bien al instante.
Pero antes buscaré y haré lo que menos me gusta, leer sobre éste protocolo.

Quizás pueda ahorrarme algo.


----------



## TRILO-BYTE (Sep 7, 2015)

Yo opino que estás haciendo las cosas mal por no conocer bien el lenguaje C.

Lo que dices de meter delays, es atorar al micro y por obvias razones no va hacer varias cosas al mismo tiempo
Es decir, el delay ejecutará sólo delay el tiempo que esté atorando, digamos, 1 segundo perdido sin hacer absolutamente NADA.

Lo ideal es que no metas delay y en modo maestro debes mandar un byte que ayude al esclavo cuando refresca la pantalla.

También, si dices que se te acaban los pines, hay maneras baratas de ampliar el bus, digamos, con un registro de corrimiento, ya sea paralelo a serie para varias entradas o un serie paralelo para varias salidas.

Esos son:
CD4021 Paralelo a serie.
74HC595 Serie a paralelo.

Obviamente se pueden manejar en cascada y obtener un bus más expandido.


----------



## Scooter (Sep 8, 2015)

Los delays son una plaga. Siempre absolutamente siempre hay algo que hacer. Si el sistema es 'multitarea' más aún.
En su día hice un sistema con dos puertos serie, display y teclado, 26 salidas, 32 entradas todo a la vez con un micro de 1mip y no había problemas, sobraba micro. Eso si, 'delays' creo que habían cuatro o cinco de 10us no mas.
Todo lo demás se gestionaba por interrupciones; los cuatro buffers fifo de los puestos serie, el control de salidas para que fueran sincronizadas, en el bucle principal se leía el teclado tu las entradas constantemente.
El LCD que yo recuerde sólo había que esperar un poco al inicializar, después ya no, y si hay que esperar para algo se aprovecha ese tiempo para adelantar otra cosa.


----------



## TRILO-BYTE (Sep 8, 2015)

Cierto. Un micro corriendo 32 PWM, leer un control infrarrojo, un puerto serie y leer pulsos, todo eso en un PIC16F886.

Digo, se debe tener muy buen conocimiento de programación en C.
Alguien que no sabe programar bien, hace mal uso de los recursos del CPU. En este caso, un micro.


----------



## Berto75 (Sep 9, 2015)

Si, interrupciones, eso es lo que he hecho al final lo que se me ocurrió al principio
A pesar de lo largo que es el código y no parece poder evitar los delay (lo he visto en otros proyectos)
Este código pasa valores de una forma significativa y creo que puede mejorar cuando quite los "rand() print_LCD y demás" porque sólo están para comprobar que funciona, lo único importante es que "slave lo sepa todo".
Como siempre, como no hay un problema, la lectura de una "long mony" es erronea spi_write(mony); mony=spi_read(); fallan

//Estas variables deben ser globales para los dos
//int peti,cliente,salir;
//int1 tarje;
//long mony;//////Hay un problema con "long" no se puede enviar con spi_write(mony);
//int1 fclee[5];
//int1 fcpos[5];

MASTER


```
main(){
while(true){//whil

refresco();//Es importante que sea funcion porque mi proyecto debe embiar las variables actualizadas
           //para que "slave" las trabaje bastantes veces


}//whil
}


void refresco(){

nt co,cu;
output_low(PIN_c6);//S.envio incial 0
lcd_putc("\f"); cu=1;
for (co=0;5>co;co++){  //for int1
valor=rand();
if (valor>20000) { fclee[co]=1; } else { fclee[co]=0; }
lcd_gotoxy(cu,1); lcd_gotoxy(cu,1); printf(lcd_putc,"fl[%d]=%d ",co,fclee[co]);  cu+=8; //printf(lcd_putc,"fl[%d]=%d ",co,valor);
spi_write(fclee[co]); 
protMaster(); /// protocolo de envio vajo SPI MAESTRO.........*
}  //for int1
cu=1;
for (co=0;5>co;co++){ // for int1_2
valor=rand();
if (valor>20000) { fcpos[co]=1; } else { fcpos[co]=0; }
lcd_gotoxy(cu,2); printf(lcd_putc,"ps[%d]=%d ",co,fcpos[co]);  cu+=8; //printf(lcd_putc,"fl[%d]=%d ",co,valor);  
spi_write(fcpos[co]); 
protMaster(); /// protocolo de envio vajo SPI MAESTRO.........*
} //  for int1_2
    delay_ms(4000);//para mostrar principales boolean master & savle
peti=rand();
spi_write(peti); /// int peti 
protMaster();
cliente=rand();    
spi_write(cliente); /// int cliente
protMaster();
salir=rand();   
spi_write(salir); /// int salir
protMaster(); 
valor=rand();
if (valor>20000) { tarje=1; } else { tarje=0; }
spi_write(tarje); /// int1 tarje
protMaster();
mony=rand();   
spi_write(mony); /// long mony
protMaster();/// protocolo de envio vajo SPI MAESTRO.........*
lcd_putc("\f");
lcd_gotoxy(1,1); printf(lcd_putc,"INT...peti=%d cliente=%d salir=%d",peti,cliente,salir);
lcd_gotoxy(1,2); printf(lcd_putc,"int1 tarje=%d    LONG mony=%lu",tarje,mony);   
  delay_ms(4000);
 
lcd_putc("\f  envio completo");  delay_ms(500);
}
void protMaster(){/// protocolo de envio vajo SPI MAESTRO
output_high(PIN_c6); // s.envio=true afecta PIN_B7_SLAVE/E.envio
delay_ms(5);
while (Input(PIN_c0)==0) { delay_ms(5); } //Impide + envios asta SLAVE lo diga con su S.pin_a1_(S.recivido) -----> E.pin_c0_MASTER(E.recivido)
output_low(PIN_c6); // s.envio=false afecta PIN_B7_SLAVE/E.envio
delay_ms(5);
}
```
Esclavo/SLAVE

```
main(){

while(true){//whil

if(Input(PIN_b7)) { //pin_b7=E.envio=true afectado por pin_c6_MASTER/S.envio=true 
lcd_putc("\f"); cu=1;
for (co=0;5>co;co++) {//for nt1_1
fclee[co]=spi_read();
lcd_gotoxy(cu,1); printf(lcd_putc,"fl[%d]=%d ",co,fclee[co]);  cu+=8;
protSlave();///protocolo de recivo vajo SPI SLAVE...........*
}//for nt1_1
cu=1;
for (co=0;5>co;co++){//for int1_2
fcpos[co]=spi_read();
lcd_gotoxy(cu,2); printf(lcd_putc,"fp[%d]=%d ",co,fcpos[co]);  cu+=8;     
protSlave();///protocolo de recivo vajo SPI SLAVE...........*
}//for it1_2*/

peti=spi_read();// int* peti
protSlave();///protocolo de recivo vajo SPI SLAVE
cliente=spi_read();// *cliente
protSlave();
salir=spi_read();// *salir
protSlave();
tarje=spi_read();// int1 targe 
protSlave();
mony=spi_read();// long mony lectura erronea******
protSlave();///protocolo de recivo vajo SPI SLAVE...........*
lcd_putc("\f");
lcd_gotoxy(1,1); printf(lcd_putc,"int...peti=%d cliente=%d salir=%d",peti,cliente,salir);
lcd_gotoxy(1,2); printf(lcd_putc,"int1 tarje=%d long mony=%lu",tarje,mony);///long mony siempre falla******
delay_ms(1000);
lcd_putc("\f  nuevo recivo"); 

}//whil
}
void protSlave(){///protocolo de recivo vajo SPI SLAVE
output_high(PIN_a1);//S.recivido=true -----> pin_c0_MASTER/E.recivido 
delay_ms(2);
output_low(PIN_a1); //S.recivido=false----->  pin_c0_MASTER/E.recivido
while (Input(PIN_b7)==0) { delay_ms(2); } //Se queda esperando asta qu b7=true (E.envio) afectado por pin_c6_MASTER/S.envio=true

delay_ms(2);
}
```
No me ha servido de mucho leer. Es cierto que no sé aprovechar los recursos ni entiendo cosas.

Por ejemplo:
setup_spi(spi_slave | spi_l_to_h | spi_clk_div_16);//| SPI_XMIT_L_TO_H); 

No tengo ni idea de que significa  "spi_l_to_h" Sólo sé que es una parte esencial para que todo funcione.
Si busco en google: maestro esclavo definicion-o-significado spi_l_to_h, no consigo nada concreto que me diga que funcion cumple.
Me pasa con muchas cosas, aunque tampoco tengo mucha paciencia buscando



Hay una cosa que no me gusta; "slave" siempre se retrasa, tarda más con los delays.
Me gustaría saber si esto es normal o puede ser porque empecé el proyecto eligiendo un solo micro o algo así.
Esto en esclavo me parece raro:

```
output_high(PIN_a1);//S.recivido=true -----> pin_c0_MASTER/E.recivido
delay_ms(2);
output_low(PIN_a1);
```
Funciona, pero es demasiado corto cuando master utiliza delay_ms(5)
Parece que para sincronizar el que escribe, debe ser más lento.
Aún así, noto con los mensajes y todo que slave se retrasa utilizando la misma cantidad de delays.
master/slave tienen por igual #use delay(clock=4M)


----------



## TRILO-BYTE (Sep 9, 2015)

aun sigo sin entender para que meter delays para hacer sincronia, si SPI trae su CLOCK para cada DATA

mi no ENTENDER!!!

H TO L me imagino que el clock es un flanco alto a uno de bajada y L to H es un clock bajo a uno alto

nunca he manejado SPI del CCS pero mas o menos asi es como funciona.


----------



## Scooter (Sep 10, 2015)

Desde la más completa ignorancia me parece que todos esos delays sobran


----------



## Berto75 (Sep 10, 2015)

Sobrarian si los dos micros actuasen en serie. El problema es que son programas paralelos. Si pic1 uno se detiene, pic2 sigue adelante (independiente) a menos k pic1 le diga lo contrario. E visto casos similares asta con delay(10). Yo tampoco os entiendo. Darme una manera de pasar tantas variables sin delays.


----------



## Scooter (Sep 10, 2015)

La verdad es que de C se lo justo. En ensamblador nunca he hecho delays de más de 20 o 30us y en pocas muy pocas ocasiones.
Si el spi funciona como debe no se que pintan los delays salvo que haya montado un protocolo asíncrono sobre un bus síncrono y para eso se espere "una eternidad" porque 10ms es eso en tiempo de máquina.
Es que tenía la mala costumbre de no usar librerías de terceros, todo lo programaba desde cero.
Me parece que hace tiempo que te estoy mareando con mis teorías puristas.

En mi opinión, sea spi o sea lo que sea.
O se usa longitud de paquete fijo y entonces no se espera, cada seis bytes o los que sean es una nueva instrucción. O se usa un empaquetado variable y a cambio caracteres de control, uno de inició, uno de final, un checksum...
Lo de los delays no me gusta. Pero bueno...


----------



## TRILO-BYTE (Sep 10, 2015)

yo opino que si ya te funcionan muchos delays metele atascalo solo no te quejes que sea monotarea


----------



## Daniel Meza (Sep 10, 2015)

> Si, interrupciones, eso es lo que he hecho al final lo que se me ocurrió al principio





AlberertO dijo:


> Sobrarian si los dos micros actuasen en serie. El problema es que son programas paralelos. Si pic1 uno se detiene, pic2 sigue adelante (independiente) a menos k pic1 le diga lo contrario. E visto casos similares asta con delay(10). Yo tampoco os entiendo. Darme una manera de pasar tantas variables sin delays.



Hola, he estado leyendo el tema y decido meter mi cuchara: No debería de haber problema con esto si es que usas las interrupciones que acompañan al SPI pero no las veo escritas en tu código. Vamos, sugiero que entrenes con la función SPI de C, sus interrupciones y algún simulador. Ya después encaras el proyecto con el "arma adecuada". Saludos


----------



## Berto75 (Sep 19, 2015)

Si es cierto, puede que extrañes que no tenga if(spi_data_is_in())
Pero con mi "if(Input(PIN_b7)) { //pin_b7=E.envio" 
Es similar y no tengo porque hacerle perder el tiempo preguntándolo 2 veces.

Me he salido del proyecto principal para tratar sólo SPI en un subproyecto.
Manda un montón de variables y me he dado cuenta que no necesito siempre todas, las mando en grupos y aunque le haga leer todas, muchas superior a int16, te aseguro que lo hace todo muy rápido comparado a otros proyectos que he visto.

Lo que más me interesa ahora, son los float.
Esta parece por defecto "float32", pero no me sirve el "make8" y luego hacer la conversión (master-->slave) (slave-->master), porque a mi me conviene que cualquiera de los microcontroladores pueda leer al otro en un momento dado.

Saludos.


----------



## Daniel Meza (Sep 19, 2015)

AlberertO dijo:


> Si es cierto, puede que extrañes que no tenga if(spi_data_is_in())
> Pero con mi "if(Input(PIN_b7)) { //pin_b7=E.envio"
> Es similar y no tengo porque hacerle perder el tiempo preguntándolo 2 veces.
> 
> ...



No compañero, me refiero a la interrupción del periférico por hardware, no por "polling". Será eficiente el código si es que haces uso de ella. En la hoja de datos del micro en la sección de UART vienen los registros y el procedimiento para implementar la interrupción. 
Así como lo tienes malgastas tiempo de ejecución del CPU esperando a que se reciban los datos.


----------



## TRILO-BYTE (Sep 19, 2015)

yo opino que si al compañero le funciona que ya no le mueva es como cuando uno rompe algo le hecha kolaloca y aunque todo mundo diga que es una marranada

pues ya que lo deje asi , ya cuando vea que su programa es una marranada aprendera a corregirlo


----------



## Berto75 (Sep 20, 2015)

No encuentro en mis dataset Sea lo que sea UART Es algo que devi tratar antes En algunos casos hay un 4º pin que sincroniza todos los "clok-internos" a los demas micros Lo que acavo de ver no mustra el sectris ni el puerto. Crei que SPI era estandar de b3 a b5 pero hay graficos que muestran Utilizar c6 y c7 O pines ya ocupados Tendria que cambiar muchas cosas La marranada continua Intentare hacerlo mejor la prosimavec Aunque de momento esta hace lo que le pido


----------



## luis30 (Sep 23, 2015)

Universal Asynchronous Receiver-Transmitter, el micro viene preparado para sincronizar el spi por hardware y es mas facil que por software, para manejarlo por hardware ya vienen pines predefinidos y en tu caso cambiarias el diagrama cosa que es lo correcto mas en tu caso solo te queda optimizar el codigo y para la proxima como dices.


----------



## Scooter (Sep 24, 2015)

Es que tener spi hardware y no usarlo es tan absurdo como tener uart, interrupciones etc y no usarlos.
Se limita la aplicación y la usabilidad en un porcentaje aberrante.
Desde el principio pensé que se usaba el hard.

Hacer un máster spi por soft es fácil y no consume mucho. Hacer un esclavo puede significar que se tumbe el sistema casi por completo.
Veo mucha afición al atajo últimamente.


----------



## savad (Sep 24, 2015)

Creo que necesitas leer la AN585 A Real-Time Operating System for PICmicro™ Microcontrollers
la puedes bajar de la web.  Uahuu ... 10 tareas corriendo con prioridades dadas y coperativo  y en un PIC16F54 .... solo 512F y 25 de RAM con 12-io, solo TMR0 y WDT.  ..... un chip de solo $0.10 US.
Clato viene en Assembler pero ...lo puedes estudiar y ver como lo implemtaron


----------



## savad (Oct 1, 2015)

Otra fuente de información es la viejita pero aun buena AN # 9: Managing Multiple Tasks
de Parallax. Ellos desarroyaron su Basic-Stamp con un PIC.  



```
; PROGRAM: TASK.SRC
; Taken from Parallax Application Note #9: Managing Multiple Tasks
; Written     July 26, 1993
; Revised February 24, 1996 (to accomodate SPASM v4.2)
;
;Introduction. This application note presents a program in Parallax
;assembly language that demonstrates a technique for organizing a
;program into multiple tasks.
;
;Background. Like most computers, the PIC executes its instructions
;one at a time. People tend to write programs that work the same way;
;they perform one task at a time.
;It’s often useful to have the controller do more than one thing at a time,
;or at least seem to. The first step in this direction is often to exploit the
;dead time from one task—the time it would normally spend in a delay
;loop, for instance—to handle a second task. The PIC’s speed makes this
;quite practical in many cases.
;
;When several tasks must be handled at once, this approach can quickly
;become unworkable. What we need is a framework around which to
;organize the tasks. We need an operating system.
;
;The program in the listing illustrates an extremely simple operating
;system that runs each of eight small subprograms in turn. When the
;subprograms finish their work, they jump back to the system. Notice
;that this method does not require the call instruction, so it leaves the
;two-level stack free for the use of the subprograms.
;
;How it works. The circuit and program comprise an eight-LED flasher.
;Each of the LED’s flashes at a different rate. While this could be
;accomplished differently, the program is easier to understand and
;maintain because the code that controls each LED is a separate task.
;The “system” portion of the program acts like a spinning rotary switch.
;Each time it executes, it increments the task number and switches to the
;next task. It does this by taking advantage of the PIC’s ability to modify
;the program counter. Once the task number is loaded into the working
;register, the program executes the instruction jmp pc+w. The destinations
;of these jumps contain jmp instructions themselves, and send the
;program to one of the eight tasks. Not surprisingly, a list of jmp
;instructions arranged like this is called a “jump table.”
;
;Modifications. For the sake of simplicity, this task-switching program
;lacks one important attribute: fixed timing. The individual tasks are
;permitted to take as much or as little time as they require. In some real
;applications, this wouldn’t be acceptable. If the system maintains a
;master timer (like the variable ticks in this program) it should increment
;at a consistent rate.
;
;With many possible paths through the code for each task, this may seem
;like another problem. A straightforward solution is to use the PIC’s
;RTCC to time how long a particular task took, then use that number to
;set a delay to use up all of the task’s remaining time. All you need to
;know is the worst-case timing for a given task.

	include 'C:\pictools\16c54.inc'
	FUSES _XT_OSC
	FUSES _WDT_OFF
	FUSES _CP_OFF
	reset	start

LEDs		=	rb

                org     8       ;Start of available RAM

task		ds	1	; The task number used by the system.
ticks		ds	1	; Master time clock, increments once for each system cycle.
time0		ds	1	; Timer for task 0. 
time1		ds	1	; Timer for task 1. 
time2		ds	1	; Timer for task 2. 
time3		ds	1	; Timer for task 3. 
time4		ds	1	; Timer for task 4. 
time5		ds	1	; Timer for task 5. 
time6		ds	1	; Timer for task 6. 
time7		ds	1	; Timer for task 7. 

                org     0       ; Start of code space (ROM)

start		mov	!rb,#00000000b	; Set port rb to output. 
		mov	task, #7	; Set task number. 
		clr	ticks	; Clear system clock. 
		clr	LEDs	; Clear LEDs

system		inc	task	; Next task number. 
		cjne	task, #8, :cont	; No rollover? Continue. 
		clr	task	; Rollover: reset task and 
		inc	ticks	; increment the clock. 
:cont		mov	w, task	; Prepare to jump. 
		jmp	pc+w	; Jump into table, and from there
		jmp	task0	; to task #. 
		jmp	task1
		jmp	task2
		jmp	task3
		jmp	task4
		jmp	task5
		jmp	task6
		jmp	task7

task0		cjne	ticks, #255,:cont	; Every 255 ticks of system clock
		inc	time0	; increment task timer. Every 3 ticks
		cjne	time0, #3, :cont	; of task timer, toggle LED, and 
		clr	time0	; reset task timer. 
		xor	LEDs, #00000001b
:cont		jmp	system

task1		cjne	ticks, #255,:cont
		inc	time1
		cjne	time1, #8, :cont
		clr	time1
		xor	LEDs, #00000010b
:cont		jmp	system

task2		cjne	ticks, #255,:cont
		inc	time2
		cjne	time2, #6,:cont
		clr	time2
		xor	LEDs, #00000100b
:cont		jmp	system

task3		cjne	ticks, #255,:cont
		inc	time3
		cjne	time3, #11,:cont
		clr	time3
		xor	LEDs, #00001000b
:cont		jmp	system

task4		cjne	ticks, #255,:cont
		inc	time4
		cjne	time4, #12, :cont
		clr	time4
		xor	LEDs, #00010000b
:cont		jmp	system

task5		cjne	ticks, #255,:cont
		inc	time5
		cjne	time5, #4, :cont
		clr	time5
		xor	LEDs, #00100000b
:cont		jmp	system

task6		cjne	ticks, #255,:cont
		inc	time6
		cjne	time6, #23,:cont
		clr	time6
		xor	LEDs, #01000000b
:cont		jmp	system

task7		cjne	ticks, #255,:cont
		inc	time7
		cjne	time7, #9,:cont
		clr	time7
		xor	LEDs, #10000000b
:cont		jmp	system
```


----------



## VMAN (Oct 4, 2015)

Estimados:

Soy nuevo en programación de microcontroladores y estoy estudiando en una carrera relacionada

Mi consulta es, ¿se pueden generar instrucciones "Paralelas" en el código?

Les explico mi problemática:
Al programar dentro de la función principal (Main) pongo un While para verificar constantemente las entradas.
La cosa es que quiero que al apretar un pulsador se prenda un ciclo en el cual un LED parpadee y con el otro se apaga.
Aplicando mi código, funciona, pero queda en una especie de "serie" de programación.

¿Existe alguna manera de llamar una función que se ejecute en paralelo?
Ya que con el código que hice, mientras se ejecuta la secuencia de parpadeo, no le es capaz al programa leer el pulsador, una vez terminada la secuencia de parpadeo, la lee.

 ** PIC16f84a ó PIC16f877a **

```
void main(){

   int id = 0;
   while(true){

   if(id ==1){
   output_high(pin_b1);
   delay_ms(200);
   output_low(pin_b1);
   delay_ms(200);
   }

   if (input(pin_b7)==0)
   id = 1;
   
   if (input(pin_b6)==0)
   id = 0;

   }
}
```
Muchas gracias, de antemano.


----------



## Dr. Zoidberg (Oct 4, 2015)

No hay posibilidad de programacion paralela en un PIC, pero podes usar programacion concurrente via interrupciones a pedal o usar un RTOS como el de CCS.


----------



## electroconico (Oct 4, 2015)

No se puede en paralelo porque va ejecutando tus lineas de código una tras otra.
Si fuera programación de hardware si se podría,pero ya hablamos de otras cosas.

La solución a esto es el uso de interrupciones.

Puedes usar interrupción externa,la mayoria de los pics tienes pines dedicados a esta función marcados como "INT1,INT2,INT..."
Usar la interrupción por cambio de estado de X puerto. ( IOC interrupt on change)

Saludos!


----------



## TRILO-BYTE (Oct 4, 2015)

o quitar los delays y hacer una base de tiempo


----------



## VMAN (Oct 4, 2015)

Dr. Zoidberg dijo:
			
		

> No hay posibilidad de programacion paralela en un PIC, pero podes usar programacion concurrente via interrupciones a pedal o usar un RTOS como el de CCS.



Lo de las interrupciones sí estoy al tanto, el problema para mí es que mi PIC al menos, tiene sólo el RB0 como interrupción "Específica". En qué consiste lo de RTOS?




			
				electroconico dijo:
			
		

> No se puede en paralelo porque va ejecutando tus lineas de código una tras otra.
> Si fuera programación de hardware si se podría,pero ya hablamos de otras cosas.
> 
> La solución a esto es el uso de interrupciones.
> ...



En teoría, usando "rb_int" y "if (input(pin_bx)==0)" dentro de la interrupción, uno para cada entrada deseada, debería funcionar?



			
				TRILO-BYTE dijo:
			
		

> o quitar los delays y hacer una base de tiempo



A qué te refieres con una base de tiempo?

Muchas gracias a todos por sus respuestas!!


----------



## Dr. Zoidberg (Oct 4, 2015)

VMAN dijo:
			
		

> Lo de las interrupciones sí estoy al tanto, el problema para mí es que mi PIC al menos, tiene sólo el RB0 como interrupción "Específica". *En qué consiste lo de RTOS?*


RTOS = Real Time Operating System ==> Sistema Operativo de Tiempo Real
Te permite programar funciones para ejecutarlas a intervalos precisos predefinidos... entre otras cosas.


----------



## VMAN (Oct 4, 2015)

Dr. Zoidberg dijo:


> RTOS = Real Time Operating System ==> Sistema Operativo de Tiempo Real
> Te permite programar funciones para ejecutarlas a intervalos precisos predefinidos... entre otras cosas.



Gracias por la info! Ahí le pegaré una ojeada a RTOS


----------



## Ardogan (Oct 5, 2015)

No creo que un RTOS en un pic16f sea lo más conveniente, primero por una cuestión de tamaño y segundo que es demasiado para ejecutar algo tan sencillo como leer entradas y parpadear un led.

VMAN, depende de tu experiencia, ¿utilizaste interrupciones alguna vez?.
Voy a suponer que no.
Desde un punto de vista estricto no se ejecuta nada en paralelo en ningún procesador, pero si tenes:


```
while(1)
{
  tarea1();
  tarea2();
  tarea3();
}
```

Se puede pensar como que tarea1/2/3 se ejecutan en paralelo, siempre y cuando no compartan variables (algo que casi nunca pasa). Pero bueno, la idea es dar una intro lo más sencilla/breve posible, eso es lo que se conoce como "multitarea cooperativo", porque si tarea1 se queda colgada (un while(1) por ejemplo) no hay nada que interrumpa la ejecución de tarea1 y le pase tiempo de micro a tarea2 y 3.

Yendo a tu caso particular serían 2 tareas: leerEntradas, parpadearLed. En cuanto a la base de tiempo, para hacerlo simple por ahora usamos una variable tick que se incrementa con cada loop del while(1). Solo para no complicarla, porque esta lejos de ser lo ideal. Podria quedar asi:


```
unsigned int hayQueParpadearLed, tickPeriodoLed, tick;
const unsigned int periodoLed = 1000; //cambiar de acuerdo a carga del micro, un embole

void main(void)
{
    Inicializar();
    tick = 0;
    tickPeriodoLed = periodoLed;
    while(1)
    {
        leerEntradas();
        parpadearLed();
        tick++;
    }
}

void parpadearLed()
{
    if (hayQueParpaderLed)
    {
        if(tick==tickPeriodoLed)
        {
            output_toggle( );        
            tickPeriodoLed += periodoLed;
        }
    }        
}

void leerEntradas(void)
{
    if(condicion para parpadear)
    {
        hayQueParpadearLed = 1;
        tickPeriodoLed =  tick + periodoLed;
        output_high(pinLed);        
    }

    if(condicion para no parpadear)
    {
        hayQueParpadearLed = 0;
        output_low(pinLed);        
    }
}
```

Se entiende la idea?, la tarea leerEntradas() mira las entradas y cambia una variable global "hayQueParpadearLed", y la tarea parpadearLed() mira esa variable global, si está activa mira si se cumplió el período de tiempo, y de ser así conmuta la salida del led.
Luego la base de tiempo se puede mejorar, porque no hay una relación entre tick tiempo real. Para eso hay que usar un timer (el timer0 por ejemplo) con entrada = reloj del sistema. Pero ya es un tema aparte. Después vemos que modificaciones hacer, pero primero digerí esta parte.
Este es un tema con el que los usuarios del delay_ms() tienen muchísimos problemas, cuando se trata de usar un periférico.


----------



## VMAN (Oct 5, 2015)

Gracias Ardogan, sí, entiendo al menos conceptualmente la ídea. Aunque para comprenderla de mejor manera, transcribí el código a PIC C Compiler. Reemplacé las variables necesarias pero... y disculpa mi ignorancia, no sé como solucionar el mensaje de error "Undefined identifier" que me tira con cada función excluyendo el Main.

Según entiendo ese mensaje de error es cuando no encuentra declaradas las variables, pero sí las veo declaradas, exceptuando por "Inicializar" :S.

Adjunto el código final:


```
void main(){

   // TODO: USER CODE!!
   
   // Eliminar Ruido
   
   output_low(pin_b1);
   output_low(pin_b2);

   
 Inicializar();
    tick = 0;
    tickPeriodoLed = periodoLed;
    while(1){
        leerEntradas();
        parpadearLed();
        tick++;
    }
}

void parpadearLed(){

    if (hayQueParpaderLed){
        if(tick==tickPeriodoLed)
        {
            output_toggle(pin_b1);        
            tickPeriodoLed += periodoLed;
        }
    }        
}

void leerEntradas(){

    if(input(pin_b7)==0){
        hayQueParpadearLed = 1;
        tickPeriodoLed =  tick + periodoLed;
        output_high(pin_b1);        
    }

    if(input(pin_b6)==0){
        hayQueParpadearLed = 0;
        output_low(pin_b1);        
    }
}
```


----------



## TRILO-BYTE (Oct 5, 2015)

lo unico que lograran haser usando el RTOS es aprender a programar *de una forma bien marrana* pues no se esta aprendiendo bien yo sugiero que usen una base de tiempo

se puede haser con un delay muy corto o usando interrupcion del timer


----------



## Dr. Zoidberg (Oct 5, 2015)

TRILO-BYTE dijo:


> lo unico que lograran haser usando el RTOS es aprender a programar *de una forma bien marrana* pues no se esta aprendiendo bien yo sugiero que usen una base de tiempo
> se puede haser con un delay muy corto o usando interrupcion del timer


   

Si necesitás procesamiento concurrente *NO ES NINGUNA MARRANADA*, y a menos que quieras terminar el desarrollo para cuando finalice la próxima era glacial, es la solución más rápida a la pregunta original. El RTOS ya usa un timer interno, tiene un scheduler para despacho COOPERATIVO de tareas (no es preemptivo) y tiene un montón de cosas adicionales que pueden ayudar a evitar deadlocks.... entre otras cosas importantes.

Un tema aparte es que entiendan para que sirve y como trabaja...


----------



## Ardogan (Oct 5, 2015)

VMAN dijo:


> Gracias Ardogan, sí, entiendo al menos conceptualmente la ídea. Aunque para comprenderla de mejor manera, transcribí el código a PIC C Compiler. Reemplacé las variables necesarias pero... y disculpa mi ignorancia, no sé como solucionar el mensaje de error "Undefined identifier" que me tira con cada función excluyendo el Main.
> 
> Según entiendo ese mensaje de error es cuando no encuentra declaradas las variables, pero sí las veo declaradas, exceptuando por "Inicializar" :S.
> 
> ...



Inicializar es una función que no definí, lo puse como para decir que en algún lado se inicializan los puertos de entrada/salida, módulos, etc, etc.
En tu caso particular sería inicializar los botones como entradas y el pin del led como salida. Eso te lo dejo a vos. Podés no usar una función y ponerlo antes del while(1), como te guste.

Después antes del main hay que declarar los prototipos de las funciones
void leerEntradas(void);
void parpadearLed(void);
El compilador lee el programa línea a línea, llega a leerEntradas() en el main, y dice "esto no me dijeron donde esta", por eso tira error.
No pretendía dar un código que funcionara al hacer copy-paste, solo plantear una primera idea.


----------



## TRILO-BYTE (Oct 5, 2015)

pero eso quiere hacer el compañero la ley del minimo esfuerzo

por eso decia que es una *marranada usar RTOS*

no estoy en contra de RTOS para quienes quieran arrancarse los pelos del coraje

NO estoy viendo que no quiere pensar esa es mi opinion


----------



## VMAN (Oct 5, 2015)

Gracias nuevamente a todos. En mi caso no se trata del mínimo esfuerzo, sin embargo, ya que no soy un erudito el área y recién estoy saliendo del nido, prefiero ir tomando escalón por escalón para procurar entender todo desde las raíces. Nuevamente gracias a todos por darse el tiempo de responder de manera tan completa y útil!


----------



## Berto75 (Oct 14, 2015)

A*mb*os micros me co*nv*ienen en un momento dado que sean master o slave*.
L*a funci*ó*n que falla es; recoge();  (es aquí donde se atascan) ambos la utilizan para leer cuando son slave y tienen otra de escritura llamada refresco(,,,) para escribir al otro cuando son master*.*

No consigo ver el problema*,* pic1 tiende a atascarse en el la lectura*.*
No deber*í*a*,* ya que dentro de esta funci*ó*n; recoge(); una ve*z *que empieza a leer, no hay manera de que se quede encerrado dentro de ella*.*


```
///originalmente micro1 siempre era master
///Hay un jaleo en la combersion pic1-master--->pic1-slave CONCEPTO RECIVIDO es ENVIO  ENVIO es RECIVIDO
void protSlave(){///protocolo de recivo vajo SPI pic2SLAVE
output_high(PIN_C6);//S.envio=true -----> pic2_b7_MASTER/E.envio
delay_ms(7); ///valido (8)
output_low(PIN_C6);
delay_ms(7); 
}
```
Hay algo malo*.* No utilizo interrupciones*.*
El slave se queda esperando en recoge(); sin hacer nada más hasta que master le diga que tiene que empezar a leer*.*
En una comunicación y co*nv*ersi*ó*n continua, lo hacen bien pero no consigo ver porque falla cuando la espera es larga*.*
En principio la acci*ó*n es la misma*.*

El c*ó*digo es lioso pero adjunto el proyecto para que se vea lo que pretendo hacer.
Me gustar*í*a saber qué tengo que elegir cuando reali*c*e un proyecto de este tipo*.*
*T*odo lo que eli*j*o es por defecto cuando creo un wizard.
Creo que tambi*é*n ser*á* mejor utilizar un cristal para que en todos los micros coincidan el clock de flanco de subida y bajada.


```
//////////////////RECOGE 
void recoge(){//0-1-2 IDEN 4 peti,salir,cliente,target[cliente]
output_low(PIN_C6);///S.envio  0 = pic2_B7_MASTER/E.envio
//output_high(PIN_C1);//RS TRUE
int8 co;
int1 muta=0;
//if (Input(PIN_C0)==1) { }
while (Input(PIN_C0)==0) { } 
//while(!spi_data_is_in()){ output_low(PIN_C1); delay_ms(2); output_high(PIN_C1); delay_ms(2); }
output_high(PIN_C1);//RS TRUE ESTE PIN DEBERIA PASAR A FALSE pero micro1 se suele quedar aqui atrapado
grp2=spi_read();
//if (peti==101) { output_high(PIN_C6); delay_ms(4); }
protSlave();  
grp3=spi_read();///gp[2] = int1_gp3_Master refresco(x,x,1,x,x)
protSlave();
if (grp2==1) {////**per 2 (estado-salidas pic1)
for (co=0;5>co;co++){
ladra[co]=spi_read();   
protSlave();
}
}////**per 2
if (grp3==1) {////**per 3
peti=spi_read();// int8 peti
protSlave();
salir=spi_read();// salir
protSlave();
cliente=spi_read();// cliente
protSlave();
target[cliente]=spi_read();// int1 targe[ ]  
protSlave();
tarje=spi_read();// int1 tarje   
protSlave();
union UNI M32;//int32 mony
   for(co=0;co<4;co++){ 
      M32.bytes[co] = spi_read();  
      protSlave(); }
mony=M32.dat32; 
}////**per 3
muta=spi_read();// *cliente
output_high(PIN_C6);//S.envio=true -----> pic2_b7_MASTER/E.envio
if (grp2==1) {
if (ladra[0]) { output_high(PIN_a0); } else { output_low(PIN_a0); } delay_ms(1); //delay_ms(2);
if (ladra[1]) { output_high(PIN_a1); } else { output_low(PIN_a1); } delay_ms(1);
if (ladra[2]) { output_high(PIN_a2); } else { output_low(PIN_a2); } delay_ms(1);
if (ladra[3]) { output_high(PIN_a3); } else { output_low(PIN_a3); } delay_ms(1);
if (ladra[4]) { output_low(PIN_a4);  } else { output_high(PIN_a4);  } delay_ms(1);//neg
} else {
delay_ms(5); }
delay_ms(2);
output_low(PIN_C6);
output_low(PIN_C1);//RS FIN
if (muta) { COMBERSION(1);  delay_ms(16); }
}
```
Utilizo la sig*u*iente funci*ó*n para hacer la co*nv*ersi*ó*n:
Este es el caso de micro1*,* cada uno tiene un sec_tris diferente seg*ú*n les co*nv*enga


```
void COMBERSION(int1 Soy){
if (Soy){
MASTER=1; set_tris_c(208); delay_ms(2);// 1101 0000 /// "C5_S C4_E C3_S  01 0"
setup_spi(spi_master | spi_l_to_h | spi_clk_div_16); delay_ms(4);//| SPI_XMIT_L_TO_H); //);// | SPI_XMIT_L_TO_H);  
output_high(PIN_c7);
} else {
MASTER=0; set_tris_c(240);  delay_ms(2); // 1111 0000 /// "C5_E C4_E C3_S  11 0"
setup_spi(spi_slave | spi_l_to_h | spi_clk_div_16); delay_ms(2);//| SPI_XMIT_L_TO_H); //);// | SPI_XMIT_L_TO_H);  
output_low(PIN_c7);
}
```
MASTER es una int1 que coincide a True cuando el micro es master, siempre se pasa este valor "muta"en refresco(muta,x,x) por si es co*nv*eniente la co*nv*ersi*ó*n*.*


----------



## JUAMPYHDR (Oct 17, 2015)

Hola. Tengo una duda.
¿Cómo hacer que el circuito 1 y 2 estén en el mismo PIC16F628A?
Estoy usando el programa mikroC Pro For PIC.

Les dejo una simulación en vídeo de éste circuito, junto con los archivos de programación.


----------



## D@rkbytes (Oct 18, 2015)

Algo sencillo, usando la interrupción externa por RB0 para el caso 1 y para el caso del circuito 2 en el mismo microcontrolador, lo ejecutas en el bucle del void main.

Ahora que si quieres hacerlo de forma un poco más complicada, utiliza los timers y también las interrupciones.


----------



## TRILO-BYTE (Oct 18, 2015)

es que no les gusta buscar en el foro

hubo uno que tenia dudas, busco en el foro un codigo viejo que explique hace un tiempo, me pego el pedazo de codigo que no entendio y me pregunto dudas

eso es tener perseverancia
hay que buscar


----------



## JUAMPYHDR (Oct 18, 2015)

Estuve buscando por el foro pero no encuentraba la respuesta de hecho aun sigo trabajando en buscar una solucion pero en los Timer como me dijo D@rkbytes para asi hacer lo que pido en el video.


----------



## Scooter (Oct 18, 2015)

A ver, por partes: Los leds siempre necesitan resistencia y los pulsadores no necesitan resistencia porque el micro ya lleva un dentro que se puede activar por software. Número cien mil millones de billones que se repite en el foro.

Borro el resto por que estaba un tanto subida de tono la respuesta.

De verdad que no comprendo donde está la dificultad de hacer cosas tan tan pero tan tontas a la vez.
Borrad de vuestra mente el maldito delay de las narices y veréis la luz.


----------



## JUAMPYHDR (Oct 18, 2015)

Yo no lo puedo lograr e hecho artas pruebas demuestrame de que puedes hacer.


----------



## Scooter (Oct 18, 2015)

Pseudocódigo:

Lanzar el timer con interrupción y activar las primeras luces

Bucle de mirar pulsador y encender el led. Te quedas aquí por los siglos de los siglos.

Funcion de atención a  la interrupción, se reprograma el timer si no es autorecargable y se pone la siguiente secuencia de luces.




Más o menos tres funciones. No morirás en el empeño. Busca información de como se hace en c con un pic. 
En c programo más bien poco y pics más bien nada. Si quieres el código en ensamblador de 8052 o en wiring del arduino te lo preparo.


Pon resistencias en los leds y quita las de los pulsadores.


----------



## cosmefulanito04 (Oct 18, 2015)

Con hoja de datos del uC en mano, tenés que hacer lo siguiente:

1- Elegir un timer, creo que tenés 3 (0, 1 y 2).
2- Ver como se conecta ese timer con el circuito oscilador principal, esto implica posibles preescaler en clock principal.
3- Configurar la cuenta según los tiempos a fijar (buscar registro de cuenta).
4- Configurar los registros del timer para habilitar una interrupción por desborde.
5- Posiblemente se necesite habilitar las interrupciones en forma general (buscar registros).

Desde el código:

1- Implementar la inicialización en base a los puntos anteriores.
2- Implementar una rutina de interrupción que permita modificar el estado de una variable según el tiempo transcurrido (flags).
3- En el loop principal (while), preguntar c/tanto el estado de esos flags y modificar el estado de los leds según corresponda.

De todo eso, una mejora sería implementarlo usando una función de sleep que solo se despierte cuando exista una interrupción, en este caso del timer o por el pulsador.


----------



## Scooter (Oct 18, 2015)

Bueno, echar a dormir el micro ya sería para nota. Pero estaría bien, para un caso tan simple es perfectamente posible; tienes interrupciones sin usar para aburrir...


----------



## cosmefulanito04 (Oct 18, 2015)

Scooter dijo:


> Bueno, echar a dormir el micro ya sería para nota. Pero estaría bien, para un caso tan simple es perfectamente posible; tienes interrupciones sin usar para aburrir...



Digamos que si, ya que está, que aproveche y aprenda lo más posible.

Igual no siempre se puede, en los AVR los muy.... no me salen las palabras en neutro... "¿mal bichos?".... no se despiertan cuando hay una interrupción por timer, así que habría que ver bien en esta familia que pasa.


----------



## Scooter (Oct 18, 2015)

Que ponga la salida del timer por el pin de interrupción externa, pierde dos pines pero total...


----------



## TRILO-BYTE (Oct 18, 2015)

como no como no si esta el tema de alguien que supo buscar

pero como aveces peco de bondad pondre el link de ese compañero que busco y se puso a estudiar

https://www.forosdeelectronica.com/showthread.php?p=1059527#post1059527


----------



## JUAMPYHDR (Oct 18, 2015)

Gracias a las personas que comentan para ayudarme a dar una solucion en mi problema de progracion pic 
sigo atento a las ideas que me estan dando.


----------



## Dr. Zoidberg (Oct 18, 2015)

JUAMPYHDR dijo:


> sigo atento a las ideas que me estan dando.


Mas que "seguir atento" creo que lo que debes hacer es comenzar el diseño.... o vas a esperar alguna propuesta que se adapte a tu gusto...?????


----------



## TRILO-BYTE (Oct 18, 2015)

yo cuando tenia dudas a quien recurria era ami mismo pues tenia profesores muy ineptos anclados al 8051 en modo ASM o al 16f84 en ASM

el foro no lo conocia, y un libro de base de datos en C para PC de 1994 me enseñaron mucho.

lo aprendido lo meti al pic16f628 y funciono!

lo que trato de decir es que no te debes apoyar al 100% en los demas si no en ti mismo y si salen dudas debes razonar apoyarte en ti


----------



## kirby123 (Jun 10, 2016)

¿Podrían ayudarnos en este programa?
Lo que se quiere es que sea una rutina de pasos múltiples, la cual encienda un servo y un motor y después de 5 veces que el servo actúe, el motor debe parar.
Aparte el motor cuando llegue a un tope y lo capte un sensor, se debe regresar.

Hasta ahora tenemos esto. El problema es que compila pero ya en la simulación no funciona.
El servo no se mueve y no parece funcionar el motor.

Estamos usando el PIC16F648A.


```
@3F10H
I VAR BYTE
PAUS VAR BYTE
SENSOR2 VAR BYTE
SENSOR1 VAR BYTE
CORTA VAR BYTE 
TRISB=%00001111
SENSOR1=PORTB.0
SENSOR2=PORTB.1

INICIACION:
IF SENSOR2= 0 THEN
    CALL SERV90
    PORTB.4=%01
    GOTO INICIACION
    ELSE 
    PORTB.4=0
    ENDIF 
GOTO PROCESO

PROCESO: 
CORTA = 0

AQUI: PORTB.6=%10

    IF SENSOR1 = 1 THEN 
    CALL CORTAR
        GOTO AQUI
    ELSE
    GOTO PROCESO
     ENDIF   
SERV90: FOR I = 0 TO 20 
        PULSOUT PORTB.5,98
        next i
        return
    
  CORTAR: CORTA = CORTA +1
        FOR I = 0 TO 20 
        PULSOUT PORTB.5,180
        pause 10 
        next i
        IF CORTA = 5 THEN
        GOTO PAUSA
        ENDIF
        return
        
  PAUSA:
    FOR PAUS=0 TO 60
    PAUSE 1000
    NEXT PAUS
    
  END
```


----------



## savad (Abr 27, 2017)

Cuando te hacen faltan Timers .... Usa soft-timers
Cuando no hay interrupciones .... Usa monitoreo continuo
Aqui les dejo una pequeña aportación para el viejisimo pero muy funcional PIC16F54 ... me gusta por
su precio ...solo una soda ($0.54 us) y si quiero hacer aplicaciones más complejas uso su upgrade el
PIC16F18855 a un costo de una soda y su hotdog ($1.40 us).

Basado en MicroChip nota de aplicación an-585 " A RTOS"


```
; Ultima edicion: Abril 27, 2017
; - decrements active timers
; - 4 MHz oscillator
; - period: 1 millisec. (prescaler divide by 8)
; - subClock: 0 .. 125  (125 * 8 = 1000 nsec )
;Usa las variables:
;     sample     EQU   0x07   = copia actual del TMR0
;     incr       EQU   0x08   = Incremento de TMR0 desde ultima revicion
;     prevTMR0   EQU   0x09   = 
;     subClock   EQU   0x0A   =
;     timer1     EQU   0x0B   = Timer de 8-Bits
;     timer2L    EQU   0x0C   = Timer de 16 bits
;     timer2H    EQU   0x0D
;     Time_bit   EQU   0x0E   = Bits para indicar cuando ya
;                                    transcurrio el Tiempo


timerTick
      MOVF  TMR0,W       ; Obten copia del tiempo actual en TMR0
      MOVWF sample
                         ; Calcula cuanto tiempo ha trascurrido
      MOVF  prevTMR0,W   ; desde la ultima vez que revisamos
      SUBWF sample,W     ; incr = sample - prevTMR0
      MOVWF incr

      MOVF  sample,W     ; y actualiza prevTMR0
      MOVWF prevTMR0
 
      MOVF  incr,W       ; Lleva B a W
      SUBWF subClock,1   ; Has A - B  (subClock -incr)
                         ; 
      BTFSC STATUS,CarryF ; A > B -> C = 0
      GOTO  m002         ; Si: Salte todavia no trascurre un Tick
                         ; No: Ya transcurrio más de un Tick
      MOVLW 125          ;     /* new 1 millisec. tick */
      ADDWF subClock,1   ; Añade la cuenta para otro milisec
	  
;   Incrementa Soft-Timers y ve si han llegado a su cuenta limite
;     cuando esto ocurre las banderas de timeout en Time_bit
;     son ajustadas a zero 
;   Revisa si los Soft-Timers son > a su limite

Test_ST01
      BTFSC Time_bit,timeout1 ; Timer1 > Limite -> timeout=0  
      GOTO  Test_ST02     ; No: Revisa siguiente Soft-timer
                          ; Si: timeout = 0
      DECF  timer1,1      ;     Decrementa soft-Timer 
      INCF  timer1,W      ;     y Revisa si llego al limite
      BTFSC STATUS,ZeroF  ;       == 255?
      BSF   Time_bit,timeout1 ; Si: Ajusta Bandera a 1
	                      ; y revisa el siguiente Soft-Timer
;
Test_ST02
      BTFSC Time_bit,timeout2 ; Timer1 > Limite -> timeout=0 
      GOTO  Test_ST03     ; No: Revisa siguiente Soft-timer
                          ; Si: timeout = 0
      DECF  timer2L,1     ;     Decrementa soft-Timer 
      INCFSZ timer2L,W    ;     y Revisa si llego al limite
                          ;     el byte bajo == 255?
      GOTO  Test_ST03     ;  No: Todavia no se llega al limite
                          ;      asi que revisa el siguiente Soft-Timer
      DECF  timer2H,1     ;  Si: Incrementa la cuenta del bit alto
      INCF  timer2H,W     ;      Revisa si se llego al limite
      BTFSC STATUS,ZeroF  ;      el byte alto == 255?
      BSF   Time_bit,timeout2 ; Si: Ajusta Bandera a 1
                          ;     No: Checamos el siguiente Soft-Timer
Test_ST03                 ;
m002                      ; Como ya no hay mas Soft-Timers
	  RETLW 0             ; Nos regresamos
```

Observen que tiene dos soft-timers ...uno de 16-Bits y otro de 8-Bits



Y como la primera vez que lei la nota AN-585 " A RTOS" se me hizo muy engorrosa, les dejo
el desgloce que edite para ayudarme a entenderla

```
El RTOS ejemplo "Alarma serie"

; The code segment lines 1-87 are explained in this paragraph.
; Line 4 tells the MPASM assembler which PICmicro you
; are using.
        list    p=16C54,t=ON,c=132

        include "P16C5X.INC"

; The include file PICREG.H follows with the
; equates and assignments to make the code more
; readable and changeable. You should use equates that
; relate symbols to each other.
;
; The Constants  lines 10-12 are the values to change
;   for different Baud rates.
; Constants
INDIR          equ     0           ;Indirect Register
OUT_BIT_TIME   equ     33h         ;9600 Baud, 104uS Bit Rate
IN_BIT_TIME    equ     64h         ;4800 Baud, 208uS Bit Rate
FUDGE_TIME     equ     23h         ;Current Time within a Fudge Factor

; They represent the Bit Times for the Baud rates divided
;  by 2 minus some latency factor. You might have to
;  adjust the “Fudge Factor” and other values to fine tune
;  the performance. The value used for the “Fudge
;  Factor” is related to the longest path of code.
;
; Lines 21-24 are an experiment that allows a simple
;  name to be associated to a single bit.
;  This allows for easily changeable assignments.
; PORTB Register Definitions
#define Level_Reset    PORTB,0     ;Low will cause Past Level to reset
                                   ;RB.7 - RB.1 == Input from Sensors
RB_TRIS        equ     B'11111111' ;RB TRIS at INIT State == all input
RB_MASK        equ     B'00000000' ;What is High/Low for RB at INIT State

; A Register Definitions - Programmable Inputs
#define Serial_IN_1    PORTA,0     ;Serial Input #1 - 8 bits
#define LED            PORTA,1     ;LED Output - Level/State Indicator
#define Serial_Out     PORTA,2     ;Serial Output - 8 bits + passwords
#define Serial_IN_2    PORTA,3     ;Serial Input #2 - 8 bits

RA_TRIS        equ     B'11111001' ;RA TRIS at INIT State  RA1,2 Output
RA_MASK        equ     B'00000000' ;What is High/Low for RA at INIT State
;
; Lines 30-54 are the variable assignments.
;   Variables (lines 35-39) are used as time counters.
;   They count the number of units of time, and are compared
;   to literals to see if an Event has just happened.
;
; Register Files RAM
temp           equ     07h          ;Tempary holding register - PIC16C54/56
Timer_Bits     equ     08h          ;Indicates which Timer(s) are Active = 1
Flags          equ     09h          ;Error Flags
LED_Mode       equ     0Ah          ;(0-2)=Mode, 3=LED_B, (4-6)=Seq #, 7=NEW
OState         equ     0Bh          ;Serial Out State
T_5_M_LO       equ     0Ch          ;5 Min Timer Counter - Low
T_5_M_HI       equ     0Dh          ;5 Min Timer Counter - High
T_5_S_CO       equ     0Eh          ;5 Second Timer - lack of Serial Input
T_20_mS_CO     equ     0Fh          ;20 mS Timer - used for debouncing
LED_C          equ     10h          ;LED Counter
Last_TMR0      equ     11h          ;Last value of the TMR0
First_TMR0_O   equ     12h          ;Starting time for next Output event
xmt_byte       equ     13h          ;Serial xmit byte - destroyed in use
Curnt_Cnt      equ     14h          ;256 * TMR0 time μs
RCV_Storage    equ     15h          ;Long term storage of rcv_byte #1 & 2
Old_RB         equ     16h          ;Oldest/Master copy of RB
Last_RB        equ     17h          ;Last copy of RB
IState1        equ     18h          ;Serial In #1 State
First_TMR0_I1  equ     19h          ;Starting time for next #1 Input event
nbti1          equ     1Ah          ;Next Bit #1 In Time - variable time 
rcv_byte_1     equ     1Bh          ;Receive Serial #1 In byte
IState2        equ     1Ch          ;Serial In #2 State
First_TMR0_I2  equ     1Dh          ;Starting time for next #2 Input event
nbti2          equ     1Eh          ;Next Bit #2 In Time - variable time
rcv_byte_2     equ     1Fh          ;Receive Serial #2 In byte

; The bits defined in lines 57-64 are used as Binary Semaphores.
;   They keep Critical Sections of data protected.
;   We will see them in action later in the code.

; Indicates which Timer(s) are Active = 1 & Flags
#define OState_B      Timer_Bits,0  ;Serial Out Active Bit
#define IState1_B     Timer_Bits,1  ;Serial IN #1 Active Bit
#define IState2_B     Timer_Bits,2  ;Serial IN #2 Active Bit
#define T_5_S_B       Timer_Bits,3  ;5 Second Timer Active Bit
#define T_5_M_B       Timer_Bits,4  ;5 Min Timer Active Bit
#define RCV_Got_One_B Timer_Bits,5  ;Got a NEW Received byte to send out
#define RB_NEW_B      Timer_Bits,6  ;Indicates a change in RB input
#define S_5_S_B       Timer_Bits,7  ;Serial In 5 secs of inactivity

; The bits defined in lines 67-73 are error flags.
;   They define the current or last error states of the Serial
;   routines, and whether data was lost coming in or out.

; Error Flags
#define FS_Flag_1      Flags,0       ;Serial #1 IN had a False Start Error
#define FE_Flag_1      Flags,1       ;Last Serial #1 IN had a Frame Error
#define FS_Flag_2      Flags,2       ;Serial #2 IN had a False Start Error
#define FE_Flag_2      Flags,3       ;Last Serial #2 IN had a Frame Error
#define RCV_Overflow   Flags,4       ;Lost Serial Input Byte - too Slow
#define RB_Overflow    Flags,5       ;Lost RB Input Byte - too Slow
#define S_5_S_Overflow Flags,6       ;Lost '5S Inactivity' msg - too Slow

;
; The section of equates in lines 76-85 are used to define
; the different LED activity. They are used by Task #7 to
; keep the LED blinking.
;
;Equates for LED Task #7
#define LED_B          LED_Mode,3    ;LED is active
#define LED_NEW_B      LED_Mode,7    ;LED has just changed Modes = 1
LED_OFF_MODE    equ    B'00001000'   ;LED OFF
LED_SEQ1_MODE   equ    B'10001001'   ;LED Sequence 1: .2s On, 1s Off
LED_SEQ2_MODE   equ    B'10001010'   ;LED Sequence 2: 3x(.2s), 1s Off
LED_SEQ3_MODE   equ    B'10001011'   ;LED Sequence 3: 5x(.2s), 1s Off
LED_SLOW_MODE   equ    B'10011100'   ;LED Slow Pulsing - .3 Hz
LED_MEDIUM_MODE equ    B'10011101'   ;LED Medium Pulsing - 1 Hz
LED_FAST_MODE   equ    B'10011110'   ;LED Fast Pulsing - 3 Hz
LED_ON_MODE     equ    B'10001111'   ;LED ON Continuously

; In lines 89-94, we try to save the all important first 256
;  bytes of any page.

; Clear Registers 7-1Fh 

Clear_Regs   

        GOTO    Do_Clear_Regs        ;Save space in first 256 bytes

; Determine the Highest Error Level & Start Task #7 outputing the new Level

D_H_E_L   

        GOTO    Do_D_H_E_L           ;Save space in first 256 bytes
;
; Task #1 outputs a byte Asynchronously over the Serial
; Output pin. Task #1 is started at line 98.
;  The time units used for Tasks #1-4 are 2μS.
;  Task #1 - Asynchronous 9600 Baud Serial Output (LOW=0)
;
; When Tasks #1-4 are then allowed to run, they check the
; difference between the first sample and the current time.
;  If the delta is greater than or equal to the delay,
;    then that Event has just happened.
;  We first check if the state of the Serial Output is zero.
;  We then jump to OStateS to start the outputting of the
;   “Start Bit”. Because any Serial Output timings must be
;   rock solid, we use a trick in lines 101-116 that helps greatly.
; We check if we are within a certain amount of time BEFORE
;   the deadline and then wait for the time to output another bit.
; This trick allows us to be within a certain ± amount of time
; within the expected time to output that bit. With this code,
; we are about <±8% accurate for the Serial Output.
; You can only use this trick on the most critical tasks,
;  and only on one. In this section of code, we are constantly checking
;  the delta of time from the “FIRST_TMR0_O” reading
;  and the current reading of TMR0.
; When we are very close to the output time, we jump to line 117 (_0005).
;
Do_OState
        MOVF    OState, F           ;if OState == 0   
        BTFSC   STATUS, Z           ;        
        GOTO    OStateS             ;   then goto Output-Start-Bit

		;  We first sample the TMR0 and store the count.

        MOVF    TMR0, W             ;Get current time  
        MOVWF   temp                ;    & store in temporary variable
        MOVF    First_TMR0_O, W     ;Get elapsed time; Time Unit = 2 uS 
        SUBWF   temp, F             ;Delta of Current Time & Orginal Time
        MOVLW   FUDGE_TIME          ;Take in account processing time to do it
        SUBWF   temp,W              ;Time within fudge factor ? 
        BTFSS   STATUS, C
        GOTO    _0005               ;Not time yet to change States so return
;   Check if we are within a certain amount of time BEFORE
;   the deadline and then wait for the time to output another bit.
_0003   MOVLW   OUT_BIT_TIME        ;Past time for next out-bit ? 
        SUBWF   temp,W          
        BTFSC   STATUS, C           ;Do some delaying until it is time
        GOTO    _0004               ;It is now time to out put a bit
        MOVLW   H'04'               ;Account for loop delay 
        ADDWF   temp, F
        NOP                         ;   make loop delay even
        GOTO    _0003               ;Wait for exact time to output bit


;****** 
; If we are not even close to the proper time, we exit back to the
; main loop, so we can check the other timers and tasks.
;
; Now look at Figure 4 for a description of the Output Pulses,
; the “Bit units of Time”, and the associated state numbers.
; Note that the activities are spread out over time.
; The timer Events help to define the different states and
;  their associated output activities. Each Event is
;  handled in a very short, well-defined set of code as Task #1.
; Lines 117-131, are a quick state jump table.
		; State machine  --- OState = state table Entry
_0004   MOVF    OState, W          ;Get (0-A) mode #
        ANDLW   H'0F'              ;Get only mode #  
        ADDWF   PCL, F             ;jump to subroutine
        GOTO    OStateS            ;Serial Start Bit
        GOTO    OState0_7          ;Bit 0 
        GOTO    OState0_7          ;Bit 1 
        GOTO    OState0_7          ;Bit 2 
        GOTO    OState0_7          ;Bit 3 
        GOTO    OState0_7          ;Bit 4 
        GOTO    OState0_7          ;Bit 5 
        GOTO    OState0_7          ;Bit 6 
        GOTO    OState0_7          ;Bit 7 
        GOTO    OStateE            ;Serial Stop Bit
        GOTO    OStateL            ;Last State 
_0005   RETLW   H'00'

; You need to break all Real-Time code into very short
;  segments  in and then out.
;  Each segment is just a few lines long.
; You do your activity, save status, and increment to
;  the next state.
; Notice that OState0_7 code is used several times to
;   output all 8 bits.
; The state variable (OState) is used also to count the number
;   of bits already outputted.
; The time to the next outputting of a bit is calculated
;   and is adjusted to take out the accumulation of
;   errors in lines 151-152.
OS_End  MOVLW   OUT_BIT_TIME       ;Adjust out the cumulation of error   
        ADDWF   First_TMR0_O, F
        INCF    OState, F          ;increment to next state 
        RETLW   H'00'

; We make sure of a full “Stop Bit” length in the OStateE code.
; In the OStateL code,
OStateL   
        CLRF    OState             ;Ready to send next byte out  
        BCF     OState_B           ;Serial Out not active  
        RETLW   H'00'

;   we reset the OState variable to zero, and tell the world
;   that we are not outputting now in line 157.
; This is important because we use that bit (OState_B)
;   to Signal that we need to protect the variable xmt_byte
;   that changes over several states.
; We also use it to Signal that we are ready for another byte
;   to output.
; Look at Task #4. See how it uses this Semaphore to full advantage.
; We have just explained a Critical Segment variable as
;   outlined in the theory sections of this article.
;
; Task #2 starts at line 302.
; Task #2’s subroutines continue at line 363 and continue until line 423.
; This task reads the Serial Input #1 for Asynchronous data.
; Task #2 can be described as a State Machine for outputting
;  a byte Serially.
; Task #2 reads the Serial Input line 1, running at 4800 Baud.
;******         ;Task #2 - Asynchronous 4800 Baud Serial Input (LOW=0)
Do_I1State   
        MOVF    IState1, F          ;if IState1 == 0  
        BTFSC   STATUS, Z           ;      then Do Start Bit 
        GOTO    I1StateS
        MOVF    TMR0, W             ;Get current time
        MOVWF   temp
        MOVF    First_TMR0_I1, W    ;Get elapsed time; Time Unit = 2 uS 
        SUBWF   temp, F
        MOVF    nbti1, W            ;Past time for next input bit ? 
        SUBWF   temp, W
        BTFSS   STATUS, C
        GOTO    _0033
	; State machine - IState1 = State table entry
        MOVF    IState1, W          ;Get (0-B) mode #
        ANDLW   H'0F'               ;Get only mode #  
        ADDWF   PCL, F              ;jump to subroutine
        GOTO    I1StateS            ;State - Serial Start Bit 
        GOTO    I1State2            ;State - 1/2 of Start Bit - see if False Start
        GOTO    I1State0_7          ;State - Bit 0  
        GOTO    I1State0_7          ;State - Bit 1  
        GOTO    I1State0_7          ;State - Bit 2  
        GOTO    I1State0_7          ;State - Bit 3  
        GOTO    I1State0_7          ;State - Bit 4  
        GOTO    I1State0_7          ;State - Bit 5  
        GOTO    I1State0_7          ;State - Bit 6  
        GOTO    I1State0_7          ;State - Bit 7  
        GOTO    I1StateE            ;State - Serial Stop Bit 
        GOTO    I1StateL            ;Last State - End of Stop Bit
_0033   
        RETLW   H'00'


;***            ;Subroutines for Task #2
I1StateS                           ;Start Bit - Setup timing variables
        BSF     IState1_B          ;Serial Input Active 
        MOVF    TMR0,W             ;Store starting time
        MOVWF   First_TMR0_I1
        MOVLW   H'0D'              ;Fudge again 
        SUBWF   First_TMR0_I1, F
        MOVLW   H'32'              ;Time delay = 1/2 bit time 
        MOVWF   nbti1
        INCF    IState1, F         ;Increment to next state  
        RETLW   H'00'

I1State2                           ;Check if still a Start Bit
        BTFSS   Serial_IN_1        ;False Start Error ? 
        GOTO    FS_Error_1
        BCF     FS_Flag_1          ;Start Bit OK  
        MOVF    nbti1, W           ;Adjust out the error
        ADDWF   First_TMR0_I1, F
        MOVLW   IN_BIT_TIME        ;Time Delay = full bit time
        MOVWF   nbti1
        INCF    IState1, F         ;increment to next state
        RETLW   H'00'

I1State0_7                         ;Bit 0 - 7 
        BTFSS   Serial_IN_1        ;Move Input bit into C    
        BCF     STATUS,C
        BTFSC   Serial_IN_1
        BSF     STATUS,C
        RRF     rcv_byte_1, F      ;Move C into left most bit 
        MOVF    nbti1,W
        ADDWF   First_TMR0_I1, F   ;Adjust out the error
        INCF    IState1, F         ;increment to next state 
        RETLW   H'00'

I1StateE                           ;Check if we have a proper Stop Bit
        BTFSC   Serial_IN_1        ;Frame Error
        GOTO    F_Error_1
        BCF     FE_Flag_1          ;Stop Bit OK
        CLRF    T_5_S_CO           ;Reset 5 Sec Timer - got a good byte 
        ;Process the msg Here !
        MOVF    rcv_byte_1, W      ;Make a copy of just received byte  
        MOVWF   RCV_Storage
        BTFSS   RCV_Got_One_B      ;Report Lost data 
        BCF     RCV_Overflow
        BTFSC   RCV_Got_One_B
        BSF     RCV_Overflow
        BSF     RCV_Got_One_B      ;We Now have a RB Value to go out 
I1StateL   
        CLRF    IState1            ;Ready to receive next byte
        BCF     IState1_B          ;Serial In not currently active
        RETLW   H'00'

FS_Error_1                         ;False Start - Shut Down Checking
        BCF     IState1_B          ;Serial Input NOT Active
        BSF     FS_Flag_1          ;False Start Error 
        GOTO    I1StateL           ;Start All Over 
F_Error_1                          ;Frame Error - Wait for End of Stop Bit 
        MOVF    nbti1,W            ;Adjust out the error
        ADDWF   First_TMR0_I1, F
        MOVLW   H'32'              ;Time Delay = 1/2 bit time
        MOVWF   nbti1
        BSF     FE_Flag_1          ;Frame Error for this Byte ?
        INCF    IState1, F         ;Increment to next state
        RETLW   H'00'

; The code structure is very similar to that of Task #1 (Figure 3).
; Notice that there are more states than the Serial Output Task #1.
;  Once the “Start Bit” is detected, we half step into the
;  “Start Bit” to see if it was a “False Start” or not.
; We then sample and store theincoming bits to form an 8-bit byte
;  just like Task #1.
;  We sample the “Stop Bit” to see if it is a “Frame Error”.
;  We delay another 1/2 bit to get to the end of the “Stop Bit”
;   if there was an “Frame Error” before resetting Task #1’s
;   state to 0. Otherwise, we reset Task #1’s state to 0, and
;   Signal that we are ready for another “Start Bit”.
; The just received byte is stored in variable “RCV_Storage”.
; A check is made to see if we already sent out the last
;  received byte before clobbering the old byte with the new byte.
;

; Task #3 reads the Serial Input line 2, running at 4800 Baud.
; The code structure is the same as Task #2
; Task #3 interrupts the code of Task #2 at line 333 and continues
;   until line 362.
; Task #3’s subroutines continue at line 424 and continue until line 484
;   is reached.
; The received byte is also put into the same
; storage variable as Task #2 - “RCV_Storage”.
; When either Task #2 or Task #3 receives a valid byte,
;   Task #8’s counter is reset.
;   You can up the Baud rate of Task #2 and 3 if you lower the output
;   Baud rate of Task #1.
; Note that for reading the Serial Input Lines, you can be
; off by ±15% for each sampling, but not accumulatively

;******    ;Task #3 - Asynchronous 4800 Baud Serial Input (LOW=0)

Do_I2State   
        MOVF    IState2, F          ;if IState1 == 0
        BTFSC   STATUS, Z           ;   then Do Start Bit 
        GOTO    I2StateS
        MOVF    TMR0, W             ;Get current time
        MOVWF   temp
        MOVF    First_TMR0_I2, W    ;Get elapsed time; Time Unit = 2 uS
        SUBWF   temp, F
        MOVF    nbti2, W            ;Past time for next input bit ? 
        SUBWF   temp, W
        BTFSS   STATUS, C
        GOTO    _0035
	; State machine - IState2 = State table entry
        MOVF    IState2,W           ;Get (0-B) mode #
        ANDLW   H'0F'               ;Get only mode #  
        ADDWF   PCL, F              ;jump to subroutine 
        GOTO    I2StateS            ;State - Serial Start Bit 
        GOTO    I2StateS2           ;State - 1/2 of Start Bit - see if False 
Start  
        GOTO    I2State0_7          ;State - Bit 0
        GOTO    I2State0_7          ;State - Bit 1
        GOTO    I2State0_7          ;State - Bit 2
        GOTO    I2State0_7          ;State - Bit 3
        GOTO    I2State0_7          ;State - Bit 4
        GOTO    I2State0_7          ;State - Bit 5
        GOTO    I2State0_7          ;State - Bit 6
        GOTO    I2State0_7          ;State - Bit 7
        GOTO    I2StateE            ;State - Serial Stop Bit 
        GOTO    I2StateL            ;Last State - End of Stop Bit 
_0035   RETLW   H'00'
;
;***    ;Subroutines for Task #3
I2StateS                           ;Start Bit - Setup timing variables
        BSF     IState2_B          ;Serial Input Active  
        MOVF    TMR0, W            ;Store starting time 
        MOVWF   First_TMR0_I2
        MOVLW   H'0D'              ;Fudge again   
        SUBWF   First_TMR0_I2, F
        MOVLW   H'32'              ;Time delay = 1/2 bit time
        MOVWF   nbti2
        INCF    IState2, F         ;Increment to next state
        RETLW   H'00'

I2StateS2                          ;Check if still a Start Bit
        BTFSS   Serial_IN_2        ;False Start Error ?  
        GOTO    FS_Error_2
        BCF     FS_Flag_2          ;Start Bit OK 
        MOVF    nbti2,W            ;Adjust out the error 
        ADDWF   First_TMR0_I2, F
        MOVLW   IN_BIT_TIME        ;Time Delay = full bit time 
        MOVWF   nbti2
        INCF    IState2, F         ;increment to next state
        RETLW   H'00'

I2State0_7                         ;Bit 0 - 7 
        BTFSS   Serial_IN_2        ;Move Input bit into C
        BCF     STATUS, C
        BTFSC   Serial_IN_2
        BSF     STATUS, C
        RRF     rcv_byte_2, F      ;Move C into left most bit 
        MOVF    nbti2, W
        ADDWF   First_TMR0_I2, F   ;Adjust out the error    
        INCF    IState2, F         ;increment to next state 
        RETLW   H'00'

I2StateE                           ;Check if we have a proper Stop Bit
        BTFSC   Serial_IN_2        ;Frame Error
        GOTO    F_Error_2
        BCF     FE_Flag_2          ;Stop Bit OK 
        CLRF    T_5_S_CO           ;Reset 5 Sec Timer - got a good byte
        ;Process the msg Here !
        MOVF    rcv_byte_2, W      ;Make a copy of just received byte 
        MOVWF   RCV_Storage
        BTFSS   RCV_Got_One_B      ;Report Lost data 
        BCF     RCV_Overflow
        BTFSC   RCV_Got_One_B
        BSF     RCV_Overflow
        BSF     RCV_Got_One_B      ;We Now have a RB Value to go out   
I2StateL   
        CLRF    IState2            ;Ready to receive next byte 
        BCF     IState2_B          ;Serial In not currently active 
        RETLW   H'00'

FS_Error_2   
        BCF     IState2_B          ;False Start - Shut Down Checking
        BSF     FS_Flag_2          ;False Start Error 
        GOTO    I2StateL           ;Start All Over

F_Error_2                          ;Frame Error - Wait for End of Stop Bit
        MOVF    nbti2,W            ;Adjust out the error 
        ADDWF   First_TMR0_I2, F
        MOVLW   H'32'              ;Time Delay = 1/2 bit time 
        MOVWF   nbti2
        BSF     FE_Flag_2          ;Frame Error for this Byte ? 
        INCF    IState2, F         ;Increment to next state 
        RETLW   H'00'

;
; Task #4 starts at line 538 and finishes at line 561.
; Task #4 controls the feeding of Task #1 from several other tasks
; that want data to be outputted. It uses several Semaphores
; to make sure that Task #1 is not bothered until
; it is ready for another byte.
;
Task_4 ;Task #4 - Finds next Buffered Byte to Send Out through Task 1
        BTFSC   OState_B           ;if outputing now then skip call
        GOTO    _0059
        BTFSS   RCV_Got_One_B      ;Got a NEW Received byte to send
        GOTO    _0057
        MOVF    RCV_Storage, W     ;Send just received byte 
        MOVWF   xmt_byte
        BCF     RCV_Got_One_B      ;Clear need to send old byte 
        BSF     OState_B           ;Start Task #1 & Lock Out Others 
        GOTO    _0059

_0057   BTFSS   RB_NEW_B           ;Indicates a change in RB input 
        GOTO    _0058
        MOVF    Old_RB, W          ;Send New RB value  
        MOVWF   xmt_byte
        BCF     RB_NEW_B           ;Clear need to send out newest value 
        BSF     OState_B           ;Start Task #1 & Lock Out Others
        GOTO    _0059

_0058   BTFSS   S_5_S_B            ;Serial In 5 secs of inactivity
        GOTO    _0059
        MOVLW   H'FF'              ;Tell of inactivity of Serial In 
        MOVWF   xmt_byte
        BCF     S_5_S_B            ;Clear need to send msg  
        BSF     OState_B           ;Start Task #1 & Lock Out Others
                
       ;Heart Beat - Time unit = 512 uS for Tasks #5 & #6
_0059   MOVF    TMR0,W             ;Step-up time units * 512  
        MOVWF   temp
        MOVF    Last_TMR0, W       ;Test to see if it overflowed 
        SUBWF   temp, W
        BTFSS   STATUS, C
        GOTO    Inc_Time
        MOVF    temp, W            ;unit error = < |+-512 uS| 
        MOVWF   Last_TMR0
        GOTO    Task_1             ;Serial Output

Inc_Time
        MOVF    temp, W            ;Save current TMR0 into Last_TMR0
        MOVWF   Last_TMR0

; Task #4 finds the next buffered byte to send out through Task #1.
; Task #4 also controls the order of which byte
; goes first over another less important byte of data.
; It can be said that Task #1 Blocks Task #4 from running.
; You can think of the Serial Output Line as a Shared Resource.
; The use of Semaphores here allow the Synchronization of data
;  and actions.
;
; Task#5 runs through lines 576-581, and is very short.

Task_5  ; - Monitor Level Reset Input Line - Always Running ! 
        BTFSC   Level_Reset
        GOTO    Task_6             ; Debounces sensors input lines
        MOVLW   LED_OFF_MODE       ;Lowest Level Indicator output
        MOVWF   LED_Mode

; Task #5 monitors the Level Reset Input Line and will
; reset the LED state variable if the line ever goes low.
; This task is always in the Ready State. This task is said
; to simply “pole the input line” when ever it can.
;
; Task #6 debounces the seven sensor input lines,
; running every 20 ms. Lines 582-611 represent Task #6.
; The variable “T_20_mS_CO” is incremented every 512 μs (Clock Tick)
; and is compared to the count needed to equal 20 ms. If it is time,
; the subroutine QCheck_T123 is called to see if Tasks
; #1-3 are in the Ready State.

Task_6  ;Task #6 - Debounce 8 bit Input Sensors - Runs every 20 mS 
        INCF    T_20_mS_CO, F      ;Inc Counter - Time Unit = 512 uS
        MOVLW   H'27'              ;Used to debounce the input 
        SUBWF   T_20_mS_CO, W
        BTFSS   STATUS, Z
        GOTO    _0065
        CLRF    T_20_mS_CO         ;Reset T_20_mS_CO to start over again
        CALL    QCheck_T123        ;Quick Check of Tasks #1, #2 and #3 
        MOVF    PORTB,W            ;Last copy of RB same as Current ? 
        SUBWF   Last_RB,W
        BTFSC   STATUS, Z        
        GOTO    _0062
        MOVF    PORTB, W           ;  Store Current RB - diff from Last
        MOVWF   Last_RB
        GOTO    _0063

_0062   MOVF    Last_RB, W         ;New Old RB <- same value over 20 mS 
        MOVWF   Old_RB
_0063   MOVF    Old_RB, F          ;See if RB is now 0 
        BTFSC   STATUS,Z           ;RB == 0 ? then keep timer running 
        GOTO    _0064
        CLRF    T_5_M_LO           ;Reset 5 Min Timer 
        CLRF    T_5_M_HI           ;  still not zero yet 
_0064   CALL    D_H_E_L            ;Determine the Highest Error Level
        BTFSS   RB_NEW_B           ;Check for Lost Data Error 
        BCF     RB_Overflow
        BTFSC   RB_NEW_B
        BSF     RB_Overflow
        BSF     RB_NEW_B           ;Every 20 mS send Old_RB out
                
        ;Heart Beat - Time unit = 131072 uS for Tasks #7, #8 & #9
_0065   MOVLW   H'F9'              ;RA TRIS - refresh RA=1001
        TRIS    5
        MOVLW   H 'FF'             ;RB TRIS - refresh 
        TRIS    6
        DECFSZ  Curnt_Cnt, F	   ;Step-up time units * 256 
        GOTO    Task_1             ;Serial Output
;
; If any of the Tasks #1-3 are ready, they are ran and we then
;  continue with Task #6.
; We compare the current value of the input Port_B to
; see if it stayed the same from the last reading 20 ms back.
; If the two readings are the same, then Port_B is
;  considered to be stable and the possibly new value is
;  placed in the variable “Old_RB” to be outputted by Task#1.
;

; The subroutine D_H_E_L is called to determine the new LED state.
; We then check if Task #1 was too busy to output the last sensor
; status byte, if so then that error is recorded.
;
; Do_D_H_E_L determines the LED’s next state based
;   on the 7 sensor input status.
; This subroutine checks each bit to see if it is active and then
;   checks if a change in the LED’s current state needs changing.

;****

Do_D_H_E_L      
; Determine the Highest Error Level & Start Task #7
; - Output Highest Level Indication on LED

        MOVLW   H'07'              ;Check top 7 bits 
        MOVWF   temp
        MOVF    Old_RB, W          ;Get copy of 7 debounced Sensor Input
        MOVWF   Last_RB
_0070   RLF     Last_RB, F         ;Put top bit into C bit
        BTFSC   STATUS, C          ;Check if C bit is set 
        GOTO    _0072
        DECFSZ  temp, F            ;Continue to check lesser bits
        GOTO    _0070
_0071   MOVF    PORTB, W           ;Restore current value of RB 
        MOVWF   Last_RB
        RETLW   H'00'

_0072  ; Implimentation off  missing instruccion SUBLF  L, RAM-Reg
        MOVF    LED_Mode, W        ;Get current Level Indicator 
        ANDLW   H'07'              ;Get only      "       "  
        MOVWF   Last_RB            ;Store into a tempary register 
        MOVF    temp, W            ;Check if already at this Level
        SUBWF   Last_RB, W
		                   ; end implementation
        BTFSC   STATUS, C
        GOTO    _0071
        MOVLW   H'88'              ;Start to build LED_Mode 
        IORWF   temp, W            ;Put new Level Indicator into reg 
        MOVWF   LED_Mode           ;Store new LED Mode 
        GOTO    _0071

; Task #7 outputs the Highest Severity Level Indicationon the LED.
; Task #7’s main code is lines 621-628.
;
; Do_LED starts at line 161, and continues to 276.
; This task is also broken into small time units of code.
; It is constantly checking to see if it is time to switch the
;  on/off condition of the LED.
;  The time units for Task #7 are regulated by the code in lines 613-619.
;  131072 μS = time unit for Tasks #7-9.
; Task #7 has many state jump tables so it is included in the
;  first 256 bytes of the first page.
;  Lines 168-175 explain the on and off sequences and offs that
;  represent levels of severity of the input status lines.
;  The variable “LED_Mode” has both Task #7’s current state number
;   and the sub-state-number for that state’s output sequence.
;
;  Task #8 is a 5 second lack of input from either of the two
;  Serial input timers. Tasks #2 and #3 will reset the time
;   counter for Task #8, when either receives a full byte.
;  If the time counter “T_5_S_CO” equals 5 secs, then the
;   LED’s state is bumped to the highest, and a special
;   byte is sent down the line to the next “Remote Alarm” unit.
;   The counter variable is reset, and count starts all over.
; We then check if Task #1 was too busy to output
;   the last special status byte, if so then that error is recorded.
;
; Task #9 measures 5 minutes of calm on the 7 sensor
;   lines and then resets the LED’s state.
; Lines 646-663 compose Task #9.

Task_9  ; - 5 Min. Lack of Severe Error from Sensors Reset Timer 
        BTFSS   T_5_M_B            ;5 Min Timer Active ? 
        GOTO    Task_A
        ; 16-bit increment
        INCF    T_5_M_LO, F        ;Inc LO Counter; Time Unit = 131072 uS
        BTFSC   STATUS,Z           ;  See if carry needs to be passed on ?
        INCF    T_5_M_HI, F        ;Inc HI Counter; Time Unit = 131072 uS
		                   ; end of 16-bit increment
        MOVLW   H'08'              ;#2288<  Check T_5_M_HI if time  
        SUBWF   T_5_M_HI, W
        BTFSS   STATUS, Z
        GOTO    Task_A
        MOVLW   H'F0'              ;#2288>  Check T_5_M_LO if time 
        SUBWF   T_5_M_LO, W
        BTFSS   STATUS, Z
        GOTO    Task_A
        CLRF    T_5_M_LO           ;Reset T_5_M_LO 
        CLRF    T_5_M_HI           ;Reset T_5_M_HI 
        MOVLW   LED_OFF_MODE       ;Lowest Level Indicator output 
        MOVWF   LED_Mode
Task_A   
        GOTO    Task_1             ; Loop Forever

; Task #9 needs 16 bits of counter power to record 5 minutes of time.
; The counter variables are reset after being triggered.


; Subrutie Do_Clear_Regs clears registers 7-1Fh.
; It leaves the FSR register zeroed out.
; This is very important for the PIC16C57 chip.

Do_Clear_Regs   ; Limpia Registros RAM  07h-1Fh
        MOVLW   H'1F'             ;Write the base (First regs to clear)
        MOVWF   FSR               ; into FSR 
Loop_C  CLRF    INDIR             ; Limpia el registro apuntado 
        DECF    FSR, F            ;   y apunta al siguiente registro
; Con el PIC16F54, en el reg FSR, solo podemos cambiar los bits 4 -> 0
; cuando estan en 1 1111 e incrementamos FSR este pasa a 0 0000 sin afectar
;      los bits 5,6 & 7.
        MOVLW   H'E7'             ;Vemos si ya alcanzamos el limite bajo
        SUBWF   FSR, W            ;  de RAM el cual es 07h pero como los
                                  ;  bits 5, 6, y 7 de
                                  ; FSR estan ajustados a 1 ... debemos
                                  ; subtraer  E7h
        BTFSC   STATUS, C         ;
        GOTO    Loop_C            ;  No: Regresa hasta que se alcanze
        CLRF    FSR               ;  Si:  Limpia el reg FSR
        RETLW   H'00'             ;       y regresa

; The main or starting code is started at line 485.
; From that line to line 515, all variables are initialized, and all tasks
;  are initialized at this time also.
;******         ;Code Starting point

Main
	; Configura Puertos y TMR0
        MOVLW   H'00'             ;What is High/Low for RA at INIT State 
        MOVWF   PORTA
        MOVLW   H'00'             ;What is High/Low for RB at INIT State 
        MOVWF   PORTB
        MOVLW   H'F9'             ;RA TRIS at INIT State  
        TRIS    5
        MOVLW   H'FF'             ;RB TRIS at INIT State 
        TRIS    6
        MOVLW   H'00'             ; Set TMR0/2 
	; bit5-Clock source = Internal  bit4-Edge =low-to-high transition
	; bit3-Prescaler on TMR0        bit210- Prescaler = 1:2
        OPTION
        CALL    Clear_Regs        ;Clear Registers 7-1F - Same Memory Page
        CLRF    TMR0              ;Start timers

;Initialize Tasks
                                  ;Task #1 waits for byte to output
                                  ;Asynchronous 9600 Baud Serial Output
;
                                  ;Task #2 waits for Serial IN Start Bit
                                  ;1st Asynchronous 4800 Baud Serial Input
;
                                  ;Task #3 waits for Serial IN Start Bit
                                  ;2nd Asynchronous 4800 Baud Serial Output
;
                                  ;Task #4 runs when Task 1 is Not
                                  ;  Finds next Buffered Byte to Send Out
                                  ;  through Task 1
;
                                  ;Task #5 is always running
                                  ;   Monitor Level Reset Input Line
;
                                  ;Task #6  - Runs every 20 mS
                                  ;   Debounce 8 bit Input Sensors
        MOVF    PORTB, W        ;Task #6 is Initialized here 
        MOVWF   Old_RB
        MOVF    Old_RB, W         ;Make all the same initial value 
        MOVWF   Last_RB
        BSF     RB_NEW_B          ;Tell Task #4: RB byte ready to output
;								
                                  ;Task #7  - Runs every 20 mS
                                  ;   Output Highest Level Indication on LED
        MOVLW   LED_OFF_MODE
        MOVWF   LED_Mode          ;Task #7 is Started 
;
                                  ;Task #8 - Runs every 20 mS
                                  ;   5 Second Serial Input Lack of
                                  ;   Activity Timer
        BSF     T_5_S_B           ;Task #8 is Started here  
;
                                  ;Task #9 - 5 Min. Lack of Severe Error
                                  ;   from Sensors Reset Timer
        BSF     T_5_M_B           ;Task #9 is Started here  

; The Main Loop is started at line 516 and ends at line 665.
; This is where the real action is done.
; Each task checks the time to see if the conditions are correct for it to run.
; The tasks that are not Blocked, and have a job to do now are in a Ready State.
; In the Main Loop, we check the current state of
; each task in order of Priority (1-9). If ready, we do a
; very simple Task Switch and place that task in the
; Executing State/Running State. Several time unit
; changes take place in the Main Loop.
; Handle Task & Timer activities - Main Loop

Task_1  ;Task #1 - Asynchronous 9600 Baud Serial Output (LOW=0)
        BTFSS   OState_B          ;if not outputing now then skip call 
        GOTO    Task_2
        CALL    Do_OState         ;Go Do Task #1

Task_2  ;Task #2 - Asynchronous 4800 Baud Serial Input (LOW=0)
        BTFSC   IState1_B         ;if already started then call
        GOTO    _0053
        BTFSC   Serial_IN_1       ;if Start bit ? then call
        GOTO    _0053
        GOTO    Task_3

_0053   CALL    Do_I1State        ;Go Do Task #2


Task_3  ;Task #3 - Asynchronous 4800 Baud Serial Input (LOW=0)
        BTFSC   IState2_B         ;if already started then call 
        GOTO    _0055
        BTFSC   Serial_IN_2       ;if Start bit ? then call 
        GOTO    _0055
        GOTO    Task_4
_0055   CALL    Do_I2State        ;Go Do Task #3

Task_4  ;Task #4 - Finds next Buffered Byte to Send Out through Task 1
        BTFSC   OState_B          ;if outputing now then skip call
        GOTO    _0059
        BTFSS   RCV_Got_One_B     ;Got a NEW Received byte to send
        GOTO    _0057
        MOVF    RCV_Storage, W    ;Send just received byte 
        MOVWF   xmt_byte
        BCF     RCV_Got_One_B     ;Clear need to send old byte 
        BSF     OState_B          ;Start Task #1 & Lock Out Others 
        GOTO    _0059

_0057   BTFSS   RB_NEW_B          ;Indicates a change in RB input 
        GOTO    _0058
        MOVF    Old_RB, W         ;Send New RB value  
        MOVWF   xmt_byte
        BCF     RB_NEW_B          ;Clear need to send out newest value 
        BSF     OState_B          ;Start Task #1 & Lock Out Others
        GOTO    _0059

_0058   BTFSS   S_5_S_B           ;Serial In 5 secs of inactivity
        GOTO    _0059
        MOVLW   H'FF'             ;Tell of inactivity of Serial In 
        MOVWF   xmt_byte
        BCF     S_5_S_B           ;Clear need to send msg  
        BSF     OState_B          ;Start Task #1 & Lock Out Others
                
        ;Heart Beat - Time unit = 512 uS for Tasks #5 & #6
_0059   MOVF    TMR0,W            ;Step-up time units * 512  
        MOVWF   temp
        MOVF    Last_TMR0, W      ;Test to see if it overflowed 
        SUBWF   temp, W
        BTFSS   STATUS, C
        GOTO    Inc_Time
        MOVF    temp, W           ;unit error = < |+-512 uS| 
        MOVWF   Last_TMR0
        GOTO    Task_1
            ;Serial Output
Inc_Time   
        MOVF    temp, W           ;Save current TMR0 into Last_TMR0
        MOVWF   Last_TMR0

Task_5  ;Task #5 - Monitor Level Reset Input Line - Always Running ! 
        BTFSC   Level_Reset
        GOTO    Task_6
        MOVLW   LED_OFF_MODE      ;Lowest Level Indicator output
        MOVWF   LED_Mode

Task_6  ;Task #6 - Debounce 8 bit Input Sensors - Runs every 20 mS 
        INCF    T_20_mS_CO, F     ;Inc Counter - Time Unit = 512 uS
        MOVLW   H'27'             ;Used to debounce the input 
        SUBWF   T_20_mS_CO, W
        BTFSS   STATUS, Z
        GOTO    _0065
        CLRF    T_20_mS_CO        ;Reset T_20_mS_CO to start over again
        CALL    QCheck_T123       ;Quick Check of Tasks #1, #2 and #3 
        MOVF    PORTB,W           ;Last copy of RB same as Current ? 
        SUBWF   Last_RB,W
        BTFSC   STATUS, Z        
        GOTO    _0062
        MOVF    PORTB, W          ;  Store Current RB - diff from Last
        MOVWF   Last_RB
        GOTO    _0063

_0062   MOVF    Last_RB, W        ;New Old RB <- same value over 20 mS 
        MOVWF   Old_RB
_0063   MOVF    Old_RB, F         ;See if RB is now 0 
        BTFSC   STATUS,Z          ;RB == 0 ? then keep timer running 
        GOTO    _0064
        CLRF    T_5_M_LO          ;Reset 5 Min Timer 
        CLRF    T_5_M_HI          ;  still not zero yet 
_0064   CALL    D_H_E_L           ;Determine the Highest Error Level
        BTFSS   RB_NEW_B          ;Check for Lost Data Error 
        BCF     RB_Overflow
        BTFSC   RB_NEW_B
        BSF     RB_Overflow
        BSF     RB_NEW_B          ;Every 20 mS send Old_RB out
                
        ;Heart Beat - Time unit = 131072 uS for Tasks #7, #8 & #9
_0065   MOVLW   H'F9'             ;RA TRIS - refresh RA=1001
        TRIS    5
        MOVLW   H'FF'             ;RB TRIS - refresh 
        TRIS    6
        DECFSZ  Curnt_Cnt, F	  ;Step-up time units * 256 
        GOTO    Task_1            ;Serial Output

Task_7          ;Task 7 - Output Highest Level Indication on LED
        BTFSS   LED_B             ;Is LED active ? 
        GOTO    Task_8
        CALL    QCheck_T123       ;Quick Check of Tasks #1, #2 and #3 
        CALL    Do_LED            ;Handle LED timing
                                  
Task_8  ;Task #8 - 5 Second Serial Input Lack of Activity Timer
        BTFSS   T_5_S_B           ;5 Sec Timer Active ?  
        GOTO    Task_9
        INCF    T_5_S_CO, F       ;Inc Counter - Time Unit = 131072 uS 
        MOVLW   H'26'             ;Check T_5_S_CO if time  
        SUBWF   T_5_S_CO, W
        BTFSS   STATUS, Z
        GOTO    Task_9
        CLRF    T_5_S_CO          ;Reset T_5_S_CO 
        MOVLW   LED_ON_MODE       ;Highest Level Indicator output 
        MOVWF   LED_Mode
        BTFSS   S_5_S_B           ;Check if Lost Data Error 
        BCF     S_5_S_Overflow
        BTFSC   S_5_S_B
        BSF     S_5_S_Overflow
        BSF     S_5_S_B           ;Send notice of 5 seconds of inaction  

Task_9  ;Task #9 - 5 Min. Lack of Severe Error from Sensors Reset Timer 
        BTFSS   T_5_M_B           ;5 Min Timer Active ? 
        GOTO    Task_A

        ; 16-bit increment
        INCF    T_5_M_LO, F       ;Inc LO Counter; Time Unit = 131072 uS 
        BTFSC   STATUS,Z          ;  See if carry needs to be passed on ?
        INCF    T_5_M_HI, F       ;Inc HI Counter; Time Unit = 131072 uS
	; end of 16-bit increment
        
        MOVLW   H'08'             ;#2288 < Check T_5_M_HI if time  
        SUBWF   T_5_M_HI, W
        BTFSS   STATUS, Z
        GOTO    Task_A
        MOVLW   H'F0'             ;#2288 > Check T_5_M_LO if time 
        SUBWF   T_5_M_LO, W
        BTFSS   STATUS, Z
        GOTO    Task_A
        CLRF    T_5_M_LO          ;Reset T_5_M_LO 
        CLRF    T_5_M_HI          ;Reset T_5_M_HI 
        MOVLW   LED_OFF_MODE      ;Lowest Level Indicator output 
        MOVWF   LED_Mode

Task_A   
        GOTO    Task_1          ; Loop Forever
```
Espero que les sea útil.


----------



## ERICK ATEM (Jun 3, 2017)

Hola amigos soy nuevo pero necesito ayuda, lo que tengo que hacer es un encoder con un PIC18F4550, mi señal esta siendo analisada en mi puerto A0, con el sensor H21a1 y estoy utilizando un KEYPAD, si coloco 123 el motor gira a la derecha y mi a=a+1; si coloco 456 mi motor se detiene; si coloco 789 el motor giro a la izquierda y mi a=a-1.
en mi programación se queda anclado en el ciclo While y no logro sacarlo de ahi porque solo hace mi adquisición de datos de mi KEYPAD 1 sola vez y se queda en el contador.

esta es mi programación, porfavor que devo agregar


----------



## Scooter (Jun 3, 2017)

Para hacer varias cosas a la vez, lo primero de todo es borrar de tu mente y para siempre los delays. (la plaga de la programación)

Lo segundo es trabar ambos programas o hacer primero uno y luego otro consecutivamente. 
Yo personalmente voto por la primera opción si se puede.


----------



## Nuyel (Jun 3, 2017)

Para eso debes emplear interrupciones y banderas, el timer es recomendable para escanear el teclado, el procesamiento lo haces por condicional o durante la interrupción del teclado, el encoder también debes controlarlo por interrupción o el timer en modo capture.


----------



## Dr. Zoidberg (Jun 3, 2017)

O usas el kernel real time del ccs


----------



## Saint_ (Jun 4, 2017)

hola ERICK ATEM, te comento que tu programa esta muy mal estructurado.
Usando solo la rutina principal no podras tener un buen rendimiento en tu programa, las interrupciones son lo que le dan poder a un uC.
Este un ejemplo de como usarlas en tu programa.

```
/*****************************************************************************/
#include <18F4550.h>
#device PASS_STRINGS=IN_RAM
#FUSES NOWDT,NOPUT,NOPROTECT,NOBROWNOUT,NOLVP,NOCPD,NOWRT,NODEBUG
#use delay(xtal=4000000)
/*****************************************************************************/
#define LCD_ENABLE_PIN  PIN_B3
#define LCD_RS_PIN      PIN_B1
#define LCD_RW_PIN      PIN_B2
#define LCD_DATA4       PIN_B4
#define LCD_DATA5       PIN_B5
#define LCD_DATA6       PIN_B6
#define LCD_DATA7       PIN_B7
#include <lcd.c>
/*****************************************************************************/
#include "kbd_4x4.c"
/*****************************************************************************/
#INCLUDE <ctype.h>
#include <stdlib.h>
#include <string.h>
/*****************************************************************************/
signed int16 a=0;
char bandera_teclado=0;
char bandera_lcd=0;
/*****************************************************************************/
#INT_TIMER1
void interrupcion_timer1()
{
   static unsigned int8 contador;
   set_timer1(64536);
   bandera_teclado=1;
   if(++contador==50)
   {
      contador=0;
      bandera_lcd=1;
   }
}
/*****************************************************************************/
#INT_EXT
void interrupcion_externa0()
{
   if(input_state(pin_a0))
   {
      a++;
   }
   else if(input_state(pin_a1))
   {
      a--;
   }
}
/*****************************************************************************/
void leer_teclado()
{
   char c;
   static char codigo[4];
   static unsigned int8 n;
   c=kbd_getc();
   if(isdigit(c))
   {
      if(n==3)
      {
         n=0;
         lcd_gotoxy(1,2);
         lcd_putc("   ");
      }
      codigo[n++]=c;
      lcd_gotoxy(n,2);
      lcd_putc(c);
   }
   else if(c=='#')
   {
      codigo[n]='\0';
      n=0;
      lcd_gotoxy(1,2);
      lcd_putc("   ");
      lcd_gotoxy(12,1);
      if(!strcmp(codigo,"123"))
      {
         lcd_putc("ON  H");
         output_high(pin_a0);
         output_low(pin_a1);
      }
      else if(!strcmp(codigo,"456"))
      {
         lcd_putc("OFF");
         output_low(pin_a0);
         output_low(pin_a1);
      }
      else if(!strcmp(codigo,"789"))
      {
         lcd_putc("ON  A");
         output_low(pin_a0);
         output_high(pin_a1);
      }
   }
}
/*****************************************************************************/
void main()
{
   output_low(pin_a0);
   output_low(pin_a1);
   kbd_init();
   lcd_init();
   lcd_putc("INTRODUCIR|OFF H\n");
   lcd_putc("   |N=          ");
   lcd_gotoxy(1,2);
   set_timer1(64536);
   setup_timer_1(T1_INTERNAL|T1_DIV_BY_1);
   enable_interrupts(INT_TIMER1);
   enable_interrupts(INT_EXT_H2L);
   enable_interrupts(GLOBAL);
   while(true)
   {
      if(bandera_teclado)
      {
         leer_teclado();
         bandera_teclado=0;
      }
      if(bandera_lcd)
      {
         lcd_gotoxy(7,2);
         printf(lcd_putc,"%06Ld",a);
         bandera_lcd=0;
      }
   }
}
```


----------



## pandacba (Jun 4, 2017)

AN585 for microchip


----------

