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

📄 main.c

📁 ATtiny261 461 861 这份资料介绍了执行Attiny261 461 861微控制器系列正弦波驱动三相无刷直流电动机霍尔传感器。
💻 C
📖 第 1 页 / 共 3 页
字号:
/* This file has been prepared for Doxygen automatic documentation generation.*/
/*! \file *********************************************************************
 *
 * \brief
 *      Motor control implementation.
 *
 *      This file contains the full implementation of the motor control,
 *      except the PID-controller.
 *
 * \par Application note:
 *      AVR449: Sinusoidal driving of three-phase permanent motor using
 *      ATtiny261/461/861.
 *
 * \par Documentation
 *      For comprehensive code documentation, supported compilers, compiler
 *      settings and supported devices see readme.html
 *
 * \author
 *      Atmel Corporation: http://www.atmel.com \n
 *      Support email: avr@atmel.com
 *
 * $Name: RELEASE_1_0 $
 * $Revision: 1.5 $
 * $RCSfile: main.c,v $
 * $Date: 2006/05/18 09:15:31 $  \n
 ******************************************************************************/

#include <ioavr.h>
#include <inavr.h>
#include "stdint.h"
#include "PMSM.h"
#include "PMSM_tables.h"
#include "TinyX61_macros.h"


#if (SPEED_CONTROL_METHOD == SPEED_CONTROL_CLOSED_LOOP)
#include "pid.h"
#endif


/*! \brief Motor control flags placed in I/O space for fast access.
 *
 *  This variable contains all the flags used for motor control.
 *  It is placed in GPIOR0 register, which allows usage of several
 *  fast bit manipulation/branch instructions.
 */
volatile __io PMSMflags_t fastFlags                         @0x0a;


/*! \brief Increment used for sine table iteration.
 *
 *  This variable holds the increment used for sine table iteration.
 *  It is stored in an 8.8 fixed point format.
 */
__no_init __regvar volatile uint16_t sineTableIncrement     @14;


/*! \brief Index into sine table
 *
 *  This variable is used as an index into the sine table. It is
 *  stored in a 8.8 fixed point format, so it can be directly incremented by
 *  sineTableIncrement. Only the high byte is used as table index.
 */
__no_init __regvar volatile uint16_t sineTableIndex         @12;


/*! \brief The number of 'ticks' between two hall sensor changes.
 *
 *  This variable is used to count the number of 'ticks' between each hall
 *  sensor change. One 'tick' is one PWM period, 510 CPU clock cycles. This is
 *  used to calculate sine table increment. The speed of the motor is inversely
 *  proportional to this value.
 */
__no_init __regvar volatile uint16_t commutationTicks       @10;


/*! \brief The amplitude of the generated sine waves.
 *
 *  This variable controls the amplitude of the generated sine waves.
 *  The range is 0-0x03ff.
 */
__no_init __regvar volatile uint16_t amplitude               @8;


/*! \brief The advance commutation angle
 *
 *  This variable specifies a shift in the sine table that will
 *  generate a lead angle. For a 192 element sine table, an increase
 *  of one in advanceCommutationSteps will result in 1.875 degrees lead
 *  angle.
 */
__no_init __regvar volatile uint8_t advanceCommutationSteps      @7;


/*! \brief Index to the end of the current commutation sector
 *
 *  This variable holds the index where the next commutation sector starts,
 *  including the advance commutation angle.
 *
 *  It is used to prevent the output from going further until the hall sensors
 *  changes.
 */
__no_init __regvar volatile uint8_t sineTableNextSectorStart     @6;


/*! \brief Time in ticks until the speed controller loop should be executed
 *
 *  This variable holds the number of ticks until the speed controller should
 *  be executed.
 */
__no_init __regvar volatile uint8_t speedControllerTimer @5;


#if (SPEED_CONTROL_METHOD == SPEED_CONTROL_CLOSED_LOOP)
//! Struct used to hold PID controller parameters and variables.
pidData_t pidParameters;
#endif


void main(void)
{
  PortsInit();
  PLLInit();
  PWMInit();
  PinChangeInit();
  ADCInit();

#if (SPEED_CONTROL_METHOD == SPEED_CONTROL_CLOSED_LOOP)
  pid_Init(PID_K_P, PID_K_I, PID_K_D, &pidParameters);
#endif

  speedControllerTimer= SPEED_CONTROLLER_TIME_BASE;
  SetAdvanceCommutation(0);

  //Initialize fastflags. Use temporary variable to avoid several accesses to
  //volatile variable.
  {
    PMSMflags_t fastFlagsInitial;

    // Set motorStopped to FALSE at startup. This will make sure that the motor
    // is not started if it is not really stopped. If it is stopped, this variable
    // will quickly be updated.
    fastFlagsInitial.motorStopped = FALSE;

    // Set motorSyncronized to FALSE at startup. This will prevent the motor from being
    // driven until the motor is in synch or stopped.
    fastFlagsInitial.motorSynchronized = FALSE;
    fastFlagsInitial.actualDirection = DIRECTION_UNKNOWN;
    fastFlagsInitial.desiredDirection = 0;
    fastFlagsInitial.driveWaveform = WAVEFORM_UNDEFINED;

    fastFlags = fastFlagsInitial;
  }

  DesiredDirectionUpdate();

  //Enable Timer/counter1 overflow.
  TIMSK |= (1 << TOV1);

  //Enable interrupts globally and let motor driver take over.
  __enable_interrupt();

  for (;;)
  {
    uint16_t speedReference;

    if ( !(ADCSRA & (1 << ADSC)) )
    {
      speedReference = ADC;
      ADCSRA |= (1 << ADSC);
    }

    if (speedControllerTimer == 0)
    {
      speedControllerTimer = SPEED_CONTROLLER_TIME_BASE;
      SpeedController(speedReference);
    }
  }
}



/*! \brief Initialize port pin directions and internal pull-ups
 *
 *  Initializes port pin directions and internal pull-ups if needed.
 */
static void PortsInit(void)
{
#if (HALL_PULL_UP_ENABLE)
  PORTA = (1 << H1_PIN) | (1 << H2_PIN) | (1 << H3_PIN);
#endif

  DDRA = (1 << PA4);
}


/*! \brief Initialize PLL.
 *
 *  Waits for PLL lock and enables the PLL.
 *  This function does not enable the PLL clock. This should be done by setting
 *  the CKSEL fuses to 0001. Otherwise, the application will hang here forever.
 */
static void PLLInit(void)
{
  //Wait for PLL lock
  while ( !(PLLCSR & (1 << PLOCK)) )
  {

  }
  PLLCSR = (1 << PCKE);
}


/*! \brief Initialize PWM on Timer/counter1
 *
 *  Initializes PWM on Timer/counter1.
 */
static void PWMInit(void)
{
  //Start TCNT1 with prescaler 1.
  TCCR1B = (PWM_INVERT_OUTPUT << PWM1X) | (1 << CS10);

  //Enable fault protection input on INT0 pin, falling edge.
  TCCR1D = (1 << FPIE1) | (1 << FPEN1) | (0 << FPES1);

  //Set dead-time.
  DT1 = (DEAD_TIME << 4) | (DEAD_TIME);

  //Set PWM top value.
  TC1_WRITE_10_BIT_REGISTER(OCR1C, PWM_TOP_VALUE);
}


/*! \brief Sets up the ADC for speed reference input on PA7
 *
 *  This function initializes the ADC for speed reference measurements.
 *  The ADC will operate in single conversion mode.
 */
static void ADCInit(void)
{
  //Use PA7 as ADC input, 2.56V internal voltage reference without bypass capacitor.
  ADMUX = (1 << REFS1) | (0 << MUX4) | (0 << MUX3) | (1 << MUX2) | (1 << MUX1) | (0 << MUX0);

  ADCSRA = (1 << ADEN) |                  //Enable the ADC.
           (1 << ADSC) |                  //Start the first converssion.
           (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0);   // Prescaler CK/128.
  ADCSRB = (1 << REFS2); //2.56V internal reference.
}


/*! \brief Initialize Pin change interrupt for hall sensors.
 *
 *  Initializes the pin change interrupt for hall sensors.
 */
static void PinChangeInit(void)
{
  //Initialize hall change interrupt
  PCMSK1 = (1 << PCINT2) | (1 << PCINT1) | (1 << PCINT0); // Should really be PCMSK0, but the data sheet, header files and AVR studio does not agree.

  //Clear all other pin change interrupts.
  PCMSK0 = 0x00;

  //
  GIMSK |= (1 << PCIE1);

  //Force the pin change interrupt to be triggered.
  DDRA |= ((1 << PA2) | (1 << PA1) | (1 << PA0));
  DDRA &= ~((1 << PA2) | (1 << PA1) | (1 << PA0));
}


/*! \brief  Speed controller loop.
 *
 *  This function is called every SPEED_CONTROLLER_TIME_BASE ticks. In this
 *  implementation, a simple PID controller loop is called, but this function
 *  could be replaced by any speed (or other) controller.
 */
static void SpeedController(uint16_t speedReference)
{
  #if (SPEED_CONTROL_METHOD == SPEED_CONTROL_CLOSED_LOOP)

  //Calculate an increment setpoint from the analog speed input.
  int16_t incrementSetpoint = ((uint32_t)speedReference * SPEED_CONTROLLER_MAX_INCREMENT) / SPEED_CONTROLLER_MAX_INPUT;

  //PID controller with feed forward from speed input.
  int32_t outputValue;
  outputValue = (uint32_t)speedReference;
  outputValue += pid_Controller(incrementSetpoint, (int16_t)sineTableIncrement, &pidParameters);

  if (outputValue < 0)
  {
    outputValue = 0;
  }
  else if (outputValue > PWM_TOP_VALUE)
  {
    outputValue = PWM_TOP_VALUE;
  }
  __disable_interrupt();
  amplitude = outputValue;
  __enable_interrupt();
#else
  __disable_interrupt();
  amplitude = speedReference;
  __enable_interrupt();
#endif
}


/*! \brief Configures Timer/Counter1 in Sinusoidal mode.
 *
 *  This function configures Timer/counter1 in sinusoidal mode in a safe way
 *  way by disabling all output before any configuration bits are changed.
 *  After running this function, Timer/counter1 will be in phase and frequency
 *  correct mode used for sine wave generation.
 */
#pragma inline = forced
static void TimerSetModeSinusoidal(void)
{
  //Set PWM pins to input (Hi-Z) while changing modes.
  DisablePWMOutputs();

  //Set Timer/counter1 in phase and frequency correct mode.
  TCCR1A = (0 << COM1A1) | (1 << COM1A0) | (0 << COM1B1) | (1 << COM1B0) | (1 << PWM1A) | (1 << PWM1B);
  TCCR1C =  (0 << COM1A1S) | (1 << COM1A0S) | (0 << COM1B1S) | (1 << COM1B0S) | (0 << COM1D1) | (1 << COM1D0) | (1 << PWM1D);
  TCCR1D &= ~((1 << WGM11) | (1 << WGM10));
  TCCR1D |= (0 << WGM11) | (1 << WGM10);

  TC1_SET_ALL_COMPARE_VALUES(0x0000);

  fastFlags.driveWaveform = WAVEFORM_SINUSOIDAL;

  //Wait for the PWM cycle to complete.
  TIFR = TIFR;
  while ( ! (TIFR & (1 << TOV1)) )
  {

  }

  //Change PWM pins to output again to allow PWM control.
  EnablePWMOutputs();
}


/*! \brief Configures Timer/counter1 in block commutation mode.
 *
 *  This function configures Timer/counter1 in block commutation mode in a safe
 *  way by disabling all output before any configuration bits are changed.
 *  After running this function, Timer/counter1 will be in dual slop PWM6 mode.
 *  Commutations can be performed by running 'BlockCommutate'.
 *  The duty cycle can be adjusted by calling 'BlockCommutationSetDuty'.
 */
#pragma inline = forced
static void TimerSetModeBlockCommutation(void)
{
  //Set PWM pins to input (Hi-Z) while changing modes.
  DisablePWMOutputs();

  //Set Timer/counter1 in dual slope PWM6 mode, clear on upcounting, set on downcounting (non-inverting).
  TCCR1A = (1 << COM1A1) | (0 << COM1A0) | (1 << COM1B1) | (0 << COM1B0) | (1 << PWM1A) | (1 << PWM1B);
  TCCR1C = (1 << COM1A1S) | (0 << COM1A0S) | (1 << COM1B1S) | (0 << COM1B0S) | (1 << COM1D1) | (0 << COM1D0) | (1 << PWM1D);
  TCCR1D |= (1 << WGM11) | (1 << WGM10);

  TCCR1E = 0x00;

  //Set output duty cycle to 0.
  BlockCommutationSetDuty(0x0000);

  fastFlags.driveWaveform = WAVEFORM_BLOCK_COMMUTATION;

⌨️ 快捷键说明

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