cebotor 0 17 августа, 2017 Опубликовано 17 августа, 2017 · Жалоба Приветствую всех! Вопрос по работе затертого уже всеми давно до дыр 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 видим следующую картинку но если заполнить еще чуть больше mas_sin[0]==170 то будет вот так: Долго смотрел в картинку схемы работы узла Capture/Compare - Может быть запись из DMA по событию совпадения CCR3 == CNT совпадает с событием UEV и пропускается? при этом релоад регистр не переписывается в теневой? Но с этим должны были столкнуться десятки и сотни? Не подскажет ли уважаемое сообщество, отчего не едут лыжи. Заранее спасибо. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
amiller 2 18 августа, 2017 Опубликовано 18 августа, 2017 · Жалоба Может быть запись из DMA по событию совпадения CCR3 == CNT совпадает с событием UEV и пропускается? при этом релоад регистр не переписывается в теневой? Но с этим должны были столкнуться десятки и сотни? В общем и целом не совсем корректно писать в теневой регистр ровно в тот момент, когда он переписывается в основной регистр сравнения. И неважно через ДМА или напрямую. Время от события до выполнения записи ДМА не всегда одинаковое и может зависеть от многих факторов (частоты шин, их занятость и т.п.) Я бы синхронизировал ДМА по какому нибудь событию таймера, которое гарантированно не совпадает с апдейтом. Для этого можно задействовать свободный канал сравнения, если такие есть. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
SSerge 4 18 августа, 2017 Опубликовано 18 августа, 2017 · Жалоба https://electronix.ru/forum/index.php?s=&am...t&p=1201975 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Timmy 1 18 августа, 2017 Опубликовано 18 августа, 2017 · Жалоба Тут принципиально неверное управление таймером. Надо записывать теневые регистры только в ответ на UE, никаких CCxDE, только UDE, тогда гарантируется латентность записи в один период таймера(по ARR). Запись регистров непосредственно по событиям от ССR - это очень специальные случаи и они не для чайников. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
cebotor 0 19 августа, 2017 Опубликовано 19 августа, 2017 · Жалоба Большое спасибо откликнувшимся! Ошибка найдена - именно такая как указал 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), то есть позже самой записи https://electronix.ru/forum/index.php?s=&am...t&p=1201975 Спасибо за ссылку, сам механизм работает без бёрста, то есть запускается трансфер по запросу дма в обычный регистр релоада. Проблема в выпадениях (была) Тут принципиально неверное управление таймером. Надо записывать теневые регистры только в ответ на UE, никаких CCxDE, только UDE, тогда гарантируется латентность записи в один период таймера(по ARR). Запись регистров непосредственно по событиям от ССR - это очень специальные случаи и они не для чайников. Вы абсолютно правы, пропустил маразмус мимо глаз! кроме того можно за один период при работе по событию CCR схватить больше чем одно обновление, что было бы еще веселее. Отдельное спасибо за чайника ;) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться