2017-09-17 10:46 PM
Hiya,
I'm using the UART in 9 bit mode, with the 9th bit being used as an address indication.
I have a version of this working well with 16 bit DMA transfers. Ideally though I want to use 8 bit DMA (byte wide) as all my data is packed for byte wide transfers, and I don't really care about the 9th bit except as an address indicator.
I managed to get this sort of working with byte wide DMA receiving and populating TDR on the transmitter side manually with:
UARTD6.usart->TDR = (uint16_t)addr;
(stuff)
UARTD6.usart->TDR = (uint8_t)val;
UARTD6.usart->TDR = (uint8_t)val;
(etc)
This works ok.
I would like to replace the sending of the byte wide values with a DMA transfer, but I can't get DMA to transmit correctly though, in that I see incomplete transfers or garbage on the receiving side.
Is this idea achievable? Or is my thinking flawed in trying to send byte wide DMA to the 9 bit UART?
Thanks very much
#usart #dma-usart #9-bit #dma2017-09-18 03:04 AM
I'd expect 8-bit memory read and 16-bit peripheral write to behave in a predictable way. Perhaps not the way you want.
2017-09-18 05:34 AM
8-bit memory read and 16-bit peripheral write to behave in a predictable way.
There are basically two ditinct DMAs in the STM32 (the OP didn't care to tell us the model he is using):
In the simpler one used in 'F0/'F3/'Lx , in this case the controller expands the upper 8 bits by 0.
In the complex dual-port DMA ('F2/F4/F7), there is a FIFO inserted into the data path. If FIFO is not used (aka Direct mode) such transfer is impossible, as in that case the memory-side width-setting is ignored. If FIFO is used, the controller always reads at least 4 bytes into FIFO (bytewise if set so) and then writes the concatenated result halfword-wise into the peripheral port.
Neither case fits OP's need.
[rant mode on] Adult UARTs either have dedicated addressed (Intel-originated) mode, or parity settable [also] to MARK/SPACE. Pity the STM32 peripherals were/are designed by staff inexperienced in making practical applications. IMO it wouldn't be that hard nor silicon-consuming to design STM32 UARTs to be register- and functionally compatible with existing industry-standard UARTs, which would help avoid already explored pitfalls, increase functional compatibility and facilitate their usage and code reuse too. [rant mode off]
JW
2017-09-18 05:00 PM
Thanks very much for your replies.
I'm using the STM32F746 devices - sorry I forgot to include this!
This is a frustrating issue. Moreso given that I can make this work if I write directly to the TXD register. Even moreso given that it works when I write 8bit values to the UART TXD register in 9bit mode. It just wont work when I switch over to DMA.
This leads to a question - is the UART sending triggered differently when writing directly than when writing with DMA?
Anyway, thanks again.
2017-09-19 12:12 AM
A bit more on this.
Trying to write 8 bits into the UART (TDR) via byte wide DMA seems to result in whatever appears in bit 0 of TDR getting reflected into bit 8.
EG, if I write (uint8_t)5 via DMA, TDR looks like: 100000101 (it should be 000000101)
Then if I write (uint8_t)4 via DMA, TDR looks like: 000000100, which is correct.
This appears to be the case for other values (1 and 2) etc and explains why I was getting seemingly OK data.
All my hopes and dreams have been ruined.
2017-09-19 01:07 AM
Even moreso given that it works when I write 8bit values to the UART TXD register in 9bit mode.
That ''works'' because it's *not* an 8-bit write:
UARTD6.usart->TDR = (uint8_t)val;
Look up how is the structure containing TDR defined - most likely TDR is defined as uint16_t. Thus, the line above tells the compiler to *read* 8-bit from address given by val, and then write it into address give by TDR according to its definition (i.e. 16-bit), which involves an implicit conversion from unsigned 8-bit to unsigned 16-bit, which according to rules of C is extension by zeros.
Look at the generated asm (disasm).
8-bit write might in C look like:
*(volatile uint8_t *)&UARTD6.usart->TDR = (uint8_t)val;
Trying to write 8 bits into the UART (TDR) via byte wide DMA seems to result in whatever appears in bit 0 of TDR getting reflected into bit 8.
This is the expected behaviour with true 8-bit write:
JW
2017-09-19 01:24 AM
Thanks again for your reply JW. I think I will have to look for an alternate way of sorting this out. Thank you!
2019-02-08 04:26 AM
Hi.
I'm using the USART2 the STM32F334xx in 9-bit mode (multiprocessor communication as said in the reference manual) for "Address Mark detection". I populate a 16 bits buffer with first the address (0x01XX) and then the data (N x 0x00YY). It works well manually doing this in a loop:
uint16_t usartCharSendBuffer[128];
... populating the frame in usartCharSendBuffer[]
loop on (with while (!LL_USART_IsActiveFlag_TXE(USARTXX)) {} or IRQ)
LL_USART_TransmitData9(USARTXX, usartCharSendBuffer[idx_send++]);
I want to use the DMA to minimize the IT requests but I have the similar issue as @Community member (but I want to have a 16 bits buffer) : whatever data size I set for the memory and the peripheral's sides, the bit 0 of TDR is reflected into bit 8. It acts as the TDR forces the DMA to work with the LL_DMA_MDATAALIGN_BYTE option. Despite I set:
dma.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_HALFWORD;
dma.PeriphOrM2MSrcDataSize = LL_DMA_MDATAALIGN_HALFWORD;
Reference manual says:
• To write the halfword “0xABCD�?, the DMA sets the HWDATA bus to “0xABCDABCD�?
with HSIZE = HalfWord
• To write the byte “0xAB�?, the DMA sets the HWDATA bus to “0xABABABAB�? with
HSIZE = Byte
Does someone know if I missed a setting or if it is a limitation of the STM32F334xx's UART?
Thanks a lot.