Ejemplo n.º 1
0
/**
 * @brief   ChibiOS/RT initialization.
 * @details After executing this function the current instructions stream
 *          becomes the main thread.
 * @pre     Interrupts must be still disabled when @p chSysInit() is invoked
 *          and are internally enabled.
 * @post    The main thread is created with priority @p NORMALPRIO.
 * @note    This function has special, architecture-dependent, requirements,
 *          see the notes into the various port reference manuals.
 *
 * @special
 */
void chSysInit(void) {
  static Thread mainthread;

  port_init();
  scheduler_init();
  vt_init();
#if CH_USE_MEMCORE
  core_init();
#endif
#if CH_USE_HEAP
  heap_init();
#endif
#if CH_DBG_ENABLE_TRACE
  trace_init();
#endif

  /* Now this instructions flow becomes the main thread.*/
  setcurrp(_thread_init(&mainthread, NORMALPRIO));
  currp->p_state = THD_STATE_CURRENT;
  chSysEnable();

#if !CH_NO_IDLE_THREAD
  /* This thread has the lowest priority in the system, its role is just to
     serve interrupts in its context while keeping the lowest energy saving
     mode compatible with the system status.*/
  chThdCreateStatic(_idle_thread_wa, sizeof(_idle_thread_wa), IDLEPRIO,
                    (tfunc_t)_idle_thread, NULL);
#endif
}
Ejemplo n.º 2
0
bool Flasher_::write(PageID page, volatile const Data *bufp) {

  R2P_ASSERT(is_aligned(const_cast<const Data *>(bufp)));

  if (!check_bounds(address_of(page), PAGE_SIZE, get_program_start(),
                    get_program_length())) {
    return false;
  }

  volatile Data *const flashp = reinterpret_cast<volatile Data *>(
    reinterpret_cast<uintptr_t>(address_of(page))
  );

  chSysDisable();

  // Unlock flash for write access
  if (!flash_unlock()) {
    flash_lock();
    chSysEnable();
    return false;
  }
  flash_busy_wait();

  for (size_t pos = 0; pos < PAGE_SIZE / WORD_ALIGNMENT; ++pos) {
    // Enter flash programming mode
    FLASH->CR |= FLASH_CR_PG;

    // Write half-word to flash
    flashp[pos] = bufp[pos];
    flash_busy_wait();

    // Exit flash programming mode
    FLASH->CR &= ~FLASH_CR_PG;

    // Check for flash error
    if (flashp[pos] != bufp[pos]) {
      flash_lock();
      chSysEnable();
      return false;
    }
  }

  flash_lock();
  chSysEnable();
  return true;

}
Ejemplo n.º 3
0
void stepperSetDetails(Stepper* s)
{
  if (!s->timerRunning && (s->position != s->destination) && (s->speed != 0)) {
    s->timerRunning = true;
    chSysDisable();
    fasttimerStart(&s->fastTimer, s->speed, true);
    chSysEnable();
  }
  else {
    if ((s->timerRunning) && ((s->position == s->destination) || (s->speed == 0))) {
      chSysDisable();
      fasttimerStop(&s->fastTimer);
      chSysEnable();
      s->timerRunning = false;
    }
  }
}
Ejemplo n.º 4
0
/*
max_freq = 10000000
for prescaler in xrange(int(TIMEVT_INPUT_CLOCK / float(max_freq) + 0.5), 65535 + 1):
    if TIMEVT_INPUT_CLOCK % prescaler:
            continue
    if int(1e9) % (TIMEVT_INPUT_CLOCK / prescaler):
            continue
    print prescaler, TIMEVT_INPUT_CLOCK / prescaler, int(1e9) / (TIMEVT_INPUT_CLOCK / prescaler)
 */
void motor_timer_init(void)
{
	chSysDisable();

	// Power-on and reset
	TIMEVT_RCC_ENR |= TIMEVT_RCC_ENR_MASK;
	TIMEVT_RCC_RSTR |=  TIMEVT_RCC_RSTR_MASK;
	TIMEVT_RCC_RSTR &= ~TIMEVT_RCC_RSTR_MASK;

	TIMSTP_RCC_ENR |= TIMSTP_RCC_ENR_MASK;
	TIMSTP_RCC_RSTR |=  TIMSTP_RCC_RSTR_MASK;
	TIMSTP_RCC_RSTR &= ~TIMSTP_RCC_RSTR_MASK;

	chSysEnable();

	// Find the optimal prescaler value
	uint32_t prescaler = (uint32_t)(TIMEVT_INPUT_CLOCK / ((float)MAX_FREQUENCY)); // Initial value
	if (prescaler < 1)
		prescaler = 1;

	for (;; prescaler++) {
		ASSERT_ALWAYS(prescaler < 0xFFFF);

		if (TIMEVT_INPUT_CLOCK % prescaler) {
			continue;
		}
		const uint32_t prescaled_clock = TIMEVT_INPUT_CLOCK / prescaler;
		if (INT_1E9 % prescaled_clock) {
			continue;
		}
		break; // Ok, current prescaler value can divide the timer frequency with no remainder
	}
	_nanosec_per_tick = INT_1E9 / (TIMEVT_INPUT_CLOCK / prescaler);
	ASSERT_ALWAYS(_nanosec_per_tick < 1000);      // Make sure it is sane

	printf("Motor: Timer resolution: %u nanosec\n", (unsigned)_nanosec_per_tick);

	// Enable IRQ
	nvicEnableVector(TIMEVT_IRQn,  MOTOR_IRQ_PRIORITY_MASK);
	nvicEnableVector(TIMSTP_IRQn,  MOTOR_IRQ_PRIORITY_MASK);

	// Start the event timer
	TIMEVT->ARR = 0xFFFF;
	TIMEVT->PSC = (uint16_t)(prescaler - 1);
	TIMEVT->CR1 = TIM_CR1_URS;
	TIMEVT->SR  = 0;
	TIMEVT->EGR = TIM_EGR_UG;     // Reload immediately
	TIMEVT->CR1 = TIM_CR1_CEN;    // Start

	// Start the timestamping timer
	TIMSTP->ARR = 0xFFFF;
	TIMSTP->PSC = (uint16_t)(prescaler - 1);
	TIMSTP->CR1 = TIM_CR1_URS;
	TIMSTP->SR  = 0;
	TIMSTP->EGR = TIM_EGR_UG;     // Reload immediately
	TIMSTP->DIER = TIM_DIER_UIE;
	TIMSTP->CR1 = TIM_CR1_CEN;    // Start
}
Ejemplo n.º 5
0
/**	
	Simply take a number of steps from wherever the motor is currently positioned.
  This function will move the motor a given number of steps from the current position.
	@param stepper Which stepper (0 or 1).
	@param steps An integer specifying the number of steps.  Can be negative to go in reverse.
  @return status (0 = OK).
  
  \b Example
	\code
	// take 1200 steps forward from our current position
	stepperStep(0, 1200);
	\endcode
*/
int stepperStep(int stepper, int steps)
{
  Stepper* s = &steppers[stepper];
  chSysDisable();
  s->destination = (s->position + steps);
  chSysEnable();

  stepperSetDetails(s);
  return CONTROLLER_OK;
}
Ejemplo n.º 6
0
/**	
	Set the destination position for a stepper motor.
	This will start the stepper moving the given number of
	steps at the current speed, as set by Stepper_SetSpeed().
	
	While it's moving, you can call Stepper_GetPosition() to read
	its current position.
	@param stepper An integer specifying which stepper (0 or 1).
	@param positionRequested An integer specifying the desired stepper position.
  @return status (0 = OK).
  
  \b Example
	\code
	// start moving stepper 0 1500 steps
	stepperSetDestination(0, 1500);
	\endcode
*/
int stepperSetDestination(int stepper, int positionRequested)
{
  Stepper* s = &steppers[stepper];
  chSysDisable();
  s->destination = positionRequested;
  chSysEnable();

  stepperSetDetails(s);
  return CONTROLLER_OK;
}
Ejemplo n.º 7
0
/**	
	Reset the position of the specified stepper motor.
	Note that this will not ask the stepper to move.  It will simply set the
	stepper's current position as 0.  To move the stepper, see
	stepperSetDestination() or stepperStep().
	@param stepper An integer specifying which stepper (0 or 1).
	@param position An integer specifying the stepper position.
  @return status (0 = OK).
  
  \b Example
	\code
	// reset stepper 1 to call its current position 0
	stepperSetPosition(1, 0);
	\endcode
*/
int stepperResetPosition(int stepper, int position)
{
  Stepper* s = &steppers[stepper];
  
  chSysDisable();
  s->position = position;
  s->destination = position;
  chSysEnable();

  stepperSetDetails(s);
  return CONTROLLER_OK;
}
Ejemplo n.º 8
0
/**
 * @brief   ChibiOS/RT initialization.
 * @details After executing this function the current instructions stream
 *          becomes the main thread.
 * @pre     Interrupts must disabled before invoking this function.
 * @post    The main thread is created with priority @p NORMALPRIO and
 *          interrupts are enabled.
 *
 * @special
 */
void chSysInit(void) {
#if CH_DBG_ENABLE_STACK_CHECK == TRUE
  extern stkalign_t __main_thread_stack_base__;
#endif

  port_init();
  _scheduler_init();
  _vt_init();
#if CH_CFG_USE_TM == TRUE
  _tm_init();
#endif
#if CH_CFG_USE_MEMCORE == TRUE
  _core_init();
#endif
#if CH_CFG_USE_HEAP == TRUE
  _heap_init();
#endif
#if CH_DBG_STATISTICS == TRUE
  _stats_init();
#endif
#if CH_DBG_ENABLE_TRACE == TRUE
  _dbg_trace_init();
#endif

#if CH_CFG_NO_IDLE_THREAD == FALSE
  /* Now this instructions flow becomes the main thread.*/
  setcurrp(_thread_init(&ch.mainthread, NORMALPRIO));
#else
  /* Now this instructions flow becomes the idle thread.*/
  setcurrp(_thread_init(&ch.mainthread, IDLEPRIO));
#endif

  currp->p_state = CH_STATE_CURRENT;
#if CH_DBG_ENABLE_STACK_CHECK == TRUE
  /* This is a special case because the main thread thread_t structure is not
     adjacent to its stack area.*/
  currp->p_stklimit = &__main_thread_stack_base__;
#endif
  chSysEnable();

  /* Note, &ch_debug points to the string "main" if the registry is
     active, else the parameter is ignored.*/
  chRegSetThreadName((const char *)&ch_debug);

#if CH_CFG_NO_IDLE_THREAD == FALSE
  /* This thread has the lowest priority in the system, its role is just to
     serve interrupts in its context while keeping the lowest energy saving
     mode compatible with the system status.*/
  (void) chThdCreateStatic(ch.idle_thread_wa, sizeof(ch.idle_thread_wa),
                           IDLEPRIO, (tfunc_t)_idle_thread, NULL);
#endif
}
Ejemplo n.º 9
0
/**	
	Set the speed at which a stepper will move.
  This is a number of ms per step, rather than the more common steps per second.  
  Arranging it this way makes it easier to express as an integer.  
  Fastest speed is 1ms / step (1000 steps per second) and slowest is many seconds.
	@param stepper An integer specifying which stepper (0 or 1).
	@param speed An integer specifying the stepper speed in ms per step
  @return status (0 = OK).
  
  \b Example
	\code
	// set the speed to 1ms / step (1000 steps per second)
	stepperSetSpeed(0, 1);
	\endcode
*/
int stepperSetSpeed(int stepper, int speed)
{
  Stepper* s = &steppers[stepper];
  s->speed = speed * 1000;

  chSysDisable();
  fasttimerStop(&s->fastTimer);
  fasttimerStart(&s->fastTimer, s->speed, true);
  chSysEnable();

  stepperSetDetails(s);
  return CONTROLLER_OK;
}
Ejemplo n.º 10
0
/**
  Disable a stepper motor.
  @param stepper Which stepper (0 or 1).
*/
void stepperDisable(int stepper)
{
  Stepper* s = &steppers[stepper];
  if (s->timerRunning) {
    chSysDisable();
    fasttimerStop(&s->fastTimer);
    chSysEnable();
  }

  int i;
  for (i = 0; i < 4; i++)
    pinOff(s->pins[i]);

  int pwm = stepper * 2;
  pwmDisableChannel(pwm);
  pwmDisableChannel(pwm + 1);
}
Ejemplo n.º 11
0
/**
 * @brief   Initializes the kernel.
 * @details Initializes the kernel structures, the current instructions flow
 *          becomes the idle thread upon return. The idle thread must not
 *          invoke any kernel primitive able to change state to not runnable.
 * @note    This function assumes that the @p nil global variable has been
 *          zeroed by the runtime environment. If this is not the case then
 *          make sure to clear it before calling this function.
 *
 * @special
 */
void chSysInit(void) {
  thread_t *tp;
  const thread_config_t *tcp;

  /* Port layer initialization.*/
  port_init();

  /* System initialization hook.*/
  NIL_CFG_SYSTEM_INIT_HOOK();

  /* Iterates through the list of defined threads.*/
  tp = &nil.threads[0];
  tcp = nil_thd_configs;
  while (tp < &nil.threads[NIL_CFG_NUM_THREADS]) {
#if NIL_CFG_ENABLE_STACK_CHECK
    tp->stklim  = (stkalign_t *)tcp->wbase;
#endif

    /* Port dependent thread initialization.*/
    PORT_SETUP_CONTEXT(tp, tcp->wend, tcp->funcp, tcp->arg);

    /* Initialization hook.*/
    NIL_CFG_THREAD_EXT_INIT_HOOK(tp);

    tp++;
    tcp++;
  }

#if NIL_CFG_ENABLE_STACK_CHECK
  /* The idle thread is a special case because its stack is set up by the
     runtime environment.*/
  tp->stklim  = THD_IDLE_BASE;
#endif

  /* Runs the highest priority thread, the current one becomes the null
     thread.*/
  nil.current = nil.next = nil.threads;
  port_switch(nil.current, tp);

  /* Interrupts enabled for the idle thread.*/
  chSysEnable();
}
Ejemplo n.º 12
0
int motor_adc_init(void)
{
	_shunt_resistance = configGet("mot_i_shunt_mr") / 1000.0f;

	chSysDisable();

	RCC->AHBENR |= RCC_AHBENR_DMA1EN;  // Never disabled

	RCC->CFGR &= ~RCC_CFGR_ADCPRE;
#if STM32_PCLK2 == 72000000
	// ADC clock 72 / 6 = 12 MHz
	RCC->CFGR |= RCC_CFGR_ADCPRE_DIV6;
#else
#  error "What's wrong with PCLK2?"
#endif
	chSysEnable();

	enable();

	return 0;
}
Ejemplo n.º 13
0
/*
 * @brief   Used to terminate all running tasks and disable the OS. All
 *          deinit of drivers and modules should be placed here.
 */
void vSystemDeinit(void)
{
    /* Starting the shutdown sequence. */
    if (system_state == SYSTEM_RUNNING)
        system_state = SYSTEM_TERMINATING;
    else
        osalSysHalt("System is not running, invalid operation");

    /* Terminate critical tasks */
    vSystemTerminateCriticalTasks();

    /* Stop drivers */
    vSystemDeinitList();

     /* Stop SysTick */
    chSysDisable();
    vSystemDisableSystick();
    chSysEnable();

    system_state = SYSTEM_STOPPED;
}
Ejemplo n.º 14
0
/**
 * @brief   ChibiOS/RT initialization.
 * @details After executing this function the current instructions stream
 *          becomes the main thread.
 * @pre     Interrupts must be still disabled when @p chSysInit() is invoked
 *          and are internally enabled.
 * @post    The main thread is created with priority @p NORMALPRIO.
 * @note    This function has special, architecture-dependent, requirements,
 *          see the notes into the various port reference manuals.
 *
 * @special
 */
void chSysInit(void) {
  static Thread mainthread;
#if CH_DBG_ENABLE_STACK_CHECK
  extern stkalign_t __main_thread_stack_base__;
#endif

  port_init();
  _scheduler_init();
  _vt_init();
#if CH_USE_MEMCORE
  _core_init();
#endif
#if CH_USE_HEAP
  _heap_init();
#endif
#if CH_DBG_ENABLE_TRACE
  _trace_init();
#endif

  /* Now this instructions flow becomes the main thread.*/
  setcurrp(_thread_init(&mainthread, NORMALPRIO));
  currp->p_state = THD_STATE_CURRENT;
#if CH_DBG_ENABLE_STACK_CHECK
  /* This is a special case because the main thread Thread structure is not
     adjacent to its stack area.*/
  currp->p_stklimit = &__main_thread_stack_base__;
#endif
  chSysEnable();

  chRegSetThreadName("main");

#if !CH_NO_IDLE_THREAD
  /* This thread has the lowest priority in the system, its role is just to
     serve interrupts in its context while keeping the lowest energy saving
     mode compatible with the system status.*/
  chThdCreateStatic(_idle_thread_wa, sizeof(_idle_thread_wa), IDLEPRIO,
                    (tfunc_t)_idle_thread, NULL);
#endif
}
Ejemplo n.º 15
0
static void enable(void)
{
	// DMA
	DMA1_Channel1->CCR = 0;  // Deinitialize
	DMA1_Channel1->CMAR = (uint32_t)_adc1_2_dma_buffer;
	DMA1_Channel1->CNDTR = sizeof(_adc1_2_dma_buffer) / 4;
	DMA1_Channel1->CPAR = (uint32_t)&ADC1->DR;
	DMA1_Channel1->CCR =
		DMA_CCR_PL_0 | DMA_CCR_PL_1 |   // Max priority
		DMA_CCR_MSIZE_1 |               // 32 bit
		DMA_CCR_PSIZE_1 |               // 32 bit
		DMA_CCR_MINC |
		DMA_CCR_CIRC |
		DMA_CCR_EN;

	// ADC enable, reset
	const uint32_t enr_mask = RCC_APB2ENR_ADC1EN | RCC_APB2ENR_ADC2EN;
	const uint32_t rst_mask = RCC_APB2RSTR_ADC1RST | RCC_APB2RSTR_ADC2RST;
	chSysDisable();
	RCC->APB2ENR |= enr_mask;
	RCC->APB2RSTR |= rst_mask;
	RCC->APB2RSTR &= ~rst_mask;
	chSysEnable();

	usleep(5);  // Sequence: enable ADC, wait 2+ cycles, poweron, calibrate?

	// ADC calibration
	ADC1->CR2 = ADC_CR2_ADON;
	adc_calibrate(ADC1);

	ADC2->CR2 = ADC_CR2_ADON;
	adc_calibrate(ADC2);

	/*
	 * ADC channel sampling:
	 *   A B C A B C VOLT
	 *   C A B C A B CURR
	 */
	ADC1->SQR1 = ADC_SQR1_L_1 | ADC_SQR1_L_2;
	ADC1->SQR3 =
		ADC_SQR3_SQ1_0 |
		ADC_SQR3_SQ2_1 |
		ADC_SQR3_SQ3_0 | ADC_SQR3_SQ3_1 |
		ADC_SQR3_SQ4_0 |
		ADC_SQR3_SQ5_1 |
		ADC_SQR3_SQ6_0 | ADC_SQR3_SQ6_1;
	ADC1->SQR2 = ADC_SQR2_SQ7_2;

	ADC2->SQR1 = ADC1->SQR1;
	ADC2->SQR3 =
		ADC_SQR3_SQ1_0 | ADC_SQR3_SQ1_1 |
		ADC_SQR3_SQ2_0 |
		ADC_SQR3_SQ3_1 |
		ADC_SQR3_SQ4_0 | ADC_SQR3_SQ4_1 |
		ADC_SQR3_SQ5_0 |
		ADC_SQR3_SQ6_1;
	ADC2->SQR2 = ADC_SQR2_SQ7_2 | ADC_SQR2_SQ7_0;

	// SMPR registers are not configured because they have right values by default

	// ADC initialization
	ADC1->CR1 = ADC_CR1_DUALMOD_1 | ADC_CR1_DUALMOD_2 | ADC_CR1_SCAN | ADC_CR1_EOCIE;
	ADC1->CR2 = ADC_CR2_ADON | ADC_CR2_EXTTRIG | MOTOR_ADC1_2_TRIGGER | ADC_CR2_DMA;

	ADC2->CR1 = ADC_CR1_DUALMOD_1 | ADC_CR1_DUALMOD_2 | ADC_CR1_SCAN;
	ADC2->CR2 = ADC_CR2_ADON | ADC_CR2_EXTTRIG | ADC_CR2_EXTSEL_0 | ADC_CR2_EXTSEL_1 | ADC_CR2_EXTSEL_2;

	// ADC IRQ (only ADC1 IRQ is used because ADC2 is configured in slave mode)
	chSysDisable();
	nvicEnableVector(ADC1_2_IRQn, MOTOR_IRQ_PRIORITY_MASK);
	chSysEnable();
}
Ejemplo n.º 16
0
/**
 * @brief   ChibiOS/RT initialization.
 * @details After executing this function the current instructions stream
 *          becomes the main thread.
 * @pre     Interrupts must disabled before invoking this function.
 * @post    The main thread is created with priority @p NORMALPRIO and
 *          interrupts are enabled.
 *
 * @special
 */
void chSysInit(void) {
  extern stkalign_t __main_thread_stack_base__;

  _scheduler_init();
  _vt_init();

#if CH_DBG_SYSTEM_STATE_CHECK == TRUE
  ch.dbg.isr_cnt  = (cnt_t)0;
  ch.dbg.lock_cnt = (cnt_t)0;
#endif
#if CH_CFG_USE_TM == TRUE
  _tm_init();
#endif
#if CH_CFG_USE_MEMCORE == TRUE
  _core_init();
#endif
#if CH_CFG_USE_HEAP == TRUE
  _heap_init();
#endif
#if CH_DBG_STATISTICS == TRUE
  _stats_init();
#endif
#if CH_DBG_TRACE_MASK != CH_DBG_TRACE_MASK_NONE
  _dbg_trace_init();
#endif

#if CH_CFG_NO_IDLE_THREAD == FALSE
  /* Now this instructions flow becomes the main thread.*/
#if CH_CFG_USE_REGISTRY == TRUE
  currp = _thread_init(&ch.mainthread, (const char *)&ch_debug, NORMALPRIO);
#else
  currp = _thread_init(&ch.mainthread, "main", NORMALPRIO);
#endif
#else
  /* Now this instructions flow becomes the idle thread.*/
  currp = _thread_init(&ch.mainthread, "idle", IDLEPRIO);
#endif

  /* Setting up the base address of the static main thread stack.*/
  currp->stklimit = &__main_thread_stack_base__;

  /* Setting up the caller as current thread.*/
  currp->state = CH_STATE_CURRENT;

  /* Port layer initialization last because it depend on some of the
     initializations performed before.*/
  port_init();

#if CH_DBG_STATISTICS == TRUE
  /* Starting measurement for this thread.*/
  chTMStartMeasurementX(&currp->stats);
#endif

  /* It is alive now.*/
  chSysEnable();

#if CH_CFG_NO_IDLE_THREAD == FALSE
  {
    static const thread_descriptor_t idle_descriptor = {
      "idle",
      THD_WORKING_AREA_BASE(ch_idle_thread_wa),
      THD_WORKING_AREA_END(ch_idle_thread_wa),
      IDLEPRIO,
      _idle_thread,
      NULL
    };

    /* This thread has the lowest priority in the system, its role is just to
       serve interrupts in its context while keeping the lowest energy saving
       mode compatible with the system status.*/
    (void) chThdCreate(&idle_descriptor);
  }
#endif
}
Ejemplo n.º 17
0
 ~CriticalSectionLocker() { chSysEnable(); }
Ejemplo n.º 18
0
void motor_pwm_beep(int frequency, int duration_msec)
{
	static const float DUTY_CYCLE = 0.01;
	static const int ACTIVE_USEC_MAX = 20;

	motor_pwm_set_freewheeling();

	frequency = (frequency < 100)  ? 100  : frequency;
	frequency = (frequency > 5000) ? 5000 : frequency;

	duration_msec = (duration_msec < 1)    ? 1    : duration_msec;
	duration_msec = (duration_msec > 5000) ? 5000 : duration_msec;

	/*
	 * Timing constants
	 */
	const int half_period_hnsec = (HNSEC_PER_SEC / frequency) / 2;

	int active_hnsec = half_period_hnsec * DUTY_CYCLE;

	if (active_hnsec > ACTIVE_USEC_MAX * HNSEC_PER_USEC) {
		active_hnsec = ACTIVE_USEC_MAX * HNSEC_PER_USEC;
	}

	const int idle_hnsec = half_period_hnsec - active_hnsec;

	const uint64_t end_time = motor_timer_hnsec() + duration_msec * HNSEC_PER_MSEC;

	/*
	 * FET round robin
	 * This way we can beep even if some FETs went bananas
	 */
	static unsigned _phase_sel;
	const int low_phase_first  = _phase_sel++ % MOTOR_NUM_PHASES;
	const int low_phase_second = _phase_sel++ % MOTOR_NUM_PHASES;
	const int high_phase       = _phase_sel++ % MOTOR_NUM_PHASES;
	_phase_sel++;              // We need to increment it not by multiple of 3
	assert(low_phase_first != high_phase && low_phase_second != high_phase);

	/*
	 * Commutations
	 * No high side pumping
	 */
	phase_set_i(low_phase_first, 0, false);
	phase_set_i(low_phase_second, 0, false);
	phase_set_i(high_phase, 0, false);
	apply_phase_config();

	while (end_time > motor_timer_hnsec()) {
		chSysSuspend();

		irq_primask_disable();
		phase_set_i(high_phase, _pwm_top, false);
		apply_phase_config();
		irq_primask_enable();

		motor_timer_hndelay(active_hnsec);

		irq_primask_disable();
		phase_set_i(high_phase, 0, false);
		apply_phase_config();
		irq_primask_enable();

		chSysEnable();

		motor_timer_hndelay(idle_hnsec);
	}

	motor_pwm_set_freewheeling();
}
Ejemplo n.º 19
0
static void init_timers(void)
{
	assert_always(_pwm_top > 0);   // Make sure it was initialized

	chSysDisable();

	// TIM1
	RCC->APB2ENR |= RCC_APB2ENR_TIM1EN;
	RCC->APB2RSTR |=  RCC_APB2RSTR_TIM1RST;
	RCC->APB2RSTR &= ~RCC_APB2RSTR_TIM1RST;

	// TIM2
	RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;
	RCC->APB1RSTR |=  RCC_APB1RSTR_TIM2RST;
	RCC->APB1RSTR &= ~RCC_APB1RSTR_TIM2RST;

	chSysEnable();

	// Reload value
	TIM2->ARR = TIM1->ARR = _pwm_top;

	// Left-aligned PWM, direction up (will be enabled later)
	TIM2->CR1 = TIM1->CR1 = 0;

	// Output idle state 0, buffered updates
	TIM1->CR2 = TIM_CR2_CCUS | TIM_CR2_CCPC;

	/*
	 * OC channels
	 * TIM1 CC1, CC2, CC3 are used to control the FETs; TIM1 CC4 is not used.
	 * TIM2 CC2 is used to trigger the ADC conversion.
	 */
	// Phase A, phase B
	TIM1->CCMR1 =
		TIM_CCMR1_OC1PE | TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1 |
		TIM_CCMR1_OC2PE | TIM_CCMR1_OC2M_2 | TIM_CCMR1_OC2M_1;

	// Phase C
	TIM1->CCMR2 =
		TIM_CCMR2_OC3PE | TIM_CCMR2_OC3M_2 | TIM_CCMR2_OC3M_1;

	// ADC sync
	TIM2->CCMR1 =
		TIM_CCMR1_OC2PE | TIM_CCMR1_OC2M_2 | TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2M_0;

	// OC polarity (no inversion, all disabled except ADC sync)
	TIM1->CCER = 0;
	TIM2->CCER = TIM_CCER_CC2E;

	/*
	 * Dead time generator setup.
	 * DTS clock divider set 0, hence fDTS = input clock.
	 * DTG bit 7 must be 0, otherwise it will change multiplier which is not supported yet.
	 * At 72 MHz one tick ~ 13.9 nsec, max 127 * 13.9 ~ 1.764 usec, which is large enough.
	 */
	const float pwm_dead_time = PWM_DEAD_TIME_NANOSEC / 1e9f;
	const float pwm_dead_time_ticks_float = pwm_dead_time / (1.f / PWM_TIMER_FREQUENCY);
	assert(pwm_dead_time_ticks_float > 0);
	assert(pwm_dead_time_ticks_float < (_pwm_top * 0.2f));

	uint16_t dead_time_ticks = (uint16_t)pwm_dead_time_ticks_float;
	if (dead_time_ticks > 127) {
		assert(0);
		dead_time_ticks = 127;
	}
	lowsyslog("Motor: PWM dead time %u ticks\n", (unsigned)dead_time_ticks);

	TIM1->BDTR = TIM_BDTR_AOE | TIM_BDTR_MOE | dead_time_ticks;

	/*
	 * Default ADC sync config, will be adjusted dynamically
	 */
	TIM2->CCR2 = _pwm_top / 4;

	// Timers are configured now but not started yet. Starting is tricky because of synchronization, see below.
	TIM1->EGR = TIM_EGR_UG | TIM_EGR_COMG;
	TIM2->EGR = TIM_EGR_UG | TIM_EGR_COMG;
}