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

📄 main.c

📁 Atmel算法(pid)-步进电机驱动源码 HOWTO,不好用你找我,绝对ok!
💻 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:
 *      AVR447: Sinusoidal driving of three-phase permanent motor using
 *      ATmega48/88/168
 *
 * \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.8 $
 * $RCSfile: main.c,v $
 * $Date: 2006/03/27 07:20:51 $  \n
 ******************************************************************************/

#include <ioavr.h>
#include <inavr.h>
#include "stdint.h"
#include "PMSM.h"
#include "PMSMtables.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.
 */
__io volatile PMSMflags_t fastFlags                               @0x1e;


/*! \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-255.
 */
__no_init __regvar volatile uint8_t amplitude                     @9;


/*! \brief The lead angle or advance commutation angle
 *
 *  This variable specifies a shift in the sine table that will
 *  generate advance commutaiton. 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              @8;


/*! \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     @7;


/*! \brief Current measurement
 *
 *  The most recent current measurement is stored in this variable. It is not
 *  used for any purpose in this implementation, but the measurement is
 *  updated.
 */
volatile uint8_t current;


//! The most recent speed input measurement.
volatile uint8_t speedInput;

/*! \brief Speed controller run flag.
 *
 *  This variable is set to TRUE every time the speed controller should be run.
 */
volatile uint8_t SpeedControllerRun = FALSE;


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


/*! \brief Main function / initialization
 *
 *  The main function initializes all subsystems needed for motor control
 *  and enables interrupts, which kicks off the fully interrupt-driven
 *  motor control. The main function goes into an eternal loop where it
 *  does nothing.
 */
void main(void)
{
  //Initialize peripherals.
  PortsInit();
  TimersInit();
  PinChangeIntInit();
  ADCInit();

  SetAdvanceCommutation(0);

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

  //Do not start until everything is ready.
  CheckEmergencyShutdown();

  //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 Timer1 capture event interrupt.
  TIMSK1 |= (1 << ICIE1);

  //Enable interrupts globally and let motor driver take over.
  __enable_interrupt();
  for(;;)
  {
    if (SpeedControllerRun)
    {
      SpeedController();
      SpeedControllerRun = FALSE;
    }
  }
}


/*! \brief Initializes I/O port directions and pull-up resistors
 *
 *  This function initializes all I/O ports with correct
 *  direction and pull-up resistors, if needed.
 */
static void PortsInit(void)
{
#if (HALL_PULLUP_ENABLE == TRUE)
  // Set hall sensor pins as input, pullups enabled.
  PORTC = (1 << H1_PIN) | (1 << H2_PIN) | (1 << H3_PIN);
#endif

  // PORTD outputs
  DDRD = (1 << REV_ROTATION_PIN) | (1 << TACHO_OUTPUT_PIN);

  //Enable pull-up on direction signal.
  PORTD |= (1 << DIRECTION_COMMAND_PIN);
}


/*! \brief Initializes and synchronizes Timers
 *
 *  This function sets the correct prescaler and starts all
 *  three timers. The timers are synchronized to ensure that
 *  all PWM signals are aligned.
 */
static void TimersInit(void)
{
  //Set all timers in "Phase correct mode". Do not enable outputs yet.
  TCCR0A = (1 << WGM00);
  TCCR1A = (1 << WGM11);
  TCCR2A = (1 << WGM20);

  //Set top value of Timer/counter1.
  ICR1 = 0xff;

  //Synchronize timers
  TCNT0 = 0;
  TCNT1 = 2;
  TCNT2 = 4;

  // Start all 3 timers.
  TCCR0B = (0 << CS01) | (1 << CS00);
  TCCR1B = (1 << WGM13) | (0 << CS11) | (1 << CS10);
  TCCR2B = (0 << CS21) | (1 << CS20);
}


/*! \brief Initialize pin change interrupts.
 *
 *  This function initializes pin change interrupt on hall
 *  sensor input pins input, emergency shutdown
 *  input and motor direction control input.
 */
static void PinChangeIntInit(void)
{
  // Initialize pin change interrupt on emergency shutdown pin.
  PCMSK0 = (1 << PCINT5);

  // Initialize pin change interrupt on hall sensor inputs.
  PCMSK1 = (1 << PCINT10) | (1 << PCINT9) | (1 << PCINT8);

  // Initialize pin change interrupt on direction input pin.
  PCMSK2 = (1 << PCINT18);

  // Enable pin change interrupt on ports with pin change signals
  PCICR = (1 << PCIE2) | (1 << PCIE1) | (1 << PCIE0);
}


/*! \brief Initializes the ADC
 *
 *  This function initializes the ADC for speed reference measurements.
 *  The ADC will operate in free-running mode, and reading is done in the
 *  ADC complete interrupt service routine.
 */
static void ADCInit(void)
{
  //Select initial AD conversion channel.
  ADMUX = ADMUX_SPEED_REF;

  //Initialize ADC.
  ADCSRA = (1 << ADEN) | (1 << ADSC) | (1 << ADATE) | (1 << ADIF) | (1 << ADIE) | ADC_PRESCALER;

  //Set trigger source to Timer/Counter0 overflow.
  ADCSRB = (1 << ADTS2) | (0 << ADTS1) | (0 << ADTS0);
}


/*! \brief Checks whether emergency shutdown is set at startup.
 *
 *  This function checks whether emergency shut-off signal is
 *  set at startup and takes action accordingly. This function is only needed
 *  at startup, since pin change interrupts are enabled for these signals.
 *
 *  \note In this version, nothing is done in these situations.
 *
 *  \todo Write code to handle a situation where either emergency shut-off
 *  set at startup.
 */
static void CheckEmergencyShutdown(void)
{
  if ( (PINB & (1 << EMERGENCY_SHUTDOWN_PIN)) != 0)
  {
    //Insert code here to handle emercency shutdown signal at startup.
  }
}


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

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

  //PID regulator with feed forward from speed input.
  int32_t outputValue;
  outputValue = (uint32_t)speedInput;
  outputValue += PID_Controller(incrementSetpoint, (int16_t)sineTableIncrement, &pidParameters);

  if (outputValue < 0)
  {
    outputValue = 0;
  }
  else if (outputValue > 255)
  {
    outputValue = 255;
  }

  amplitude = outputValue;
#else
  amplitude = speedInput;
#endif
}


/*! \brief Configures timers for sine wave generation
 *
 *  This function is called every time sine wave generation is
 *  needed. PWM outputs are safely disabled while configuration
 *  registers are changed to avoid unintended driving or shoot-
 *  through.
 */
#pragma inline=forced
static void TimersSetModeSinusoidal(void)
{
  //Set PWM pins to input (Hi-Z) while changing modes.
  DisablePWMOutputs();

  //Sets all 3 timers in inverted pair mode.
  TCCR0A = (1 << COM0A1) | (0 << COM0A0) | (1 << COM0B1) | (1 << COM0B0) | (1 << WGM00);
  TCCR1A = (1 << COM1A1) | (0 << COM1A0) | (1 << COM1B1) | (1 << COM1B0) | (1 << WGM11);
  TCCR2A = (1 << COM2A1) | (0 << COM2A0) | (1 << COM2B1) | (1 << COM2B0) | (1 << WGM20);

  //Make sure all outputs are turned off before PWM outputs are enabled.
  OCR0A = OCR1AL = OCR2A = 0;
  OCR0B = OCR1BL = OCR2B = 0xff;

  //Wait for next PWM cycle to ensure that all outputs are updated.
  TimersWaitForNextPWMCycle();

  fastFlags.driveWaveform = WAVEFORM_SINUSOIDAL;

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


/*! \brief Configures timers for block commutation.
 *
 *  This function is called every time block commutation is
 *  needed. PWM outputs are safely disabled while configuration
 *  registers are changed to avoid unintended driving or shoot-
 *  through.
 */
#pragma inline=forced
static void TimersSetModeBlockCommutation(void)
{
  //Set PWM pins to input (Hi-Z) while changing modes.
  DisablePWMOutputs();

  //Sets both outputs of all 3 timers in "clear on up-counting, set on down-counting" mode.
  TCCR0A = (1 << COM0A1) | (0 << COM0A0) | (1 << COM0B1) | (0 << COM0B0) | (1 << WGM00);
  TCCR1A = (1 << COM1A1) | (0 << COM1A0) | (1 << COM1B1) | (0 << COM1B0) | (1 << WGM11);
  TCCR2A = (1 << COM2A1) | (0 << COM2A0) | (1 << COM2B1) | (0 << COM2B0) | (1 << WGM20);

⌨️ 快捷键说明

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