2025-02-05 02:57 AM
Hi Folks,
I have an issue with the RS485 Driver Enable Pin with the USART6 on an H7A3 (Nucleo).
The Perhiperal is set up in CubeMX with "Hardware Flow Control (RS485) enabled.
I have developed a driver using the Low Level functions.
The Peripheral is setup by Cube MX, in "uart.c" the "DE Mode" is enabled:
LL_USART_EnableDEMode(USART6);
LL_USART_SetDESignalPolarity(USART6, LL_USART_DE_POLARITY_HIGH);
LL_USART_SetDEAssertionTime(USART6, 0);
LL_USART_SetDEDeassertionTime(USART6, 0);
When I start a transmission with
LL_DMA_SetMemoryAddress(hrUA->txDMA.hDMA, hrUA->txDMA.dmaCh, txAddr); // set TxBuffer Address
LL_DMA_SetDataLength(hrUA->txDMA.hDMA, hrUA->txDMA.dmaCh, dataSz); // set Tx Size
LL_DMA_EnableIT_TC(hrUA->txDMA.hDMA, hrUA->txDMA.dmaCh); // enable Tx Transfer Complete IRQ
LL_DMA_EnableIT_TE(hrUA->txDMA.hDMA, hrUA->txDMA.dmaCh); // enable Tx Transfer Error IRQ
LL_USART_ClearFlag_TC(hrUA->hUA); // clear Tx complete IRQ
// NOTE: Transfer Complete IRQ will be enabled by DMA TC Interrupt handler
LL_USART_EnableDMAReq_TX(hrUA->hUA);
LL_USART_EnableDirectionTx(hrUA->hUA); // enable U(S)ART Tx
I can see the Bits beeing clocked out on the Tx Pin in my Oscilloskope. The DMA Interrupt and the USART Transfer complete Interrupt are fired, so generally I'd say: the peripheral is working and is sending data. But the Scope shows clearly that the Driver Enable Pin is not toggling during TX.
My assumption was: when the "DE-Mode" is enabled (LL_USART_EnableDEMode(USART6);) an I enable the Transmission (LL_USART_EnableDirectionTx(hrUA->hUA);) the Peripheral would set the DE-Pin automatically - otherwise settings like DE Asserting-Time and DE Deassertion Time would not make much sense.
Am I missing something? Do I have to take an extra step to tell the Peripheral to enable the DE Pin? I was looking for something like that, but could not find anything ...
thank you for any input!
Solved! Go to Solution.
2025-02-05 04:07 AM
hmm ... i have no idea, why, but it is working now.
The working Code:
LL_DMA_SetMemoryAddress(hrUA->txDMA.hDMA, hrUA->txDMA.dmaCh, txAddr); // set TxBuffer Address
LL_DMA_SetDataLength(hrUA->txDMA.hDMA, hrUA->txDMA.dmaCh, dataSz); // set Tx Size
do {
LL_DMA_EnableStream(hrUA->txDMA.hDMA, hrUA->txDMA.dmaCh); // enable RX DMA stream
} while(!(LL_DMA_IsEnabledStream(hrUA->txDMA.hDMA, hrUA->txDMA.dmaCh)));
LL_DMA_EnableIT_TC(hrUA->txDMA.hDMA, hrUA->txDMA.dmaCh); // enable Tx Transfer Complete IRQ
LL_DMA_EnableIT_TE(hrUA->txDMA.hDMA, hrUA->txDMA.dmaCh); // enable Tx Transfer Error IRQ
LL_USART_ClearFlag_TC(hrUA->hUA); // clear Tx complete IRQ
// NOTe: Transfer Complete IRQ will be enabled by DMA TC Interrupt handler
LL_USART_EnableDMAReq_TX(hrUA->hUA);
LL_USART_EnableDirectionTx(hrUA->hUA); // enable U(S)ART Tx
Setup is done automatically by Cube MX. Just select "LL" for the UART in the Project-Settings-Advance and configure it in the CubeMX as if you would configure it with HAL.
The obvious difference between the code I posted in my initial question and this code is the do () while loop. I forgot this piece of code in my initial posting. This code was there in my first attempts. So I see no difference, and I don't know why it is working now and did not work before.
All I did was a Project Clean
Just in case anyone is searching for that, this is the one-time initialisation of the Peripheral and the DMA Channels:
LL_USART_DisableDirectionTx(hrUA->hUA); // Disable U(S)ART Tx
LL_USART_DisableDirectionRx(hrUA->hUA); // Disable U(S)ART Rx
uint32_t rxaddr = (uint32_t)(&UA_RxDummy);
uint32_t txaddr = (uint32_t)(&UA_TxDummy);
LL_DMA_DisableStream(hrUA->rxDMA.hDMA, hrUA->rxDMA.dmaCh); // Disable DMA (Rx)
LL_USART_DisableDMAReq_RX(hrUA->hUA); // Remove DMA (Rx)
// Configure RX
// LL_DMA_ConfigAddresses(DMA, DMA Channel, Source Addr, Dest Addr, Direction)
LL_DMA_ConfigAddresses(
hrUA->rxDMA.hDMA, // DMA Peripheral
hrUA->rxDMA.dmaCh, // DMA Channel
LL_USART_DMA_GetRegAddr(hrUA->hUA, LL_USART_DMA_REG_DATA_RECEIVE), // Source Address = RX Data register
rxaddr, // Destination Address = dummy
LL_DMA_GetDataTransferDirection( // Direction
hrUA->rxDMA.hDMA,
hrUA->rxDMA.dmaCh));
LL_DMA_DisableStream(hrUA->txDMA.hDMA, hrUA->txDMA.dmaCh); // Disable DMA (Tx)
LL_USART_DisableDMAReq_TX(hrUA->hUA); // Remove DMA (Tx)
// Configure TX
// LL_DMA_ConfigAddresses(DMA, DMA Channel, Source Addr, Dest Addr, Direction)
LL_DMA_ConfigAddresses(
hrUA->txDMA.hDMA, // DMA Peripheral
hrUA->txDMA.dmaCh, // DMA Channel
txaddr, // Source Address = dummy
LL_USART_DMA_GetRegAddr(hrUA->hUA, LL_USART_DMA_REG_DATA_TRANSMIT), // Destination Address = TX Data Register
LL_DMA_GetDataTransferDirection( // Direction
hrUA->txDMA.hDMA,
hrUA->txDMA.dmaCh));
// clear Interrupt Status registers
*hrUA->rxDMA.IFCR = hrUA->rxDMA.regmask.all;
*hrUA->txDMA.IFCR = hrUA->txDMA.regmask.all;
It is a bit unsatisfying, when a issue solves itself and you don't know why, but on the other hand, it was one of the fastest solved "strange behaviour" bugs I had so far ...
thanks and cheers,
cb
2025-02-05 03:29 AM
Did you configure the DE pin for the proper alternate function?
2025-02-05 03:30 AM
yes. This is done automatically by CubeMX. I checked that
2025-02-05 03:40 AM
If you have triple checked the schematics, layout, soldering and setup and thinsg still do not work, enter your code in the debugger, read out the GPIO and USART peropheral registers and compare the bits with what you expect.
2025-02-05 03:50 AM
ok, thanks for your input, Uwe.
May I interpret your answers in that direction, that you'd also expect the peripheral to set the DE pin automatically as soon as the transfer starts?
Looks like I have cought me a "strange behaviour" bug ... again. Looks like I gonna invest some time to find out where this little su*ker is hiding ...
2025-02-05 04:07 AM
hmm ... i have no idea, why, but it is working now.
The working Code:
LL_DMA_SetMemoryAddress(hrUA->txDMA.hDMA, hrUA->txDMA.dmaCh, txAddr); // set TxBuffer Address
LL_DMA_SetDataLength(hrUA->txDMA.hDMA, hrUA->txDMA.dmaCh, dataSz); // set Tx Size
do {
LL_DMA_EnableStream(hrUA->txDMA.hDMA, hrUA->txDMA.dmaCh); // enable RX DMA stream
} while(!(LL_DMA_IsEnabledStream(hrUA->txDMA.hDMA, hrUA->txDMA.dmaCh)));
LL_DMA_EnableIT_TC(hrUA->txDMA.hDMA, hrUA->txDMA.dmaCh); // enable Tx Transfer Complete IRQ
LL_DMA_EnableIT_TE(hrUA->txDMA.hDMA, hrUA->txDMA.dmaCh); // enable Tx Transfer Error IRQ
LL_USART_ClearFlag_TC(hrUA->hUA); // clear Tx complete IRQ
// NOTe: Transfer Complete IRQ will be enabled by DMA TC Interrupt handler
LL_USART_EnableDMAReq_TX(hrUA->hUA);
LL_USART_EnableDirectionTx(hrUA->hUA); // enable U(S)ART Tx
Setup is done automatically by Cube MX. Just select "LL" for the UART in the Project-Settings-Advance and configure it in the CubeMX as if you would configure it with HAL.
The obvious difference between the code I posted in my initial question and this code is the do () while loop. I forgot this piece of code in my initial posting. This code was there in my first attempts. So I see no difference, and I don't know why it is working now and did not work before.
All I did was a Project Clean
Just in case anyone is searching for that, this is the one-time initialisation of the Peripheral and the DMA Channels:
LL_USART_DisableDirectionTx(hrUA->hUA); // Disable U(S)ART Tx
LL_USART_DisableDirectionRx(hrUA->hUA); // Disable U(S)ART Rx
uint32_t rxaddr = (uint32_t)(&UA_RxDummy);
uint32_t txaddr = (uint32_t)(&UA_TxDummy);
LL_DMA_DisableStream(hrUA->rxDMA.hDMA, hrUA->rxDMA.dmaCh); // Disable DMA (Rx)
LL_USART_DisableDMAReq_RX(hrUA->hUA); // Remove DMA (Rx)
// Configure RX
// LL_DMA_ConfigAddresses(DMA, DMA Channel, Source Addr, Dest Addr, Direction)
LL_DMA_ConfigAddresses(
hrUA->rxDMA.hDMA, // DMA Peripheral
hrUA->rxDMA.dmaCh, // DMA Channel
LL_USART_DMA_GetRegAddr(hrUA->hUA, LL_USART_DMA_REG_DATA_RECEIVE), // Source Address = RX Data register
rxaddr, // Destination Address = dummy
LL_DMA_GetDataTransferDirection( // Direction
hrUA->rxDMA.hDMA,
hrUA->rxDMA.dmaCh));
LL_DMA_DisableStream(hrUA->txDMA.hDMA, hrUA->txDMA.dmaCh); // Disable DMA (Tx)
LL_USART_DisableDMAReq_TX(hrUA->hUA); // Remove DMA (Tx)
// Configure TX
// LL_DMA_ConfigAddresses(DMA, DMA Channel, Source Addr, Dest Addr, Direction)
LL_DMA_ConfigAddresses(
hrUA->txDMA.hDMA, // DMA Peripheral
hrUA->txDMA.dmaCh, // DMA Channel
txaddr, // Source Address = dummy
LL_USART_DMA_GetRegAddr(hrUA->hUA, LL_USART_DMA_REG_DATA_TRANSMIT), // Destination Address = TX Data Register
LL_DMA_GetDataTransferDirection( // Direction
hrUA->txDMA.hDMA,
hrUA->txDMA.dmaCh));
// clear Interrupt Status registers
*hrUA->rxDMA.IFCR = hrUA->rxDMA.regmask.all;
*hrUA->txDMA.IFCR = hrUA->txDMA.regmask.all;
It is a bit unsatisfying, when a issue solves itself and you don't know why, but on the other hand, it was one of the fastest solved "strange behaviour" bugs I had so far ...
thanks and cheers,
cb
2025-02-05 04:09 AM
Yes, if set up right and no hardware error, DE is set automatic. I have done so from some device families. H7A as a recent family offers more changeable parameters, e.g. the RTS/DE timing but will work too.