amaora 20 15 января Опубликовано 15 января (изменено) · Жалоба Делаю обработку сигналов STEP/DIR или CW/CCW на stm32f4. Как я понял нет никакой возможности настроить таймер, так чтобы он считал импульсы STEP с учётом направления заданного уровнем DIR. Для CW/CCW можно задействовать два таймера в режиме счётчика внешних импульсов, но не сошлось по ногам (оба входа идут на один таймер), да и не важен этот режим если не работает STEP/DIR. Все, что удалось придумать это поллинг GPIO регистров с помощью DMA и дальнейший ручной анализ фронтов. Сейчас это отнимает около ~2.5 мкс (каждые 35 мкс) при частоте опроса 500 кГц. Как можно сделать лучше? Spoiler void STEP_mode_DMA() { /* Enable clock. * */ RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN; RCC->APB2ENR |= RCC_APB2ENR_TIM8EN; hal.STEP_frequency = (hal.STEP_frequency < 50000) ? 50000 : (hal.STEP_frequency > CLOCK_TIM8_HZ) ? CLOCK_TIM8_HZ : hal.STEP_frequency; TIM8->CR1 = 0; TIM8->CR2 = 0; TIM8->SMCR = 0; TIM8->DIER = TIM_DIER_UDE; TIM8->CCMR1 = 0; TIM8->CCMR2 = 0; TIM8->CCER = 0; TIM8->CNT = 0; TIM8->PSC = 0; TIM8->ARR = CLOCK_TIM8_HZ / hal.STEP_frequency; TIM8->CCR1 = 0; TIM8->CCR2 = 0; TIM8->CCR3 = 0; TIM8->CCR4 = 0; hal.STEP_frequency = CLOCK_TIM8_HZ / TIM8->ARR; #define XGPIO_GET_IDR(xGPIO) (GPIOA_BASE + 0x0400U * XGPIO_GET_PORT(xGPIO) + 0x10U) /* DMA on TIM8_UP. * */ DMA2_Stream1->CR = (7U << DMA_SxCR_CHSEL_Pos) | DMA_SxCR_PL_1 | (1U << DMA_SxCR_MSIZE_Pos) | (1U << DMA_SxCR_PSIZE_Pos) | DMA_SxCR_MINC | DMA_SxCR_CIRC; DMA2_Stream1->NDTR = STEP_DMA_MAX; DMA2_Stream1->PAR = (uint32_t) XGPIO_GET_IDR(GPIO_STEP); DMA2_Stream1->M0AR = (uint32_t) priv_STEP.dmabuf; DMA2_Stream1->FCR = DMA_SxFCR_DMDIS; priv_STEP.dma_rp = 0; priv_STEP.wire_STEP = GPIO_get_STATE(GPIO_STEP); priv_STEP.wire_DIR = GPIO_get_STATE(GPIO_DIR); priv_STEP.NCNT = 32767; /* Start TIM8. * */ TIM8->CR1 |= TIM_CR1_CEN; /* Start DMA2. * */ DMA2_Stream1->CR |= DMA_SxCR_EN; /* Enable STEP/DIR pins. * */ GPIO_set_mode_INPUT(GPIO_STEP); GPIO_set_mode_INPUT(GPIO_DIR); GPIO_set_mode_PULL_DOWN(GPIO_STEP); GPIO_set_mode_PULL_DOWN(GPIO_DIR); } int STEP_get_POSITION() { int wp = (STEP_DMA_MAX - DMA2_Stream1->NDTR) & (STEP_DMA_MAX - 1); int rp = priv_STEP.dma_rp; #ifdef STM32F7 /* Invalidate D-Cache. * */ SCB_InvalidateDCacheByAddr((uint32_t *) priv_STEP.dmabuf, sizeof(uint16_t) * STEP_DMA_MAX); #endif /* STM32F7 */ #define XGPIO_GET_STATE(xGPIO, IDR) ((IDR & (1U << XGPIO_GET_N(xGPIO))) ? 1 : 0) if (hal.STEP_mode == STEP_ON_STEP_DIR) { while (likely(rp != wp)) { uint16_t IDR = priv_STEP.dmabuf[rp]; int wire_STEP, wire_DIR; wire_STEP = XGPIO_GET_STATE(GPIO_STEP, IDR); wire_DIR = XGPIO_GET_STATE(GPIO_DIR, IDR); if (unlikely( priv_STEP.wire_STEP == 0 && wire_STEP != 0)) { if (wire_DIR != 0) { priv_STEP.NCNT++; } else { priv_STEP.NCNT--; } } priv_STEP.wire_STEP = wire_STEP; priv_STEP.wire_DIR = wire_DIR; rp = (rp + 1) & (STEP_DMA_MAX - 1); } } else if (hal.STEP_mode == STEP_ON_CW_CCW) { while (likely(rp != wp)) { uint16_t IDR = priv_STEP.dmabuf[rp]; int wire_CW, wire_CCW; wire_CW = XGPIO_GET_STATE(GPIO_STEP, IDR); wire_CCW = XGPIO_GET_STATE(GPIO_DIR, IDR); if (unlikely( priv_STEP.wire_STEP == 0 && wire_CW != 0)) { priv_STEP.NCNT++; } if (unlikely( priv_STEP.wire_DIR == 0 && wire_CCW != 0)) { priv_STEP.NCNT--; } priv_STEP.wire_STEP = wire_CW; priv_STEP.wire_DIR = wire_CCW; rp = (rp + 1) & (STEP_DMA_MAX - 1); } } priv_STEP.dma_rp = rp; return priv_STEP.NCNT; } Изменено 15 января пользователем amaora Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
EdgeAligned 53 15 января Опубликовано 15 января · Жалоба EXTIx по фронту для определения момента смены направления По обеим фронтам. И в прерывании - чтение входа, чтобы определить конкретный уровень. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
amaora 20 15 января Опубликовано 15 января · Жалоба 4 minutes ago, EdgeAligned said: EXTIx по фронту для определения момента смены направления В ODrive так делают. Но мне не нравится неопределённость, не знаю как часто будет происходить это прерывание, и приоритет ему надо дать высокий. В худшем случае на предельно высокой частоте издержки будут выше (на вход/выход из прерываний), чем если поллингом через DMA. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
EdgeAligned 53 15 января Опубликовано 15 января · Жалоба Полагаю, не выше, чем программно просматривать массив, захваченный по ДМА Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться