Jump to content
16F876

Spi And Rs232 With 16f876 And Ext Adc

Recommended Posts

Hi there

 

I am having trouble getting ADC values from an external AD7706( very similar to AD7705) IC to a PIC16F876 via SPI. The PIC is running at 4MHZ and the AD7706 is running at 2.4578 MHZ.

 

I want to use the hardware SPI function(no bit banging) of the PIC and no interupts.

 

I am trying to communicate to the external IC via SPI and want to report back to the computer via RS232. At the moment all the readings I get is zero and with my picoscope scope there seems to be no SPI data coming from the 16F876.

 

 

 

void setup (void)
{
/* Setup*/
trisa=101111b;				//make all portA input except RA4 output
trisb=0;					//All output
trisc=10010000b;			// make just RC7 input for RS232, RC5(SDO)=0,RC4(SS)=1,RC3(SCK)=0 and RC4 for SDI
clear_bit (option_reg,NOT_RBPU);	// portb pullups enabled - normally set 
clear_bit (pie1,7);			// Disables AD convert interupt 
adcon1=10000000b; 			//Justify right,FOSC/8(0),(00), Set all to analogue input
/*SetBaud for RS232*/
clear_bit (intcon, GIE);		//disables global interupt
clear_bit (intcon, PEIE);		// disables peripheral interupts
clear_bit (intcon, INTE);      // disables all external interupts
clear_bit (intcon, RBIE);		// disables all RB port interupts
clear_bit (pie1,TXIE);			// clears interupt bit 
portb=0;						//Clear port B outputs
portc=0;						//clear portc
spbrg=25;
txsta=00000100b;				//Sync=0(bit4), brgh=1(bit2)
rcsta=10010000b;  /*enables serial port, SPEN bit set-enables reception */
set_bit (txsta, TXEN);			/* enables transmission */
temp=rcreg;						//Clear receive register
/* Setup SPI */
pie1=0;				//Just to make sure all interupts are off
intcon=0;			//Just to make sure all interupts are off
sspcon=00110000b;	//SSPEN(5)=1,CKP(4) =1,Fosc/4(0001)
sspstat=00000000b;          //CKE=0    
dummy=sspbuf;				//Clear buffer
}

That was the setup part of the deal.

 

Here follows the SPI nitty gritty.

void CheckBF (void)					//Check if data was sent and answer received(Buffer Full)
{ while ((sspstat&0x01)==0)			//BF bit(0) is set once data was sent and new data received
	{;}
}

void SendSpiA (unsigned char SAnsw)    //Send SPI command, keep answer
{ 	WCOL=0;	                              //Clear Write collision
sspbuf=SAnsw;                         //Put command in sspbuf and send
CheckBF();				//Check buffer full
}

void DRDY(void)					//AD7706 stuff -Data from ADC Ready?
{	SendSpiA(00001000b);			//08h, Write to Comm register to read(poll) DRDY(Bit7)in comm register
while(sspbuf&0x80)		        //Constantly poll communication register to see when write done(bit 7=0)
{SendSpiA(00001000b);         	 //08h, Next operation a read from COM register
	}
}

void SPIADC (void)					//Read Data from channel one
{	DRDY();		                                   // Data ready?					
SendSpiA(00111000b);		                //00111000b 38h, Bit3=1(read), data register 
out[3]=sspbuf;
out[4]=sspbuf;
}

void SetupAD7706(void)          //AD7706 setup stuff which does not seem to be sent
{SendSpiA(00100000b);   	 //(20h) Write to Clock Register
SendSpiA(00000100b);		//(04h)-0,0,0,0,CLK=1(bit2), rest zero,50HZ filter 
SendSpiA(00010000b);		//(10h)Write operation to setup register,Channel 1
SendSpiA(01000100b);		//(44h) (0,1)Self Calibration, (0,0,0)gain=1, (1)Unipolar
Alive4();					//LED routine to confirm that we got this far
}

Void Testing(void)
{//Code here that calls SPIADC and sends data via RS232
}

void main (void)
{	setup ();
SetupAD7706();
Testing();	
}

 

The RS232 part works fine, but the SPI does not seem to work. Any help and clues will be appreciated.

Share this post


Link to post
Share on other sites

Hi,

Tricky point because no specified in DS. But you MUST disable USART serial port (bit RCSTA.SPEN) when using SPI. And when you need USART again simply set it back.

Stephane

Share this post


Link to post
Share on other sites

Hi

 

I disabled the RS232 before the use of SPI with no success.

 

At http://forum.microchip.com/tm.asp?m=48978&...32&anchor#48978 is says "The I2C/SPI interface, and the USART are totally seperate, and do not affect each other." So far I have not found any other reference to disabling the RS232 before SPI use, but then there is very little information on using both at the same time.

 

What did you mean by

because no specified in DS
?

 

The Slave Select(SS) method is not used because the slave chip is hardwired as being selected.

 

Thank you for your help I appreciate it.

Share this post


Link to post
Share on other sites

I am getting there slowly.

 

Because the SPI is hardware driven I was under the impression that the chip would know when to receive answers or that the slave chip would tell the main chip to receive data. Well it does not work that way.

 

After the chip is asked a question of which you want an answer, the data has to be "clocked out" of the chip being interrogated. This can be done by sending a dummy data to the chip.

 

By sending a zero(0) by hardware SPI, clock cycles will be provided in exactly the same way to the slave chip, as it would if you where to "clock out"(bit bang) an answer in software.

 

Previously I did not notice that the SPI clock was working because the burst of the clock is so quick. Not until I put the SPI in a loop did I see on the scope that the clock signal is operating.

 

The code is still not working, but I am getting closer I think. If anybody can give me a clue as to what I am doing wrong it will be great.

 

Thank you.

Louis

 

void SerialReset(void)     //This part is supposed to reset the AD7706- provides 32 clock cycles
{set_bit(portc,3);		//Clock High
set_bit(portc,5);		//SDO high
unsigned char j;
for ( j=0; j<64;j++ )
{
clear_bit(portc,3);					
set_bit(portc,3);
}
clear_bit(portc,5);
} 
{set_bit(portc,3);		//Clock High
set_bit(portc,5);		//SDO high
unsigned char j;
for ( j=0; j<64;j++ )
{
clear_bit(portc,3);					
set_bit(portc,3);
}
clear_bit(portc,5);
} 

void CheckBF (void)					//Check if data was sent and answer received(Buffer Full)
{while(sspstat&0x01==0)			//BF bit(0) is set once data was sent and new data received
;
}
void SpiA (unsigned char SAnsw)	 		//Send SPI command and get answer-8 bit
{ 	dummy=sspbuf;					//Clear trash
WCOL=0;							//Clear Write collision
sspbuf=SAnsw;
CheckBF();						//Check buffer full
sspbuf=0;						//Dummy data clocks out answer
CheckBF();	
}
void DRDY(void)						//Data from ADC Ready?
{	SpiA(0x08);			//08h, Write to Comm register to read(poll) DRDY(Bit7)in comm register
while(sspbuf&0x80)		        //Constantly poll com reg to see when write done(bit 7=0)
	{SpiA(0x08);		//08h, Next operation a read from COM register
	}
}

void SpiCommand (unsigned char SAnsw)	//Send SPI command and expect no answer
{ 	dummy=sspbuf;						//Clear trash
WCOL=0;								//Clear Write collision
sspbuf=SAnsw;
CheckBF();
}


void SpiChan (unsigned char SAnsw)	//Send SPI command for channel and clock out 16bit answer
{ 	dummy=sspbuf;					//Clear trash
WCOL=0;							//Clear Write collision
sspbuf=SAnsw;					//Write channel number to Data register
CheckBF();						//Check buffer full after channel selected?
DRDY();							// Is data ready?
sspbuf=0;						//Dummy data clocks out first 8 bits
CheckBF();
out[3]=sspbuf;					//MSB
sspbuf=0;						//Provides clock cycles to clock out last 8 bits
CheckBF();						//Check buffer full
out[4]=sspbuf;					//LSB	
}

void SetupAD7706(void)
{SpiCommand(00100000b);   	 //(20h) Write to Clock Register
SpiCommand(00000100b);			//(04h)-0,0,0,0,CLK=1(bit2), rest zero,50HZ filter 
SpiCommand(00010000b);			//(10h)Write operation to setup register,Channel 1
SpiCommand(01000110b);			//(44h) (0,1)Self Calibration, (0,0,0)gain=1, (1)Unipolar, buffered
DRDY();						//Test to see when calibration complete
}

void SPIADC (void)					//Read Data from channel x
{	SpiChan(0x38);			//00111000b 38h, Bit3=1(read), data register 
outserial();			//Data sent to PC via RS232
}

void GoMeasure (void)					//Normal display
{ 	while (1)
{SPIADC();
}

void main (void)
{	setup ();                                                 //Setup PIC
SerialReset();					//Routine to reset chip
SetupAD7706();
GoMeasure();	
sleep();
}

Share this post


Link to post
Share on other sites

The use of CS is optional however, if you hardwire it, you are responsible for ensuring synchronization of the SPI comms between the AD7706 and the PIC. I personally find it easier to ensure synchronization via the CS pin.

Share this post


Link to post
Share on other sites
The use of CS is optional however, if you hardwire it, you are responsible for ensuring synchronization of the SPI comms between the AD7706 and the PIC. I personally find it easier to ensure synchronization via the CS pin.

 

In your code you are doing this by bitbashing the clock line to the AD7706 but in the PIC16F87x family you have the read-modify-write problem (no latch port) to reliably do this bitbashing you need to add a nop instruction between the consecutive set_bit and clear_bit instructions (or use an 18F processor).

 

Share this post


Link to post
Share on other sites

Finally got this thing to work. Problem is threefold.

1) The AD7706 needs a reset in the beginning. RB2 is tied to the reset and kept low for a while(reset) and then brought high to activate the chip again.

2) Polling the DRDY bit I could not get to work. So finally I polled the DRDY pin on the AD7706 and that worked.

 

This I thought would work, but it did not:

void DRDYX(void)						//Data from ADC Ready?
{	delay_ms(1);
SpiSend(0x08);						//08h, Write to Comm register to read(poll) DRDY(Bit7)in comm register
SpiSend(0x0);
while(sspbuf&0x80)		        //Constantly poll com reg to see when write done(bit 7=0)
	{SpiSend(0x08);				//08h, Next operation a read from COM register
	 SpiSend(0x0);
	}
}

This did work by polling the actual pin and not reading the registry.

void DRDY(void)						//Data from ADC Ready?
{	delay_ms(1);
while (portb&0x02)				//portb,1 as input, DRDY goes low when ready
;	
}

 

3) It seems like the channel that is to be enquired needs to be calibrated before every enquiry.

 

void serial (void)					//RS232's 'send' of a character- used by outserial()
{	while( pir1&0x10==0 )			//Test bit 4
         {  nop();}
	txreg=send;
	break;

}

void outserial (void)				//RS232 Serial out
{	unsigned char zz;
for ( zz=0; zz<7;zz++ )
{send=out[zz];					
serial();
} 
}

void CheckBF (void)					//Check if data was sent and answer received(Buffer Full)
{while(sspstat&0x01==0)				//BF bit(0) is set once data was sent and new data received
;
}

void SpiSend (unsigned char SendA)	 	//Send SPI command and get answer-8 bit
{ 	sspbuf=SendA;					//Send command
CheckBF();						//Check buffer full
}



void DRDY(void)						//Data from ADC Ready?
{	delay_ms(1);
while (portb&0x02)				//portb,1 as input, DRDY goes low when ready
;	
}

void SetupAD77061(void)
{SpiSend(00100000b);   		 //(20h)(32 dec) Write to Clock Register, Chan0
SpiSend(00000100b);		//(04h)(4 dec)-0,0,0,0,CLK=1(bit2), rest zero,50HZ filter 
SpiSend(00010000b);		//(10h)(32 dec)Write operation to setup register,Channel 1
SpiSend(01000100b);		//(46h)(70 dec)(0,1)Self Calibration, (0,0,0)gain=1, (1)Unipolar, UNbuffered
DRDY();					//Test to see when calibration complete
}

void SetupAD77062(void)
{SpiSend(00100001b);    //(21h)(33 dec) Write to Clock Register, Chan1
SpiSend(00000100b);		//(04h)(4 dec)-0,0,0,0,CLK=1(bit2), rest zero,50HZ filter 
SpiSend(00010001b);		//(11h)(33 dec)Write operation to setup register,Channel 2
SpiSend(01000100b);		//(46h)(70 dec) (0,1)Self Calibration, (0,0,0)gain=1, (1)Unipolar, UNbuffered
DRDY();					//Test to see when calibration complete
}

void SetupAD77063(void)
{SpiSend(00100011b);    //(23h)(35 dec) Write to Clock Register
SpiSend(00000100b);		//(04h)(4 dec)-0,0,0,0,CLK=1(bit2), rest zero,50HZ filter 
SpiSend(00010011b);		//(13h)(35 dec)Write operation to setup register,Channel 3
SpiSend(01000100b);		//(46h)(70 dec) (0,1)Self Calibration, (0,0,0)gain=1, (1)Unipolar, UNbuffered
DRDY();					//Test to see when calibration complete
}

void ADC (void)	 	//Send SPI command and get answer-8 bit
{ 	SpiSend(0x0);
out[3]=sspbuf;
SpiSend(0x0);
out[4]=sspbuf;
}

void Whatisthevalue (void)	 	//
{ 	SetupAD77061();
SpiSend(0x38);
ADC();
outserial();
SetupAD77062();
SpiSend(0x39);
ADC();
outserial();
SetupAD77063();
SpiSend(0x3B);
ADC();
outserial();
}



void main (void)
{	setup ();
Whatisthevalue();	
sleep();
}

Share this post


Link to post
Share on other sites
Guest JoeB

When writing dummy information to the SPI bus (clocking the slave to read it from the master), it is normal to send 0xFF, rather than 0x00 as your dummy data.

 

This is because the port pins are pulled high (internally and sometimes externally), so sending 0x00 causes a voltage to be dropped accross the pullup resistors, hence drawing current.

 

By sending 0xFF you are clocking high data, hence no voltage drop is created accross the pullup restors and no current draw.

 

This also helps with EMC compliance its a comercial application, but is just good programming practice with SPI communications.

Share this post


Link to post
Share on other sites

Join the conversation

You are posting as a guest. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...

×
×
  • Create New...