Der Empfang im STM32-USART-Synchronmodus funktioniert nicht

Ich verwende einen STM32L052K6U6, um mit einem SPI-Slave im UART1-Synchronmodus zu kommunizieren (konfiguriert mit CubeMX unter Verwendung der LL-Bibliothek).

Von CubeMX generierter Setup-Code (ich habe die Tx- und Clk-Pin-Konfiguration weggelassen, da diese Pins tun, was sie sollten):

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

Von mir geschriebener Init-Code, der nach dem obigen Code ausgeführt wird (die EnableDirection-Aufrufe werden eigentlich nicht benötigt, Tx und RX sind im obigen Code bereits aktiviert):

LL_USART_Disable(USART1);

LL_USART_SetTransferBitOrder(USART1, LL_USART_BITORDER_MSBFIRST);

LL_USART_EnableDirectionTx(USART1);
LL_USART_EnableDirectionRx(USART1);

LL_USART_Enable(USART1);

Dies ist Teil des Codes, der Daten empfängt:

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

Das Sondieren des Busses zeigt, dass die Takterzeugung und das Senden der Daten gut funktionieren und der Slave mit den erwarteten Daten antwortet. Der Empfänger ist aktiviert, aber die Daten werden nicht vom Mikrocontroller empfangen. Das Empfangsdatenregister und das RXNE-Flag bleiben Null. Was könnte das verursachen?

Überprüfen Sie den USART-Abschnitt des Referenzhandbuchs. Es ist nicht ganz klar, ob Sie hier eine Ein-Wort-Transaktion oder einen Schreibvorgang gefolgt von einem Lesevorgang durchführen, aber ich vermute, was Sie sehen möchten, ist das USART_ISR_TC-Flag in USART1->ISR
Ich mache eine Ein-Wort-Transaktion mit der TransmitData-Funktion, die in USART1-> TDR schreibt. Die ReceiveData-Funktion liest nur von USART1->RDR, sodass keine neue Transaktion gestartet wird. Das Beobachten des TC-Flags anstelle oder zusätzlich zum Busy-Flag hilft nicht.
Sie müssen Ihren vollständigen Code zeigen, um das Problem zu reproduzieren, insbesondere das USART-Setup. Nach allem, was wir jetzt sagen können, ist der Empfänger möglicherweise nicht einmal aktiviert. Wurde die TC-Flagge sofort gesehen oder nie?
Ich habe den Setup-Code zur Frage hinzugefügt. Ich habe überprüft, ob der Empfänger mit dem Register-Viewer im Debugger eingeschaltet ist. Das TC-Flag wird wie erwartet am Ende der Übertragung gesetzt.
Kannst du bitte dein Projekt teilen? Ich versuche, ein ähnliches Projekt durchzuführen, aber ich kann nicht verstehen, wie CubeMx die USART1-Initialisierung generiert hat. Danke

Antworten (2)

  • Warten Sie, TXEbevor Sie senden

  • Warten Sie, RXNEanstatt das BUSYBit zu überwachen, bevor Sie die Antwort lesen. Das Warten auf BUSYdiesen Weg könnte fehlschlagen, wenn ein Interrupt zur falschen Zeit kommt, und die Daten könnten noch nicht in das Datenregister übertragen werden, wenn BUSYzu geht 0.

  • Aktivieren Sie CPOLund CPHA, möglicherweise wird die Eingabe zur falschen Zeit abgetastet

Danke für die Vorschläge! Warten auf TXE hat nicht geholfen. Das Warten auf RXNE funktioniert nicht, da das RXNE-Flag nie gesetzt wird und das Programm dort hängen bleibt. CPOL und CPHA sollten korrekt sein, da die übertragenen Daten an den richtigen Flanken ausgetaktet werden. Auch wenn zur falschen Zeit gesampelt wird, sollte RXNE am Ende gesetzt werden.

Ich habe eine Lösung gefunden, aber ich weiß nicht, warum es funktioniert.

Es gibt einige Schreibvorgänge zur Initialisierung (max30003_init wird einmal aufgerufen) und dann wiederholte Lesesequenzen (max30003_readReg wird regelmäßig aufgerufen). Das Schreiben hat immer funktioniert, aber das Empfangen von Daten (und das Setzen des RXNE-Flags) funktioniert nur, wenn der USART nach den Initialisierungsschreibvorgängen deaktiviert und dann wieder aktiviert wird. Ohne sie bleibt das Programm hängen und wartet darauf, dass RXNE eingestellt wird. Damit wird das RXNE-Flag wie erwartet gesetzt und die empfangenen Daten gelangen tatsächlich in das Empfangsdatenregister.

Vollständiger Code:

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

Ich habe keine Ahnung, warum der USART-Deaktivierungsimpuls benötigt wird. Hat jemand irgendwelche Ideen?

BEARBEITEN:

Dies wird durch das Overrun-Error-Bit verursacht, das Deaktivieren der Overrun-Erkennung löst das Problem. Das Referenzhandbuch erklärt es in einem Hinweis: "Wenn dieses Bit gesetzt ist, geht der Inhalt des RDR-Registers nicht verloren, sondern das Schieberegister wird überschrieben." Keine neuen RDR-Daten bedeuten auch, dass das RXNE-Flag nicht gesetzt wird. Neuer Init-Code:

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

Danke für die Hilfe an alle!

Sie lassen es wahrscheinlich in einen schlechten Zustand geraten, was das Zurücksetzen zunichte macht. Versuchen Sie, alle Status-/Flag-/Fehlerregister vor und nach dem Zurücksetzen auszulesen.
Es war tatsächlich das Überlauffehlerbit, das Deaktivieren der Überlauferkennung löste das Problem. Ich habe es bisher ignoriert, weil ich dachte, es sei nur ein Statusbit, das keinen Einfluss auf die Funktionalität hat, aber das Referenzhandbuch erklärt es in einem Hinweis: "Wenn dieses Bit gesetzt ist, geht der Inhalt des RDR-Registers nicht verloren, aber das Schieberegister überschrieben." Keine neuen RDR-Daten bedeuten auch, dass das RXNE-Flag nicht gesetzt wird.
Vermutlich erhalten Sie den Überlauffehler, indem Sie die Leseseite ignorieren, wenn Sie das tun, was Sie als Schreibvorgänge betrachten. Die Hardware hat keine wirkliche Möglichkeit zu wissen, dass Sie sich nicht darum kümmern, was zu diesem Zeitpunkt in MISO eingetaktet wird.
Sie haben Recht, ich habe die Lösung bereits zu meiner Antwort hinzugefügt.