Перейти к содержанию
    

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

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

 

Вопрос по работе затертого уже всеми давно до дыр 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 и пропускается? при этом релоад регистр не переписывается в теневой?

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

 

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

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

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

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

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

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

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

Ошибка найдена - именно такая как указал 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 схватить больше чем одно обновление, что было бы еще веселее. Отдельное спасибо за чайника ;)

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...