I have configured a UART to receive in DMA mode where the size of the buffer is around 64 bytes. So, basically, the HAL_UART_RxCpltCallback() DMA receive complete interrupt will only fire when 64 chars are received.
Is there a way in STM32 through which I can configure a timeout for DMA Rx where when the buffer is only partially filled (i.e. less than 64 chars are received) and we don't receive anymore chars for a specified timeout, the DMA will then raise the same HAL_UART_RxCpltCallback() based interrupt to let the consumer consume whatever partial data is currently received on the UART?
You can use the UART Idle detection interrupt in parallel to the DMA interrupt.
I have used this multiple times with ST32F0xx processors and it is working perfectly.
There Idle detection should be available on F4 and F7 processors too.
There are some tutorials in the internet which target your problem and also provide the solution with the Idle detection.
E.g. check out this one this one.
It's easy but you have to use USART receiver timeout interrupt instead.
in order to get a count of transferred bytes, you can use DMA_CNDTRx or DMA_SxNDTR register (name different for STM family, where x - channel number ).
This register decrements after each DMA transfer. Once the transfer is completed, this register can either stay at zero or be reloaded automatically by the value previously programmed if the channel is configured in autoreload mode.
Unfortunately, STM HAL doesn't provide API, you should implement it yourself.
Related
I am wondering if there is a user-definable, built-in ISR function in the HAL library that triggers as soon as a byte is received in the SPIx Rx buffer on STM32L4xx MCU? For instance, as a startup test, I would like to send one byte (0xBC) from a Master STM32L452 nucleo board via SPI2 to a Slave STM32L452 nucleo board. Once the Slave board receives the byte, it flashes LED2, and transmits a different byte (0xCD) back to the Master. Once the Master receives the byte, it flashes LED2 as confirmation. I have initialized both boards as Master/Slave, enabled DMA and global interrupts, 8 bits per transfer using MXcube. I can achieve what I want using the HAL_SPI_Transmit_DMA() and HAL_SPI_Receive_DMA() functions and delays written into the while(1) portion of my main routine (as below). However, I would like to achieve the same using an ISR function that automatically executes once a byte is received into the SPI Rx Buffer.
Master Code:
uint8_t spiDataReceive = 0;
uint8_t spiDataTransmit = 0xBC;
while(1) {
if(!HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_13)) {
//Transmit byte 0xBC to Slave and Receive Response
HAL_SPI_Transmit_DMA(&hspi2, &spiDataTransmit, 1);
HAL_Delay(20);
HAL_SPI_Receive_DMA(&hspi2, &spiDataReceive, 1);
if(spiDataReceive == 0xCD) {
flashLED2();
spiDataReceive = 0x00;
}
}
}
Slave Code:
uint8_t spiDataReceive = 0;
uint8_t spiDataTransmit = 0xCD;
while(1) {
HAL_SPI_Receive_DMA(&hspi2, &spiDataReceive, 1);
HAL_Delay(10);
if(spiDataReceive == 0xBC) {
HAL_SPI_Transmit_DMA(&hspi2, &spiDataTransmit, 1);
flashLED2();
spiDataReceive = 0x00;
}
}
No library is needed. You need to set RNEIE bit in the SPI CR register and enable in the NVIC the interrupt. 2 lines of code. No libraries needed.
The only needed resource is the Reference Manual from the STM website.
Yes, the HAL provides user callbacks. In order to use those, you have to activate the corresponding interrupt in NVIC and have the HAL handler called by the interrupt vector table (please have a look at stm32l4xx_it.c, too).
But before you do so, you should consider the following questions:
If you feel confused or frustrated by the complexity of ST HAL libraries, read the Reference Manual and follow the advice of P__J__ (see other answer).
If you feel confused or frustrated by the complexity of the hardware interface, follow the present answer.
Both HAL_SPI_Transmit_DMA() and HAL_SPI_Transmit_IT() support a variable number of transfer bytes.
If all you are going to need is that one-byte transfer, HAL functions may be an overkill.
Their advantage is that you can run some C library functions without dealing with HW register access in C (if that is quite new to you, coming from the arduino ecosystem). And of course, to transfer more than a single byte through the same interface when you extend your application.
You should decide whether you want to get an interrupt from the DMA you have tied to the UART, or if you want to avoid the DMA and get the interrupt from the UART itself. From my point of view, you should really not trigger an ISR by the same interrupt event which is used to start a DMA transfer to fetch the data!
In the same way as you find a description of the HW registers in the
Reference Manual and
Data Sheet of the controller, you find documentation on the HAL (layering concept, usage requirements etc.) in the
User manual of STM32L4/L4+ HAL and low-layer drivers
(see sections 70 and 102, resp., and chapter 3).
Of course, this interface aims mostly for abstraction and portability whereas directly addressing the HW interface usually allows much better efficiency in terms of latency/CPU load, and ROM/RAM usage. The "Low-Level" library drivers aim for a certain compromise, but if you are new to this whole topic and unsure what to start with, you should either start from the HW register interface, or from the portable HAL library API.
If the specification documents (HW or Lib description) are too abstract for you and you prefer some hands-on information source, you may want to first have a look at STM32Cube firmware examples for STM32CubeL4.
These also include SPI data exchange use cases (SPI_FullDuplex_ComIT for example) that are available for NUCLEO-L4532RE (and others) and described in application note AN4726 (page 16).
In addition to the interrupt selection/handling, you should check two more aspects of your program:
If you get an interrupt from the hardware, there is no reason for the HAL_Delay() calls.
Keep in mind that on SPI, you can only "return" data from slave to master while the master is transferring data (which may be zero data).
Otherwise, the "transmit" call on the slave side will only put data into the TX register, and the SPI peripheral will wait infinitely for the SCK trigger from the master...
I am new to STM32 and freertos. I need to write a program to send and receive data from a module via UART port. I have to send(Transmit) a data to that module(for eg. M66). Then I would return to do some other tasks. once the M66 send a response to that, my seial-port-receive-function(HAL_UART_Receive_IT) has to be invoked and receive that response. How can I achieve this?
The way HAL_UART_Receive_IT works is that you configure it to receive specified amount of data into given buffer. You give it your buffer to which it'll read received data and number of bytes you want to receive. It then starts receiving data. Once exactly this amount of data is received, a callback function HAL_UART_RxCpltCallback gets called (from IRQ) where you can do whatever you want with this data, e.g. add it to some kind of queue for later processing in the task context.
If I was to express my experiences related to working with HAL's UART module is that it's not the greatest one for generic use where you don't know the amount of data you expect to receive in advance. In the case of M66 modem you mention, this will happen all the time.
To solve this you have two choices:
Simply don't use HAL functions at all in case of UART, other than the initialization functions. Implement your own UART interrupt handler (most of the code can be copied from handler in HAL) where upon receiving data you place received bytes in a receive byte queue handled in your RTOS task. In this task you implement protocol parsing. This is the approach I use personally.
If you really want to use HAL but also work with a module that sends varying amount of data, call HAL_UART_Receive_IT and specify that you want to receive 1 byte each time. This will work, but will be (potentially much) slower than the first approach. Assuming you'll later want to implement some tcp/ip communication (you mentioned M66 GPRS module) you probably don't want to do it this way.
You should try the following way.
Enable UARTX Rx interrupt in NVIC.
Set Interrupt priority.
Unmask Interrupt request in EXTI.
Then use USARTX Interrupt Handler Function Define in you Vector.
Whenever the data is received from USARTX this function get automatically called and you can copy data from USARTX Receive Data Register.
I would rather suggest another approach. You probably want to archive higher speeds (lets say 921600 bods) and the interrupt way is fat to slow for it.
You need to implement the DMA transmition with the data end detection features. Run your USART in the DMA mode in the circular mode. You will have two events to serve. The first one is the DMA end of thransmition interrupt (then you copy the data from the current tail pointer to the end of the buffer to avoid data override) and USART IDLE interrupt - this will detect the end of the receive.
I want to use a sensor with one-wire protocol, the matter is which way of using this protocol is optimized and more rational? over usart or using bit banging?
if it is important I'm using am2305 and stm32fxx microcontrollers.
I'm prefer to use USART+DMA with one buffer for transmit and receive. And I think this choice depends on your skill and requierements of your project.
There many ways to implement one-wire protocol.
exti interrupts + timer base mode
timer input capture + dma
usart interrupts
usart (err interrupts only) + dma
All of them have thier advantages and disadvantages:
busy or free pins
busy or free periferals (tim, usart)
busy or free dma channels
lower or higher frequency of interrupts in programm
easy or hard for implement it
I have different projects where work first and last methods listed above.
You must know which method is preferred for you and your project.
I have DMA2 stream 0 configured so that it transfers given number of samples from ADC3 (triggered by TIM2 rising edge) to memory, and after completing the transfer an interrupt happens in which I make calculations on the samples gathered.
Everything is working as intented, except that I would like to be able to repeat the whole process. Transfer should be started by user command (button press, or eventually by command send through USART), therefore I can't use DMA in circular mode for this task.
From what I've found, it is possible to re-run the transfer by modifying some registers and restarting the DMA and ADC, yet I was unable to complete the task. What is the correct set of instructions I have to run to achieve my goal?
I'm using an Atmel SAM3S MCU, and their ASF stuff can do I2C (they call it TWI) communications. That's fine, except it's taking too much time from my main loop.
So, I'd like to be able to spark off a DMA transfer to read the data from the I2C device. However, all the docs say you can't turn on TX and RX simultaneously on a half-duplex device like TWI. The docs do show that it has a Peripheral DMA Controller (PDC) register section in the TWI registers, but I can't find any PDC examples, except for the USART, which is full duplex.
The only thing I can think of to try is to set TX section, and the next-RX section, and hope that it automatically enables RX after the TX is done.
Has anyone out there used DMA for an I2C read on the SAM3S? If so, could you point me to some docs or examples?
I'm not familiar with the particular part, however I would suggest that for many common usage patterns your best bet would probably be to only use DMA to handle multi-byte sequences of data. Most I2C peripherals allow data to be read out by performing a start with a "write" address byte, and, if that is acknowledged, sending out an address or other information about what data is desired. This is followed by a restart and a "read" address byte. If that is acknowledged, one may then perform all but one of the byte reads with the "ack" flag set. When that is finished, ask for the final byte to be read with the "ack" flag clear.
I'm not sure whether it would be worthwhile to use the DMA controller to clock out the bytes of the requested address, but probably not worthwhile to try to use it to clock out the first byte of the read command.