Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: STM32F429I-DISCO PWM Input Capture Mode
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > ARM, 32bit
retterberg
STM32F429I-DISCO (STM32F429ZIT6U) + датчик угла наклона. Осциллографом вижу, что при 0 градусов идут импульсы с периодом 1кгц, и шириной около 0,5кгц. При изменении угла наклона, ширина имульса меняется. Используя даташит накидал код, представленный ниже. Занимаюсь с этим микроконтроллером две недели, да и вообще занимаюсь с микроконтроллерами две недели, да и радиоэлектроникой... Ну вы поняли. biggrin.gif
Мне не удается получить ширину импульса в CCR2, лишь его период в CCR1. В CCR2 чаще всего период, иногда проскакивают 0 или 1.
И ещё, почему то не могу обнулить ни один бит регистра SR, соответственно вот эти условия if (TIM2_SR & 2) и if (TIM2_SR & 4) не имеют никакого смысла.
Как же получить в CCR2 ширину импульса?
Очень нужна помощь. Уже хочется выть и биться головой об стену.
Код
#include "define.h" //адреса и значения регистров (не пользуюсь cmsis, hal)

volatile int up;
volatile int down;

void main(void){
  RCC_AHB1ENR |= RCC_AHB1ENR_GPIOA; //GPIOA clocking
  RCC_APB1ENR |= RCC_APB1ENR_TIM2; //TIM2 clocking

  GPIOA_MODER |= GPIOA_MODER_AF_P0; //GPIOA0 alternate function
  GPIOA_AFRL |= GPIOA_AFRL_AF1_P0 //GPIOA0 TIM2_CH1

  TIM2_PSC = 16-1; //Prescaler 16
  
  TIM2_CCMR1 |= TIM2_CCMR1_CC1S_0_1; //Select TI1 active input for TIM2_CCR1
  TIM2_CCER &= ~TIM2_CCER_CC1P &
               ~TIM2_CCER_CC1NP; //TI1FP1 rising edge

  TIM2_CCMR1 |= TIM2_CCMR1_CC2S_1_0; //Select TI1 active input for TIM2_CCR2
  TIM2_CCER |= TIM2_CCER_CC2P &
              ~TIM2_CCER_CC2NP; //TI1FP2 falling edge
  
  TIM2_SMCR |= TIM2_SMCR_TS_1_0_1; //Select TI1FP1 valid trigger input
  TIM2_SMCR |= TIM2_SMCR_SMS_1_0_0; //Reset mode
  
  TIM2_CCER |= TIM2_CCER_CC1E; //Enable the capture CC1            
  TIM2_CCER |= TIM2_CCER_CC2E; //Enable the capture CC2
  
  TIM2_DIER |= TIM2_DIER_CC1IE; //Enable the interrupt CC1
  TIM2_DIER |= TIM2_DIER_CC2IE; //Enable the interrupt CC2

  TIM2_CR1 |= TIM2_CR1_CEN; //Counter enabled
  
  NVIC_ISER0 |= NVIC_ISER0_TIM2; //TIM2 Interrupt
  
  while(1);
}

//обработчик прерывания (startup.c)
void interrupt(void){
        if(TIM2_SR & 2) up=TIM2_CCR1;
        if(TIM2_SR & 4) down=TIM2_CCR2;
        TIM2_SR = 0;
}
Сергей Борщ
С этим режимом таймера не работал, поэтому несколько общих замечаний:
Цитата(retterberg @ Feb 29 2016, 11:33) *
И ещё, почему то не могу обнулить ни один бит регистра SR, соответственно вот эти условия if (TIM2_SR & 2) и if (TIM2_SR & 4) не имеют никакого смысла.
Возможно вы просто не успеваете заметить как бит обнулился. Пока вы делаете шаг в отладчике датчик присылает новый сигнал и бит устанавливается снова. И еще - не используйте "магические числа", пишите if (TIM2_SR & TIM_SR_CC1IF), if (TIM2_SR & TIM_SR_CC2IF) - вам самому потом будет понятнее. Кроме этого - после обнуления флагов надо поставить инструкцию DSB, иначе есть риск войти в этот же обработчик снова сразу после выхода, из-за конвейера. Далее, можно на входе в прерывание считать TIM2_SR во временную переменную, тут же сбросить SR записью в него инвертированного значения считанных флагов и дальше анализировать флаги в переменной. Это позволит обойтись без DSB (конвейер успеет продвинуться за время проверки флагов) и позволит вам не потерять флаги, возникшие между чтением SR и его сбросом (так как вы сбросите только те флаги, которые уже считаны) и код ваш будет короче и быстрее, ибо чтение TIM2_SR произойдет только один раз, а не при проверке каждого флага:

Код
void interrupt(void)
{
     uint32_t Flags = TIM2_SR;
     TIM2_SR = ~Flags;

     if(Flags & TIM_SR_CC1IF)
        up=TIM2_CCR1;
     if(Flags & TIM_SR_CC2IF)
        down=TIM2_CCR2;
}


Цитата
Код
TIM2_DIER |= TIM2_DIER_CC1IE; //Enable the interrupt CC1
TIM2_DIER |= TIM2_DIER_CC2IE; //Enable the interrupt CC2
Эти две записи можно объединить:

Код
TIM2_DIER |= 0
    | TIM2_DIER_CC1IE    //Enable the interrupt CC1
    | TIM2_DIER_CC2IE    //Enable the interrupt CC2
  ;
Тогда у вас будет всего одно чтение и одна запись в регистр. И если вам не нужно сохранять состояние остальных битов (а чаще всего это именно так), такой вариант позволяет писать "=" вместо "|=" и заодно обнулить ненужные биты.

Код
NVIC_ISER0 |= NVIC_ISER0_TIM2; //TIM2 Interrupt
Здесь "|=" избыточно. Запись нуля в этот регистр не меняет его состояния, поэтому достаточно писать NVIC_ISER0 = NVIC_ISER0_TIM2
retterberg
Спасибо за уделенное мне время. Судя по всему вы правы, я не успеваю проследить обнуление регистра TIMx_SR. Но это не главная проблема. Никак не пойму, почему значения по фронту в CCR1 нормальные, а значения по спаду CCR2 - нет. Есть ли тут те, кто реализовывал этот режим таймера на STM32F4XX. Взял программу разработчиков из куба, написанную для таймера 4, переделал их пример под TIM2, поскольку мне нужен 32 битный таймер. Результат тот же. Скважность порядка 100 процентов, хотя на осциллографе вижу 50.
esaulenka
Извините, некогда разбираться. Но именно такую задачу решал полгода назад.
С использованием библиотек AHTOXA https://github.com/antongus/stm32tpl получилось примерно так:


Код
typedef STM32::TIM::Timer<STM32::TIM::TIM_3>        tmr;

void Init ()
{
    tmr::EnableClocks ();

// вход - PC7, TIM3_CH2

// частота таймера - 10 кГц
    tmr::TIMx->PSC = CLOCK_APB1_TIM / 10000- 1;
    tmr::TIMx->ARR = 0xFFFF;

    tmr::TIMx->CR1 = TIM_CR1_CKD_1;                            // предделитель на 4 для фильтров

    tmr::Ch2::CCMRx =    tmr::Ch1::CCMR_CCS_IC1_CROSS |        // первый канал - на второй вход
                        tmr::Ch2::CCMR_CCS_IC1 |            // второй канал - туда же
                        tmr::Ch1::CCMR_CCS_FILTER * 15 |    // фильтр - восемь сэмплов на 1/32-й частоты
                        tmr::Ch2::CCMR_CCS_FILTER * 15;        // убирает импульсы короче 57 мкс

    tmr::TIMx->CCER =    tmr::Ch1::CCER_CCxE |                // включаем оба канала
                        tmr::Ch2::CCER_CCxE |
                        tmr::Ch2::CCER_CCxP;                // второй канал - по заднему фронту

    tmr::TIMx->SMCR =    TIM_SMCR_TS_0 * 6 |                    // trigger input - filtered timer input 2
                        TIM_SMCR_SMS_0 * 4;                    // slave mode - reset mode

// прерывание по сбросу таймера
    tmr::UpdateInterrupt::Enable ();
    NVIC_EnableIRQ (tmr_irq);

    tmr::Enable();

}


void IrqProcess ()
{
    tmr::UpdateInterrupt::Clear ();


    int32_t cur_period = tmr::TIMx->CCR2;
    int32_t on_time = tmr::TIMx->CCR1;

    int32_t off_time = cur_period - on_time;

    // тут был кусочек пред-обработки

    // перезапускаем таймер - импульсы идут
    stop_tmr.Restart ();
}


И да, Сергей дело говорит - если инициализировать регистр сразу, "кучей", это и читается легче, и выполняется быстрее (хотя кому этот десяток байт нужен? :-) ), и вот таких опечаток не будет:
Код
  TIM2_CCER |= TIM2_CCER_CC2P &
              ~TIM2_CCER_CC2NP; //TI1FP2 falling edge



PS полистайте ещё раздел "PWM input mode" reference manual'а. Я по нему делал (с учётом более удобного синтаксиса библиотеки).
retterberg
Спасибо, как раз по "PWM input mode" reference manual'а я и делал. Ещё делал по примеру разработчика и по примерам из интернета. В этом коде тоже самое. Думаю, работать у меня не будет, CCR2 так и будет 0 или 1 (может быть что то с аппаратной, а не с программной частью?). Сейчас без таймеров буду реализовывать, прерывание, когда единичка на ножке и системным счетчиком определять длительность.
PS а где тут опечатка?)) все замечательно)
Код
TIM2_CCER |= TIM2_CCER_CC2P &
              ~TIM2_CCER_CC2NP; //TI1FP2 falling edge
Сергей Борщ
Цитата(retterberg @ Mar 1 2016, 09:18) *
PS а где тут опечатка?)) все замечательно)

Если вы хотели написать
Код
TIM2_CCER |= TIM2_CCER_CC2P;
то да, замечательно wink.gif

Честно говоря я сомневаюсь, что вы будете переключать эти биты в процессе выполнения программы. Вероятнее всего такую настройку вы будете делать один раз в самом начале и тогда ее удобнее написать в таком виде:
Код
TIM2_CCER = 0
    | 0 * TIM2_CCER_CC4NP   // 0 - single edge, 1 - both edges
    | 0 * TIM2_CCER_CC4P    // 0 - rising edge, 1 - falling edge/both edges
    | 0 * TIM2_CCER_CC4E    // 0 - disabled, 1 - enabled
    | 0 * TIM2_CCER_CC3NP
    | 0 * TIM2_CCER_CC3P
    | 0 * TIM2_CCER_CC3E
    | 0 * TIM2_CCER_CC2NP
    | 1 * TIM2_CCER_CC2P
    | 1 * TIM2_CCER_CC2E
    | 0 * TIM2_CCER_CC1NP
    | 0 * TIM2_CCER_CC1P
    | 0 * TIM2_CCER_CC1E
;
Она и читается лучше, и править ее легче, и всем битам в ней присваивается конкретное значение, и в другой проект ее можно перетащить с минимальными правками.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.