STM32 SPI no funciona como espero que se base en la lectura en línea

2

Estoy usando un STM32F103C8 para conectarme a un IC de transceptor Hope RF95W con el fin de aprender.

Solo intento leer el registro de la versión del chip, luego escribir un registro de configuración que tenga un valor de restablecimiento de 0x00 y luego leerlo para asegurarme de que mi código de escritura funciona.

La hoja de datos RF95W dice acerca de la transferencia de SPI:

  

Acceso SENCILLO: se envía un byte de dirección seguido de un byte de datos para un   acceso de escritura, mientras que se envía un byte de dirección y un byte de lectura es   recibido por el acceso de lectura. El pin NSS baja al comienzo de   el marco y se eleva después del byte de datos.

     

MOSI es generado por el maestro en el borde descendente de SCK y es   muestreado por el esclavo (es decir, esta interfaz SPI) en el borde ascendente de   SCK. MISO es generado por el esclavo en el borde descendente de SCK.

     

Una transferencia siempre se inicia cuando el pin NSS baja. MISO es alto   Impedancia cuando NSS es alta. El primer byte es el byte de dirección. Es   comprende:

     
  • Un wnr bit, que es 1 para acceso de escritura y 0 para lectura   acceso.

  •   
  • Luego 7 bits de dirección, primero MSB.

  •   

Para mí, esto significa que leer un registro requiere solo una transferencia. Enviando la dirección del registro, luego esperando el indicador SPI_I2S_FLAG_RXNE , el valor de este registro estará en el registro de datos SPI.

Sin embargo, lo que sucede es que necesito dos secuencias de estas operaciones de escritura / marca / lectura para obtener el valor:

uint8_t read_register(uint8_t address){

  uint8_t data;
  GPIO_ResetBits(GPIOA, GPIO_Pin_3); // slave select (low)
  delay_ms(100);

  while(!SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE));
  SPI_I2S_SendData(SPI1, address); // send  

  while(!SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE));
  data = SPI_I2S_ReceiveData(SPI1); // read received

  while(!SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE));
  SPI_I2S_SendData(SPI1, address); // send

  while(!SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE));
  data = SPI_I2S_ReceiveData(SPI1); // read received

  GPIO_SetBits(GPIOA, GPIO_Pin_3); // slave deselect (high)
  return data; 
}

Solo al tener estos enviar / recibir y luego otro enviar / recibir puedo obtener el valor esperado del registro. Si no hace el segundo send , la recepción devuelve un 0x00 .

La escritura es un poco más sencilla, porque es una escritura de dirección, seguida de una escritura de datos.

void write_register()
{
  uint8_t numRead1 = 0;

  GPIO_ResetBits(GPIOA, GPIO_Pin_3); // slave select (low)
  delay_ms(100);

  while(!SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE));
  SPI_I2S_SendData(SPI1, 0x40 | 0x80); // send 
  while(!SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE));
  SPI_I2S_SendData(SPI1, 0x7E); // send
  while(!SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE));
  numRead1 = SPI_I2S_ReceiveData(SPI1); // read received

  GPIO_SetBits(GPIOA, GPIO_Pin_3); // slave deselect (high)
}

En este caso, espero que numRead1 sea 0x00 , que es el valor predeterminado del registro al que estoy escribiendo. Una llamada posterior a read_register con la misma dirección devuelve el valor que le escribí.

Pero estoy seguro de que hay un error fundamental en lo que estoy haciendo que, si no se corrige, dará lugar a problemas más graves en el futuro.

¿Alguna idea?

    
pregunta Sam Hammamy

2 respuestas

4

El dispositivo esclavo solo puede comunicarse cuando se le proporciona un reloj del maestro (su STM32). Esto complica la lectura del esclavo, porque debe hacer que el maestro proporcione suficientes ciclos de reloj para que el esclavo responda.

Cuando envía un comando SPI a través de SPI_I2S_SendData() , en realidad ocurren dos transmisiones durante los mismos ocho pulsos de reloj. La primera es que su byte está fuera de la línea MOSI. Pero, al mismo tiempo, los datos se registran en al microcontrolador a través de la línea MISO.

Pero como el esclavo no recibe el comando completo hasta el final de estas transacciones, no presenta ningún dato al bus. Esto da como resultado un valor recibido de 0x00 o 0xFF.

Luego debe proporcionar ocho relojes adicionales para permitir que el esclavo devuelva el valor real. Con el STM32, esto se hace enviando un "byte ficticio" al esclavo.

Tenga en cuenta que, en la primera transmisión, el maestro ignora lo que llega del esclavo. En la segunda transmisión, el esclavo ignora lo que envía el maestro.

El código debería tener este aspecto:

// Activate the SS pin
GPIO_ResetBits(...);

// Send the command
SPI_I2S_SendData(SPI_BUS, txbyte);

// When the byte completes sending, the RXNE flag will get set
// and must be cleared by reading the Data Register.
// Notice that the value in the data register is ignored.
while (SPI_I2S_GetFlagStatus(SPI_BUS, SPI_I2S_FLAG_RXNE) == RESET);
SPI_I2S_ReceiveData(SPI_BUS); // Notice there is no variable assignment here!

// Now get the response. Load the data register (DR) with a dummy
// byte to start data reception.
SPI_I2S_SendData(SPI_BUS, 0);

// When the dummy byte completes sending, the RX buffer will contain the
// response byte. Get it.
while (SPI_I2S_GetFlagStatus(SPI_BUS, SPI_I2S_FLAG_RXNE) == RESET);
rxbyte = SPI_I2S_ReceiveData(SPI_BUS);
}

// Release the SS line
GPIO_SetBits(...);
    
respondido por el bitsmack
3

El chip Hope debe obtener la dirección del registro que desea leer, que ocurre en la transferencia de primer byte, momento en el que se carga en el registro Hope SPI MISO para que se transmita a medida que se envía el siguiente byte (ficticio) por el maestro (ya que es una operación de lectura).

Alternativamente, el siguiente byte transmitido puede ser otra operación de lectura / escritura, pero la bomba debe estar cebada por así decirlo, y las lecturas se devuelven en la siguiente transacción SPI de byte.

    
respondido por el isdi

Lea otras preguntas en las etiquetas