1
\$\begingroup\$

I am using a STM32L052K6U6 to communicate with an SPI slave using the UART1 synchronous mode (configured with CubeMX, using the LL library).

Setup code generated by CubeMX (I left out the Tx and Clk pin config as those pins do what they should):

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

Init code written by me, executed after the code above (the EnableDirection calls are not actually needed, Tx and RX is already enabled in the code above):

LL_USART_Disable(USART1);

LL_USART_SetTransferBitOrder(USART1, LL_USART_BITORDER_MSBFIRST);

LL_USART_EnableDirectionTx(USART1);
LL_USART_EnableDirectionRx(USART1);

LL_USART_Enable(USART1);

This is part of the code that receives data:

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

Probing the bus reveals that the clock generation and data sending works fine and the slave responds with the expected data. The receiver is enabled, but the data is not received by the microcontroller. The receive data register and the RXNE flag stay zero. What could cause this?

\$\endgroup\$
5
  • \$\begingroup\$ Check the USART section of the reference manual. It's not entirely clear if you are doing a one word transaction here or a write followed by a read, but I suspect what you want to watch is is the USART_ISR_TC flag in USART1->ISR \$\endgroup\$ Commented Mar 26, 2019 at 22:34
  • \$\begingroup\$ I am doing a one word transaction using the TransmitData function which writes to the USART1->TDR. The ReceiveData function just reads from USART1->RDR so it doesn't start a new transaction. Watching the TC flag instead of or in addition to the busy flag does not help. \$\endgroup\$
    – Fr4nky
    Commented Mar 26, 2019 at 22:44
  • 1
    \$\begingroup\$ You need to show your full code to reproduce the problem, especially the USART setup. For all we can tell right now the receiver might not even be enabled. As for the TC flag, was it seen immediately, or never? \$\endgroup\$ Commented Mar 26, 2019 at 22:45
  • \$\begingroup\$ I added the setup code to the question. I verified that the receiver is turned on using the register viewer in the debugger. The TC flag gets set at the end of the transmission, as expected. \$\endgroup\$
    – Fr4nky
    Commented Mar 26, 2019 at 23:04
  • \$\begingroup\$ Can you share your project please? I'm trying to do the similar project, but I can't understand how CubeMx generated USART1 initialisation. Thank you \$\endgroup\$ Commented Mar 27, 2021 at 18:25

2 Answers 2

2
\$\begingroup\$
  • Wait for TXE before transmitting

  • Wait for RXNE instead of monitoring the BUSY bit before reading the answer. Waiting on BUSY that way might fail when an interrupt comes at the wrong time, and the data might not yet be transferred to the data register when BUSY goes to 0.

  • Check CPOL and CPHA, it might be sampling the input at the wrong time

\$\endgroup\$
1
  • \$\begingroup\$ Thanks for the suggestions! Waiting for TXE did not help. Waiting for RXNE does not work, as the RXNE flag never gets set, so the program gets stuck there. CPOL and CPHA should be correct, as the transmitted data is clocked out on the correct edges. Also even when sampling at the wrong time, RXNE should get set at the end. \$\endgroup\$
    – Fr4nky
    Commented Mar 26, 2019 at 16:57
0
\$\begingroup\$

I figured out a solution, but I don't know why it works.

There are some writes for initialization (max30003_init gets called once) and then repeated read sequences (max30003_readReg gets called regularly). The writing has always worked but receiving data (and the RXNE flag getting set) only works when disabling and then reenabling the USART after the initialization writes. Without it the program gets stuck waiting for RXNE to get set. With this, the RXNE flag gets set as expected and the received data actually makes it to the receive data regster.

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

I have no idea why the USART disable pulse is needed. Does anyone have any ideas?

EDIT:

This is caused by the the overrun error bit, disabling the overrun detection solves the problem. The reference manual explains it in a note: "When this bit is set, the RDR register content is not lost but the shift register is overwritten." No new RDR data also means the RXNE flag doesn't get set. New 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);
}

Thanks for the help everyone!

\$\endgroup\$
4
  • \$\begingroup\$ You're probably letting it get into a bad state, which resetting it is undoing. Try reading out all of the status/flag/error registers before and after the reset. \$\endgroup\$ Commented Apr 15, 2019 at 19:35
  • \$\begingroup\$ It was indeed the overrun error bit, disabling the overrun detection solved the problem. I ignored it until now as I thought it was only a status bit that has no influence on the functionality but the reference manual explains it in a note: "When this bit is set, the RDR register content is not lost but the shift register is overwritten." No new RDR data also means the RXNE flag doesn't get set. \$\endgroup\$
    – Fr4nky
    Commented Apr 15, 2019 at 23:53
  • \$\begingroup\$ As a guess, you may get the overrun error by ignoring the read side when doing what you think of as writes. The hardware doesn't have any real way to know that you don't care about what is being clocked in on MISO at that point. \$\endgroup\$ Commented Apr 16, 2019 at 0:00
  • \$\begingroup\$ You are correct, I already added the solution to my answer. \$\endgroup\$
    – Fr4nky
    Commented Apr 17, 2019 at 0:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.