View previous topic :: View next topic |
Author |
Message |
dbaltz
Joined: 20 Nov 2009 Posts: 13
|
16F877A spi uses 8-bit, I need 16 |
Posted: Fri Nov 20, 2009 6:37 pm |
|
|
I have a 16F877A and am trying to use spi to set up a MCP6S22 PGA which requires an uninterrupted 16-bit SCLK. Everything I have tried inserts a SCLK delay between the high and low bytes. The documentation suggests that 16-bit words are possible using the FORCE_HW mode. Has anyone done 16-bit spi mode without a SCLK delay after 8 bits? I just need to do a few bytes of setup so clock rate is not a concern. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Fri Nov 20, 2009 6:47 pm |
|
|
Don't use the #use spi() library. Use the setup_spi() function to setup
the SPI mode and speed. Then use spi_write() two consecutive times
to send two bytes. Make sure you connect your device to the hardware
spi pins on the PIC.
Look at the w5100_write_reg() routine in this code:
http://www.ccsinfo.com/forum/viewtopic.php?t=40693&start=10
Notice how several consecutive bytes are written to the slave device
with spi_write() statements.
You need to write your own routine(s) in a similar way, to make a
driver for the mcp6s22. Study the SPI bit diagrams in the mcp6s22
data sheet and make your routines conform to those diagrams. |
|
|
dbaltz
Joined: 20 Nov 2009 Posts: 13
|
|
Posted: Fri Nov 20, 2009 7:01 pm |
|
|
That is basically what I did however looking at the SCLK on a scope, there is a delay between the 8 cycles of the first byte and the second. The MCP6S22 timing diagrams (and the text) say that the SCK must be continuous for 16 cycles. |
|
|
dbaltz
Joined: 20 Nov 2009 Posts: 13
|
|
Posted: Fri Nov 20, 2009 7:04 pm |
|
|
I did try using the setup_spi() call instead of the #USE SPI with no improvement.
One note: I am using a slow (1MHz) clock. Could that be the issue? Interrupt latency? |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Fri Nov 20, 2009 8:03 pm |
|
|
In the general case, the 16 bits of the transmitted SPI word do not have
to be transmitted at the exactly the same interval. You're allowed to
have a gap between the two bytes. |
|
|
dbaltz
Joined: 20 Nov 2009 Posts: 13
|
|
Posted: Fri Nov 20, 2009 8:45 pm |
|
|
That's good to know. Perhaps the MCP6S22 spec is overly restrictive and my problem is elsewhere. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Fri Nov 20, 2009 9:10 pm |
|
|
Post your driver code and a small, complete, compilable test program
that calls the driver. Explain how it fails. |
|
|
FvM
Joined: 27 Aug 2008 Posts: 2337 Location: Germany
|
|
Posted: Sat Nov 21, 2009 8:06 am |
|
|
Quote: | MCP6S22 PGA which requires an uninterrupted 16-bit SCLK |
Quote: | The MCP6S22 timing diagrams (and the text) say that the SCK must be continuous for 16 cycles. |
Quote: | Perhaps the MCP6S22 spec is overly restrictive |
You apparently didn't read the datasheet thoroughly. Apart from showing 16 "continuous" clock cycles in the diagram,
nothing is said in this regard. The SPI clock specification in constrast is fully static, allowing to insert infinite
delays at any point.
If the device doesn't work, you obviously have a different problem, e.g. wrong SPI mode or wrong circuit. |
|
|
John P
Joined: 17 Sep 2003 Posts: 331
|
|
Posted: Sat Nov 21, 2009 9:14 am |
|
|
I agree with what others have said--I'm very skeptical that the component requires an absolutely regular clock for a 16-bit SPI input. Especially as there's apparently no spec for what the frequency needs to be!
But anyway, if you want an absolutely regular clock and you're prepared to accept a slow sending rate, you can always forget the built-in SPI routines and just simulate the whole thing with a timer interrupt and bit-banging, and nothing prevents you from using the "normal" SPI pins to communicate with. I don't think it's likely to change anything. But you can do it. |
|
|
dbaltz
Joined: 20 Nov 2009 Posts: 13
|
|
Posted: Sat Nov 21, 2009 6:00 pm |
|
|
From Section 5.1 of the MCP spec:
"There must be multiples of 16 clocks (SCK) while CS is low or commands will abort (see Section 5.3, “Registers”)."
And from my code:
Code: | int16 SetAmplifierRange(char range)
{
int16 Value;
char gain1;
char gain2;
char chan;
char datah;
setup_spi(SPI_MASTER | SPI_L_TO_H | SPI_CLK_DIV_16);
AMP1_CS = HIGH;
AMP2_CS = HIGH;
switch (range)
{
case RANGE_50R:
gain1 = PGA_GAIN_1;
gain2 = PGA_GAIN_1;
chan = 1;
break;
case RANGE_5R:
gain1 = PGA_GAIN_10;
gain2 = PGA_GAIN_1;
chan = 1;
break;
case RANGE_500MR:
gain1 = PGA_GAIN_1;
gain2 = PGA_GAIN_1;
chan = 0;
break;
case RANGE_50MR:
gain1 = PGA_GAIN_10;
gain2 = PGA_GAIN_1;
chan = 0;
break;
case RANGE_5MR:
gain1 = PGA_GAIN_10;
gain2 = PGA_GAIN_10;
chan = 0;
break;
}
AMP1_CS = LOW;
AMP1_CS = LOW;
spi_write(PGA_INST+PGA_CHAN_REG); // Address Channel Register
spi_write(chan);
AMP1_CS = HIGH;
AMP1_CS = HIGH;
AMP1_CS = LOW;
AMP1_CS = LOW;
spi_write(PGA_INST+PGA_GAIN_REG); // Address Gain Register
spi_write(gain1);
AMP1_CS = HIGH;
AMP1_CS = HIGH;
AMP2_CS = LOW;
AMP2_CS = LOW;
spi_write(PGA_INST+PGA_GAIN_REG); // Address Gain Register
spi_write(gain2);
AMP2_CS = HIGH;
AMP2_CS = HIGH;
return 0;
}
|
Admittedly, I haven't much experience with SPI devices so I'm probably not doing something else correctly. I do however have another SPI device (a motor controller) on the same board that is working fine. |
|
|
Ttelmah Guest
|
|
Posted: Sun Nov 22, 2009 3:11 am |
|
|
The 8bit SPI, is fine for what they say.
The 'point' is that the chip must _always_ receive 16 clocks, between the CS going low, and the CS rising. This is common. However the clocks do _not_ need to be the same length.
Some comments:
1) Why do you drop and raise the CS twice. Not necessary.
2) Check the data sheet. Is there a specification for how much time is needed between CS dropping and the first clock. It is common for a small delay to be needed. You may have added the second operation on the line becaue of this?.
3) Your main problem, is that you are not waiting for the SPI to send the data before raising CS. This would violate the data sheet requirement.
The SPI hardware, will still be sending the last byte.
For the transmssion, use a _read_:
Code: |
AMP1_CS = LOW;
delay_cycles(1);
dummy = spi_read(PGA_INST+PGA_CHAN_REG); // Address Channel Register
dummy = spi_read(chan);
AMP1_CS=HIGH;
|
The first is not actually needed, but the second is vital.
The difference, is a 'write', simply transfers the data to the output register, and returns _immediately_ so your code can get on with other jobs. The read, with a value, clocks out a byte, and _waits_ for the byte to be sent, then returns the value clocked back during the transmission.
So your problem is that when you send your second byte, the code returns immediately at present, and then raises the CS line. The chip at this point, has not sent all 16 clocks, so invalidates what your target requires. By using a read, to wait for the data to be sent, you will avoid the problem.
Best Wishes |
|
|
FvM
Joined: 27 Aug 2008 Posts: 2337 Location: Germany
|
|
Posted: Sun Nov 22, 2009 4:13 am |
|
|
After you finally revealed your code, there is at least one trivial problem (I guess, there aren't more):
MCP6S22 is specifying SPI mode 0 or 3 while you are using mode 1. Correct this and try again.
Code: | setup_spi(SPI_MASTER | SPI_L_TO_H | SPI_XMIT_L_TO_H | SPI_CLK_DIV_16); |
P.S.: I'm not familiar to PIC16 hardware SPI, but Ttelmah is most likely right, that spi_write() doesn't wait for the transmission to finish.
P.P.S.: No, spi_write() is waiting for end of transmission as well as spi_read(). But it's different e.g. with PIC24. |
|
|
dbaltz
Joined: 20 Nov 2009 Posts: 13
|
|
Posted: Mon Nov 23, 2009 11:12 am |
|
|
Setting the SPI mode to 1,1 was the fix. All is working now. Thanks.
The PIC16 does indeed finish the spi_write(). There was no need to add delays or an spi_read(). |
|
|
|