0

我正在编写与外部射频芯片的 SPI 通信。微控制器是 Microchip 的 PIC24FJ64GA102 型号。

我想使用SPI的增强缓冲模式

问题:从接收缓冲区中获取接收到的字节。

用到的SPI功能:

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);
    }
}

这里的电话:

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)

通信很好,用示波器和 SPI 解码模块检查。SPI 总线上的数据就像上面的注释一样: send 0x02 0x01 0xff 0xff, received 0x00 0x00 0x05 0x01,但是上面的函数没有正确地从接收缓冲区中检索数据。我已经测试了很多检查标志和中断的星座,但最终我能得到的最好结果是:(0x00 0x01只有最后一个字节是正确的)。

还检查了勘误表,其中提到了两个不(不应该)影响我的代码的 SPI 问题。

我到底做错了什么?!

如这里所要求的 SPI1_put() 函数:

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
    }
}

[最新编辑:2015-02-05]

所以今天我能够在这个特定问题上花更多的时间,并提出了 ElderBug 建议的一个端口,同时处理了勘误表中提到的错误:

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;
    }
}

这段代码基本上可以工作。除了我无法解决的例外:

通信工作如下:

  • 1字节:读/写位+长度
  • 1字节:地址
  • 1-127byte:数据

因此,当我从从芯片读取 1 个字节,即发送 3 个字节(rw+len、地址、dummybyte)时,in_buf缓冲区中的数据为 0xFF。现在发生了一件奇怪的事情:当我只读取一个字节,而不改变任何东西(总线上只有一个虚拟字节)时,我的第一个字节in_buf得到了正确的数据。但这似乎并不总是有效,因为我的程序仍然卡在某些点上。

我留下了很多问号。

奇怪的第二件事:读取 8 个字节,数据在我的缓冲区中正确,直到最后一个字节,最后一个字节是 0xFF,应该是 0x00。什么?

PS:已经在 Microchip 提交了一张票,以获得此问题的支持。

4

1 回答 1

2

你的SPI1_put.

在您的SPI1_put函数中,您SPI1BUF在开始传输后立即读取,因此您在传输未完成时读取(如果我错了,请纠正我,但在 FIFO 为空时读取不会阻塞,只会返回前一个字节)。该功能应该是这样的:

void SPI1_put(uint8_t* data, uint16_t length) {
    uint16_t sent = 0;
    uint16_t rec = 0;
    uint8_t dummy;
    while(1)
    {
        if( !SPI1STATbits.SPITBF && sent < length )
            SPI1BUF = data[sent++];
        if( !SPI1STATbits.SRXMPT && rec < length )
            dummy = SPI1BUF, rec++;
        if( sent == length && rec == length )
            break;
    }
}

关于SPI1_get,我认为这很好,但我不确定。问题是,它看起来应该与 非常相似SPI1_put,因为 SPI 在其工作方式上确实是对称的:

void SPI1_get(uint8_t* data, uint16_t length) {
    uint16_t sent = 0;
    uint16_t rec = 0;
    while(1)
    {
        if( !SPI1STATbits.SPITBF && sent < length )
            SPI1BUF = 0xff, sent++;
        if( !SPI1STATbits.SRXMPT && rec < length )
            data[rec++] = SPI1BUF;
        if( sent == length && rec == length )
            break;
    }
}

总体而言,由于您只进行同步调用,因此增强型缓冲区并不是很有用。您可以通过对每个字节执行“发送、等待、接收”来获得具有相同结果的更简单的函数。

更新后编辑

你的新代码看起来是正确的,除了while你做错的条件。这是经过一些修改的更正代码,并带有可能改进它的注释(删除了缓冲区副本):

// No additional buffers

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;
    uint16_t dummy;


    // After the call, SPI comms should have ended and RX FIFO should be empty.
    // If SPI1STATbits.SRXMPT is not 1 here,
    // it means there is a problem in the code. Report it somehow for debug ?
    // if( !SPI1STATbits.SRXMPT )
    //     error!

    // This loop is harmless but shouldnt be needed
    //while (!SPI1STATbits.SRXMPT)
        //dummy = SPI1BUF; // empty buffer

    i = 0;
    while (sent < len || recv < len) {
        if (SPI1STATbits.SPIBEC != 7 && sent < len) {
            // Here we are out of the buffer when sent>=out_len,
            // but it's ok because random bytes are fine here
            SPI1BUF = out[sent++];
        }
        if (!SPI1STATbits.SRXMPT && recv < len) {
            if( recv < out_len )
                // Before out_len, discard the data
                dummy = SPI1BUF;
            else
                // After out_len, store the data in in[]
                in[i++] = SPI1BUF;
            recv++;
        }
    }

}

这是否纠正了任何错误?此外,这可能听起来很愚蠢,但你说得对吗?我不知道你做了什么,但对于你的 1 字节读取,它应该是SPI1_com(two_byte_buffer,one_byte_buffer,2,1);.

还有一点:您似乎总是在开始时尝试清空 RX FIFO。FIFO中真的有东西吗?SPI1STATbits.SRXMPT必须为 1,甚至不尝试清空。如果不是,则软件中的某处存在错误。您还有其他使用 SPI 的代码吗?

于 2015-01-22T11:17:35.510 回答