|
|
View previous topic :: View next topic |
Author |
Message |
fmartinezs
Joined: 16 Aug 2006 Posts: 19
|
Help: Trouble communicating via SPI between two PIC18F26K22 |
Posted: Thu Sep 07, 2017 12:21 pm |
|
|
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: 9245 Location: Greensville,Ontario
|
|
Posted: Thu Sep 07, 2017 1:30 pm |
|
|
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
|
|
Posted: Thu Sep 07, 2017 1:54 pm |
|
|
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: 9245 Location: Greensville,Ontario
|
|
Posted: Thu Sep 07, 2017 2:27 pm |
|
|
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
|
|
Posted: Fri Sep 08, 2017 12:34 am |
|
|
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
|
|
Posted: Sat Sep 09, 2017 5:03 am |
|
|
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 |
|
|
|
|
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
|