cancel
Showing results for 
Search instead for 
Did you mean: 

Arbitrary waveform by timer using DMA

RAltm
Senior

Hello,

I want to generate an arbitrary waveform by a timer using DMA. The waveform is using different pulse widths and also varying periods in multiples of 25µs.

By reading AN4776 and HAL documentation I figured out it should be possible with HAL_TIM_DMABurst_MultiWriteStart() function. Unfortunately I don't get an output signal except after reset where a short pulse can be seen. I assume this pulse is due to port configuration during initialization. After that pulse, the signal stays high.

My setup (CubeMX):

  • Nucleo32-L432, core clock 80MHz
  • Used timer is Tim16
    • Prescaler is set to 399 => 80 MHz / 400 = 5µs resolution
    • ARR and CCR preload enabled
    • ARR and CCR set to 0 (initialized in source code)
    • DMA enabled
      • normal mode
      • memory to peripheral
      • Word / Word
      • increment address on memory
      • interrupt enabled

For a first run I want to use fixed pulse widths and varying periods, so all my code does is the following:

/* USER CODE BEGIN 2 */
htim16.Instance->ARR = 39;	//40 * 5µs = 200µs period
htim16.Instance->CCR1 = 4;	//5 * 5µs = 25µs pulse
HAL_TIM_DMABurst_MultiWriteStart(&htim16, TIM_DMABASE_ARR, TIM_DMA_UPDATE, (uint32_t *) Buffer, TIM_DMABURSTLENGTH_1TRANSFER, sizeof(Buffer) / sizeof(Buffer[0]));
HAL_TIM_PWM_Start(&htim16, TIM_CHANNEL_1);
/* USER CODE END 2 */

As far as I understood I need to initiate the first pulse by calling HAL_TIM_PWM_Start() and HAL_TIM_DMABurst_MultiWriteStart() setup the DMA to handle the subsequent transfers. 

When running that code, I get a single pulse of ~3.4µs at a period of 5.8µs, after that the output stays high. Since this is my first encounter with DMA I'm a bit lost. I don't get what I'm missing because other forum entries use basically the same approach as far as I can see.

Regards

8 REPLIES 8
waclawek.jan
Super User

> ARR and CCR set to 0 (initialized in source code)

ARR = 0 means, that the timer is stopped. As you also set ARR preload, and there is no Update event which would transfer the new ARR to the "active" register and start the timer, so it remains stopped.

JW

Hello JW,

I tried both deactivating preload and using non-zero values for ARR/CCR directly in CubeMX. No change in behaviour. Any other ideas what might be the issue?

Regards

RAltm
Senior

Update: seems that the silicon has a bug :( I used the Nucleo board reset button to restart and got the described behaviour. When doing a power reset it seems to (almost) work as expected.
I checked the errata sheet, but I couldn't find anything regarding the reset input only working partially...

Regards

I don't use Cube/CubeHAL, maybe there are some important details in that. And there may be also important details in the parts of your code we don't see (you could post a minimal but complete working example exhibiting the problem).

What is "(almost) work as expected"?

JW

Gyessine
ST Employee

Hello @RAltm 
Honestly, it will be helpfull if you provide more details about your application or share you main.c and msp.c files
but from global view, I think you should use circular mode not normal mode in your case
However, I attached a project that uses TIM16 with DMA to change content of ARR with different values as you wanted (3 values in this example 100,150,175)
You can base yourself on it, if it aligns with your demand 

Gyessine_0-1777903969440.png

BR
Gyessine
 

To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.


@waclawek.jan wrote:

I don't use Cube/CubeHAL, maybe there are some important details in that. And there may be also important details in the parts of your code we don't see (you could post a minimal but complete working example exhibiting the problem).

What is "(almost) work as expected"?


I'll provide a MWE as soon as possible. However, first I want to check that reset behaviour. I expect the same behaviour when pressing the reset button or doing a power reset. Only on the latter I see the output pulses. I'll also provide screenshots from the logic analyzer for the two cases. I'll also check if I have a free Nucleo board with a different MCU type to compare the behaviour.

"Almost works as expected" means that I can see the expected output pulses (when doing a power reset), but the output goes high and stays high after the last pulse. I expected the output to stay low after the last pulse. I don't know if I have to disable DMA and/or the timer after the DMA signals the end of the transfer.


@Gyessine wrote:

Honestly, it will be helpfull if you provide more details about your application or share you main.c and msp.c files
but from global view, I think you should use circular mode not normal mode in your case
However, I attached a project that uses TIM16 with DMA to change content of ARR with different values as you wanted (3 values in this example 100,150,175)
You can base yourself on it, if it aligns with your demand  


Hello Gyessine, thank you for the project, I'll compare it to my implementation. As mentioned, I'll provide a MWE of my implementation as soon as possible.

However, I indeed want to use non-circular mode. I want to output a varying number of pulses with varying period and pulse duration. Between the pulse trains, the output should stay low and the next pulse train must not start before the period of the last pulse of the previous train has expired. As mentioned, I don't know if I have to disable DMA and/or the timer after the DMA signals the end of the transfer. I'd be glad if you can give me some hints.

Regards

waclawek.jan
Super User

> I expect the same behaviour when pressing the reset button or doing a power reset.

There are few things which behave differently upon power-on reset and NRST-reset. One of them is the PEMPTY bit behaviour, which is set if the FLASH is empty during reset (which may happen during programming after erase, depending on how exactly the programmer/debugger behaves), impacts the mapping at address 0, and is not reset by NRST, only by a power-on reset. The symptom involves the system-ROM (bootloader) being mapped at address 0, and usually interrupts then end up in the 0x1fffxxxx area. I guess there may be other mechanisms for different NRST/power-on behaviour. This is a recurring theme here, but rarely the user comes to a definitive conclusion.

How the pulse train ends depends on the PWM mode you are using, and the exact way how you end the pulse train and/or what do you exactly do with the output after the pulse train finishes. One way to tackle this is to set the last CCRx item to be 0 or ARR+1; those values should ensure the output to be a steady value of either polarity.

JW


@waclawek.jan wrote:

> I expect the same behaviour when pressing the reset button or doing a power reset.

There are few things which behave differently upon power-on reset and NRST-reset. One of them is the PEMPTY bit behaviour, which is set if the FLASH is empty during reset (which may happen during programming after erase, depending on how exactly the programmer/debugger behaves), impacts the mapping at address 0, and is not reset by NRST, only by a power-on reset. The symptom involves the system-ROM (bootloader) being mapped at address 0, and usually interrupts then end up in the 0x1fffxxxx area. I guess there may be other mechanisms for different NRST/power-on behaviour. This is a recurring theme here, but rarely the user comes to a definitive conclusion.


Interesting, thank you for pointing me to that. I checked the reference manual about the PEMPTY bit. If this bit is the issue, I'll try to modify it and enforce a software reset. From the description it might be the issue - I played with the reset settings in CubeIDE and interestingly it changed the behaviour after downloading the image (not debugging, only download). I've to verify it, but it looked like the program runs okay after download, then the timer output flipped after several milliseconds. Pressing the reset button resulted in the initial behaviour (no output). But as I said, I've to verify.

 

 


@waclawek.jan wrote:

How the pulse train ends depends on the PWM mode you are using, and the exact way how you end the pulse train and/or what do you exactly do with the output after the pulse train finishes. One way to tackle this is to set the last CCRx item to be 0 or ARR+1; those values should ensure the output to be a steady value of either polarity.


I will try that, good point, thank you. Currently I'm using the period elapsed callback from DMA and disable the timer, but this shortens the last pulse. Your approach is promising because it would enable to keep the full period of the last visible pulse. If I remember correctly I initially tried one additional ARR item with value 0 to stop the timer, but that resulted in the output going high.

Regards