|
|
View previous topic :: View next topic |
Author |
Message |
hello188
Joined: 02 Jun 2010 Posts: 74
|
SPI Communication between 2 MCU's |
Posted: Thu Nov 01, 2012 8:00 pm |
|
|
Hi, I am trying to have 2 MCU's communicate with each other using SPI.
I have 18F47J53 USB PIC running at 12MHz * 4 = 48MHz as master, and 18F25K22 as slave.
I am using DMA module for simple SPI transfer of data, and interrupt driven SPI slave for 18F25K22.
I use EXT interrupt on 18F25K22(same as /SS2 pin) to synchronize the buffer index's. SPI lines are directly connected with less than an inch apart on board, with only 10k pull-up resistors on each lines.
I programmed both devices and looking at one of the device's variables at a time using debugger. It works sometimes, but most of the time, it doesn't work, yielding wierd data or skipping data's.
I looked at the wave form for the SPI lines using oscilloscope, and it looks clean.
Below is master code
Code: |
#include "18F47J53.h"
#DEVICE HIGH_INTS = TRUE
#include "def_18F47J53.h"
#include "stdint.h"
#use delay(clock = 48000000)
#fuses NOWDT, NOXINST, NODEBUG, HSPLL, PLL3,PLLEN, NOCPUDIV, STVREN
#define SS_PORT PORTD0
#define SS_TRIS TRISD0
#define SCK_TRIS TRISC0
#define SDO_TRIS TRISC1
#define SDI_TRIS TRISC2
#define SPI_BUFFER_SIZE 236
uint8_t spi_in_buffer[SPI_BUFFER_SIZE];
uint8_t spi_out_buffer[SPI_BUFFER_SIZE];
uint8_t * spi_in_ptr;
uint8_t * spi_out_ptr;
void init_main(void){
uint8_t i;
#pin_select SCK2OUT = PIN_C0
#pin_select SCK2IN = PIN_C0
#pin_select SDO2 = PIN_C1
#pin_select SDI2 = PIN_C2
SS_TRIS = 0;
SS_PORT = 1;
SCK_TRIS = 0;
SDO_TRIS = 0;
SDI_TRIS = 1;
setup_ADC_PORTS(NO_ANALOGS);
//FOSC = 48MHz
//FOSC/4 = 12MHz, 8 instruction cycles per char
//FOSC/8 = 12MHZ, 16 instruction cycles per char
//FOSC/64 = 128 instruction cycles per char
//If I add the SPI_SS_DISABLE, the DMAEN doesn't automatically reset after finishing transmission
setup_spi2(SPI_MASTER|SPI_SCK_IDLE_HIGH|SPI_XMIT_H_TO_L|SPI_SAMPLE_AT_MIDDLE|SPI_CLK_DIV_64);
for(i=0;i<SPI_BUFFER_SIZE;i++){
spi_out_buffer[i] = i;
spi_in_buffer[i] = 0;
}
spi_in_ptr = &spi_in_buffer[0];
spi_out_ptr = &spi_out_buffer[0];
//Setup full-duplex DMA
//No Toggling of SS pin. We control SS pin manually
DMACON1 = 0b00111000;
//Delay between bytes = 1 cycle. Interrupt ASAP
DMACON2 = 0x00;
//enable_interrupts(INT_SSP2);
}
void main(void){
init_main();
while(1){
SS_PORT = 1;
if(!DMACON1_DMAEN){
//Process and save SPI In buffer
SS_PORT = 0;
delay_ms(2);//delay for the slave to process the received data
DMACON2 = 0XF0;//Give maximum delay between bytes for the slave to respond
DMABCH = 0;
DMABCL = SPI_BUFFER_SIZE - 1;
TXADDRH = (int8)(spi_out_ptr >>8);
TXADDRL = (int8)(spi_out_ptr & 0xFF);
RXADDRH = (int8)((int16)spi_in_ptr >>8);
RXADDRL = (int8)((int16)spi_in_ptr & 0xFF);
DMACON1_DMAEN = 1;
}
}
}
|
below is slave code
Code: |
//main.c
#include <18F25K22.h>
#include <def_18f25k22.h>
#device HIGH_INTS = TRUE
#fuses HSH, PLLEN, NOXINST, NOEBTR, NOEBTRB, NOCPB, debug
#use delay(clock = 58982400)
#include <stdint.h>
#define SS_TRIS PORTB0
#define SCK_TRIS PORTB1
#define SDI_TRIS PORTB2
#define SDO_TRIS PORTB3
#define DEBUG_TRIS TRISC5
#define DEBUG_PORT PORTC5
#define SPI_BUFFER_SIZE 236
uint8_t spi_buffer[SPI_BUFFER_SIZE];
uint8_t input_buffer[SPI_BUFFER_SIZE];
uint8_t spi_idx = 0;
uint8_t spi_status;
uint8_t highest_idx = 0;
#define SPI_WAITING 0
#define SPI_XMITING 1
#define SPI_COMPLETE 2
#opt 0
void initialize(void){
uint8_t i;
SCK_TRIS = 1;
SDI_TRIS = 1;
SDO_TRIS = 0;
SS_TRIS = 1;
for(i=0;i<SPI_BUFFER_SIZE;i++){
spi_buffer[i] = 0;
}
DEBUG_TRIS = 0;
setup_spi2(SPI_SLAVE | SPI_SCK_IDLE_HIGH | SPI_XMIT_H_TO_L | SPI_SAMPLE_AT_MIDDLE|SPI_SS_DISABLED);
enable_interrupts(INT_SSP2);
TRISB0 = 1;
ext_int_edge(H_TO_L);
enable_interrupts(INT_EXT_H2L);//SPI index 동기화용 Interrupt
enable_interrupts(GLOBAL);
}
#INT_SSP2
ssp2_isr(){
if(spi_idx<=SPI_BUFFER_SIZE){
spi_buffer[spi_idx] = SSP2BUF;
spi_idx++;
SSP2BUF = spi_buffer[spi_idx];
if(spi_idx==SPI_BUFFER_SIZE){
spi_status = SPI_COMPLETE;
}
}
if(spi_idx>highest_idx) highest_idx = spi_idx;
}
#INT_EXT
ext_isr(){
uint8_t buffer8;
buffer8 = SSP2BUF;//Read it anyways just to clear buffer
SSP2BUF = spi_buffer[0];//Load the buffer
spi_idx = 0;
spi_status = SPI_XMITING;
DEBUG_PORT = !DEBUG_PORT;
}
void main(void){
uint8_t i;
initialize();
while(1){
switch(spi_status){
case SPI_WAITING: //Prepare spi buffer for output
for(i=0;i<SPI_BUFFER_SIZE;i++) spi_buffer[i] = i+10;
break;
case SPI_XMITING:
break;
case SPI_COMPLETE://Process spi buffer input
//do processi
for(i=0;i<SPI_BUFFER_SIZE;i++) input_buffer[i] = spi_buffer[i];
spi_status = SPI_WAITING;
break;
}//end switch
}//end while(1);
}
|
anyone experienced with SPI have any idea?? I am using PCH 4.127 for both devices.
Thank you |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19496
|
|
Posted: Fri Nov 02, 2012 3:32 am |
|
|
First comment, the slave should always read the SSPBUF in INT_SSP. This will result in SSPOV getting set if another byte is then received. You should have handling for this error anyway.
You may well get a spurious INT_EXT at start-up. Basic sequence (to avoid this), is:
Code: |
ext_int_edge(H_TO_L); //set the edge to use
clear_interrupt(INT_EXT); /clear interrupt
enable_interrupts(INT_EXT);//Now enable interrupt without setting edge
|
PIC's all have the behaviour that in some circumstances setting the interrupt edge can trigger an interrupt, so it is 'better' to set the edge, clear, then enable like this.
Generally far better to use SS. This handles the SPI synchronisation for you. Without this getting byte synchronisation is _very_ hard. What happens if (for instance) your PIC's don't wake up exactly together?. Depending on how the bus clock is biased, and the timing of wake up, the slave can effectively receive an unexpected clock edge. Now you read SSPBUF in your INT_EXT in an attempt to re-sync, but this only affects a byte received. The shift register will still be left with a bit in it. The only way to clear the shift register is to disable and re-enable the MSSP. Turn SSPEN, off, and then on again. You should do this in your slave at boot up, turning it off, waiting for the clock line to go to the idle state, and then turning this on again, and also in your INT_EXT routine.
Why #opt 0?. You want as much speed as possible.
Your return data reads beyond the end of the array. You increment the idx, then write the data from the array, and then test if the idx==buffer size. At this point you will have read a byte beyond the end of the array.
Best Wishes |
|
|
hello188
Joined: 02 Jun 2010 Posts: 74
|
|
Posted: Fri Nov 02, 2012 4:37 am |
|
|
Always reading from the INT_SSP and using SS for synchronization cleared up the problem.
Thank you so much!! |
|
|
|
|
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
|