cancel
Showing results for 
Search instead for 
Did you mean: 

9 bit UART and DMA

l m
Associate II
Posted on September 18, 2017 at 07:46

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 #dma
7 REPLIES 7
Posted on September 18, 2017 at 12:04

I'd expect 8-bit memory read and 16-bit peripheral write to behave in a predictable way. Perhaps not the way you want.

Tips, buy me a coffee, or three.. PayPal Venmo Up vote any posts that you find helpful, it shows what's working..
Posted on September 18, 2017 at 12:34

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.

0690X00000608H4QAI.png

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.

0690X00000608HTQAY.png

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

Posted on September 19, 2017 at 00:00

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.

l m
Associate II
Posted on September 19, 2017 at 07:12

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.

Posted on September 19, 2017 at 08:07

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:

0690X00000608HxQAI.png

JW

Posted on September 19, 2017 at 08:24

Thanks again for your reply JW. I think I will have to look for an alternate way of sorting this out. Thank you!

sJor
Associate

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.