I'm working on firmware for an STM32F103 which is communicating over RS232 at 115200 baud with a motor controller. The motor controller (a Copley Xenus XTL) operates on a "speak when spoken to" protocol. I'm using the ASCII programming interface in the linked documents. The STM32 always be sending the same command ("g r0x18") to poll a register, and the motor controller replies with a variable number (~4-10 bytes) of characters terminated by a carriage return ("v 12345", where the number of digits is variable). I have code to parse the response and pull a numeric value out of it. Once the response is parsed, the register poll command should be transmitted to the motor controller again. The STM32 is also reading an ADC channel over DMA in circular mode in the background.
I'd like to implement this using the DMA controller to make everything as non-blocking as possible, but I'm a bit confused as to which interrupts I should be using and when they fire. Without using the DMA controller, the parsing code currently resides in the USART RXNE interrupt. Suppose I transmit command and the motor controller starts to reply. I believe the RXNE interrupt fires for each received byte, but what about the DMA transfer complete interrupt? Is there even any functional difference in this case between using the DMA TC interrupt and the USART RXNE interrupt?
2 Answers 2
You can configure DMA to work in cyclic mode, and with reasonable big buffer you can pull characters as often as you want with simple poll
function. Just store index of last character retrieved and use DMA register to check how many characters it has to receive until roll over (AFAIR register has NDTR
in its name), and process characters received since last call, then update index of last character retrieved.
Described DMA usage makes your poll
independent from context you call it from as long as you prevent preemption. Then you can use RX interrupt to trigger poll call(s), or you can do it in other context (for example with some periodic events).
This is generally efficient if frames are long enough, baudrate is big and interrupt latency is bigger than reception time of single char.
But if data is received slow enough you can get away with processing it char by char current way, and using DMA may be overkill.
As @Chris Stratton pointed you can also just set DMA for single transmission, and wait long enough, change protocol or use other signal as "end of transmission" - and then process frame in DMA buffer.
-
\$\begingroup\$ "Then you can use RX interrupt to trigger poll call(s)" It's good way, but have you ever try to do like than in STM32F family? I've tried and there is a problem. When I activate DMA mode by writing DMAR in USART_CR3, the RX interrupts don't come. There is a small note about it in the it's RefMan: "If DMA is used for reception, do not enable the RXNEIE bit." That means don't use the RX interrupts. Unfortunately, it seems it's impossible. \$\endgroup\$user92491– user924912015年11月23日 10:28:28 +00:00Commented Nov 23, 2015 at 10:28
You should use DMA interrupts. I am using STM32F02 with STM32 STL peripheral libraries, but the idea is pretty much the same for F01:
- Initialize your UART
- Initialize DMA (don't forget providing the CLK signal for the peripheral BEFORE calling DMA_Init!!)
- Configure NVIC and DMA interrupts, and enable DMA as follows:
NVIC_InitTypeDef NVIC_InitStructure;
//Enable DMA1 channel IRQ Channel
//Note: maybe in your implementation you don't have DMAx_Streamy, look for channel/ stream configurations in your microcontroller manual
NVIC_InitStructure.NVIC_IRQChannel = DMA1_Stream1_IRQn; // This could be different in your implementation
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
// Enable DMA1 Channel Transfer Complete interrupt
DMA_ITConfig(DMA1_Stream1, DMA_IT_TC, ENABLE);
USART_DMACmd(ACCESSORY_UART, USART_DMAReq_Rx, ENABLE);
DMA_Cmd(DMA1_Stream1, ENABLE);
The USART_DMACmd function binds UART with DMA, and I think that is your answer. In this way, the DMA will copy a byte to the pointer you provided in the configuration each time a RXNE event is triggered, but interrupting ONLY when a Transfer Complete (DMA_IT_TC) event is triggered, calling the corresponding DMA function according to the configured channel/ stream.
Explore related questions
See similar questions with these tags.
the parsing code currently resides in the USART RXNE interrupt
A better way would be to only use the RX interrupt to put the current byte in a circular buffer. In the main loop then check if the buffer contains a complete message and parse that message. \$\endgroup\$