32-битные таймеры

Дэнниф
Пн, 09 октября 2017 г. 12:03
Я написал быструю часть для цепочки двух 16-битных таймеров здесь: https: // dannyelectronics.WordPress.ком/ ... It-timers/

соответствующая часть кода здесь:
//initialize tim23 as a synchronized 32-bit timer //tim2 as master / prescaler to tim3 / lsw of the 32-bit timer //tim3 as slave / msw of the 32-bit timer void tim23_init(uint16_t ps) { //initialize tim2 as master //enable clock to tim2 RCC->APB1ENR |= RCC_APB1ENR_TIM2EN | 0x00; //stop the timer to configure it TIM2->CR1 &=~TIM_CR1_CEN; //clear cen. 0=disable the timer, 1=enable the timer TIM2->CR1 &=~TIM_CR1_CKD; //clear CKD0..1. 0b00->1x clock; 0b01->2:1 clock, 0b10->4:1 clk; 0b11->reserved TIM2->CR1 &=~TIM_CR1_DIR; //clear DIR bit. 0=upcounter, 1=downcounter TIM2->CR1 &=~TIM_CR1_OPM; //clear opm bit. 0=periodic timer, 1=one-shot timer //or to simply zero the register //TIMx->CR1 = 0; //much easier TIM2->CR2 = 0x20; //MMS = 0b010->tim2 as prescaler //source from internal clock -> disable slave mode TIM2->SMCR &=~TIM_SMCR_SMS; //clear sms->master mode and use internal clock //clear the status register bits for capture / compare flags TIM2->SR &=~(TIM_SR_CC1IF | TIM_SR_CC2IF | TIM_SR_CC3IF | TIM_SR_CC4IF | TIM_SR_UIF); //disable the interrupt by clearing the enable bits TIM2->DIER &=~(TIM_DIER_CC1IE | TIM_DIER_CC2IE | TIM_DIER_CC3IE | TIM_DIER_CC4IE | TIM_DIER_UIE); //set the prescaler TIM2->PSC = ps - 1; //set the prescaler to ps TIM2->RCR = 0; //repetition counter = 0 (=no repetition) //user can specify a prescaler here. otherwise use 0xffff TIM2->ARR = 0xffff; //auto reload register / period = 0; - need to change for downcounters TIM2->CNT = 0; //reset the counter //enable the timer. TIM2->CR1 |= TIM_CR1_CEN; //enable the timer //initialize tim3 as slave RCC->APB1ENR |= RCC_APB1ENR_TIM3EN | 0x00; //stop the timer to configure it TIM3->CR1 &=~TIM_CR1_CEN; //clear cen. 0=disable the timer, 1=enable the timer TIM3->CR1 &=~TIM_CR1_CKD; //clear CKD0..1. 0b00->1x clock; 0b01->2:1 clock, 0b10->4:1 clk; 0b11->reserved TIM3->CR1 &=~TIM_CR1_DIR; //clear DIR bit. 0=upcounter, 1=downcounter TIM3->CR1 &=~TIM_CR1_OPM; //clear opm bit. 0=periodic timer, 1=one-shot timer //or to simply zero the register //TIMx->CR1 = 0; //much easier //source from internal clock -> disable slave mode TIM3->SMCR &=~TIM_SMCR_SMS; //clear sms->master mode and use internal clock //clear the status register bits for capture / compare flags TIM3->SR &=~(TIM_SR_CC1IF | TIM_SR_CC2IF | TIM_SR_CC3IF | TIM_SR_CC4IF | TIM_SR_UIF); //disable the interrupt by clearing the enable bits TIM3->DIER &=~(TIM_DIER_CC1IE | TIM_DIER_CC2IE | TIM_DIER_CC3IE | TIM_DIER_CC4IE | TIM_DIER_UIE); //set the prescaler TIM3->PSC = 0; //set the prescaler to 1:1 - master timer acts as prescaler TIM3->RCR = 0; //repetition counter = 0 (=no repetition) TIM3->ARR = 0xffff; //auto reload register / period = 0; - need to change for downcounters TIM3->CNT = 0; //reset the counter //enable the timer. TIM3->CR1 |= TIM_CR1_CEN; //enable the timer //source from trgo -> enable slave mode and trigger on trgo TIM3->SMCR = (TIM3->SMCR &~((0x07 << 4) | 0x07)) | (0x01 << 4) | //tab 71: 0b001->//slave tim3 driven by tim2 (0x07 << 0) | //0b111->external trigger on trgo 0x00; } uint32_t tmr23_get(void) { uint16_t msw, lsw; //timer's high/low words //double read to maintain atomicity do { msw = TIM3->CNT; //read the msw lsw = TIM2->CNT; //read the lsw } while (msw != TIM3->CNT); //see if overflow has taken place return (msw << 16) | lsw; //return 32-bit time }

Стивестронг
Пн, 09 октября 2017 г. 12:15
[Дэнниф - Пн, 09 октября 2017 12:03] - uint32_t tmr23_get(void) { uint16_t msw, lsw; //timer's high/low words //double read to maintain atomicity do { msw = TIM3->CNT; //read the msw lsw = TIM2->CNT; //read the lsw } while (msw == TIM3->CNT); //see if overflow has taken place return (msw << 16) | lsw; //return 32-bit time }

Дэнниф
Пн, 09 октября 2017 г. 12:59
Вы правы. мой плохой.

Стивестронг
Пн, 09 октября 2017 г. 13:23
Без проблем. Я исправил это в вашем первоначальном посте, если это хорошо для вас. ;)

Дэнниф
Пн, 09 октября 2017 г. 13:33
не проблема. Спасибо.

Дэнниф
Пн, 09 октября 2017 г. 20:19
Вот другой подход: 32-битный входной захват с использованием одного 16-битного таймера.

https: // dannyelectronics.WordPress.ком/ ... бит-timer/

Протестировано на STM32F100, но также должен работать на 103.

Ddrown
Пн, 09 октября 2017 г. 20:36
У меня также есть немного другой способ сделать это (используя HAL):
uint32_t get_counters() { uint16_t tim2_before, tim1, tim2_after; tim2_before = __HAL_TIM_GET_COUNTER(&htim2); tim1 = __HAL_TIM_GET_COUNTER(&htim1); tim2_after = __HAL_TIM_GET_COUNTER(&htim2); if(tim2_before != tim2_after) { if(tim1 > 60000) { // allow for ~5000 cycles between tim2_before/tim2_after - beware of long interrupt handlers tim2_after = tim2_before; } } return ((uint32_t)tim2_after) << 16 | tim1; }

Дэнниф
Пн, 09 октября 2017 г., 21:02
более чем один способ сделать то же самое, :)

Спасибо.

Ddrown
Пн, 09 октября 2017 г., 22:45
[Дэнниф - Пн, 09 октября 2017 г. 9:02 вечера] - более чем один способ сделать то же самое, :)
Это очень правда!

Дэнниф
Пн, 09 октября 2017 г. 11:58
Я реализовал ту же концепцию на Atmega8/8L "Ghetto Chrono": https: // github.com/dannyf00/ghetto-chro ... ER/ATMEGA8

это сработало хорошо.

vitor_boss
Пн 19 февраля 2018 г., 21:15
Я сделал 32 -битный таймер для картинки, используя флаг переполнения таймера, он используется только для того, чтобы захватить период импульса и ширину. Работает отлично.

Если у тебя есть идея не сложно. Но если захочу код, просто процитируйте это

Арпрос
Пт 23 февраля 2018 г., 18:37
Обычно я просто время с счетчиком цикла DWT. Это 32-битное, высокое разрешение (по одному циклу на счет; при 72 МГц он может рассчитывать примерно до 59 секунд до переполнения) и работает с включенными или отключенными прерываниями, что приятно для точного битового графика. Стоимость состоит в том, что это немного замедляет (10-15%?) Когда вы включите это.

Пример:
#include // https://github.com/arpruss/gamecube-usb-adapter/blob/master/dwt.h void setup() { CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; DWT->CTRL |= 1; } void pulsePinForCycles(unsigned pin, unsigned value, unsigned cycles) { nvic_globalirq_disable(); // for greater precision unsigned reversedValue = !value; digitalWrite(pin,value); // use direct register manipulation for greater precision DWT->CYCCNT = 0; while (DWT->CYCCNT < cycles) ; digitalWrite(pin,reversedValue); nvic_globalirq_enable(); }

fpistm
Сб 24 февраля 2018 г. 8:14
[Арпрос - Пт. 23 февраля 2018 г. 18:37] - Стоимость состоит в том, что это немного замедляет (10-15%?) Когда вы включите это.
Это кажется немного высоким, как замедление для аппаратного счетчика

Дэнниф
Сб 24 февраля 2018 12:17
Стоимость состоит в том, что это немного замедляет (10-15%?) Когда вы включите это. Я никогда не испытывал этого замедления. Это аппаратный счетчик, который всегда есть, без участия процессора в его операциях. Так сложно представить причину, что это замедлит оставшуюся часть чипа.

fpistm
Сб 24 февраля 2018 г. 15:34
Я согласен с Дэннифом.

Арпрос
Солнце 25 февраля 2018 г., 3:48
Я думал, что видел небольшое замедление в тесте. Но я не вижу этого сейчас. Я был неправ.

Я также замечаю, что счетчик CYCCNT активен по умолчанию в ядре. Можно отключить, но это не имеет разницы в скорости.

Дэнниф
Ср. 04 июля 2018 г. 13:00
Та же самая логика может быть применена к цепочке других таймеров, таких как TIM15/16/17.
//initialize TIM1615 as a synchronized 32-bit timer //tim16 as master / lsw (including prescaler) to tim15 / msw of the 32-bit timer //tim15 as slave / msw of the 32-bit timer void tim1615_init(uint16_t ps) { //initialize TIM16 as master //enable clock to TIM16 RCC->APB2ENR |= RCC_APB2ENR_TIM16EN | 0x00; //stop the timer to configure it TIM16->CR1 &=~TIM_CR1_CEN; //clear cen. 0=disable the timer, 1=enable the timer TIM16->CR1 &=~TIM_CR1_CKD; //clear CKD0..1. 0b00->1x clock; 0b01->2:1 clock, 0b10->4:1 clk; 0b11->reserved TIM16->CR1 &=~TIM_CR1_DIR; //clear DIR bit. 0=upcounter, 1=downcounter TIM16->CR1 &=~TIM_CR1_OPM; //clear opm bit. 0=periodic timer, 1=one-shot timer //or to simply zero the register //TIMx->CR1 = 0; //much easier //CR2 on TIM16/17 doesn't exist -> the following statement has no effect //thus it doesn't work TIM16->CR2 = 0x20; //MMS = 0b010->TIM16 as prescaler //alternative: set up OC1 as the TRGO signal TIM16->CCER&=~TIM_CCER_CC1E; //0->disable cc1, 1->enable cc1 //disable cc1e to change ccmr1 //TIM16->CCMR1&=~(0x03<<0); //0->CC1 as output TIM16->CCMR1= 0x06<<4; //1->oc1ref high on match, 3->oc1ref flip on match, 6->pwm mode 1( TIM16->CCER|= TIM_CCER_CC1E; //0->disable cc1, 1->enable cc1 //TIM16->EGR = TIM_EGR_UG | TIM_EGR_CC1G; //force an update TIM16->CCR1= 0xffff; //set the match point: anything but 0 TIM16->BDTR|= TIM_BDTR_MOE; //1->set MOE //source from internal clock -> disable slave mode TIM16->SMCR &=~TIM_SMCR_SMS; //clear sms->master mode and use internal clock //clear the status register bits for capture / compare flags TIM16->SR &=~(TIM_SR_CC1IF | TIM_SR_CC2IF | TIM_SR_CC3IF | TIM_SR_CC4IF | TIM_SR_UIF); //disable the interrupt by clearing the enable bits TIM16->DIER &=~(TIM_DIER_CC1IE | TIM_DIER_CC2IE | TIM_DIER_CC3IE | TIM_DIER_CC4IE | TIM_DIER_UIE); //set the prescaler TIM16->PSC = ps - 1; //set the prescaler to ps TIM16->RCR = 0; //repetition counter = 0 (=no repetition) //user can specify a prescaler here. otherwise use 0xffff TIM16->ARR = 0xffff; //auto reload register / period = 0; - need to change for downcounters TIM16->CNT = 0; //reset the counter //enable the timer. //TIM16->CR1 |= TIM_CR1_CEN; //enable the timer //initialize TIM15 as slave RCC->APB2ENR |= RCC_APB2ENR_TIM15EN | 0x00; //stop the timer to configure it TIM15->CR1 &=~TIM_CR1_CEN; //clear cen. 0=disable the timer, 1=enable the timer TIM15->CR1 &=~TIM_CR1_CKD; //clear CKD0..1. 0b00->1x clock; 0b01->2:1 clock, 0b10->4:1 clk; 0b11->reserved TIM15->CR1 &=~TIM_CR1_DIR; //clear DIR bit. 0=upcounter, 1=downcounter TIM15->CR1 &=~TIM_CR1_OPM; //clear opm bit. 0=periodic timer, 1=one-shot timer //or to simply zero the register //TIMx->CR1 = 0; //much easier //source from internal clock -> disable slave mode TIM15->SMCR &=~TIM_SMCR_SMS; //clear sms->master mode and use internal clock //clear the status register bits for capture / compare flags TIM15->SR &=~(TIM_SR_CC1IF | TIM_SR_CC2IF | TIM_SR_CC3IF | TIM_SR_CC4IF | TIM_SR_UIF); //disable the interrupt by clearing the enable bits TIM15->DIER &=~(TIM_DIER_CC1IE | TIM_DIER_CC2IE | TIM_DIER_CC3IE | TIM_DIER_CC4IE | TIM_DIER_UIE); //set the prescaler TIM15->PSC = 0; //set the prescaler to 1:1 - master timer acts as prescaler TIM15->RCR = 0; //repetition counter = 0 (=no repetition) TIM15->ARR = 0xffff; //auto reload register / period = 0; - need to change for downcounters TIM15->CNT = 0; //reset the counter TIM15->EGR = TIM_EGR_UG; //force an update //source from trgo -> enable slave mode and trigger on trgo TIM15->SMCR = (TIM15->SMCR &~((0x07 << 4) | (0x07 << 0))) | (0x02 << 4) | //tab 74: 0b010->//slave TIM15 driven by TIM16 (0x07 << 0) | //0b111->external trigger on trgo (1<<7) | //set sms 0x00; //enable the timer. TIM15->CR1 |= TIM_CR1_CEN; //enable the timer //enable the timer. TIM16->CR1 |= TIM_CR1_CEN; //enable the timer }

Дэнниф
Ср. 04 июля 2018 г. 13:04
Во многих чипах STM32 некоторые таймеры могут действовать как мастер, так и рабыни. Таким образом, можно объединить более 2 таймеров, чтобы сформировать 48-битные композитные таймеры (3 x 16-битные таймеры) или 64-битные композитные таймеры (4 x 16-битные таймеры). или даже больше.

Не уверен в их приложениях, но это довольно аккуратно.

Дэнниф
Ср. 04 июля 2018 г. 20:17
Вот пример реализации 48-битных таймеров путем цепочки TIM2->Тим3->TIM4:
//initialize tim2/3/4 as a synchronized 32-bit timer //tim2 as master / lsw (including prescaler) to tim3 / msw of the 48-bit timer //tim3 as slave/master / middle word of the 48-bit timer //tim4 as slave / msw word of the 48-bit timer void tim234_init(uint16_t ps) { //initialize tim2 as master //enable clock to tim2 RCC->APB1ENR |= RCC_APB1ENR_TIM2EN | 0x00; //stop the timer to configure it TIM2->CR1 &=~TIM_CR1_CEN; //clear cen. 0=disable the timer, 1=enable the timer TIM2->CR1 &=~TIM_CR1_CKD; //clear CKD0..1. 0b00->1x clock; 0b01->2:1 clock, 0b10->4:1 clk; 0b11->reserved TIM2->CR1 &=~TIM_CR1_DIR; //clear DIR bit. 0=upcounter, 1=downcounter TIM2->CR1 &=~TIM_CR1_OPM; //clear opm bit. 0=periodic timer, 1=one-shot timer //or to simply zero the register //TIMx->CR1 = 0; //much easier TIM2->CR2 = 0x20; //MMS = 0b010->tim2 as prescaler //source from internal clock -> disable slave mode TIM2->SMCR &=~TIM_SMCR_SMS; //clear sms->master mode and use internal clock //clear the status register bits for capture / compare flags TIM2->SR &=~(TIM_SR_CC1IF | TIM_SR_CC2IF | TIM_SR_CC3IF | TIM_SR_CC4IF | TIM_SR_UIF); //disable the interrupt by clearing the enable bits TIM2->DIER &=~(TIM_DIER_CC1IE | TIM_DIER_CC2IE | TIM_DIER_CC3IE | TIM_DIER_CC4IE | TIM_DIER_UIE); //set the prescaler TIM2->PSC = ps - 1; //set the prescaler to ps TIM2->RCR = 0; //repetition counter = 0 (=no repetition) //user can specify a prescaler here. otherwise use 0xffff TIM2->ARR = 0xffff; //auto reload register / period = 0; - need to change for downcounters TIM2->CNT = 0; //reset the counter //enable the timer. //TIM2->CR1 |= TIM_CR1_CEN; //enable the timer //enable clock to tim3 RCC->APB1ENR |= RCC_APB1ENR_TIM3EN | 0x00; //stop the timer to configure it TIM3->CR1 &=~TIM_CR1_CEN; //clear cen. 0=disable the timer, 1=enable the timer TIM3->CR1 &=~TIM_CR1_CKD; //clear CKD0..1. 0b00->1x clock; 0b01->2:1 clock, 0b10->4:1 clk; 0b11->reserved TIM3->CR1 &=~TIM_CR1_DIR; //clear DIR bit. 0=upcounter, 1=downcounter TIM3->CR1 &=~TIM_CR1_OPM; //clear opm bit. 0=periodic timer, 1=one-shot timer //or to simply zero the register //TIMx->CR1 = 0; //much easier TIM3->CR2 = 0x20; //MMS = 0b010->tim2 as prescaler //source from internal clock -> disable slave mode TIM3->SMCR &=~TIM_SMCR_SMS; //clear sms->master mode and use internal clock //clear the status register bits for capture / compare flags TIM3->SR &=~(TIM_SR_CC1IF | TIM_SR_CC2IF | TIM_SR_CC3IF | TIM_SR_CC4IF | TIM_SR_UIF); //disable the interrupt by clearing the enable bits TIM3->DIER &=~(TIM_DIER_CC1IE | TIM_DIER_CC2IE | TIM_DIER_CC3IE | TIM_DIER_CC4IE | TIM_DIER_UIE); //set the prescaler TIM3->PSC = 0; //set the prescaler to ps TIM3->RCR = 0; //repetition counter = 0 (=no repetition) //user can specify a prescaler here. otherwise use 0xffff TIM3->ARR = 0xffff; //auto reload register / period = 0; - need to change for downcounters TIM3->CNT = 0; //reset the counter //source from trgo -> enable slave mode and trigger on trgo TIM3->SMCR = (TIM3->SMCR &~((0x07 << 4) | (0x07 << 0))) | (0x01 << 4) | //tab 71: 0b001->//slave tim3 driven by tim2 (0x07 << 0) | //0b111->external trigger on trgo 0x00; //enable the timer. //TIM2->CR1 |= TIM_CR1_CEN; //enable the timer //initialize tim4 as slave RCC->APB1ENR |= RCC_APB1ENR_TIM4EN | 0x00; //stop the timer to configure it TIM4->CR1 &=~TIM_CR1_CEN; //clear cen. 0=disable the timer, 1=enable the timer TIM4->CR1 &=~TIM_CR1_CKD; //clear CKD0..1. 0b00->1x clock; 0b01->2:1 clock, 0b10->4:1 clk; 0b11->reserved TIM4->CR1 &=~TIM_CR1_DIR; //clear DIR bit. 0=upcounter, 1=downcounter TIM4->CR1 &=~TIM_CR1_OPM; //clear opm bit. 0=periodic timer, 1=one-shot timer //or to simply zero the register //TIMx->CR1 = 0; //much easier //source from internal clock -> disable slave mode TIM4->SMCR &=~TIM_SMCR_SMS; //clear sms->master mode and use internal clock //clear the status register bits for capture / compare flags TIM4->SR &=~(TIM_SR_CC1IF | TIM_SR_CC2IF | TIM_SR_CC3IF | TIM_SR_CC4IF | TIM_SR_UIF); //disable the interrupt by clearing the enable bits TIM4->DIER &=~(TIM_DIER_CC1IE | TIM_DIER_CC2IE | TIM_DIER_CC3IE | TIM_DIER_CC4IE | TIM_DIER_UIE); //set the prescaler TIM4->PSC = 0; //set the prescaler to 1:1 - master timer acts as prescaler TIM4->RCR = 0; //repetition counter = 0 (=no repetition) TIM4->ARR = 0xffff; //auto reload register / period = 0; - need to change for downcounters TIM4->CNT = 0; //reset the counter //enable the timer. //TIM4->CR1 |= TIM_CR1_CEN; //enable the timer //source from trgo -> enable slave mode and trigger on trgo TIM4->SMCR = (TIM4->SMCR &~((0x07 << 4) | (0x07 << 0))) | (0x02 << 4) | //tab 71: 0b010->//slave tim4 driven by tim3 (0x07 << 0) | //0b111->external trigger on trgo 0x00; //enable the timer: MSW first, and LSW last TIM4->CR1 |= TIM_CR1_CEN; //enable the timer TIM3->CR1 |= TIM_CR1_CEN; //enable the timer TIM2->CR1 |= TIM_CR1_CEN; //enable the timer }