Как правильно получать данные через расширенный SPI буферный режим микроконтроллеров Microchip PIC24F?

Я программирую связь SPI с внешним радиочастотным чипом. Микроконтроллер представляет собой модель PIC24FJ64GA102 от Microchip.

Я хочу использовать расширенный режим буфера 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 как в комментарии выше: отправлено 0x02 0x01 0xff 0xff, получено 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 байт: r/w-бит + длина
  • 1 байт: адрес
  • 1-127 байт: данные

Итак, когда я читаю 1 байт из ведомого чипа, то есть отправляю 3 байта (rw+len, address, dummybyte), данные, которые я храню в своем in_buf буфер 0xFF. Теперь странная вещь: когда я просто читаю еще один байт, ничего не меняя (просто еще один фиктивный байт в шине), первый байт моего in_buf получил правильные данные. Но это, кажется, не всегда работает, потому что моя программа все еще зависает в некоторых моментах.

Я сижу здесь с большим количеством вопросительных знаков.

Странная вещь 2: чтение 8 байтов, данные корректны в моем буфере до последнего байта, последний байт 0xFF, должен быть 0x00. WTF?

PS: Уже подали заявку в Microchip за поддержку в этом вопросе.

1 ответ

Решение

Есть проблема в вашем 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?

Другие вопросы по тегам