📄 main.c
字号:
/* 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 + -