Я использую STM32F051R8T6 на плате STM32F0-Discovery и флэш-микросхему M25P80 в качестве периферийного устройства. У меня настроен SPI на 3 линии (+ 1 линия выбора программного чипа), полнодуплексный режим, и я использую опрос для отправки и получения данных. У меня было много проблем, пытаясь заставить SPI работать правильно, и все это сводилось к этой серии тестов, выполняемых последовательно, которые выявили ошибку, которую я не могу объяснить.
Шаг 1: Запросите подпись с периферийного устройства. Подпись 0x13, работает в 99% случаев.
Шаг 2: Включите запись на периферийное устройство, введя на него команду.
Шаг 3: Прочтите содержимое регистра состояния, чтобы убедиться, что прошла предыдущая команда.
Поскольку периферийное устройство работает в полнодуплексном режиме, но данные никогда не отправляются в обоих направлениях одновременно, входной буфер должен иметь те же размеры, что и выходной буфер, а записываемые в него данные имеют некоторое смещение.
Проблема возникает на последнем шаге. Если для чтения запрашивается только один байт, SPI не регистрирует передаваемые ему данные! Если запрашиваются дополнительные байты (используя 1 + X в качестве четвертого функционального параметра), содержимое входного буфера будет следующим:
@ 1 + 3:
0x00 0x00 0x02 0x13
@ 1 + 4:
0x00 0x00 0x02 0x13 0x00
@ 1 + 5:
0x00 0x00 0x02 0x13 0x02 0x00
@ 1 + 6:
0x00 0x00 0x02 0x13 0x02 0x00 0x02
@ 1 + 7:
0x00 0x00 0x02 0x13 0x02 0x00 0x02 0x02
А вот сигналы на линии MISO (красный) и SCK (желтый) при чтении 1 + 3 байта всего. Первый пакет — это второй шаг. Как и ожидалось, микросхема флэш-памяти ничего не передает. Второй пакет - шаг №. 3. Периферия молчит в течение первого байта, а затем выводит 0x02 на все последующие байты, но успешно читается только третий байт. И тогда не удается прочитать 4 и 6 байты (все дальнейшие байты читаются правильно).
Так что я не знаю, как объяснить тот факт, что считываемые данные не полностью соответствуют тому, что я вижу на осциллографе. Самое странное, что на третьем шаге считывается байт 0x13! Если первый шаг удален, этот байт читается как 0x00. Похоже на повреждение памяти на стороне STM32, но я не могу найти причину этого, поскольку буферы очищаются с помощью memset(), а внутренний буфер SPI очищается с помощью HAL_SPI_FlushRxFifo(& hspi1).
Вот код:
uint8_t command;
uint8_t inBuffer[16];
uint8_t outBuffer[16];
/* Step 1: Check if IC is alive */
{
memset(inBuffer, 0x00, 16);
memset(outBuffer, 0x00, 16);
command = OPCODE_RES;
outBuffer[0] = command;
spi_select(M25P80);
HAL_SPI_TransmitReceive(&hspi1, outBuffer, inBuffer, (1 + 3) + 1, TIMEOUT);
spi_deselect(M25P80);
HAL_SPI_FlushRxFifo(&hspi1);
}
for (volatile uint32_t i = 0; i < 200; i++);
/* Step 2: Enable writing */
{
memset(inBuffer, 0x00, 16);
memset(outBuffer, 0x00, 16);
command = OPCODE_WREN;
outBuffer[0] = command;
spi_select(M25P80);
HAL_SPI_Transmit(&hspi1, outBuffer, 1, TIMEOUT);
spi_deselect(M25P80);
HAL_SPI_FlushRxFifo(&hspi1);
}
for (volatile uint32_t i = 0; i < 200; i++);
/* Step 3: Make sure writing is enabled */
{
memset(inBuffer, 0x00, 16);
memset(outBuffer, 0x00, 16);
command = OPCODE_RDSR;
outBuffer[0] = command;
spi_select(M25P80);
HAL_SPI_TransmitReceive(&hspi1, outBuffer, inBuffer, 1 + 1, TIMEOUT);
spi_deselect(M25P80);
HAL_SPI_FlushRxFifo(&hspi1);
}
Некоторые подробности о SPI API STM. При вызове типа:
HAL_SPI_TransmitReceive(&hspi1, outBuffer, inBuffer, (1 + 3) + 1, TIMEOUT);
&hspi1 - peripheral handle containing its settings and configurations
outBuffer - pointer to a memory location with data to be sent
inBuffer - pointer to a memory location to which received data is written
(1 + 3) + 1 - number of bytes to receive/transmit;
in this case 1 - command byte, 3 - dummy bytes, 1 - received signature byte
TIMEOUT - a value in the range of 10000 which indicates at which point transmission must be
aborted if failed to finish
РЕДАКТИРОВАТЬ 1: Код инициализации SPI, сгенерированный STM32CubeMx:
hspi1.Instance = SPI1;
hspi1.Init.Mode = SPI_MODE_MASTER;
hspi1.Init.Direction = SPI_DIRECTION_2LINES;
hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
hspi1.Init.NSS = SPI_NSS_SOFT;
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256;
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi1.Init.TIMode = SPI_TIMODE_DISABLED;
hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLED;
hspi1.Init.NSSPMode = SPI_NSS_PULSE_DISABLED;
HAL_SPI_Init(&hspi1);
РЕДАКТИРОВАТЬ 2: Теперь, когда я снизил скорость до 488 бит/с, все работает нормально. Я буду придерживаться этого взлома на некоторое время.
На мой взгляд, вам следует использовать стандартную периферийную библиотеку для STM, потому что она более стабильна и не скрывает от вас низкоуровневые вещи, что дает вам более контролируемую среду. Также вам нужно быть осторожным с синхронизацией выводов CS, многие устройства требуют это значение некоторое время, пока вы не начнете передачу.
Команды, адреса или входные данные фиксируются по переднему фронту тактового входа, а выходные данные сдвигаются по заднему фронту тактового входа.
Пытаться
hspi1.Init.CLKPhase = SPI_PHASE_2EDGE;
В даташите на ваше устройство написано "и выходные данные доступны по заднему фронту C". Я считаю, что это то место, где вы бы это установили.
битмак
Брюс Эбботт
Андрей
Андрей
FRob