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

📄 bldc.c

📁 基于ATMEGA48单片机的无刷电机控制程序
💻 C
字号:
/* This file has been prepared for Doxygen automatic documentation generation.*/
/*! \file *********************************************************************
 *
 * \brief A brief description of the file is written here.
 *
 * A more detailed description of the file should written here.
 *
 * \par Application note:
 * AVR443: Sensorbased control of three-phase BLDC motor
 * \par
 * For comprehensive code documentation, supported compilers, compiler settings
 * and supported devices see readme.html\n
* Target device: ATmega48/88/168
 *
 * \author               Atmel Corporation: http://www.atmel.com \n
 *                       Support email: avr@atmel.com
 *
 * $Name$
 * $Revision: 2441 $
 * $RCSfile$
 * $Date: 2007-09-18 07:56:18 +0200 (ti, 18 sep 2007) $  \n
 ******************************************************************************/
#include "BLDC.h"
#include <avr/io.h>
#include <avr/signal.h>
#include <avr/interrupt.h>


/*! \brief CCW rotation patterns.
 *
 * Configuration of pin drive levels
 * and timer COM bits in different
 * phases for CounterClockWise rotation.
 */
unsigned char drvPatternsCCW[] = {
  0,    //Stop
  // MC_PORT drive config
  PDP2_CCW, //Phase2
  PDP4_CCW, //Phase4
  PDP3_CCW, //Phase3
  PDP6_CCW, //Phase6
  PDP1_CCW, //Phase1
  PDP5_CCW, //Phase5
  // Configuration of Output Compare operation for timer 0
  COM0P2_CCW, //Phase2
  COM0P4_CCW, //Phase4
  COM0P3_CCW, //Phase3
  COM0P6_CCW, //Phase6
  COM0P1_CCW, //Phase1
  COM0P5_CCW, //Phase5
  // Configuration of Output Compare operation for timer 2
  COM2P2_CCW, //Phase2
  COM2P4_CCW, //Phase4
  COM2P3_CCW, //Phase3
  COM2P6_CCW, //Phase6
  COM2P1_CCW, //Phase1
  COM2P5_CCW  //Phase5
};



/*! \brief CW rotation patterns.
 *
 * Configuration of pin drive levels
 * and timer COM bits in different
 * phases for ClockWise rotation.
 */
unsigned char drvPatternsCW[] = {
  0,    //Stop
  // MC_PORT drive config
  PDP2_CW, //Phase2
  PDP6_CW, //Phase6
  PDP1_CW, //Phase1
  PDP4_CW, //Phase4
  PDP3_CW, //Phase3
  PDP5_CW,  //Phase5
  // Configuration of Output Compare operation for timer 0
  COM0P2_CW, //Phase2
  COM0P6_CW, //Phase6
  COM0P1_CW, //Phase1
  COM0P4_CW, //Phase4
  COM0P3_CW, //Phase3
  COM0P5_CW,  //Phase5
  // Configuration of Output Compare operation for timer 2
  COM2P2_CW, //Phase2
  COM2P6_CW, //Phase6
  COM2P1_CW, //Phase1
  COM2P4_CW, //Phase4
  COM2P3_CW, //Phase3
  COM2P5_CW  //Phase5
};



//! Stores the current motor driver pattern.
__regvar __no_init unsigned char *pDrvPattern @14;
//__regvar __no_init unsigned char *pComPattern @12;



//! Used for optimized temporary varables.
__regvar __no_init union _fastTemp{
  unsigned int word;
  struct{
    unsigned char LByte;
    unsigned char HByte;  //Hbyte = Zero
  };
} fastTemp @12;

__regvar __no_init unsigned char hallMask @11; //!< Workaround for internal compiler error
__regvar __no_init unsigned char count @10; //!< Optimized variable decremented every pin change int.



/*! \brief  Pin Change Interrupt Service Routine.
 *
 * Updates the PWM outputs controlling the low side of the driver and
 * the IO controlling the high side of the driver. To ensure a speed
 * optimal interrupt the variables used in the interrupt are placed
 * in reserved registers (locked for this purpose only). Further, the
 * information required to do the commutation is placed in tables that
 * are accessed very efficiently using the Hall sensor input signals
 * as offset.
 *
 *  \param void
 *
 *  \return void
 */
#pragma vector = PCINT0_vect
//!
__interrupt void PCINT0_ISR( void )
{
  unsigned char *pTemp;
  fastTemp.word = ((PIN_HALL & hallMask)>>1);  // Read Hall, Mask Pins, shift to use as pointer offset
//  Line below is desirable performance wise, but causes an internal error in compiler
//  fastTemp.LByte = (PIN_HALL & HALL_MASK)>>1;   // Read Hall, Mask Pins, shift to use as pointer offset

  pTemp = pDrvPattern + fastTemp.word;
//  TCCR0A = fastTemp.HByte; //Disable PWM outputs (and thereby close low side FET)
//  TCCR2A = fastTemp.HByte; //Disable PWM output (and thereby close low side FET)

  PORT_MC = *(pTemp);    //Change drive levels on high side

  TCCR0A = *(pTemp + PATTERN_COM0_OFFSET);    // Reconfigure output compare operation for T0
  TCCR2A = *(pTemp + PATTERN_COM2_OFFSET); // Reconfigure output compare operation for T2
  count--;
}



/*! \brief  Initialize pin change interrupts for PORTB pin 1, 2 and 3.
 *
 * Sets up the pins used to sense the Hall sensor signals to generate
 * interrupt if the pin level changes (both rising and falling edge).
 *
 *  \param void
 *
 *  \return void
 */
static void Init_MC_pin_change_interrupt( void )
{
  PCMSK0 = (1<<PCINT1)|(1<<PCINT2)|(1<<PCINT3); //Enable pin change interrupt on PB1:3
  PCICR = 1<<PCIE0;    // Enable pin change interrupt0 (PORTB)
}



/*! \brief  Start an AD convertion and return result.
 *
 * Starts an AD conversion on the specified ADC channel and returns
 * the result when the conversin is completed. Uses polling to wait for
 * the AD conversion to complete.
 *
 *  \param channel Specify the ADMUX register settings to access the correct channel.
 *
 *  \return adcResult 8-bit result (high byte of AD convertion).
 */
unsigned char Get_ADC8(unsigned char muxSetting)
{
    ADMUX = muxSetting;
    // Start AD conversion.
    ADCSRA |= (1 << ADSC);
    // Wait for ADC conversion to complete.
    while ( ADCSRA & (1 << ADSC) );
    return ADCH;
}


//!
/*! \brief  Initialize motor control timers.
 *
 * Initialize the Timer 1 and timer 2 to run in phase and frequency correct
 * PWM mode (symmetric PWM). The base frequency is set to 32kHz (can be
 * reduced at the expense of lower resolution on the speed control). The
 * functions also ensures that the timers are counting in synch.
 *
 *  \param void
 *
 *  \return void
 */
static void Init_MC_timers( void )
{
  //Timer Counter 0. OCRA and OCRB used for motor
  TCCR0A = (1<<COM0A1)|(0<<COM0A0)|        // Clear OCRA on compare match
           (1<<COM0B1)|(0<<COM0B0)|        // Clear OCRB on compare match
           (1<<WGM01)|(1<<WGM00);         // Fast PWM mode
  TCCR0B = (0<<FOC0A)|(0<<FOC0B)|
           (0<<WGM02)|                     // Fast PWM mode
           (0<<CS02)|(0<<CS01)|(1<<CS00); // Prescaler = CLK/1

  //Timer Counter 2. OCRA and OCRB used for motor
  TCCR2A = (0<<COM2A1)|(0<<COM2A0)|        // OCRA not connected
           (1<<COM2B1)|(0<<COM2B0)|        // Clear OCRB on compare match
           (1<<WGM01)|(1<<WGM00);         // Fast PWM mode
  TCCR2B = (0<<FOC2A)|(0<<FOC2B)|
           (0<<WGM22)|                     // Fast PWM mode
           (0<<CS22)|(0<<CS21)|(1<<CS20); // Prescaler = CLK/1

  // Synchronize timers
  TCNT0 = 0;
  TCNT2 = 3;

  TIFR0 = TIFR0;    // Clear TC0 interrupt flags
  TIFR1 = TIFR1;    // Clear TC2 interrupt flags
}



/*! \brief Initialize ADC module.
 *
 * Sets up the ADC with prescaler value 4, which means a maximum sample speed
 * of CPU frequency divided by 52 (13*4). With the ADC measuring the speed set
 * point and shunt voltage, this gives a reaction time of two samples for
 * detecting over-current.
 *
 *  \param void
 *
 *  \return void
 */
static void Init_ADC( void )
{
  ADCSRA = (1 << ADEN) | (1 << ADPS1); // Enable ADC, clock prescaler = 4
}



/*! \brief Set motor speed.
 *
 * Updates the output compare registers of the timer 0 and timer 2 which
 * control the duty cycle of the PWM output and thereby the speed of the
 * motor. The method used ensures that that all PWM channels are behaving
 * same duty cycle.
 *
 *  \param speed Compare match value that defines PWM duty cycle.
 *
 *  \return void
 */
static void Set_Speed(unsigned char speed)
{
  TIFR0 = TIFR0;    // Clear TC0 interrupt flags
  while( !(TIFR0 & (1<<TOV0)));  // Wait for TOV to ensure that all registers are
                            //  updated in the same timer cycle
  __disable_interrupt();
  OCR0A = speed;        // Change the duty cycle
  OCR0B = speed;
  OCR2B = speed;
  __enable_interrupt();
}



/*! \brief Set motor direction, CW og CCW.
 *
 * Set the commutation table pointer up to point at either the clockwise
 * or counter clockwise direction table. Note that it is not recommended
 * to change direction without first reducing the speed of the motor,
 * preferably stopping it fully.
 *
 *  \param direction Direction is given as Clockwise or Counter Clockwise.
 *
 *  \return void
 */
static void Set_Direction(unsigned char direction)
{
  if(direction == CLOCKWISE)
  {
    __disable_interrupt();        //Variable also used in interrupt and access most be protected
    pDrvPattern = drvPatternsCW;   // Set dir to CW, by pointing to CW pattern
    __enable_interrupt();
  }
  else
  {
    __disable_interrupt();        //Variable also used in interrupt and access most be protected
    pDrvPattern = drvPatternsCCW;   // Set dir to CCW, by pointing to CCW pattern
    __enable_interrupt();
  }
}


/*! \brief Main function for motor control example.
 *
 * Initialize speed variables to zero speed, and enabled operation in clockwise
 * direction. Hence a speed reference input is read from ADC_MUX_SPEED_REF. If
 * Current exeeds the MAX_CURRENT_ADC limit the speed (PWM duty cycle) is reduced.
 *
 *  \param void
 *
 *  \return void
 */
void main( void )
{
  unsigned char speed = 0;
  unsigned char setspeed = 0;
  signed int current;
  MCUCR |= (1<<PUD);  // Disable all pull-ups
  hallMask = HALL_MASK; // Initialize hallMask variable
  //Set initial direction.
  Set_Direction( CLOCKWISE );

  Init_MC_timers();
  Init_MC_pin_change_interrupt();
  Init_ADC();

  DDR_HALL |= HALL_MASK;    //Lock HALL sensor by driving Hall lines
  PORT_HALL |= HALL_MASK;
  PORT_HALL &= ~HALL_MASK;  //Release HALL sensor lines and trigger PC interrupt
  DDR_HALL &= ~HALL_MASK;
  __enable_interrupt();
  Set_Speed(speed);
  DDR_MC = MC_MASK;        // Enable outputs

  DDRC |= (1<<PC1);
  for(;;) {
    // Get shunt voltage (current measurement)
    current = Get_ADC8(ADC_MUX_SHUNT_H);
    // If current consumption is too high, limit current
    if (current > MAX_CURRENT_ADC )
    {
      PORTC &= ~(1<<OVERCURRENT_PIN);   //Turn on over-current LED (active low)
      if( speed >= 2 )
      {
        speed -= 2; // Slow down if too fast.
      }
    }
    else
    {
      PORTC |= (1<<OVERCURRENT_PIN); // Turn off over-current LED (active low)
      // Get speed reference voltage (Assumes 2.5V to be maximum analog input,
      // multiplied by 2 to convert to PWM range).
      setspeed = Get_ADC8(ADC_MUX_SPEED_REF)*2;
      // Approach speed set point.
      if( setspeed > speed )
      {
        ++speed;
      }
      else
      {
        if( setspeed < speed )
        {
          --speed;
        }
      }
    }

    Set_Speed(speed);
  }
}

⌨️ 快捷键说明

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