cancel
Showing results for 
Search instead for 
Did you mean: 

stm32f1 DMA to PWM first bit issue

Levi--G
Associate II

Hi,

Im new to stm forums so sorry if i make any mistakes.

Im using a stm32f103RET6 since i am familiar with f103 and i need usb/other peripherals. Thus far everything works great. However...

Im trying to offload some work from cpu to DMA by sending a premade array to the timer, it works fine for the most part except if the very first bit of the transfer (the first DMA byte) is a 1, due to an issue the pin after going high as expected in the same period it goes low and high again when it it not asked, thus confusing the ic's interpreting it as 2x 0bits.

If that is unclear i attached a screenshot showing the dip. Some things i made sure of/tested already:
- It is not a hardware issue, i designed the pcb myself and the trace to the first led is very short and away from any other digital line
- I disabled all other code/generated a minimum POC and the problem persists
- The period/baud is set and never modified by the code
- I do not change the array or cancel the DMA in my code
- It is reproduceable and i verified/reuploaded the code many times
- The array i analyzed with stlink and is correct
- The cpu is running at a steady 72Mhz on an external oscillator and the timer works fine in other modes
- Most of the code is generated by CubeMX, i only added the array, rgb logic and HAL_TIM_PWM_Start_DMA()
- I am using Platformio with Cube framework, but i doubt that is the issue since that has always worked fine, i am also unfamiliar with other ide's.

What im trying to do:
im trying to control neopixels, basically a 1.25µs (800khz baud) signal with >60%high = 1 and <40% high = 0. The pixels work perfectly on all 3 channels until the first bit goes high (green channel >=128) when the first bit errors and the ic's treat it as 2x0 causing all the next bits to shift. If i bit-bang the pixels everything is correct, but i would like to try and use the dma, since i have other things to spend cpu cycles on 😉

Has anyone come across this issue, is there some way to fix this?

PS: im trying to upload the code but for some reason its not working, ill try to do it in the comments, or if you ask me a specific bit i can post it.

6 REPLIES 6
Levi--G
Associate II

stm32f103output.png

> Most of the code is generated by CubeMX

Details matter, so we can't judge where's the problem without knowing them. Read, post relevant portions of code.

What's wrong on that waveform, exactly?

JW

Yes ive been trying to upload the code since i posted, but the forum won't allow me :D

I don't know if onedrive is an acceptable medium but here goes: source.zip

What pieces would you find "relevant"?
Here is the timer init from cube:

TIM_ClockConfigTypeDef sClockSourceConfig = {0};
  TIM_MasterConfigTypeDef sMasterConfig = {0};
  TIM_OC_InitTypeDef sConfigOC = {0};

  /* USER CODE BEGIN TIM2_Init 1 */

  /* USER CODE END TIM2_Init 1 */
  htim2.Instance = TIM2;
  htim2.Init.Prescaler = 0;
  htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim2.Init.Period = LOGIC_COUNT;
  htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
  {
    Error_Handler();
  }
  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_TIM_PWM_Init(&htim2) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sConfigOC.OCMode = TIM_OCMODE_PWM1;
  sConfigOC.Pulse = 0;
  sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
  sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
  if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN TIM2_Init 2 */

  /* USER CODE END TIM2_Init 2 */
  HAL_TIM_MspPostInit(&htim2);

Here is where i start the DMA transfer:

void led_waitDMA()
{
  while (hdma_tim2_ch1.State != HAL_DMA_STATE_READY)
  {
    HAL_Delay(1);
  }
}
void led_render()
{
  led_waitDMA();
  HAL_TIM_PWM_Start_DMA(&htim2, TIM_CHANNEL_1, (uint32_t *)dma_buffer, DMA_BYTES);
  led_waitDMA();
}

(i put in the extra delays as a test to make sure im not writing to the buffer in any way while the transfer is busy, which will eventually get removed but even with this its not working)

What is wrong with the waveform? Like i said in the original post the two first 0 bits should be one 1 bit, the dip to gnd should not be there and the pin should stay high until 60% of the period, the period of the bits should all be equal as the way the timer is configured, so there is no reason why there would be 2 bits in 1 period. Maybe the new picture is better? The black is what it should be with the array given. This mistake only occurs on the first bit (1 dma byte) of the transfer.

> Like i said in the original post the two first 0 bits should be one 1 bit,

Note, how confusingly this sounds to a person like me, who is not familiar with the encoding scheme of the "smart LEDs", and does not know what do you exactly mean by "bits" there.

---

I don't use Cube. I honestly spent significant time trying to understand, what does your code do, but there's just too much fluff in Cube for me to comprehend what's going on. For example, explain to me, what is the purpose of this in your MX_TIM2_Init():

 if (HAL_TIM_PWM_Init(&htim2) != HAL_OK)
{
Error_Handler();
}

 

My personal recommendation is not to use Cube/HAL and writing the code normally accessing registers, based on reading of the RM. I understand that this is not a popular option, I understand that there are little to no examples, and I understand that it is no guarantee that things will go well. However, it's way simpler to help somebody who already know what's going on.

But you do you.

Also, there are examples controlling these smart LEDs out there on the interwebs, you can try to get inspired there.

JW

Not to sound ungrateful for your willingness to help but...
>Note, how confusingly this sounds to a person like me, who is not familiar with the encoding scheme of the "smart LEDs", and does not know what do you exactly mean by "bits" there.
Maybe read the next bit of the line then, assuming you know what bits are (0 or 1), every led "bit" is a set period, 800khz, which i have said twice now. so anyone who understands basic clockless communication with pulse width modulation should get that making a pin low when it should be high divides the clock cycle in two, doubling the clock and splitting the bit in two as seen by the double bit literally in the example. If you don't get that im sorry but you need to do some basic engineering reading. I even marked the issue for your convenience.

For example, explain to me, what is the purpose of this in your MX_TIM2_Init():
Easy, any decent IDE will tell you just opening the source (usually F12), if you like registers you are probs looking for this:

//Part of HAL_TIM
void TIM_Base_SetConfig(TIM_TypeDef *TIMx, TIM_Base_InitTypeDef *Structure)
{
  uint32_t tmpcr1;
  tmpcr1 = TIMx->CR1;

  /* Set TIM Time Base Unit parameters ---------------------------------------*/
  if (IS_TIM_COUNTER_MODE_SELECT_INSTANCE(TIMx))
  {
    /* Select the Counter Mode */
    tmpcr1 &= ~(TIM_CR1_DIR | TIM_CR1_CMS);
    tmpcr1 |= Structure->CounterMode;
  }

  if (IS_TIM_CLOCK_DIVISION_INSTANCE(TIMx))
  {
    /* Set the clock division */
    tmpcr1 &= ~TIM_CR1_CKD;
    tmpcr1 |= (uint32_t)Structure->ClockDivision;
  }

  /* Set the auto-reload preload */
  MODIFY_REG(tmpcr1, TIM_CR1_ARPE, Structure->AutoReloadPreload);

  TIMx->CR1 = tmpcr1;

  /* Set the Autoreload value */
  TIMx->ARR = (uint32_t)Structure->Period ;

  /* Set the Prescaler value */
  TIMx->PSC = Structure->Prescaler;

  if (IS_TIM_REPETITION_COUNTER_INSTANCE(TIMx))
  {
    /* Set the Repetition Counter value */
    TIMx->RCR = Structure->RepetitionCounter;
  }

  /* Generate an update event to reload the Prescaler
     and the repetition counter (only for advanced timer) value immediately */
  TIMx->EGR = TIM_EGR_UG;
}

>My personal recommendation is not to use Cube/HAL and writing the code normally accessing registers, based on reading of the RM.

Well that might be your opinion, but telling someone hey instead learn a different way of programming cause i don't like your way, is hardly helping. I know too many different architectures/languages to program every single microcontroller i come across in assembly or registers. I COULD, but i don't want to since there are easier ways...

Also, there are examples controlling these smart LEDs out there on the interwebs, you can try to get inspired there.
I hardly read the entire internet but i searched enough to know that none come close to my example, either bit-banging, which i said in the original post works fine, or using a different DMA on a different µC, so unless you found something i couldn't, in which case i suggest you post it for me to find, no there is nothing to find. And again not really useful to tell someone "go solve it yourself" in that case, please refrain from replying.

Levi--G
Associate II

I am now treating this as a bug in the DMA of the F1 series as it does work on the G0. Sad to see such a feature fail in the most simple of uses.