Перейти к содержанию
    

Прием и обработка сигналов STEP/DIR

Делаю обработку сигналов 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;
}

 

 

Изменено пользователем amaora

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

EXTIx по фронту для  определения момента смены направления

По обеим фронтам. И в прерывании - чтение входа, чтобы определить конкретный уровень.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

4 minutes ago, EdgeAligned said:

EXTIx по фронту для  определения момента смены направления

В ODrive так делают. Но мне не нравится неопределённость, не знаю как часто будет происходить это прерывание, и приоритет ему надо дать высокий. В худшем случае на предельно высокой частоте издержки будут выше (на вход/выход из прерываний), чем если поллингом через DMA.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...