CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

Help: Trouble communicating via SPI between two PIC18F26K22

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
fmartinezs



Joined: 16 Aug 2006
Posts: 19

View user's profile Send private message

Help: Trouble communicating via SPI between two PIC18F26K22
PostPosted: Thu Sep 07, 2017 12:21 pm     Reply with quote

Hi:

I posted a few days ago regarding storing a variable in EEPROM based on reading external interrupts; taking some advice from this forum, i got it to work! Now, i have to send this settings to another PIC in the same board; i tried sending them via USART, and it worked alright, but i need to use the serial port for another tasks. So, i thought of using the SPI port to try and achieve the same, but i haven't managed to get it to work...

Let's say i need to set the USART speed in both PICs; i use the INT0 interrupt to read a pushbutton, which changes the setting and turns on and off three LEDs, corresponding to 1200, 2400 and 4800 bps respectively; then, i send a short string to the second PIC, hoping it will interpret it and change its own settings accordingly; if i want 1200 bps, i send "speed0\r" via SPI, for 2400 i send "speed1\r" and for 4800 i send "speed2\r". Implemented with an USART it works just fine.

Well, this is the code i wrote:

For the transmitter:

Code:

#include <18f26k22.h>
#fuses HSH,NOWDT,NOPROTECT,PUT,NOPLLEN,NOCPD,NOLVP
#use delay(clock=20000000)
#use rs232(UART1, stream = TEST1, baud=1200, parity=N, bits=8, xmit=PIN_C6, rcv=PIN_C7, ERRORS)
#use rs232(UART2, stream = TEST2, baud=1200, parity=N, bits=8, xmit=PIN_B6, rcv=PIN_B7, ERRORS)
#use standard_io(A)
#use standard_io(B)
#use standard_io(C)

#rom int8 0xF00000 = {0,0,0,0,0,1}

#include <spi_modes.h>

/////////////
// DEFINES //
/////////////

#define PRESET    3036

#define TX_IND_1 output_high(PIN_A0)
#define TX_IND_0 output_low(PIN_A0)

#define V_1200_1 output_high(PIN_A1)
#define V_1200_0 output_low(PIN_A1)

#define V_2400_1 output_high(PIN_A2)
#define V_2400_0 output_low(PIN_A2)

#define V_4800_1 output_high(PIN_A3)
#define V_4800_0 output_low(PIN_A3)

#define MODE0_1 output_high(PIN_A4)
#define MODE0_0 output_low(PIN_A4)

#define MODE1_1 output_high(PIN_A5)
#define MODE1_0 output_low(PIN_A5)


//////////////
// GLOBALS //
//////////////

//////////////////////////////
// Switch reading variables //
//////////////////////////////

int speed   = 0;
int1 mode    = 0;   

int1 flag_mode = 0;
int1 flag_speed = 0;

int1 flag_speed_chg = 0;
int1 flag_mode_chg = 0;


//////////////////////////
// FUNCTIONS PROTOTYPES //
//////////////////////////

void speed_select(void);
void mode_select(void);


////////////////
// INTERRUPTS //
////////////////

////////////////////////////////////////////////
// Button press of binary speed select switch //
////////////////////////////////////////////////

#INT_EXT
void speed_isr()
{
   flag_speed_chg = 1;
}


////////////////////////////////////////
// Button press of mode select switch //
////////////////////////////////////////

#INT_EXT1
void bypass_isr()
{
   flag_mode_chg = 1;
}


////////////////////////////////////////////////////////////////////////////
// Timer0 interrupt is used to debounce both speed and mode switch inputs //
////////////////////////////////////////////////////////////////////////////

#INT_TIMER0
void timer0_isr()
{
   int i,j;
   set_timer0(PRESET);

   ///////////////////////////////////////////////
   // If binary speed select switch was pressed //
   ///////////////////////////////////////////////

   if(flag_speed_chg)
   {
      if(!input(PIN_B0))
         i++;
      else
         flag_speed_chg = 0;

      if(i==3)
      {
         i = 0;
         flag_speed = 1;
         flag_speed_chg = 0;
      }
   }

   ///////////////////////////////////////
   // If mode select switch was pressed //
   ///////////////////////////////////////

   if(flag_mode_chg)
   {
      if(!input(PIN_B1))
         j++;
      else
         flag_mode_chg = 0;

      if(j==3)
      {
         j = 0;
         flag_mode = 1;
         flag_mode_chg = 0;
      }
   }
}

////////////////////////////////////////////////////////////////////////
// Wrapper function to take advantage of printf for string formatting //
////////////////////////////////////////////////////////////////////////

void send_spi(int data)
{
   spi_write(data);
   delay_us(100);
}


//////////////////
// MAIN ROUTINE //
//////////////////

void main()
{
   //////////////////////////////////////////////
   // Determine initial settings of the device //
   //////////////////////////////////////////////

   speed   = read_eeprom(0);
   speed_select();
   delay_ms(25);

   mode    = read_eeprom(1);      
   mode_select();
   delay_ms(25);

   /////////////////////////////////////
   // Set edge of external interrupts //
   /////////////////////////////////////

   ext_int_edge(0,H_TO_L);
   ext_int_edge(1,H_TO_L);

   //Configure TIMER0
   setup_timer_0(T0_INTERNAL|T0_DIV_4);

   //Configure SPI
   setup_timer_2(T2_DIV_BY_1, 208, 5);
   setup_spi(SPI_MODE_1|SPI_MASTER|SPI_CLK_T2);

   // Enable necessary interrupts
   enable_interrupts(GLOBAL);
   enable_interrupts(INT_EXT);
   enable_interrupts(INT_EXT1);
   enable_interrupts(INT_TIMER0);

   // Infinite loop
   while(1)
   {
      ///////////////////////////////////////////////
      // If binary speed select switch was pressed //
      ///////////////////////////////////////////////

      if(flag_speed)
      {
         speed++;

         if(speed>2)
            speed = 0;

         speed_select();
         //write_eeprom(0,speed);

         switch(speed)
         {
            case(0):
            {            
               printf(send_spi,"speed0\r");
               break;
            }

            case(1):
            {
               printf(send_spi,"speed1\r");
               break;
            }

            case(2):
            {
               printf(send_spi,"speed2\r");
               break;
            }
         }

         flag_speed = 0;   
      }

      ///////////////////////////////////////
      // If mode select switch was pressed //
      ///////////////////////////////////////

      if(flag_mode)
      {
         mode=~mode;
         mode_select();
         //write_eeprom(1,mode);

         // Added to test if it makes any difference

         spi_write(0);
         delay_us(100);

         switch(mode)
         {
            case(0):
            {               
               printf(send_spi,"mode0\r");
               break;
            }

            case(1):
            {            
               printf(send_spi,"mode1\r");
               break;
            }
         }

         flag_mode = 0;         
      }      
   }
}


////////////////////////////
// FUNCTIONS DEFINITIONS  //
////////////////////////////

/////////////////////////////////
// Binary speed select routine //
/////////////////////////////////

void speed_select(void)
{
   switch(speed)
   {
      case 0:
      {
         set_uart_speed(1200,TEST1);
         V_1200_1;
         V_2400_0;
         V_4800_0;
         break;
      }

      case 1:
      {
         set_uart_speed(2400,TEST1);      
         V_1200_0;
         V_2400_1;
         V_4800_0;
         break;
      }

      case 2:
      {
         set_uart_speed(4800,TEST1);      
         V_1200_0;
         V_2400_0;
         V_4800_1;
         break;
      }

      default:
      {
         speed = 0;
         set_uart_speed(1200,TEST1);
         V_1200_1;
         V_2400_0;
         V_4800_0;
         break;
      }
   }   
}


/////////////////////////
// Mode select routine //
/////////////////////////

void mode_select(void)
{
   if(!mode)
   {
      MODE0_1;   
      MODE1_0;
   }
   else
   {
      MODE0_0;
      MODE1_1;
   }   
}



For the receiver:

Code:

#include <18f26k22.h>
#fuses HSH,NOWDT,NOPROTECT,PUT,NOPLLEN,NOCPD,NOLVP
#use delay(clock=20000000)
#use rs232(UART1, stream = TEST1, baud=1200, parity=N, bits=8, xmit=PIN_C6, rcv=PIN_C7, ERRORS)
#use rs232(UART2, stream = TEST2, baud=1200, parity=N, bits=8, xmit=PIN_B6, rcv=PIN_B7, ERRORS)
#use standard_io(A)
#use standard_io(B)
#use standard_io(C)

#rom int8 0xF00000 = {0,0}

#include "spi_modes.h"

/////////////
// Defines //
/////////////

#define V_1200_1 output_high(PIN_A1)
#define V_1200_0 output_low(PIN_A1)

#define V_2400_1 output_high(PIN_A2)
#define V_2400_0 output_low(PIN_A2)

#define V_4800_1 output_high(PIN_A3)
#define V_4800_0 output_low(PIN_A3)

#define MODE0_1 output_high(PIN_A4)
#define MODE0_0 output_low(PIN_A4)

#define MODE1_1 output_high(PIN_A5)
#define MODE1_0 output_low(PIN_A5)


/////////////
// Globals //
/////////////

int speed   = 0;
int1 mode    = 0;   


////////////////////////////////////////////////////
// Variables for SPI comm between both processors //
////////////////////////////////////////////////////

int      spi_buffer[16];
int      spi_buffer_ctr = 0;
int1   flag_spi_command = 0;

//////////////////////////
// Functions prototypes //
//////////////////////////

void speed_select(void);
void mode_select(void);

void inic_spi_buff(void);
void add_spi_buffer(char c);
void spi_command_process(void);


////////////////
// INTERRUPTS //
////////////////


#INT_SSP
void spi_rx_isr()
{
   int data;
   
   if(spi_data_is_in())
   {
      data = spi_read(0xCC);
         add_spi_buffer(data);         
   }
}


//////////////////
// Main routine //
//////////////////

void main()
{
   // Determine initial settings of the device
   speed   = read_eeprom(0);
   speed_select();
   delay_ms(25);

   mode    = read_eeprom(1);      
   mode_select();
   delay_ms(25);

   //Configure SPI
   setup_spi(SPI_MODE_1|SPI_SLAVE|SPI_SS_DISABLED);

   //Enable necessary interrupts
   enable_interrupts(GLOBAL);
   enable_interrupts(INT_SSP);

   //Infinite loop
   while(1)
   {
      if(flag_spi_command)
         spi_command_process();
   }
}


/////////////////////////////////
// Binary speed select routine //
/////////////////////////////////

void speed_select(void)
{
   switch(speed)
   {
      case 0:
      {
         set_uart_speed(1200,TEST1);
         V_1200_1;
         V_2400_0;
         V_4800_0;
         break;
      }

      case 1:
      {
         set_uart_speed(2400,TEST1);      
         V_1200_0;
         V_2400_1;
         V_4800_0;
         break;
      }

      case 2:
      {
         set_uart_speed(4800,TEST1);      
         V_1200_0;
         V_2400_0;
         V_4800_1;
         break;
      }

      default:
      {
         speed = 0;
         set_uart_speed(1200,TEST1);
         V_1200_1;
         V_2400_0;
         V_4800_0;
         break;
      }
   }   
}


////////////////////////////////
// MODE0 mode select routine //
////////////////////////////////

void mode_select(void)
{
   if(!mode)
   {
      MODE0_1;   
      MODE1_0;
   }
   else
   {
      MODE0_0;
      MODE1_1;
   }   
}



/////////////////////////
// Clean SPI Rx buffer //
/////////////////////////

void inic_spi_buff(void)
{                   
      int i;

      for(i=0;i<16;i++)
         spi_buffer[i] = 0;     
 
      spi_buffer_ctr = 0;             
}


////////////////////////////////
// Add byte to SPI Rx buffer  //
////////////////////////////////

void add_spi_buffer(char c)
{                 
     switch(c)
   {
      // Case Enter
          case 0x0D:
      {
         flag_spi_command=1;             
         break;
      }
      // Case Backspace
         case 0x08:
      {                   
            if(spi_buffer_ctr>0) spi_buffer[--spi_buffer_ctr]=0;
            break;
      }
      // Case Escape
          case 0x01B:
      {                   
            inic_spi_buff();
            break;
      }
      // In any other case, the byte is added to the buffer
          default:
            spi_buffer[spi_buffer_ctr++]=c;         
      }
}

void spi_command_process(void)
{
    int i;
   int16 aux=0;      
   char arg[16];   // Argument of command

   // Turn flag off
   flag_spi_command=0;                       
      
   // Clean argument
   for(i=0;i<16;i++)
      arg[i]=0;     

   ////////////////////////
   // Switch UART1 speed //
   ////////////////////////

   if(spi_buffer[0]=='s'&&spi_buffer[1]=='p'&&spi_buffer[2]=='e'&&spi_buffer[3]=='e'&&spi_buffer[4]=='d')
   {
      switch(spi_buffer[5])
      {
         case '0':
         {
            set_uart_speed(1200,TEST1);
            V_1200_1;
            V_2400_0;
            V_4800_0;
            //write_eeprom(0,0);
            break;
         }

         case '1':
         {
            set_uart_speed(2400,TEST1);      
            V_1200_0;
            V_2400_1;
            V_4800_0;
            //write_eeprom(0,1);
            break;
         }

         case '2':
         {
            set_uart_speed(4800,TEST1);      
            V_1200_0;
            V_2400_0;
            V_4800_1;
            //write_eeprom(0,2);
            break;
         }
      }
   }

   //////////////////
   // Switch modes //
   //////////////////

   if(spi_buffer[0]=='m'&&spi_buffer[1]=='o'&&spi_buffer[2]=='d'&&spi_buffer[3]=='e')
   {
      switch(spi_buffer[4])
      {
         case '0':
         {
            mode = 0;
            MODE0_1;   
            MODE1_0;
            //write_eeprom(1,0);
            break;
         }

         case '1':
         {
            mode = 1;
            MODE0_0;   
            MODE1_1;
            //write_eeprom(1,1);
            break;
         }
      }
   }

   //////////////////
   // Clean buffer //
   //////////////////

      inic_spi_buff();   
}



SPI modes (spi_modes.h):

Code:

//     MOTOROLA    |      MICROCHIP      |           CCS
//-----------------------------------------------------------------------
//   SPI Mode 0,0  |   CKP = 0, CKE = 1  |  SPI_L_TO_H | SPI_XMIT_L_TO_H
//   SPI Mode 0,1  |   CKP = 0, CKE = 0  |  SPI_L_TO_H
//   SPI Mode 1,0  |   CKP = 1, CKE = 1  |  SPI_H_TO_L
//   SPI Mode 1,1  |   CKP = 1, CKE = 0  |  SPI_H_TO_L | SPI_XMIT_L_TO_H

#define SPI_MODE_0_0  (SPI_L_TO_H | SPI_XMIT_L_TO_H)
#define SPI_MODE_0_1  (SPI_L_TO_H)
#define SPI_MODE_1_0  (SPI_H_TO_L)
#define SPI_MODE_1_1  (SPI_H_TO_L | SPI_XMIT_L_TO_H)

#define SPI_MODE_0  (SPI_L_TO_H | SPI_XMIT_L_TO_H)
#define SPI_MODE_1  (SPI_L_TO_H)
#define SPI_MODE_2  (SPI_H_TO_L)
#define SPI_MODE_3  (SPI_H_TO_L | SPI_XMIT_L_TO_H)


I looked at the data lines with the oscilloscope, and it seems to send the strings just fine whenever i press the pushbuttons, however, the receiver doesn't toggle its own LEDs.

I have no clue what I'm missing here, so any help will be really appreciated!

Best regards,
Fernando
temtronic



Joined: 01 Jul 2010
Posts: 9225
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Thu Sep 07, 2017 1:30 pm     Reply with quote

That PIC has 2 HW UARTS so can the 'slave' PIC not use one of the HW UARTs ? That way you get the ISR capability. If you need 2 UARTs ont the 'slave', could one be a SW UART, which is fine for transmitting data as no ISR is needed?

SPI is 3 or 4 I/O pins... serial can be 2 or 1 depending on how you code. Even CCS shows a 'one wire' serial system in the FAQ section of the manual.

I've used single wire, full duplex, interlaced for remote systems up to 15 miles on copper for decades.

Just trying to think of options here.

Jay
fmartinezs



Joined: 16 Aug 2006
Posts: 19

View user's profile Send private message

PostPosted: Thu Sep 07, 2017 1:54 pm     Reply with quote

Hi Jay:

I might get away with using one of the UARTS for this, but as the SPI port was available, i thought "why not?"... i believed using SSP interrupt for this would be similar to using the RDA interrupt, but i guess i was wrong... I haven't used SPi communication in many years, so i'm more than a little rusty.

I included SPI_SS_DISABLED in the settings... should i wire a chip select pin anyway?

Best regards,
Fernando
temtronic



Joined: 01 Jul 2010
Posts: 9225
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Thu Sep 07, 2017 2:27 pm     Reply with quote

While I use the 18F46K22 for most projects, I haven't used it's SPI ports... I see CCS has some SPI examples but they are not interrupt driven.
Hopefully someone who does can show use old guys the 'basics' on how to setup a simple master-slave using SPI hardware.
Never too old to learn, but have to write EVERYTHING down 3 times and then I still forget ... where I wrote it down !!


Jay
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Fri Sep 08, 2017 12:34 am     Reply with quote

Let's look at the .LST file for your #int_ssp routine, since that's where you
receive data.

1. Your main problem is that you give a parameter to spi_read().
This then loads SSP1BUF with the parameter. Since the purpose of
your routine is to read the incoming data from the other PIC, loading
SSP1BUF with 0xCC is a bad idea. It overwrites the incoming data.
Remove the 0xCC parameter.

2. If you got an #int_ssp interrupt in SPI slave mode, you are guaranteed
to have a byte in SSP1BUF. You do not need to check if "spi data is in".
Remove the if() statement.
Quote:

....... if(spi_data_is_in())
00116: BTFSS SSP1STAT.BF
00118: BRA 012E
....... {
....... data = spi_read(0xCC); // *** Remove the 0xCC
0011A: MOVF SSP1BUF,W // Move data from SSPBUF into W
0011C: MOVLW CC
0011E: MOVWF SSP1BUF // Load SSPBUF with 0xCC (Not a good idea)

00120: RRCF SSP1STAT,W // Is the BF bit set ? (Yes, it will be)
00122: BNC 0120 // Since BF is set, Cy is set, so don't branch
00124: MOVFF SSP1BUF,data // Now read 0xCC from SSP1BUF (not good)



fmartinezs wrote:

I included SPI_SS_DISABLED in the settings... should i wire a chip select pin anyway?

Yes you should use the slave select signal. This section of the PIC data
sheet gives reasons for using it:
15.2.5 SLAVE SELECT SYNCHRONIZATION

Make these changes:
1. Remove SPI_SS_DISABLED from the setup_spi() statement in the slave.
2. Add the wire for chip select from an unused i/o pin on the master pic
to the \SS pin on the slave PIC.
3. Make the following changes to the master program:
Code:

#define CS_PIN   PIN_XX  // Assign some unused i/o pin for slave select

void send_spi(int data)
{
   output_low(CS_PIN);   // Add this line
   spi_write(data);
   output_high(CS_PIN);  // Add this line
   delay_us(100);
}


4. Add this line at the start of main() in the master program:
Code:
output_high(CS_PIN);

This initializes slave select to the disabled state.
fmartinezs



Joined: 16 Aug 2006
Posts: 19

View user's profile Send private message

PostPosted: Sat Sep 09, 2017 5:03 am     Reply with quote

Hi PCM programmer:

I implemented your suggestions and it worked like a charm! I can't thank you enough, as this will help me greatly in my project. This forum is very useful thanks to people like you, Ttelmah and temtronic, which help a lot of people with their knowledge and experience.

Best regards,
Fernando
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group