⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 main.c

📁 一个用GCC做的atmel芯片控制的无刷无传感器的程序.包括各种参考文档!
💻 C
📖 第 1 页 / 共 2 页
字号:
    // Wait for conversion to complete.
    while((ADCSRA & (1 << ADSC)))
    {

    }
    speedReferenceADC = ADCH;

    // Read voltage reference.
    // Change ADC channel.
    ADMUX = ADMUX_REF_VOLTAGE;
    // Start conversion manually.
    ADCSRA |= (1 << ADSC);
    // Wait for conversion to complete.
    while((ADCSRA & (1 << ADSC)))
    {

    }
    referenceVoltageADC = ADCH;

    // Enable current measurements in ADC ISR.
    ADMUX = ADMUX_CURRENT;
    ADCSRA |= (1 << ADATE) | (1 << ADIE) | ADC_PRESCALER;
  }
  else
  {
    unsigned char tempADMUX;

    tempADMUX = ADMUX;
    // Read current

    // Make sure that a sample is not in progress
    while (ADCSRA & (1 << ADSC))
    {

    }

    // Change channel
    ADMUX = ADMUX_CURRENT;

    // Start conversion manually.
    ADCSRA |= (1 << ADSC);
    // Wait for conversion to complete.
    while((ADCSRA & (1 << ADSC)))
    {

    }

    shuntVoltageADC = ADCH;
    currentUpdated = TRUE;

    // Restore ADC channel.
    ADMUX = tempADMUX;
    ADCSRA |= (1 << ADATE) | (1 << ADIE) | ADC_PRESCALER;
  }
}


/*! \brief Commutates and prepares for new zero-cross detection.
 *
 *  This interrupt service routine is triggered exactly when a commutation
 *  is scheduled. The commutation is performed instantly and Timer/counter0
 *  is reset to measure the delay between commutation and zero-cross detection.
 *
 *  Commutation causes large transients on all phases for a short while that could
 *  cause false zero-cross detections. A zero cross detection hold-off period is
 *  therefore used to avoid any false readings. This is performed by using Timer/counter1
 *  compare B. The compare is set to happen after the specified hold-off period.
 *  Timer/counter1 compare B interrupt handler then enables the zero-cross detection.
 */
#pragma vector=TIMER1_COMPA_vect
__interrupt void Commutate()
{
  // Commutate and clear commutation timer.
  DRIVE_PORT = nextDrivePattern;
  TCNT1 = 0;

  zcPolarity = nextCommutationStep & 0x01;

  // Set zero-cross detection holdoff time.
  CLEAR_ALL_TIMER1_INT_FLAGS;
  OCR1B = ZC_DETECTION_HOLDOFF_TIME_US;
  SET_TIMER1_INT_HOLDOFF;

  __watchdog_reset();
}


/*! \brief Enables zero-cross detection.
 *
 *  This interrupt service routine is triggered when the zero cross detection
 *  hold-off time after commutation is over. All Timer/counter1 interrupts are
 *  disabled and Timer/counter0 (PWM) overflow interrupt is enabled to allow
 *  the ADC readings to be used for zero-cross detection.
 */
#pragma vector=TIMER1_COMPB_vect
__interrupt void EnableZCDetection()
{
  // Enable TCNT0 overflow ISR.
  CLEAR_ALL_TIMER0_INT_FLAGS;
  CLEAR_ALL_TIMER1_INT_FLAGS;
  SET_TIMER0_INT_ZC_DETECTION;
  DISABLE_ALL_TIMER1_INTS;

  // Set up ADC for zero-cross detection
  ADMUX = ADMUXTable[nextCommutationStep];

  // Wait for ADC to complete
  while (!(ADCSRA & (1 << ADIF)))
  {

  }
  ADCSRA &= ~(1 << ADIE);
  ADCSRA |= (1 << ADSC) | (1 << ADATE);

  // Rotate commutation step counter.
  nextCommutationStep++;
  if (nextCommutationStep >= 6)
  {
    nextCommutationStep = 0;
  }
  nextDrivePattern = driveTable[nextCommutationStep];
}


/* \brief ADC complete interrupt service routine, used for current measurements.
 *
 *  This interrupt service routine is only enabled when current measurements are
 *  auto-triggered by the PWM counter overflow. The measured value is simply
 *  copied to \ref shuntVoltageADC, the \ref currentUpdated flag is set and
 *  Timer0 (PWM timer) interrupt flags are cleared.
 */
#pragma vector=ADC_vect
__interrupt void CurrentMeasurementComplete()
{
  shuntVoltageADC = ADCH;
  currentUpdated = TRUE;
  CLEAR_ALL_TIMER0_INT_FLAGS;
}


/*! \brief Watchdog interrupt
 *
 *  This ISR is called before the watchdog timer resets the device because of a stall.
 *  It simply disables driving, but other tasks that must be done before a watchdog reset,
 *  such as storage of variables in non-volatile memory can be done here.
 */
#pragma vector=WDT_vect
__interrupt void WatchdogISR()
{
  DISABLE_DRIVING;
  for(;;)
  {
    ;
  }
}

/*! \brief Overcurrent interrupt
 *
 *  This interrupt service routine cuts power to the motor when an overcurrent situation
 *  is detected.
 */
#ifdef ANALOG_COMPARATOR_ENABLE
#pragma vector=ANA_COMP_vect
__interrupt void OverCurrentISR()
{
  DISABLE_DRIVING;
  for(;;)
  {
    ;
  }
}
#endif


/*! \brief Generates a delay used during startup
 *
 *  This functions is used to generate a delay during the startup procedure.
 *  The length of the delay equals delay * STARTUP_DELAY_MULTIPLIER microseconds.
 *  Since Timer/Counter1 is used in this function, it must never be called when
 *  sensorless operation is running.
 */
void StartupDelay(unsigned int delay)
{
  CLEAR_ALL_TIMER1_INT_FLAGS;
  do
  {
    TCNT1 = 0xffff - STARTUP_DELAY_MULTIPLIER;
    // Wait for timer to overflow.
    while (!(TIFR1 & (1 << TOV1)))
    {

    }

    CLEAR_ALL_TIMER1_INT_FLAGS;
    delay--;
  } while (delay);
}



#ifdef SPEED_CONTROL_CLOSED_LOOP
/*! \brief Controls the PWM duty cycle based on speed set-point and current consumption.
 *
 *  This function controls the PWM duty cycle by calling a speed controller and a
 *  current controller. The speed controller signal is directly applied to the duty
 *  cycle. The current controller signal is used to limit the maximum duty cycle.
 */
static void PWMControl(void)
{
  signed int speedCompensation;
  static unsigned char currentCompensation = 0;
  static signed int duty = STARTUP_PWM_COMPARE_VALUE;

  // Run speed control only if a new speed measurement is available.
 if (speedUpdated)
  {
    speedCompensation = SpeedControl();
    speedUpdated = FALSE;
    duty += speedCompensation;
  }

  // Run current control only if a new current measurement is available.
  if (currentUpdated)
  {
     currentCompensation = CurrentControl();
     currentUpdated = FALSE;
  }

 // Keep duty cycle within limits.
  if (duty < MIN_PWM_COMPARE_VALUE)
  {
    duty = MIN_PWM_COMPARE_VALUE;
  }
  if (duty > (MAX_PWM_COMPARE_VALUE - currentCompensation))
  {
    duty = MAX_PWM_COMPARE_VALUE - currentCompensation;
  }

  SET_PWM_COMPARE_VALUE((unsigned char)duty);
}
#endif

#ifdef SPEED_CONTROL_OPEN_LOOP
static void PWMControl(void)
{
  // Only update duty cycle if a new speed reference measurement has been made. (Done right after speed measurement is ready)
  if (speedUpdated)
  {
    // Calculate duty cycle from speed reference value.
    SET_PWM_COMPARE_VALUE(MIN_PWM_COMPARE_VALUE + speedReferenceADC * (MAX_PWM_COMPARE_VALUE - MIN_PWM_COMPARE_VALUE) / ADC_RESOLUTION);
  }
}
#endif


/*! \brief Calculates the current speed in electrical RPM.
 *
 *  This function calculates the current speed in electrical rotations per
 *  minute from the global variable \ref filteredTimeSinceCommutation.
 */
static unsigned long CalculateSpeed()
{
  // Copy used to minimize period where interrupts are disabled.
  unsigned int filteredTimeSinceCommutationCopy;
  unsigned long rotationPeriod;
  unsigned long speed;

  /*
  Disable interrupts to ensure that \ref filteredTimeSinceCommutation is accessed in
  an atomic operation.
  */
  __disable_interrupt();
  filteredTimeSinceCommutationCopy = filteredTimeSinceCommutation;
  __enable_interrupt();

  /*
  filteredTimeSinceCommutation is one half commutation time. Must be multiplied by 12 to get
  one full rotation.
  */
  rotationPeriod = (unsigned long)filteredTimeSinceCommutationCopy * 12;
  speed = (TICKS_PER_MINUTE / rotationPeriod);

  return speed;
}


/*! \brief Calculates the speed set-point in electrical RPM.
 *
 *  This function calculates the speed set-point from the global variable
 *  speedReferenceADC.
 *
 *  In this implementation, the speed reference values from 0x00 to 0xff are
 *  linearly mapped into the allowable speed range, set by \ref MIN_SPEED and
 *  \ref MAX_SPEED.
 */
static unsigned long CalculateSpeedSetpoint()
{
  return (MIN_SPEED + ((MAX_SPEED - MIN_SPEED) * (unsigned int)speedReferenceADC) / ADC_RESOLUTION);
}


/*! \brief Calculates current consumption.
 *
 *  This function calculates the current consumption in milliAmperes from the
 *  global variable \ref shuntVoltageADC. The external know reference voltage
 *  is used to compensate for varying AREF voltage.
 */
static unsigned int CalculateCurrent()
{
  unsigned long ADCref;
  unsigned int current;

  // Calculate the voltage at AREF pin (scaled down motor supply voltage),
  // using the known reference voltage. (In milliVolts)
  ADCref = EXTERNAL_REF_VOLTAGE * 256UL / referenceVoltageADC;

  // Calculate the current through the shunt. (In milliAmperes)
  current = (unsigned int)((shuntVoltageADC * ADCref * 1000UL / 256UL) / SHUNT_RESISTANCE);

  return current;
}


/*! \brief Speed control loop
 *
 *  This function runs a simple P-regulator speed control loop. The duty cycle
 *  is only updated each time a new speed measurement is ready (on each zero-crossing).
 *  The time step is thus variable, so the P-gain of the P-regulator corresponds to
 *  a speed-varying P-gain for the continous system.
 */
static signed int SpeedControl(void)
{
  unsigned long speedSetpoint;
  unsigned long currentSpeed;
  signed long speedError;
  signed long dutyChange;



  speedSetpoint = CalculateSpeedSetpoint();
  currentSpeed = CalculateSpeed();
  speedError = (speedSetpoint - currentSpeed);
  dutyChange = speedError * P_REG_K_P / P_REG_SCALING;

  return dutyChange;
}


/*! \brief Current control loop
 *
 *  This function is called after the speed control loop. The desired duty cycle
 *  calculated by the speed control loop is available, and this function is
 *  responsible for adjusting the duty cycle to ensure that the current stays
 *  within limits.
 */
static unsigned char CurrentControl(void)
{
  unsigned int current;
  unsigned int overCurrentCorrection = 0;

  current = CalculateCurrent();

  // Cut power to motor if current is critically high.
  if (current > CURRENT_LIMITER_CRITICAL)
  {
    DRIVE_PORT = 0x00;
    for (;;)
    {
      // Stop and let watchdog timer reset part.
    }
  }

  if (current > CURRENT_LIMITER_START)
  {
    overCurrentCorrection = (current - CURRENT_LIMITER_START) * CURRENT_LIMITER_FACTOR;
  }

  if (overCurrentCorrection > 255)
  {
    return 255;
  }

  return overCurrentCorrection;
}

/*! \mainpage
 * \section Intro Introduction
 * This documents data structures, functions, variables, defines, enums, and
 * typedefs in the software for application note AVR444.
 *
 * \section CI Compilation Info
 * This software was written for the IAR Embedded Workbench 4.11A.
 *
 * To make project:
 * <ol>
 * <li> Add the file main.c to project.
 * <li> Under processor configuration, select device ATmega48, ATmega88
 * or ATmega168
 * <li> Enable bit definitions in I/O include files
 * <li> Under "C/C++ compiler->Code->Register utilization", select 5
 * registers (R11..R15) locked for global variables.
 * <li> High optimization on speed is recommended for best performance
 * </ol>
 *
 * \section DI Device Info
 * The included source code is written for ATmega48/88/168.
 */


⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -