La recepción del modo síncrono USART STM32 no funciona

Estoy usando un STM32L052K6U6 para comunicarme con un esclavo SPI usando el modo síncrono UART1 (configurado con CubeMX, usando la biblioteca LL).

Código de configuración generado por CubeMX (Omití la configuración de pines Tx y Clk ya que esos pines hacen lo que deberían):

  GPIO_InitStruct.Pin = USART1_RX_ECG_Pin;
  GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
  GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_MEDIUM;
  GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_OPENDRAIN;
  GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
  GPIO_InitStruct.Alternate = LL_GPIO_AF_4;
  LL_GPIO_Init(USART1_RX_ECG_GPIO_Port, &GPIO_InitStruct);

  USART_InitStruct.BaudRate = 2000000;
  USART_InitStruct.DataWidth = LL_USART_DATAWIDTH_8B;
  USART_InitStruct.StopBits = LL_USART_STOPBITS_0_5;
  USART_InitStruct.Parity = LL_USART_PARITY_NONE;
  USART_InitStruct.TransferDirection = LL_USART_DIRECTION_TX_RX;
  USART_InitStruct.OverSampling = LL_USART_OVERSAMPLING_8;

  LL_USART_Init(USART1, &USART_InitStruct);

  USART_ClockInitStruct.ClockPolarity = LL_USART_POLARITY_LOW;
  USART_ClockInitStruct.ClockPhase = LL_USART_PHASE_1EDGE;
  USART_ClockInitStruct.LastBitClockPulse = LL_USART_LASTCLKPULSE_OUTPUT;
  LL_USART_ClockInit(USART1, &USART_ClockInitStruct);

  LL_USART_EnableDEMode(USART1);

  LL_USART_DisableDEMode(USART1);

  LL_USART_DisableDMADeactOnRxErr(USART1);

  LL_USART_Enable(USART1);

  LL_USART_ConfigSyncMode(USART1);

  LL_USART_Enable(USART1);

Código de inicio escrito por mí, ejecutado después del código anterior (las llamadas EnableDirection no son realmente necesarias, Tx y RX ya están habilitados en el código anterior):

LL_USART_Disable(USART1);

LL_USART_SetTransferBitOrder(USART1, LL_USART_BITORDER_MSBFIRST);

LL_USART_EnableDirectionTx(USART1);
LL_USART_EnableDirectionRx(USART1);

LL_USART_Enable(USART1);

Esto es parte del código que recibe datos:

LL_USART_TransmitData8(USART1, 0);
while(!LL_USART_IsActiveFlag_BUSY(USART1));
while(LL_USART_IsActiveFlag_BUSY(USART1));
uint8_t data_1 = LL_USART_ReceiveData8(USART1);

Sondear el bus revela que la generación del reloj y el envío de datos funcionan bien y el esclavo responde con los datos esperados. El receptor está habilitado, pero el microcontrolador no recibe los datos. El registro de datos de recepción y la bandera RXNE permanecen en cero. Que podria causar esto?

Consulte la sección USART del manual de referencia. No está del todo claro si está haciendo una transacción de una palabra aquí o una escritura seguida de una lectura, pero sospecho que lo que quiere ver es el indicador USART_ISR_TC en USART1->ISR
Estoy haciendo una transacción de una palabra usando la función TransmitData que escribe en USART1->TDR. La función ReceiveData solo lee desde USART1->RDR para que no inicie una nueva transacción. Ver el indicador de TC en lugar o además del indicador de ocupado no ayuda.
Debe mostrar su código completo para reproducir el problema, especialmente la configuración de USART. Por lo que podemos decir en este momento, es posible que el receptor ni siquiera esté habilitado. En cuanto a la bandera del TC, ¿se vio inmediatamente o nunca?
Agregué el código de configuración a la pregunta. Verifiqué que el receptor está encendido usando el visor de registro en el depurador. El indicador TC se establece al final de la transmisión, como se esperaba.
¿Puedes compartir tu proyecto por favor? Estoy tratando de hacer un proyecto similar, pero no puedo entender cómo CubeMx generó la inicialización de USART1. Gracias

Respuestas (2)

  • Esperar TXEantes de transmitir

  • Espere RXNEen lugar de monitorear el BUSYbit antes de leer la respuesta. Esperar de BUSYesa manera puede fallar cuando llega una interrupción en el momento equivocado, y es posible que los datos aún no se transfieran al registro de datos cuando BUSYvaya a 0.

  • Marque CPOLy CPHA, podría estar muestreando la entrada en el momento equivocado

¡Gracias por las sugerencias! Esperar a TXE no ayudó. Esperar a RXNE no funciona, ya que el indicador RXNE nunca se establece, por lo que el programa se atasca allí. CPOL y CPHA deben ser correctos, ya que los datos transmitidos se registran en los bordes correctos. Además, incluso cuando se muestrea en el momento equivocado, RXNE debe configurarse al final.

Encontré una solución, pero no sé por qué funciona.

Hay algunas escrituras para la inicialización (max30003_init se llama una vez) y luego secuencias de lectura repetidas (max30003_readReg se llama regularmente). La escritura siempre ha funcionado, pero la recepción de datos (y la configuración del indicador RXNE) solo funciona cuando se deshabilita y luego se vuelve a habilitar el USART después de que se escribe la inicialización. Sin él, el programa se atasca esperando que se configure RXNE. Con esto, el indicador RXNE se establece como se esperaba y los datos recibidos realmente llegan al registro de datos de recepción.

Código completo:

void max30003_init(void)
{
    LL_GPIO_SetOutputPin(ECG_CS_GPIO_Port, ECG_CS_Pin);

    LL_USART_Disable(USART1);
    LL_USART_SetTransferBitOrder(USART1, LL_USART_BITORDER_MSBFIRST);
    LL_USART_Enable(USART1);

    LL_TIM_EnableCounter(TIM22);
    LL_TIM_CC_EnableChannel(TIM22, LL_TIM_CHANNEL_CH1); //32.7...kHz FCLK

    max30003_writeReg(REG_SW_RST,0x000000); //reset

    delay_ms(10);

    max30003_writeReg(REG_CNFG_GEN, 0x081007);
    max30003_writeReg(REG_CNFG_CAL, 0x720000);
    max30003_writeReg(REG_CNFG_EMUX,0x0B0000);
    max30003_writeReg(REG_CNFG_ECG, 0x805000);

    max30003_writeReg(REG_CNFG_RTOR1,0x3fc600);
    max30003_writeReg(REG_SYNCH,0x000000);

    //FIXME: receive doesn't work without this!?
    LL_USART_Disable(USART1);
    LL_USART_Enable(USART1);

    delay_ms(10);
}

uint32_t max30003_readReg(uint8_t reg)
{
    while(LL_USART_IsActiveFlag_BUSY(USART1));
    while(!LL_USART_IsActiveFlag_TXE(USART1));

    LL_USART_RequestRxDataFlush(USART1); //clear RXNE

    LL_GPIO_ResetOutputPin(ECG_CS_GPIO_Port, ECG_CS_Pin);

    LL_USART_TransmitData8(USART1, ((reg << 1) | READ));
    while(!LL_USART_IsActiveFlag_RXNE(USART1));
    LL_USART_RequestRxDataFlush(USART1); //clear RXNE

    LL_USART_TransmitData8(USART1, 0);
    while(!LL_USART_IsActiveFlag_RXNE(USART1));
    uint8_t data_2 = LL_USART_ReceiveData8(USART1);

    LL_USART_TransmitData8(USART1, 0);
    while(!LL_USART_IsActiveFlag_RXNE(USART1));
    uint8_t data_1 = LL_USART_ReceiveData8(USART1);

    LL_USART_TransmitData8(USART1, 0);
    while(!LL_USART_IsActiveFlag_RXNE(USART1));
    uint8_t data_0 = LL_USART_ReceiveData8(USART1);

    LL_GPIO_SetOutputPin(ECG_CS_GPIO_Port, ECG_CS_Pin);

    return ((data_2 << 16) | (data_1 << 8) | data_0);
}

void max30003_writeReg(uint8_t reg, uint32_t data)
{
    while(LL_USART_IsActiveFlag_BUSY(USART1));
    while(!LL_USART_IsActiveFlag_TXE(USART1));

    LL_GPIO_ResetOutputPin(ECG_CS_GPIO_Port, ECG_CS_Pin);

    LL_USART_TransmitData8(USART1, ((reg << 1) | WRITE));
    while(!LL_USART_IsActiveFlag_TXE(USART1));

    LL_USART_TransmitData8(USART1, (uint8_t)(data >> 16));
    while(!LL_USART_IsActiveFlag_TXE(USART1));

    LL_USART_TransmitData8(USART1, (uint8_t)(data >> 8));
    while(!LL_USART_IsActiveFlag_TXE(USART1));

    LL_USART_TransmitData8(USART1, (uint8_t)data);
    while(!LL_USART_IsActiveFlag_TC(USART1));

    LL_GPIO_SetOutputPin(ECG_CS_GPIO_Port, ECG_CS_Pin);
}

No tengo idea de por qué se necesita el pulso de desactivación de USART. ¿Alguien tiene alguna idea?

EDITAR:

Esto es causado por el bit de error de desbordamiento, deshabilitar la detección de desbordamiento resuelve el problema. El manual de referencia lo explica en una nota: "Cuando se establece este bit, el contenido del registro RDR no se pierde, pero se sobrescribe el registro de desplazamiento". No hay nuevos datos RDR también significa que la bandera RXNE no se establece. Nuevo código de inicio:

void max30003_init(void)
{
    LL_GPIO_SetOutputPin(ECG_CS_GPIO_Port, ECG_CS_Pin);

    LL_USART_Disable(USART1);
    LL_USART_SetTransferBitOrder(USART1, LL_USART_BITORDER_MSBFIRST);
    LL_USART_DisableOverrunDetect(USART1);
    LL_USART_Enable(USART1);

    LL_TIM_EnableCounter(TIM22);
    LL_TIM_CC_EnableChannel(TIM22, LL_TIM_CHANNEL_CH1); //32.7...kHz FCLK

    max30003_writeReg(REG_SW_RST,0x000000); //reset

    delay_ms(10);

    max30003_writeReg(REG_CNFG_GEN, 0x081007);
    max30003_writeReg(REG_CNFG_CAL, 0x720000);
    max30003_writeReg(REG_CNFG_EMUX,0x0B0000);
    max30003_writeReg(REG_CNFG_ECG, 0x805000);

    max30003_writeReg(REG_CNFG_RTOR1,0x3fc600);
    max30003_writeReg(REG_SYNCH,0x000000);

    delay_ms(10);
}

¡Gracias por la ayuda a todos!

Probablemente estés dejando que se ponga en mal estado, y restablecerlo es deshacerlo. Intente leer todos los registros de estado/bandera/error antes y después del reinicio.
De hecho, fue el bit de error de desbordamiento, deshabilitar la detección de desbordamiento resolvió el problema. Lo ignoré hasta ahora porque pensé que era solo un bit de estado que no tiene influencia en la funcionalidad, pero el manual de referencia lo explica en una nota: "Cuando se establece este bit, el contenido del registro RDR no se pierde pero el registro de desplazamiento es sobrescrito". No hay nuevos datos RDR también significa que la bandera RXNE no se establece.
Como suposición, puede obtener el error de desbordamiento al ignorar el lado de lectura cuando hace lo que piensa que es escritura. El hardware no tiene ninguna forma real de saber que no le importa lo que se registra en MISO en ese momento.
Tienes razón, ya agregué la solución a mi respuesta.