|
|
View previous topic :: View next topic |
Author |
Message |
das Guest
|
SPI help |
Posted: Sun Dec 03, 2006 9:25 am |
|
|
I am having SPI issues trying to use a MCP3551 (A/D converter) with a PIC16F877. I use the setup_spi(MASTER | H_TO_L | DIV_4) and use the format to read with byte_1 = spi_read(byte1); (continuing to clock for the other 2 bytes). I am assuming the data that I want is byte1, but both byte_1 and byte1 return 0. Can anyone help... even when I tried bit-banging the SPI I still get zeros. Please help - I need to get some sort of a solution working today... Thanks. |
|
|
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Sun Dec 03, 2006 10:09 am |
|
|
I'm not really sure how you have set up your program. As always, post a very short but complete example program showing your problem, this makes the discussion much easier and gives you quicker and better responses.
Code: | byte_1 = spi_read(byte1); | Using this construction you are writing the value byte1 to the external device and the returned value will be in byte_1, not byte1. Please note that the MCP2551 has no data input, so sending data to this chip over SPI doesn't make sense. Use spi_read() without parameter.
I also suggest you change the variable names to names that are more different in notation, the difference between byte1/byte_1 is easy to read wrong. |
|
|
das Guest
|
SPI Help |
Posted: Sun Dec 03, 2006 10:20 am |
|
|
Here is a short snippet from the code...
if(input(PIN_C4 == 0))
{
delay_us(10);
byte_3 = spi_read(0);
delay_us(5);
byte_2 = spi_read(0);
delay_us(5);
byte_1 = spi_read(0);
value = make32(byte_4, byte_3, byte_2, byte_1);
This was the latest that I wrote (that failed). You think I should use:
if(input(PIN_C4 == 0))
{
delay_us(10);
byte_3 = spi_read();
delay_us(5);
byte_2 = spi_read();
delay_us(5);
byte_1 = spi_read();
value = make32(byte_4, byte_3, byte_2, byte_1); |
|
|
das Guest
|
SPI Help |
Posted: Sun Dec 03, 2006 10:24 am |
|
|
Changing to the following:
if(input(PIN_C4 == 0))
{
delay_us(10);
byte_3 = spi_read();
delay_us(5);
byte_2 = spi_read();
delay_us(5);
byte_1 = spi_read();
value = make32(byte_4, byte_3, byte_2, byte_1);
Still results in a return of 0 for all the bytes... |
|
|
Ttelmah Guest
|
|
Posted: Sun Dec 03, 2006 12:55 pm |
|
|
You don't show what you are doing to the CS line?.
You _must_ have a pull-up resistor on the SD line.
The sequence needed, is to drop CS, raise it, then drop it again, and raise it again. You then read the SDI pin, and when it drops, data is available. You don't need any delays between the reads.
I suspect you don't have a pull up resistor on the SD line, so it is being seen as 'low', when it is actually in the high impedance state (conversion not yet complete).
Best Wishes |
|
|
das Guest
|
SPI Help |
Posted: Sun Dec 03, 2006 1:13 pm |
|
|
When I initialize (at the start of the program) I set CS to low (and have a pull down resistor on it. I don't currently have a pull up or pull down resistor on DO. I messed around with putting one there, but there were no change in results. I put the delays in because initially when I was running this bits 23 and 22 were both 1 (indicating that there was an SPI communication error - from the datasheets).
From what the data sheets said, it did not appear to need a pull up or pull down resistor on the Data Out pin. |
|
|
Ttelmah Guest
|
|
Posted: Sun Dec 03, 2006 2:09 pm |
|
|
You are running the chip in continuous conversion mode. Unfortunately, this is a pig to drive from the PIC. The reason is that the 'status' doesn't become available, until an extra clock is generated on the clock line. After transferring 24bits, the line remains as the last data bit, until the extra clock is sent. You are going to have to turn off the SPI peripheral, and generate one extra clock on the SPI clock line, then you can use the data line to detect the status. Without this, the status is not available.
Using 24 clocks, you need to operate the CS, to get the status.
Best Wishes |
|
|
das Guest
|
SPI Help |
Posted: Sun Dec 03, 2006 2:15 pm |
|
|
I am trying to operate it in single conversion mode right now. Do you know if anyone has ever written a driver for this? |
|
|
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Sun Dec 03, 2006 2:25 pm |
|
|
Code: | if (input(PIN_C4 == 0)) | Change this to Code: | if (input(PIN_C4) == 0) |
|
|
|
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Sun Dec 03, 2006 2:57 pm |
|
|
Quote: | I use the setup_spi(MASTER | H_TO_L | DIV_4) | With respect to what Ttelmah wrote about operating the ADC in continuous mode and reading the status bit I recommend you study chapter 5.5 of the MCP3551 datasheet. It is recommended to operate the SPI-bus either in mode 0,0 or mode 1,1. You are operating the SPI-bus in mode 1,0.
In mode 0,0: read 4 bytes
In mode 1,1: read 3 bytes
Configuring the SPI-modes is always very confusing to me because Motorola (the inventor of SPI), Microchip and CCS do things in different ways. That's why I use the table below as reference
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 |
Use this in your program as Code: |
#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)
setup_spi(SPI_MASTER | SPI_MODE_0_0 | SPI_CLK_DIV_4 );
|
Edit: Fixed error in number of bytes to read in mode 0,0 and 1,1.
Last edited by ckielstra on Sun Dec 03, 2006 4:01 pm; edited 1 time in total |
|
|
das Guest
|
SPI Help |
Posted: Sun Dec 03, 2006 3:05 pm |
|
|
I will try that. Right now, here is the current code:
#int_RDA
#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)
int byte1, byte2, byte3, byte4;
int byte_1, byte_2, byte_3, byte_4;
int32 value;
int i;
int completed, loop;
RDA_isr()
{
puts("RDA was tripped");
}
void main()
{
int loop = 1;
OUTPUT_HIGH(PIN_D0); //Start with CS high
setup_spi(SPI_MASTER | SPI_MODE_0_0 | SPI_CLK_DIV_4 );
delay_ms(5000); //Delay 5 seconds
puts("Magnum Torque Meter Initialized");
while(TRUE)
{
enable_interrupts(INT_RDA);
enable_interrupts(GLOBAL);
loop = 1;
OUTPUT_LOW(PIN_D0); //Start the conversion
delay_ms(10); //Delay 10 ms for the conversion to complete
do //Do...while loop
{
if(input(PIN_C4) == 0) //Check to see if the conversion is completed
{
byte_4 = spi_read(byte4);
byte_3 = spi_read(byte3);
byte_2 = spi_read(byte2);
byte_1 = spi_read(byte1);
value = make32(byte_4, byte_3, byte_2, byte_1);
for(i = 0; i < 32; i++)
{
putc(bit_test(value, 31 - i));
}
OUTPUT_HIGH(PIN_D0);
completed = 1;
}
if(input(PIN_C4) == 1)
{
delay_us(1);
} //Closing if PIN_
}while(loop = 1);
delay_ms(10000);
}
} |
|
|
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Sun Dec 03, 2006 3:41 pm |
|
|
Some general remarks:
- When posting code on this forum please use the 'code' buttons. It will help to preserve the layout of your program and makes it easier for us to read the code.
- When debugging a problem like this, get rid of all other program code that might interfere. Especially enabled interrupts might cause unexpected side effects.
- Never use a printf or put instruction in an interrupt handler unless you know and accept the side effects. Receiving a character on the UART will take the same amount of time as sending a single character. You are receiving one character and transmitting 10-fold, this will block receiving and cause buffer overflow. The UART will stop responding at receive buffer overflow (the third character). Resetting the UART requires specific actions or adding the ERRORS directive to the #use RS232 directive.
Then about your program:
- Move the enable_interrupt() functions out of the while loop. You never disable these functions, so why add the overhead?
- I made a mistake in an earlier post. For the spi_read() to generate a clock signal you have to pass the spi_read() function a value, however this doesn't have to be a variable. Just any constant value will do for generating the clock signal, for example spi_read(0).
- while(loop = 1); Change this to '=='
- In SPI-mode 0,0 you are supposed to read 3 bytes, not 4.
- You said you want to operate in single conversion mode. If so, then your handling of the ChipSelect line seems incorrect (read Ttelmah's note on this).
Last edited by ckielstra on Sun Dec 03, 2006 3:46 pm; edited 1 time in total |
|
|
das Guest
|
SPI Help |
Posted: Sun Dec 03, 2006 3:43 pm |
|
|
I refined some of the code shown above - and made changes to keep it in constant conversion mode. I am still seeing all outputs as zeros. Any other ideas? Current code is:
#int_RDA
#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)
int byte1, byte2, byte3, byte4;
int byte_1, byte_2, byte_3, byte_4;
int32 value;
int i;
int completed, loop;
RDA_isr()
{
puts("RDA was tripped");
}
void main()
{
int loop = 1;
OUTPUT_LOW(PIN_D0); //Start with CS high
setup_spi(SPI_MASTER | SPI_MODE_0_0 | SPI_CLK_DIV_4 );
delay_ms(5000); //Delay 5 seconds
puts("Initialized"); //Output string for debug
while(TRUE)
{
enable_interrupts(INT_RDA);
enable_interrupts(GLOBAL);
loop = 1;
delay_ms(10); //Delay 10 ms for the conversion to complete
do //Do...while loop
{
if(input(PIN_C4) == 0) //Check to see if the conversion is completed
{
byte_4 = spi_read(byte4);
byte_3 = spi_read(byte3);
byte_2 = spi_read(byte2);
byte_1 = spi_read(byte1);
value = make32(byte_4, byte_3, byte_2, byte_1);
for(i = 0; i < 32; i++)
{
putc(bit_test(value, 31 - i) + 48);
}
putc(10);
putc(13);
loop = 0;
}
if(input(PIN_C4) == 1)
{
delay_us(1);
}
delay_ms(10000);
}while(loop = 1);
}
}
I know that you said in a previous response something about the
if(input(PIN_C4) == 0)
Can you elaborate? |
|
|
Ttelmah Guest
|
|
Posted: Sun Dec 03, 2006 3:44 pm |
|
|
Your code, now generates 32 clocks. This won't work.
You need to either generate 25 clocks, and not operate the CS, or generate 24 and operate the CS.
Generating 32 clocks, will result in the bytes being shifted, and a garbage result.
The way to generate the extra clock, is to simply toggle it in software.
Code: |
#bit SSPEN=0x14.5
#bit SCL=0x7.3
//Add this after your three reads.
//Ensure the line is the same polarity as the SPI leaves it
SCL=1;
//Turn off the SPI
SSPEN=0;
//Clock the line low
SCL=0;
delay_cycles(1);
//clock the line high
SCL=1;
//turn the SSP back on
SSPEN=1;
//Now you can read the input bit, and see the conversion status in
//continuous mode.
|
I am directly accessing the bit, rather than using the 'output' instruction, so that TRIS doesn't get changed (it is already set as output for the SPI).
As an aside, Ckielstra is dead right that the SPI mode is wrong, but it shouldn't matter. The reason is that the value of the second bit in the SPI settings (CKE), only affects how data is sent. The chip you are using, only receives data, so shouldn't 'care' if this is right. In fact the description in the data sheet is 'misleading' here really, since it should accept modes 0,x, or 1,x, according to how the data is being read. It is still 'better' to get it right, but I don't think that affects your problem....
Best Wishes |
|
|
das Guest
|
SPI Help |
Posted: Sun Dec 03, 2006 3:51 pm |
|
|
Sorry - I didn't realize about the code posting. I disabled what you recommended. Looking at the Mode 0 0 it generates 4 bytes - not three based on the Figure 5-7. That would seem to work better for the continuos conversion anyway as it will send out the 25th clock cycle. |
|
|
|
|
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
|