|
|
View previous topic :: View next topic |
Author |
Message |
bharatwalia
Joined: 04 May 2009 Posts: 35 Location: India
|
spi read/write problem |
Posted: Thu Jul 15, 2010 2:51 am |
|
|
Hi,
I am using a master/slave architecture two transfer a 10-bit data from master (16F876A) to slave (16F72) via SPI.
The main purpose of this project is to read voltage from ADC and transmit the read data to slave to display the read voltage on 7-Segment.
The problem I am facing here is that I am not getting the same data on the receiver's side. For example if I am sending 1023 (300V) from master's side I am receiving 770 (226V) on receiver's side.
Please help me guys.
master
Code: |
//master.c
#include <16f876A.h>
#device adc=10
#include <timers.h>
#USE DELAY( CLOCK=4000000 ) /* Using a 4 Mhz clock */
#FUSES XT,NOWDT,NOPROTECT,NOPUT
int16 result=0;
void transmitData(void);
void getVoltage(void);
void spi_write16(int16 data);
//-------------------------------------------------------------------
// The rtcc interrupt occurs when the rtcc rolls over from FF to 00.
// I have programmed it to interrupt at a 100 Hz rate.
//
// RTCC interrupt rate = Fosc / (4 * rtcc pre-scaler * rtcc pre-load)
//
// = 4 MHz / (4 * 256 * 39)
//
// = 100.16 Hz
//
// This gives us a timer tick approx. every 10 ms (9.98 ms actually).
//-------------------------------------------------------------------
#int_rtcc
void rtcc_isr(void)
{
// Reload the RTCC, so it will keep overflowing every 10 ms.
set_rtcc(RTCC_PRELOAD);
// Decrement any timers that are running.
if(gc_transmit_timer)
gc_transmit_timer--;
}
//--------------------------------------------------------
void main()
{
setup_counters(RTCC_INTERNAL,RTCC_DIV_256);
enable_interrupts(INT_RTCC);
enable_interrupts(GLOBAL);
setup_spi(SPI_MASTER | SPI_H_TO_L | SPI_CLK_DIV_4 );
setup_adc_ports(AN0_AN1_AN3);
setup_adc(ADC_CLOCK_DIV_64);
set_tris_a(0xDB);
gc_transmit_timer = TRANSMIT_TIMER_TICKS;
while(1)
{
getVoltage();
transmitData();
}
}
//--------------------------------------------------------
void getVoltage(void) //reading 10-bit data from ADC(Voltage 0-300V)
{
result=0;
set_adc_channel(0);
delay_ms(1);
result=read_adc();
}
//---------------------------------------------------------
void transmitData(void)
{
if(gc_transmit_timer) //refresh rate.
return;
else
gc_transmit_timer = TRANSMIT_TIMER_TICKS;
spi_write16(result); //sending data every 400ms.
//spi_write16(1023); //for testing.
}
void spi_write16(int16 data) //sending 10-bit data in two parts.
{
spi_write(make8(data, 1)); // Send MSB
spi_write(make8(data, 0)); // Send LSB
}
|
slave
Code: |
//slave.c
#include <16f72.h>
#device *=8
#fuses HS,NOWDT,PUT,BROWNOUT,NOPROTECT
#use delay(clock=4000000)
void display(void);
void HTO7S(unsigned int16 Num);
#define TICKS_BETWEEN_INTERRUPTS 5000 //5000 4/4=1 =1ms
#define INTERRUPT_OVERHEAD 35 //cycles wasted
#define TMR1RESET (0xFFFF-(TICKS_BETWEEN_INTERRUPTS-INTERRUPT_OVERHEAD))
#byte port_b=6 /* define the location of register port_b */
#byte port_c=7 /* define the location of register port_c */
byte CONST LED_MAP[11] = {0x40,0x79,0x24,0x30,0x19,0x12,0x02,0x78,0x00,0x10,0xFF};
// 0 1 2 3 4 5 6 7 8 9 OFF
void spi_read16(void);
byte cnt=0,value,i;
byte Column[4] = {0x80,0x40,0x02,0x10}; //Low bits(right,center,left)
byte Segment[4] = {0x12,0xE3,0xAB,0xAF};
int16 res,data=0;
int8 x,y;
//--------------------------------------------------------
#INT_TIMER1
void Timer1(void)
{
set_timer1(TMR1RESET);
display();
}
void main()
{
set_tris_b(0); //1 set port_b as outputs
set_tris_c(0);
setup_timer_1(T1_INTERNAL|T1_DIV_BY_1); //no division
set_timer1(TMR1RESET); //65535-4965=60570
enable_interrupts(INT_TIMER1);
enable_interrupts(GLOBAL);
setup_spi(SPI_SLAVE | SPI_H_TO_L | spi_ss_disabled );
enable_interrupts(INT_SSP);
while(1)
{
/* while(!spi_data_is_in()) ;
data=spi_read();
while(!spi_data_is_in()) ;
data1=spi_read();
x=make16(data,data1);
*/
HTO7S(data);
}
}
//-------------------------------------------------------
void display()
{
if(cnt>=4){
cnt=0;}
port_b=Segment[cnt];
//port_b=data;
port_c=Column[cnt];
//port_c=0xFF;
cnt++;
}
//--------------------------------------
// Convet HEX 2 byte to 7-Segment code
//--------------------------------------
void HTO7S(unsigned int16 Num)
{
unsigned int16 res; //500ms interrupt to control display
Segment[0]=LED_MAP[30*Num/10230]; //calculating look-up value from LED_MAP array.
if (Segment[0]==0x40) //turning off 1st digit if 0
Segment[0]=0xFF; //dividing the three digits
res = 30*Num%10230;
Segment[1]=LED_MAP[10*res/10230];
res=10*res%10230;
Segment[2]=LED_MAP[10*res/10230];
res=10*res%10230;
Segment[3]=LED_MAP[10*res/10230];
}
#INT_SSP
void ssp_isr(void)
{
spi_read16();
}
void spi_read16(void) //reading both LSB and MSB from Master
{
x=spi_read();
y=spi_read();
data=make16(x,y); //making 16-bit data again.
}
|
|
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19537
|
|
Posted: Thu Jul 15, 2010 5:01 am |
|
|
Your big problem is _time_.
You are sending the two bytes one after the other.
Now the transmission takes just 2uSec to complete. There is no time for the slave to realise that the first byte has arrived, get into the interrupt, and retrieve it, before the second byte arrives.
It takes typically a minimum of about 30 instruction times to get into an interrupt. However the 'worst case' here will be if the chip has just decided to go into the timer interrupt, at the moment the data arrives. The two array retrievals, will each take perhaps 30uSec, add the time to increment the counter, set the RTCC register etc., and this will take perhaps 150uSec. Then another 30uSec to get into the RTCC interrupt.
There is only one character of buffering on the SPI. As soon as the second byte completes it transfers to the buffer register, and overwrites the earlier byte.
You need to allow time between sending the first, and second byte. Perhaps 150uSec.
Separately, there is the issue of sync. There is nothing in the code to ensure that the slave is expecting the bytes when the master sends them. It could easily see a spurious clock edge as the chip wakes up, and be clocking at the wrong point. This is where the 'Slave select' line comes in. Without this, you need to design a strategy to recover. Have the slave verify it is in the gap between characters before enabling the SSP (look for the clock line being high, and staying high). Add a pair of 'marker' bits at the top of the word (say '11' together in the top two bits). If this is not present in the received data stop the SSP, and synchronise again. Using SPI, without a select line, is not a good idea....
Best Wishes |
|
|
bharatwalia
Joined: 04 May 2009 Posts: 35 Location: India
|
|
Posted: Sat Jul 17, 2010 8:29 am |
|
|
Hey thanks.
At last I solved the problem.
Its all about right timing and interrupt.
Thanks a lot... |
|
|
|
|
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
|