I'm programming a SPI communication with an external RF chip. The microcontroller is the model PIC24FJ64GA102 from Microchip.
I want to use the enhanced buffer mode of the SPI.
Problem: Get the received bytes out of the receive buffer.
Used SPI function:
void SPI1_get(uint8_t* data, uint16_t length) {
uint16_t i = 0, l = length;
uint8_t dummy;
while (length > 0) {
while (SPI1STATbits.SPITBF || SPI1STATbits.SPIRBF) {
dummy = SPI1STAT;
}
do {
SPI1BUF = 0xff;
} while (SPI1STATbits.SPIRBF == 0 && --length > 0);
do {
while (SPI1STATbits.SRMPT == 0) {
}
data[i] = SPI1BUF;
++i;
} while (i < l && SPI1STATbits.SRXMPT != 1);
}
}
Here the calls:
uint8_t cmd[2]; cmd[0] = length; cmd[1] = address;
SPI1_put(cmd,2); // e.g: 0x02, 0x01
SPI1_get(buf,2); // e.g: 0x05, 0x01 (received data)
The communication is just fine, checked with an oscilloscope and SPI decoding module. The data on the SPI bus are like in the comment above: sent 0x02 0x01 0xff 0xff
, received 0x00 0x00 0x05 0x01
, but the function above does not correctly retrieve the data out of the receive buffer. I've already tested a lot of constellations of checking flags and interrupts but in the end the best result I can get is: 0x00 0x01
(only the last byte is correct).
Also already checked the errata sheet, where two SPI problems are mentioned which do not (should not) affect my code.
What the hell am I doing wrong?!
As requested here the SPI1_put() function:
void SPI1_put(uint8_t* data, uint16_t length) {
uint16_t i = 0;
uint8_t dummy;
for (; i < length; ++i) {
while (SPI1STATbits.SPITBF)
; // maybe change to (_SPI1BEC == 7) ?
SPI1BUF = data[i];
dummy = SPI1BUF; //dummy read
}
}
[latest edit: 2015-02-05]
So today I was able to spend some more time on this particular issue, and came up with a port of ElderBug's suggestion, also taking care of the bugs mentioned in the errata sheet:
uint8_t out_buf[128];
uint8_t in_buf[128];
void SPI1_com(uint8_t* out, uint8_t* in, uint16_t out_len, uint16_t in_len) {
uint16_t len = out_len + in_len;
uint16_t sent = 0, recv = 0, i = 0;
// while (!SPI1STATbits.SRXMPT)
sent = SPI1BUF; // empty buffer
sent = SPI1BUF; // empty buffer
sent = 0;
if (out != out_buf && out != 0)
memcpy(out_buf, out, out_len);
while (sent < len && recv < len) {
if (SPI1STATbits.SPIBEC != 7 && sent < len) {
SPI1BUF = out_buf[sent++];
}
if (!SPI1STATbits.SRXMPT && recv < len) {
in_buf[recv] = SPI1BUF, recv++;
}
}
if (in != 0) {
for (i = 0; i < in_len; ++i) {
in[i] = in_buf[out_len + i];
}
// memcpy(in, in_buf + out_len, in_len);
}
for (i = 0; i < len; ++i) {
out_buf[i] = 0xff;
in_buf[i] = 0xff;
}
}
This code basically works. With an exception which I was not able to work around:
The communication works as follows:
- 1byte: r/w-bit+length
- 1byte: address
- 1-127byte: data
So, when I read 1 byte from the slave chip, meaning sending 3 bytes (rw+len, address, dummybyte), the data I have in my in_buf
buffer is 0xFF.
Now a weird thing: When I just read one more byte, without changing anything (just one more dummy byte on the bus), the 1st byte of my in_buf
has got the correct data. But this seems not to work always, because my program still stucks at some points.
I'm left sitting here with a lot of question marks.
Weird thing the 2nd: Reading 8 bytes, data correct in my buffer until the last byte, last byte is 0xFF, should be 0x00. Wtf?
PS: Already filed a ticket at Microchip for support in this issue.
SPI1_put
? The problem can also come from there. – ElderBugSPI1_put
function, shouldn't thefor
loop's step section havei++
instead of++i
? Because now you never process the 0th byte of the buffer. – Eimantasc++
or++c
. And as I wrote in my question, the data on the bus itself (measured with a scope) is completely correct. The problem here is to get the received data out of the SPI enhanced buffer, not the communication itself. – qwc