Jump to content

    
Sign in to follow this  
cebotor

Stm32f103 +Tim PWM странный глюк

Recommended Posts

Приветствую всех!

 

Вопрос по работе затертого уже всеми давно до дыр stm32f103 в части связки PWM + DMA

Обратился бывший коллега - задача проста, выдавать в PWM аналоговый сигнал, при чем больше

частота PWM - тем лучше.

 

Частоты CPU 72000000 PWM 400000 - разрешение получается 180 отсчетов CPU.

Выдаем четыре отсчета в кольцевом режиме

  mas_sin[0]=160; //!!!!!!!!!!! если здесь заменить на 170 то увидим сдвиг
  mas_sin[1]=90;
  mas_sin[2]=30;
  mas_sin[3]=10;

Наблюдаемый эффект: при небольшом заполнении PWM все хорошо, при приближении заполнения к 100%

- импульс дублируется в следующий, следующий сдвигается в третий, а третий "выпадает"...

 

Не поверил я что может быть неописанная засада в таком не сложном деле.

Он прислал код. Я проверил и упростил код до минимального "сбоящего" варианта без кучи ненужностей.

 

Далее собственно полный (не длинный ) код

//  задаем частоты  - выдернуто из SystemClock_Config();
 FLASH->ACR= LL_FLASH_LATENCY_2;
 RCC->CR|= RCC_CR_HSEON;
 while ((RCC->CR&RCC_CR_HSERDY)!=(RCC_CR_HSERDY));
 RCC->CFGR=(LL_RCC_PLLSOURCE_HSE_DIV_1 & (RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE)) | LL_RCC_PLL_MUL_9;
 RCC->CR|=RCC_CR_PLLON;
 while((RCC->CR&RCC_CR_PLLRDY)!=RCC_CR_PLLRDY);
 RCC->CFGR|=LL_RCC_SYSCLK_DIV_1;
 RCC->CFGR|=LL_RCC_SYS_CLKSOURCE_PLL;
 while (RCC->CFGR& RCC_CFGR_SWS!= LL_RCC_SYS_CLKSOURCE_STATUS_PLL);
 RCC->CFGR|=LL_RCC_APB1_DIV_2;
 RCC->CFGR|=LL_RCC_APB2_DIV_1;

 mas_sin[0]=160; //!!!!!!!!!!! если здесь заменить на 170 то увидим сдвиг
 mas_sin[1]=90;
 mas_sin[2]=30;
 mas_sin[3]=10;
 RCC->APB2ENR |= RCC_APB2ENR_IOPBEN | RCC_APB2ENR_AFIOEN;
 AFIO->MAPR &= ~AFIO_MAPR_SWJ_CFG;
 AFIO->MAPR |= AFIO_MAPR_TIM3_REMAP_PARTIALREMAP | AFIO_MAPR_SWJ_CFG_JTAGDISABLE;
 GPIOB->CRL &= ~GPIO_CRL_CNF4 & ~GPIO_CRL_CNF5 & ~GPIO_CRL_CNF0 & ~GPIO_CRL_CNF1;
 GPIOB->CRL |=  GPIO_CRL_CNF4_1 | GPIO_CRL_MODE4_0 | GPIO_CRL_CNF5_1 | GPIO_CRL_MODE5_0 | GPIO_CRL_CNF0_1 | GPIO_CRL_MODE0_0 | GPIO_CRL_CNF1_1 | GPIO_CRL_MODE1_0;

 RCC->APB1ENR |= RCC_APB1ENR_TIM3EN;
 TIM3->CCER   |= TIM_CCER_CC1E | TIM_CCER_CC2E | TIM_CCER_CC3E | TIM_CCER_CC4E;
 TIM3->CCMR1  |= TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1PE | TIM_CCMR1_OC2M_2 | TIM_CCMR1_OC2PE;
 TIM3->CCMR2  |= TIM_CCMR2_OC3M_2 | TIM_CCMR2_OC3PE | TIM_CCMR2_OC4M_2 | TIM_CCMR2_OC4M_1 | TIM_CCMR2_OC4PE;
 TIM3->CCMR2  |=  TIM_CCMR2_OC4M_0 |  TIM_CCMR2_OC3M_1;
 TIM3->DIER   |= TIM_DIER_CC1DE | TIM_DIER_CC3DE;
 TIM3->CR1    |= TIM_CR1_CEN | TIM_CR1_ARPE;
 TIM3->PSC = 0;
 TIM3->ARR = PREC-1;

 RCC->AHBENR |= RCC_AHBENR_DMA1EN;
 DMA1_Channel2->CPAR = (uint32_t)&TIM3->CCR3;
 DMA1_Channel2->CMAR = (uint32_t)&mas_sin[0];
 DMA1_Channel2->CNDTR = POINTS;
 DMA1_Channel2->CCR = DMA_CCR_MINC | DMA_CCR_CIRC | DMA_CCR_DIR | DMA_CCR_EN | DMA_CCR_MSIZE_0 | DMA_CCR_PSIZE_0;

 while(1);

когда mas_sin[0]==160 видим следующую картинку

 

IMAGE005.png

но если заполнить еще чуть больше mas_sin[0]==170

то будет вот так:

 

IMAGE004.png

 

Долго смотрел в картинку схемы работы узла Capture/Compare -

 

Capture_Compare.jpg

Может быть запись из DMA по событию совпадения CCR3 == CNT совпадает с событием UEV и пропускается? при этом релоад регистр не переписывается в теневой?

Но с этим должны были столкнуться десятки и сотни?

 

Не подскажет ли уважаемое сообщество, отчего не едут лыжи.

Заранее спасибо.

Share this post


Link to post
Share on other sites
Может быть запись из DMA по событию совпадения CCR3 == CNT совпадает с событием UEV и пропускается? при этом релоад регистр не переписывается в теневой?

Но с этим должны были столкнуться десятки и сотни?

В общем и целом не совсем корректно писать в теневой регистр ровно в тот момент, когда он переписывается в основной регистр сравнения. И неважно через ДМА или напрямую.

Время от события до выполнения записи ДМА не всегда одинаковое и может зависеть от многих факторов (частоты шин, их занятость и т.п.)

Я бы синхронизировал ДМА по какому нибудь событию таймера, которое гарантированно не совпадает с апдейтом.

Для этого можно задействовать свободный канал сравнения, если такие есть.

Share this post


Link to post
Share on other sites

Тут принципиально неверное управление таймером. Надо записывать теневые регистры только в ответ на UE, никаких CCxDE, только UDE, тогда гарантируется латентность записи в один период таймера(по ARR). Запись регистров непосредственно по событиям от ССR - это очень специальные случаи и они не для чайников.

Share this post


Link to post
Share on other sites

Большое спасибо откликнувшимся!

Ошибка найдена - именно такая как указал Timmy.

Как и часто бывает корень зла - донор кода, а им являются библиотечные функции.

В актуальных на данный момент библиотеках от ST есть файл stm32f1xx_hal_tim.c (у меня версия от мая сего года).

В этом файле в функции HAL_TIM_PWM_Start_DMA есть строки (есть для всех каналов но данные строки для 3го канала)

    case TIM_CHANNEL_3:
    {
      /* Set the DMA Period elapsed callback */
      htim->hdma[TIM_DMA_ID_CC3]->XferCpltCallback = TIM_DMADelayPulseCplt;
      /* Set the DMA error callback */
      htim->hdma[TIM_DMA_ID_CC3]->XferErrorCallback = TIM_DMAError;
      /* Enable the DMA channel */
      HAL_DMA_Start_IT(htim->hdma[TIM_DMA_ID_CC3], (uint32_t)pData, (uint32_t)&htim->Instance->CCR3,Length);
      /* Enable the TIM Output Capture/Compare 3 request */
      __HAL_TIM_ENABLE_DMA(htim, TIM_DMA_CC3); //!!!!!!! неверно, нужно TIM_DMA_UPDATE

в которых выставляется TIM_DMA_CC3 как источник ДМА запроса.

что неверно. требуется установить в качестве источника запроса TIM_DMA_UPDATE

__HAL_TIM_ENABLE_DMA(htim, TIM_DMA_UPDATE);

 

 

 

В общем и целом не совсем корректно писать в теневой регистр ровно в тот момент, когда он переписывается в основной регистр сравнения. И неважно через ДМА или напрямую.

Время от события до выполнения записи ДМА не всегда одинаковое и может зависеть от многих факторов (частоты шин, их занятость и т.п.)

Я бы синхронизировал ДМА по какому нибудь событию таймера, которое гарантированно не совпадает с апдейтом.

Для этого можно задействовать свободный канал сравнения, если такие есть.

Точно, не корректно одновременно с UE, надо это делать в ответ на этот самый UE( ставить источник UDE как написал Timmy), то есть позже самой записи

 

Спасибо за ссылку, сам механизм работает без бёрста, то есть запускается трансфер по запросу дма в обычный регистр релоада. Проблема в выпадениях (была)

 

 

Тут принципиально неверное управление таймером. Надо записывать теневые регистры только в ответ на UE, никаких CCxDE, только UDE, тогда гарантируется латентность записи в один период таймера(по ARR). Запись регистров непосредственно по событиям от ССR - это очень специальные случаи и они не для чайников.

Вы абсолютно правы, пропустил маразмус мимо глаз! кроме того можно за один период при работе по событию CCR схватить больше чем одно обновление, что было бы еще веселее. Отдельное спасибо за чайника ;)

 

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Sign in to follow this