Practica PIC18f4550 comunicacion SPI

Estoy tratando de comunicar 2 pic 18f4550 via spi, he hecho la simulacion en Proteus y ya he conseguido la comunicacion completa,
Desde el master envio un dato el slave lo recibe y para comprobar que lo recibio bien se lo reenvio al master, y al parecer en la simulacion todo funciona muy bien

Sin embargo en el circuito solo consigo que el master envie el dato a enviar, el pulso de reloj y el SS (slave select en bajo) pero el slave, no me detecta la recepcion del dato.

si alguien tiene informacion, le agradeceria mucho la ayuda.

PD: he probado con ccristal externo a 16 MHz y tambien interno a 8 y 4 Mhz el comportamiento
es semejante en todos los casos.
 
Les comento que he solucionado el problema despues de unos dias de sufrirle leyendo el datasheet y algunos foros como este lo arregle, resulta que el pic para usar de manera obtima sus pines como I/O se deben de configurar dentro de los registros de ADC como pin I/O y desactivarlos de que puedan ser analogicos, ya que aun configurando solo con el TRIS era insuficiente.

si le surge alguna duda, pregunten que estamos todos para ayudarnos
 
resulta que el pic para usar de manera optima sus pines como I/O se deben de configurar dentro de los registros de ADC como pin I/O y desactivarlos de que puedan ser analógicos, ya que aun configurando solo con el TRIS era insuficiente.

Benja, podrías explicarme un poquito más este tema de la configuración en los registros?
Tengo una simulación de PROTEUS funcionando perfecto, un maestro 14k50 enviando datos sincronizados con el /SS, todo chequeado en los pines de entrada del esclavo, la info llega bien al segundo PCB, los niveles lógicos no se degradan en la transmisión.
Sin embargo, en el esclavo no salta la interrupción por recepción completa de SSP.
Aunque tiene el dato disponible y el valor de /SS bajo, o no recibe el valor, o bien la interrupción no salta a pesar de haber recibido el dato.

CODIGO MAESTRO

Código:
#include <18F14K50.h>
#device adc=8                    //8 bts 256 cuentas

#FUSES INTRC_IO                  //High Speed Crystal/Resonator with PLL enabled
#FUSES CPUDIV2
#FUSES MCLR                      //Master Clear pin enabled
#FUSES NOWDT                     //No Watch Dog Timer
#FUSES NOPROTECT                 //Code not protected from reading
#FUSES NOLVP                     //No utilizamos el modo de programación con bajo voltaje, B5(PIC18) used for I/O
#FUSES NODEBUG                   //No usamos codigo para debuguear 

#use delay(clock=8MHz)

#define VMAX    255.00                
#define AN1M    127.00    
#define AN2M    127.00
#define AN_DERECHA    128    
#define AN_IZQUIERDA    127
#define AN_ADELANTE    128
#define AN_ATRAS        127

#define SS PIN_B7

int8 AN1=0, AN2=0, V1, V2, direccion=0;    // para atras 1
int1 adq=0;
float Vo1, Vd1, Vi1;
float Vo2, Vd2, Vi2;

////////////////////////////////////////////////////////////////////////////////
////////// INTERRUPCIONES //////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

#int_TIMER0
void  TIMER0_isr(void) 
{
   set_timer0(15536);             // 50ms
   
   adq=1;
   output_toggle(PIN_C1);      // solo para chequeo en simulacion u operación
}

////////////////////////////////////////////////////////////////////////////////
////////// FUNCIONES ///////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

void adq_pote(void)
{
        set_adc_channel(7);        // desplazamiento longitudinal             
        delay_us(10);
        AN1 = read_adc();

        set_adc_channel(8);        // desplazamiento lateral
        delay_us(10);
        AN2 = read_adc();
}


void calc_vel(void)
{

}
    
    
void send(void)
{
    output_low(SS);

    spi_write(V1);
    delay_us(10);

    spi_write(V2);
    delay_us(10);

    spi_write(direccion);
    delay_us(10);

    output_high(SS);
}

////////////////////////////////////////////////////////////////////////////////
////////// PROGRAMA PRINCIPAL //////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

void main()
{
   set_tris_b(0b00011111);
   set_tris_c(0b01001101);

   setup_adc_ports(sAN7|sAN8|VSS_VDD);
   setup_adc(ADC_CLOCK_INTERNAL);
      
   setup_spi(SPI_MASTER|SPI_CLK_DIV_16|SPI_XMIT_H_TO_L|SPI_SCK_IDLE_LOW);
   
   setup_timer_0(T0_INTERNAL);
   setup_timer_1(T1_DISABLED);
   setup_timer_2(T2_DISABLED,0,1);
   
   setup_comparator(NC_NC_NC_NC);
   
   set_timer0(0);
   
   enable_interrupts(INT_TIMER0);
   enable_interrupts(GLOBAL);
    
   output_high(PIN_C2);
   output_high(PIN_B5);
   output_high(SS);
    
   while(TRUE)
   {
       
      if(adq==1)                // se genero la interrupcion teporizada
      {
          disable_interrupts(GLOBAL);
            
          adq_pote();
          calc_vel();
          send();                // se envian los valores de Vel al esclavo 
            
          adq=0;
          enable_interrupts(GLOBAL);
      }
   }
}

CODIGO ESCLAVO
Código:
#include <18F4550.h>

#FUSES NOWDT                     //No Watch Dog Timer
#FUSES INTRC_IO                  
#FUSES MCLR                      //Master Clear pin enabled
#FUSES NOPROTECT                 //Code not protected from reading
#FUSES NOLVP                     //No utilizamos el modo de programación con bajo voltaje, B5(PIC18) used for I/O
#FUSES NODEBUG                   //No usamos codigo para debuguear 
#FUSES NOPBADEN                  //PORTB pins are configured as digital I/O on RESET
#FUSES CPUDIV2                   //System Clock by 2

#use delay(clock=8MHZ)                 

#define IN1 PIN_C0
#define IN2 PIN_C2    //CPP1 PWM
#define IN3 PIN_A6
#define IN4 PIN_C1    //CPP2 PWM
#define ENA PIN_E2                    
#define ENB PIN_E1                    
#define LED PIN_D2                    

int8 duty1, duty2, direccion;
int8 set=0;

#int_SSP
void SSP_isr(void)
{
    disable_interrupts(global);
    
    duty1=spi_read();
    
    while(!spi_data_is_in());
    duty2=spi_read();

    while(!spi_data_is_in());
    direccion=spi_read();

    set=1;
    output_toggle(LED);        // SOLO PARA CHEQUEAR QUE ENTRO EN LA INTERRUPCION
    clear_interrupt(int_ssp);
    enable_interrupts(global);
}


////////////////////////////////////////////////////////////////////////////////
////////// PROGRAMA PRINCIPAL ////////////////////////////////////////////////// 
//////////////////////////////////////////////////////////////////////////////// 

void main()
{
   set_tris_A(0b10111111);
   set_tris_B(0b11);
   set_tris_C(0b01101000);
   set_tris_E(0b11111001);
   set_tris_D(0b000);
    
   setup_adc_ports(NO_ANALOGS);
   setup_adc(ADC_OFF);
   
   setup_timer_0(RTCC_OFF);
   setup_timer_1(T1_INTERNAL | T1_DIV_BY_1);    //genera los ticks que miden el pulso
   
   setup_timer_2(T2_DIV_BY_1, 255, 1);            //modulacion de frecuencia del PWM
   setup_CCP1(CCP_PWM);
   setup_CCP2(CCP_PWM);
  
   setup_spi(SPI_SLAVE||SPI_XMIT_H_TO_L|SPI_SCK_IDLE_LOW);  
   
   output_high(IN1);
   output_high(IN3);
   set_pwm1_duty(150);
   set_pwm2_duty(150);
    
   clear_interrupt(int_ssp);
   enable_interrupts(int_ssp);
   enable_interrupts(global);

   output_high(LED);

   while (TRUE)
   {
       if(set==1)
       {  
           disable_interrupts(global);
           if(direccion==0)
           {
               output_high(IN1);
               output_high(IN2);
               set_pwm1_duty(255-duty1);
               set_pwm2_duty(255-duty2);
            }
           if(direccion==1)
           {
               output_low(IN1);
               output_low(IN2);
               set_pwm1_duty(duty1);
               set_pwm2_duty(duty2);
            }
            set=0;
           enable_interrupts(global);
       }
   } 
}


Gracias!!
 
Última edición:
Muy posiblemente sea estas funciones las que esten mal definidas en el slave
setup_adc_ports(NO_ANALOGS);
setup_adc(ADC_OFF);
En mi opinion personal yo no use las funciones de las librerias del compilador
tales como spi.h, estube teniendo muchos problemas con esas funciones
Yo tube que hacer mis propias rutinas de incicializacion para que
el micro hiciera exactamente lo que yo queria

Estas son unas de las funciones que necesite hacer tanto para el master como para el slave y al final las que necesite para hacer funcionar el esclavo y que detectara la informacion empezando por el SS fueron las funciones de
limpia_ports();
void apaga_adc (void)

podras ver que es una programacion quizas un poco de mas bajo nivel pero para mi fue 100%
funcional; despues de mucho leer la hoja de datos
Espero te funcione y puedas adaptarlo a tus necesidades, suerte

void spi_inicializacion(void)
{
SSPSTATbits.SMP =0; //SSPSTAT = 0b01XXXX
SSPSTATbits.CKE =0;

// SSPSTATbits.BF = 0;
//SSPSTAT |= 0x40;
//SSPSTAT &= 0xBF; //SSPSTAT = 0b00XXXX
//SSPSTAT = 0b00000000;
//SSPCON1 = 0X34; //SSPCON1 = 0b00110100
SSPCON1 = 0X24; //SSPCON1 = 0b00100100

}

void gpio_ini(void)
{
limpia_ports();

}

void int_prioridad (void)
{
RCONbits.IPEN=1;
INTCONbits.GIE_GIEH=1;

PIE1 = 0x08; //habilita la interrupcion SSPIF

IPR1 = 0x08; //ser como de alta prioridad la interrupcion SSPIF

PIR1bits.SSPIF = 0;
}

void apaga_adc (void)
{
ADCON0bits.ADON = 0; //DESACTIVA ADC
ADCON1bits.PCFG3 = 1; //PONE TODOS LOS CANALES DE
ADCON1bits.PCFG2 = 1; //ADC COMO I/O DIGITALES
ADCON1bits.PCFG1 = 1;
ADCON1bits.PCFG0 = 1;
}



void limpia_ports (void)
{
PORTA = 0X00;
PORTB = 0X00;
PORTC = 0X00;
PORTD = 0X00;
}
 
Última edición:
Gracias por los consejos! Sigo con el problema de que no salta la interrupción del MSSP.
Implementé todo lo que me pasaste salvo la función de limpiar los puertos. No se porqué no anda nada si la llamo durante la configuración! Porque puede pasar eso?
Dejo el código nuevo para que se vea como quedó

CODIGO ESCLAVO
Código:
#include <18F4550.h>

#FUSES NOWDT                     //No Watch Dog Timer
#FUSES INTRC_IO                  //High Speed Crystal/Resonator with PLL enabled
#FUSES MCLR                      //Master Clear pin enabled
#FUSES NOPROTECT                 //Code not protected from reading
#FUSES NOLVP                     //No utilizamos el modo de programación con bajo voltaje, B5(PIC18) used for I/O
#FUSES NODEBUG                   //No usamos codigo para debuguear 
#FUSES NOPBADEN                  //PORTB pins are configured as digital I/O on RESET
#FUSES CPUDIV2                   //System Clock by 2

#use delay(clock=8MHZ) 				//Clock en la entrada de la CPU
//#use SPI(FORCE_HW, SLAVE, MODE=0, BITS=8)

#define IN1 PIN_C0
#define IN2 PIN_C2	//CPP1 PWM
#define IN3 PIN_A6
#define IN4 PIN_C1	//CPP2 PWM
#define ENA PIN_E2					
#define ENB PIN_E1					
#define LED PIN_D2					

#BYTE SSPBUF = 0xFC9
#BYTE SSPSTAT = 0xFC7
#BIT SMP = SSPSTAT.7
#BIT CKE = SSPSTAT.6
#BYTE SSPCON1 = 0xFC6
#BYTE SSPCON2 = 0xFC5

#BYTE ADCON0 = 0xFC2
#BIT ADON = ADCON0.0
#BYTE ADCON1 = 0xFC1

#BYTE INTCON = 0xFF2
#BIT GIEH = INTCON.7	//GLOBAL interrupt enable bit
#BYTE PIR1 = 0xF9E
#BIT SSPIF = PIR1.3	//MSSP interrupt flag bit
#BYTE PIE1 = 0xF9D
#BIT SSPIE = PIE1.3	//MSSP interrupt enable bit
#BYTE IPR1 = 0xF9F
#BIT SSPIP = IPR1.3	//MSSP interrupt priority bit
#BYTE RCON = 0xFD0
#BIT IPEN = RCON.7	//Interrupt priority feature enable bit


int8 duty1, duty2, direccion;
int8 set=0;

////////////////////////////////////////////////////////////////////////////////
////////// INTERRUPCIONES ////////////////////////////////////////////////////// 
//////////////////////////////////////////////////////////////////////////////// 

#int_SSP
void SSP_isr(void)
{
	disable_interrupts(global);
	
	duty1=SSPBUF;					// cualquiera de estas dos lineas 
//	duty1=spi_read(); 				// hace lo mismo, las dos funcionan 
//	delay_us(10);
	
	while(!spi_data_is_in());
	duty2=SSPBUF;
//	duty2=spi_read();
//	delay_us(10);

	while(!spi_data_is_in());
	direccion=SSPBUF;
//	direccion=spi_read();
//	delay_us(10);

	set=1;
	output_toggle(LED);
	clear_interrupt(int_ssp);
	enable_interrupts(global);
}

////////////////////////////////////////////////////////////////////////////////
////////// FUNCIONES /////////////////////////////////////////////////////////// 
//////////////////////////////////////////////////////////////////////////////// 

void apaga_adc (void)
{
//	ADCON0 = 0b00000000;		//DESACTIVA ADC
	ADON = 0;			//DESACTIVA ADC
	ADCON1 = 0b00001111;		//PONE TODOS LOS CANALES DE ADC COMO I/O DIGITALES
}

void limpia_ports (void)
{
	output_A(0X00);
	output_B(0X00);
	output_C(0X00);
	output_D(0X00);
	output_E(0X00);
}

void config_int()
{
	IPEN=1;			//Habilita prioridades
	SSPIE=1;		//Habilita int x MSSP
	SSPIP=1;		//Alta prioridad para int x MSSP
	SSPIF=0;		//Clarea flag del MSSP
	
	GIEH=1;  		//Habilita int GLOBAL        
}

void spi_init()
{
//	SSPSTAT = 0b01000000;	//detallada mejor en las dos lineas de abajo
	SMP=0;
	CKE=1;
	SSPCON1 = 0b00100100;
}

////////////////////////////////////////////////////////////////////////////////
////////// PROGRAMA PRINCIPAL ////////////////////////////////////////////////// 
//////////////////////////////////////////////////////////////////////////////// 

void main()
{
	set_tris_A(0b10111111);
	set_tris_B(0b11);
	set_tris_C(0b01101000);
	set_tris_D(0b000);
	set_tris_E(0b11111001);
	
//	setup_adc_ports(NO_ANALOGS);
//	setup_adc(ADC_OFF);
	apaga_adc();
//	limpia_ports();
	
	setup_timer_0(RTCC_OFF);
	setup_timer_1(T1_INTERNAL | T1_DIV_BY_1);	//genera los ticks que miden el pulso
   
	setup_timer_2(T2_DIV_BY_1, 255, 1);		//modulacion de frecuencia del PWM
	setup_CCP1(CCP_PWM);
	setup_CCP2(CCP_PWM);
  
//	setup_spi(SPI_SLAVE||SPI_XMIT_H_TO_L|SPI_SCK_IDLE_LOW);  
	spi_init();
   
	///////// Rutina de testeo de los motores ///////////
	output_high(IN1);
	output_high(IN3);
	set_pwm1_duty(150);
	set_pwm2_duty(150);
	/////////////////////////////////////////////////////
	
//	clear_interrupt(int_ssp);
//	enable_interrupts(int_ssp);
//	enable_interrupts(global);
	config_int();

	output_high(LED);

	while (TRUE)
	{
   		if(set==1)
   		{  
   			disable_interrupts(global);
   			if(direccion==0)
	   		{
				output_high(IN1);
				output_high(IN2);
				set_pwm1_duty(255-duty1);
				set_pwm2_duty(255-duty2);
			}
			if(direccion==1)
   			{
				output_low(IN1);
				output_low(IN2);
				set_pwm1_duty(duty1);
				set_pwm2_duty(duty2);
			}
			set=0;
			enable_interrupts(global);
		}
	} 
}
 
Última edición:
Hola.

Alguien sabe como apagar el reloj interno del 18F4550 para utilizar un cristal externo utilizando Proton ? en este ejemplo al parecer utilizan C.

En el codigo escribi:

Código:
Device 18F4550 
Declare Onboard_USB No 

Xtal 4

Config_Start
    IESO = OFF ;Oscillator Switchover mode enabled 
Config_End

Pero no se si esta bien, por ahora ni siquiera arranca el micro, pero no si es por eso. ...

Gracias
 
Eso se define con una palabra de configuración más concreta.
Con lo que declaras no es suficiente para que el microcontrolador funcione.
El PIC18F4550 dispone de varios tipos de configuración para el oscilador principal y el interno.
A su vez, el bit IESO permite seleccionar entre qué oscilador interno se ejecutará para el modo de reposo.
Éste opera en conjunto con el registro OSCTUNE (bit INTRC) y el registro OSCCON (bits IRCF2:IRCF0)

Puedes mirar por aquí:
Reloj interno del pic 18f2550
Y existen más temas en el Foro, acerca de la palabra de configuración en los PIC18F/25/4455/4550

Algo interesante en donde entra en función el bit IESO y FCMEN, lo puedes ver por aquí:
Fail-Safe Clock Monitor
 
Atrás
Arriba