LINUX.ORG.RU

TIM1 захват входного сигнала

 , ,


0

0

Всем привет, уже пару дней ломаю голову над этой проблемой - не выходит настроить TIM1 input capture (для измерения длины импульсов, посылаемых HC-SR04 - ультразвуковым дальнометром). Плата - stm32vldiscovery, для разработки использую CMSIS и STDPeriph. Я новичок, если что.

Вкратце для тех кто не в курсе, как устроен протокол HC-SR04 - данный дальнометр располагает двумя пинами - Trigger и Echo. Получив на Trigger 10-микросек. сигнал, дальнометр активируется и определяет расстояние до ближайшей преграды с помощью ультразвука. Далее, он отвечает - на пин Echo посылается сигнал, длительность которого пропорциональна определенному расстоянию. (150-2500 мкс)

Вот скриншот с логического анализатора, как весь этот процесс выглядит: https://imgur.com/a/MXN13ZP. Как видно на этом фото, первая часть (триггер) у меня отлично работает (взят TIM4 CH4).

Проблема выходит со второй частью - я пытаюсь настроить TIM1 для определения длительности импульса Echo, вот код:

static void echo_init()
{
    // reconfigure PA8 to alternative function push-pull
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

    GPIO_InitTypeDef Gpio;
    GPIO_StructInit(&Gpio);
    
    Gpio.GPIO_Pin = GPIO_Pin_8;
    Gpio.GPIO_Mode = GPIO_Mode_AF_PP;
    //Gpio.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &Gpio);

    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    TIM_ICInitTypeDef TIM_ICInitStructure;
    
    // enable timer clock
    RCC_APB1PeriphClockCmd(RCC_APB2Periph_TIM1 , ENABLE);

    // configure timer
    // PWM frequency = 10 hz with 24,000,000 hz system clock

    TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
    TIM_TimeBaseStructure.TIM_Prescaler = SystemCoreClock/100000 - 1; // 10 hz or 100 ms cycle
    TIM_TimeBaseStructure.TIM_Period = 10000 - 1; // 0..9999
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);

    // Channel 1 latches the timer on a rising input on t1
    TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;
    TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
    TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
    TIM_ICInitStructure.TIM_ICPrescaler = 0;
    TIM_ICInitStructure.TIM_ICFilter = 0;
    TIM_ICInit(TIM1, &TIM_ICInitStructure);

    // Channel 2 latches the timer on a falling input on t2
    TIM_ICInitStructure.TIM_Channel = TIM_Channel_2;
    TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Falling;
    TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_IndirectTI;
    TIM_ICInitStructure.TIM_ICPrescaler = 0;
    TIM_ICInitStructure.TIM_ICFilter = 0;
    TIM_ICInit(TIM1, &TIM_ICInitStructure);

    // Configure the timer slave mode with TI1FP1 as reset signal
    TIM_SelectInputTrigger(TIM1, TIM_TS_TI1FP1);
    TIM_SelectSlaveMode(TIM1, TIM_SlaveMode_Reset);
    TIM_SelectMasterSlaveMode(TIM1, TIM_MasterSlaveMode_Enable);

    // Enable Timer
    TIM_Cmd(TIM1, ENABLE);
}

При этом, по идее, таймер должен быть полностью сконфигурирован, но каждый раз, когда я заглядываю в регистры TIM1->CCR1 и TIM1->CCR2, они пусты! Т.е. хранят 0. Что я делаю не так, может кто-то подскажет?

int ultrasonic_get_distance()
{
    return TIM1->CCR2 - TIM1->CCR1 * 17/100;
    
    //is supposed to return distance in mm
    //always returns zero for some reason
}

ЗЫ: конфиг взят из этой книги, глава 10.2, ну и частично дописан мной.



Последнее исправление: x86- (всего исправлений: 2)

Ответ на: комментарий от apt_install_lrzsz

О, спасибо, сейчас гляну.

x86-
() автор топика
Ответ на: комментарий от apt_install_lrzsz
/* Start the timer counter */
TIM3->CR1 |= TIM_CR1_CEN;
/* Clear the Capture event flag for channel 1 */
TIM3->SR = ~TIM_SR_CC1IF;
/* Loop until the capture event flag is set */
while (!(TIM3->SR & TIM_SR_CC1IF));
/* An active edge was detected, so store the timestamp */
TimeStamp = TIM3->CCR1;

Вот вроде ровно тоже самое, что я и делаю.. Нет, увы, данные там сниппеты не помогли, как и прочтение документа. Попробовав парочку данных там конфигураций, вижу, что сравнительные регистры все так же пустуют.

x86-
() автор топика

Ладно, думаю пока забить на Input Capture режим таймеров. (Я читаю книгу, где они даны до DMA и прерываний). Изучить две следующие главы, а позже вернуться к ним, и переписать код уже с прерываниями, авось окажется проще.

Всем спасибо за помощь! (Хотя если вдруг заметите ошибку в моей конфигурации, обязательно кастаните меня в этой теме)

x86-
() автор топика
28 октября 2021 г.
    Gpio.GPIO_Mode = GPIO_Mode_AF_PP;
    //Gpio.GPIO_Mode = GPIO_Mode_IN_FLOATING;

мне кажется, должно быть так

Gpio.GPIO_Mode = GPIO_Mode_IN_FLOATING;

потому что это вход, а AF_PP — режим выхода

Harald ★★★★★
()

Проверенный, рабочий код, измеряет длительность положительного импульса на PA8 в миллисекундах (максимум до 65 секунд) и отправляет результат текстом по UART на скорости 115200:

#include ...

#define F_CPU_MHz       8
#define BAUDRATE        115200

uint32_t u32tod(uint32_t num, uint8_t *s) {
    uint8_t *p = s + 11;
    uint32_t i = 0, cnt = 0;
    do {  *(--p) = num % 10 + '0';  num /= 10;  cnt++;  } while (num);
    for (; i<cnt; i++) { s[i] = p[i]; };  s[cnt++] = '\n';
    return cnt;
}

void uart_send_blocking(uint8_t *s, uint32_t cnt) {
    while (cnt--) {
        while (!(USART1->SR & USART_SR_TXE));
        USART1->DR = *s++;
    }
}

int main() {
    RCC->APB2ENR = RCC_APB2ENR_USART1EN | RCC_APB2ENR_IOPAEN | RCC_APB2ENR_TIM1EN;

    /* Pins; PA8 is TI1, pulled low; PA9 is UART TX */
    CONFIGURE_PIN(GPIOA, 8, I_PULLED);
    CONFIGURE_PIN(GPIOA, 9, O_ALT_PUSH_PULL);

    /* Timer 1; CC2 captures on falling TI1; timer reset on rising TI1 */
    #define CC1S_IC1_ON_TI1     (0b01<<0)     /* CC1 as input capture on TI1 */
    #define CC2S_IC2_ON_TI1     (0b10<<8)     /* CC2 as input capture also on TI1 */
    #define FILTER_IC1          (0b1111<<4)   /* Input 1 filter config = f_DTS/32, N=8 */
    #define FILTER_IC2          (0b1111<<12)  /* Input 2 filter config = f_DTS/32, N=8 */
    #define CC1EP_NOCAP_RISING  (0b00<<0)     /* CC1 polarity is positive; actual capture not enabled */
    #define CC2EP_CAP_FALLING   (0b11<<4)     /* CC2 polarity is negative; THIS IS OUR CAPTURE CHANNEL */
    #define SLAVEMODE_TRIG_TI1  (0b100<<4)    /* Timer is in slave mode, triggered by filtered/polarized TI1 (CC1) */
    #define SLAVEMODE_RESET     (0b100<<0)    /* Trigger resets the timer but doesn't stop it */

    TIM1->CCMR1 = FILTER_IC2 | CC2S_IC2_ON_TI1 | FILTER_IC1 | CC1S_IC1_ON_TI1;
    TIM1->CCER = CC2EP_CAP_FALLING | CC1EP_NOCAP_RISING;
    TIM1->SMCR = SLAVEMODE_TRIG_TI1 | SLAVEMODE_RESET;
    TIM1->PSC = F_CPU_MHz * 1000;   /* 1 ms resolution */
    TIM1->CR1 = 1;

    /* UART; transmission only */
    USART1->BRR = F_CPU_MHz * 1000000 / BAUDRATE;
    USART1->CR1 = USART_CR1_UE | USART_CR1_TE;

    uint8_t buf[11];
    uint32_t i, cnt, cap, old_cap = 0;

    while (1) {
        cap = TIM1->CCR2;
        if (cap != old_cap && cap > 10) {
            uart_send_blocking(buf, u32tod(cap, buf));
            old_cap = cap;
        }
    }
}
$ make
$ stty < /dev/ttyUSB0 115200
$ cat /dev/ttyUSB0
1546
3347
^C
$ base64 tim1capture.bin
AFAAIAkCACAPShBLmkII0A9Ig0IF0hL4ARsD+AEbmEL50QxLDEqTQgTSACED+AEbk0L70U/w4CMI
SAlJiGDD+PgtAPBHuAC/XAMAIFwDACBcAwAgXAMAIFwDACAAAgAgAO0A4HC1ACQSTgHxCwKGRqb7
AFPbCAPrgwyg60wAMDBf+oD8vvEJDyVGGEYE8QEEAvgBzevYRLFLHhgZAeAS+AHPA/gBz4NC+dEK
I6gcC1VwvQC/zczMzEmxBUoBRBNoGwb81RD4ATuIQlNg99FwRwA4AUBE9gQCHUuAtTAmT/LxJ0/w
RA5P9PpcASRFIELyCAGaYQAjF02EsNX4BCgi8A8CQvAIAsX4BCjV+AQoIvDwAkLwkALF+AQoD0oF
9TBVr2EuYsX4CODF+CjALGCQYNFgrGujQvzQCiz62QGpIEb/95D/AUYBqP/3tv8jRvDnABACQAAA
AUAAOAFA

Если захочешь скомпилировать, то придётся добавить заголовки и заменить макрос CONFIGURE_PIN на что-то своё.

Оба канала захвата (CC1 и CC2) настроены на один и тот же вход TI1. По положительному фронту первого канала триггерится сброс таймера, по отрицательному — происходит захват значения. Первый канал не используется для захвата, а только для сброса таймера. Судя по скриншоту из мануала, функции CC1 и CC2 можно поменять местами так, чтобы таймер триггерился от TI2FP2, а не от TI1FP1, как сейчас, и захват производился первым каналом.

GPIO_Mode_AF_PP

Если всё же у тебя не заработает, то проверь, не попалил ли ты вход МК или выход датчика.

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.