STM32 SPI Peripheral bug?

3

Estoy usando STM32F051R8T6 en STM32F0-Discovery Board y M25P80 flash IC como periférico. Tengo el SPI configurado en el modo dúplex completo de 3 líneas (+1 de selección de chip de software) y estoy usando el sondeo para enviar y recibir datos. He tenido muchos problemas al intentar que SPI funcione correctamente y todo se redujo a esta serie de pruebas ejecutadas consecutivamente, lo que expuso un error que no puedo explicar.

Paso 1: Solicite una firma desde el periférico. La firma es 0x13, funciona el 99% del tiempo.

Paso 2: habilite la escritura en el periférico emitiéndole un comando.

Paso 3: lea el contenido del registro de estado para asegurarse de que el comando anterior lo hizo.

Dado que el periférico funciona en modo dúplex completo, pero los datos nunca se envían en ambas direcciones al mismo tiempo, el búfer de entrada debe tener las mismas dimensiones que el búfer de salida y los datos escritos en él tienen algún desplazamiento.

El problema surge en el último paso. Si solo se solicita la lectura de un solo byte, SPI no registra los datos que se le transmiten. Si se solicitan más bytes (utilizando 1 + X como el cuarto parámetro de la función), los contenidos del búfer de entrada son los siguientes:

@ 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

Y aquí están las señales en la línea MISO (roja) y SCK (amarilla) cuando se lee 1 + 3 bytes en total. El primer paquete es el segundo paso. Como era de esperar, nada se transmite por el flash IC. El segundo paquete es el paso no. 3. El periférico permanece en silencio durante el primer byte y luego emite 0x02 en todos los bytes siguientes, pero solo el tercer byte se lee correctamente. Y luego falla en leer los bytes 4 y 6 (todos los demás bytes se leen correctamente).

Por lo tanto, no sé cómo explicar el hecho de que los datos que se leen no se corresponden completamente con lo que estoy viendo en el osciloscopio. Lo más extraño es que se lee un byte de 0x13 durante el tercer paso. Si se elimina el primer paso, este byte se lee como 0x00. Parece una corrupción de memoria en el lado STM32, pero no puedo encontrar una razón para ello, ya que los búferes se desinfectan con memset () y el búfer interno de SPI se vacía con HAL_SPI_FlushRxFifo (& hspi1).

Aquí está el código:

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

Algunos detalles sobre la API SPI de STM. En una llamada como:

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

EDIT 1: Código de inicialización SPI generado por 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);

EDIT 2: Ahora que he reducido la velocidad a 488 b / s, todo funciona bien. Me quedaré con este truco por un tiempo.

    
pregunta andrey g

3 respuestas

1

En mi opinión, debería usar la biblioteca periférica estándar para STM porque es más estable y no le oculta el nivel de bajo nivel que le brinda el entorno más controlable. También debe tener cuidado con la sincronización de pines CS, muchos dispositivos debe mantener ese valor un tiempo hasta que comience la transmisión.

    
respondido por el Stefan Merfu
0

Los comandos, las direcciones o los datos de entrada están bloqueados en el borde ascendente de la entrada del reloj, mientras que los datos de salida se desplazan en el borde descendente de la entrada del reloj.

    
respondido por el sam
0

Probar

hspi1.Init.CLKPhase = SPI_PHASE_2EDGE;

La hoja de datos de su dispositivo dice "y los datos de salida están disponibles desde el borde descendente de C." Creo que aquí es donde estarías configurando eso.

    
respondido por el Scott Seidman

Lea otras preguntas en las etiquetas