📄 pwm_ctrl.c
字号:
//*****************************************************************************
//
// pwm_ctrl.c - PWM control routines.
//
// Copyright (c) 2007-2008 Luminary Micro, Inc. All rights reserved.
//
// Software License Agreement
//
// Luminary Micro, Inc. (LMI) is supplying this software for use solely and
// exclusively on LMI's microcontroller products.
//
// The software is owned by LMI and/or its suppliers, and is protected under
// applicable copyright laws. All rights are reserved. You may not combine
// this software with "viral" open-source software in order to form a larger
// program. Any use in violation of the foregoing restrictions may subject
// the user to criminal sanctions under applicable laws, as well as to civil
// liability for the breach of the terms and conditions of this license.
//
// THIS SOFTWARE IS PROVIDED "AS IS". NO WARRANTIES, WHETHER EXPRESS, IMPLIED
// OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE.
// LMI SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR
// CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER.
//
// This is part of revision 716 of the BLDC motor application.
//
//*****************************************************************************
#include "../../DriverLib/hw_ints.h"
#include "../../DriverLib/hw_memmap.h"
#include "../../DriverLib/hw_nvic.h"
#include "../../DriverLib/hw_pwm.h"
#include "../../DriverLib/hw_types.h"
#include "../../DriverLib/src/gpio.h"
#include "../../DriverLib/src/interrupt.h"
#include "../../DriverLib/src/pwm.h"
#include "../../DriverLib/src/sysctl.h"
#include "../../DriverLib/src/watchdog.h"
#include "faults.h"
#include "main.h"
#include "pins.h"
#include "pwm_ctrl.h"
#include "ui.h"
//*****************************************************************************
//
//! \page pwm_ctrl_intro Introduction
//!
//! The generated motor drive waveforms are driven to the inverter bridge with
//! the PWM module. The PWM generators are run in a fully synchronous manner;
//! the counters are synchronized (that is, the values of the three counters
//! are always the same) and updates to the duty cycle registers are
//! synchronized to the zero value of the PWM counters.
//!
//! The dead-band unit in each PWM generator is used to prevent shoot-through
//! current in the inverter bridge when switching between the high side to the
//! low of a phase. Shoot-through occurs because the turn-on time of one gate
//! doesn't always match the turn-off time of the other, so both may be on for
//! a short period despite the fact that only one of their inputs is on. By
//! providing a period of time where both inputs are off when making the
//! transition, shoot-through is not possible.
//!
//! The PWM outputs can be in one of four modes during the operation of the
//! motor drive. The first is off, where all six outputs are in the inactive
//! state. This is the state used when the motor drive is stopped; the motor
//! is electrically disconnected during this time (effectively the same as
//! disconnecting the cable) and is free to spin as if it were unplugged.
//!
//! The next mode is precharge, where the three outputs to the high
//! side switches are inactive and the three outputs to the low side switches
//! are switches at a 50% duty cycle. The high side gate drivers have a
//! bootstrap circuit for generating the voltage to drive the gates that only
//! charges when the low side is switching; this precharge mode allows the
//! bootstrap circuit to generate the required gate drive voltage before real
//! waveforms are driven. Failure to precharge the high side gate drivers
//! would simply result in distortion of the first part of the output waveform
//! (until the bootstrap circuit generates a voltage high enough to turn on the
//! high side gate). This mode is used briefly when going from a non-driving
//! state to a driving state.
//!
//! The next mode is running, where all six outputs are actively toggling.
//! This will create a magnetic field in the stator of the motor, inducing a
//! magnetic field in the rotor and causing it to spin. This mode is used to
//! drive the motor.
//!
//! The final mode is DC injection braking, where the first PWM pair are
//! actively toggling, the low side of the second PWM pair is always on, and
//! the third PWM pair is inactive. This results in a fixed DC voltage being
//! applied across the motor, resulting in braking. This mode is optionally
//! used briefly when going from a driving state to a non-driving state in
//! order to completely stop the rotation of the rotor. For loads with high
//! inertia, or low friction rotors, this can reduce the rotor stop time from
//! minutes to seconds. This mode should only be used for as long as required
//! to stop the rotor and no longer.
//!
//! The PWM outputs are configured to immediately switch to the inactive state
//! when the processor is stopped by a debugger. This prevents the current
//! PWM state from becoming a DC voltage (since the processor is no longer
//! running to change the duty cycles) and damaging the motor. In general,
//! though, it is not a good idea to stop the processor when the motor is
//! running. When no longer driven, the motor will start to slow down due to
//! friction; when the processor is restarted, it will continue driving at the
//! previous drive frequency. The difference between rotor and target
//! speed has become much greater due to the time that the motor was not being
//! driven. This will likely result in an immediate motor over-current fault
//! since the increased slip will result in a rise in motor current. While not
//! harmful, it does not allow the typically desired behavior of being able to
//! stop the application, look at the internal state, then restart the
//! application as if nothing had happened.
//!
//! An interrupt is generated at each zero value of the counter in PWM
//! generator zero; this is used as a time base for the generation of waveforms
//! as well as a time to queue the next duty cycle update into the hardware.
//! At any given time, the PWM module is outputting the duty cycle for period
//! N, has the duty cycle for period N+1 queued in its holding registers
//! waiting for the next zero value of the counter, and the microcontroller is
//! computing the duty cycle for period N+2.
//!
//! Two ``software'' interrupts are generated by the PWM interrupt handler.
//! One is used to update the waveform; this occurs at a configurable rate of
//! every X PWM period. The other is used to update the drive frequency and
//! perform other periodic system tasks such as fault monitoring; this occurs
//! every millisecond. The unused interrupts from the second and third PWM
//! generator are used for these ``software'' interrupts; the ability to fake
//! the assertion of an interrupt through the NVIC software interrupt trigger
//! register is used to generate these ``software'' interrupts.
//!
//! The code for handling the PWM module is contained in <tt>pwm_ctrl.c</tt>,
//! with <tt>pwm_ctrl.h</tt> containing the definitions for the variables and
//! functions exported to the remainder of the application.
//
//*****************************************************************************
//*****************************************************************************
//
//! \defgroup pwm_ctrl_api Definitions
//! @{
//
//*****************************************************************************
//*****************************************************************************
//
//! The frequency of the clock that drives the PWM generators.
//
//*****************************************************************************
#define PWM_CLOCK 50000000
//*****************************************************************************
//
//! The width of a single PWM clock, in nanoseconds.
//
//*****************************************************************************
#define PWM_CLOCK_WIDTH 20
//*****************************************************************************
//
//! The number of PWM clocks in a single PWM period.
//
//*****************************************************************************
static unsigned long g_ulPWMClock;
//*****************************************************************************
//
//! The frequency of the output PWM waveforms.
//
//*****************************************************************************
unsigned long g_ulPWMFrequency;
//*****************************************************************************
//
//! The duty cycle of the waveform output to the A phase of the bridge.
//
//*****************************************************************************
static unsigned long g_ulPWMDutyCycleA;
//*****************************************************************************
//
//! The duty cycle of the waveform output to the B phase of the bridge.
//
//*****************************************************************************
static unsigned long g_ulPWMDutyCycleB;
//*****************************************************************************
//
//! The duty cycle of the waveform output to the C phase of the bridge.
//
//*****************************************************************************
static unsigned long g_ulPWMDutyCycleC;
//*****************************************************************************
//
//! The minimum width of an output PWM pulse, in PWM clocks.
//
//*****************************************************************************
static unsigned long g_ulMinPulseWidth;
//*****************************************************************************
//
//! A set of flags that control the operation of the PWM control routines. The
//! flags are #PWM_FLAG_NEW_FREQUENCY, and #PWM_FLAG_NEW_DUTY_CYCLE.
//
//*****************************************************************************
static unsigned long g_ulPWMFlags = 0;
//*****************************************************************************
//
//! The bit number of the flag in #g_ulPWMFlags that indicates that a new
//! PWM frequency (that is, period) is ready to be supplied to the PWM module.
//
//*****************************************************************************
#define PWM_FLAG_NEW_FREQUENCY 0
//*****************************************************************************
//
//! The bit number of the flag in #g_ulPWMFlags that indicates that a new duty
//! cycle (that is, compare) is ready to be supplied to the PWM module.
//
//*****************************************************************************
#define PWM_FLAG_NEW_DUTY_CYCLE 1
//*****************************************************************************
//
//! The bit number of the flag in #g_ulPWMFlags that indicates that a Pre-
//! charge process has been started.
//
//*****************************************************************************
#define PWM_FLAG_NEW_PRECHARGE 2
//*****************************************************************************
//
//! A count of the number of PWM periods have occurred, based on the number of
//! PWM module interrupts. This is incremented when a PWM interrupt is handled
//! and decremented by the waveform generation handler.
//
//*****************************************************************************
static unsigned long g_ulPWMPeriodCount;
//*****************************************************************************
//
//! A counter that is used to determine when a millisecond has passed. The
//! millisecond software interrupt is triggered based on this count.
//
//*****************************************************************************
static unsigned long g_ulPWMMillisecondCount;
//*****************************************************************************
//
//! The duty cycle (0 to 100) used for trapezoid current calculations.
//
//*****************************************************************************
unsigned long g_ulTrapDutyCycle = 0;
//*****************************************************************************
//
//! Computes the minimum PWM pulse width.
//!
//! This function computes the minimum PWM pulse width based on the minimum
//! pulse width parameter and the dead time parameter. The dead timers will
//! reduce the width of a PWM pulse, so their value must be considered to avoid
//! pulses shorter than the parameter value being produced.
//!
//! \return None.
//
//*****************************************************************************
void
PWMSetMinPulseWidth(void)
{
//
// Compute the minimum pulse width in PWM clocks.
//
g_ulMinPulseWidth = ((((g_sParameters.ucDeadTime + 1) * 20) +
(g_sParameters.ucMinPulseWidth * 100) +
(PWM_CLOCK_WIDTH - 1)) / PWM_CLOCK_WIDTH);
//
// If the minimum pulse width parameter is zero, then increment the minimum
// pulse width (that is, the dead time) by one to avoid sending pulses into
// the dead band unit that are too short.
//
if(g_sParameters.ucMinPulseWidth == 0)
{
g_ulMinPulseWidth++;
}
}
//*****************************************************************************
//
//! Configures the dead timers for the PWM generators.
//!
//! This function configures the dead timers for all three PWM generators based
//! on the dead time parameter.
//!
//! \return None.
//
//*****************************************************************************
void
PWMSetDeadBand(void)
{
//
// Set the dead band times for all three PWM generators.
//
PWMDeadBandEnable(PWM_BASE, PWM_GEN_0, g_sParameters.ucDeadTime,
g_sParameters.ucDeadTime);
PWMDeadBandEnable(PWM_BASE, PWM_GEN_1, g_sParameters.ucDeadTime,
g_sParameters.ucDeadTime);
PWMDeadBandEnable(PWM_BASE, PWM_GEN_2, g_sParameters.ucDeadTime,
g_sParameters.ucDeadTime);
//
// Update the minimum PWM pulse width.
//
PWMSetMinPulseWidth();
}
//*****************************************************************************
//
//! Sets the frequency of the generated PWM waveforms.
//!
//! This function configures the frequency of the generated PWM waveforms. The
//! frequency update will not occur immediately; the change will be registered
//! for synchronous application to the output waveforms to avoid
//! discontinuities.
//!
//! \return None.
//
//*****************************************************************************
void
PWMSetFrequency(void)
{
//
// Disable the PWM interrupt temporarily.
//
IntDisable(INT_PWM0);
//
// Determine the configured PWM frequency.
//
switch(g_sParameters.usFlags & FLAG_PWM_FREQUENCY_MASK)
{
//
// The PWM frequency is 8 KHz.
//
case FLAG_PWM_FREQUENCY_8K:
{
//
// Set the PWM frequency variable.
//
g_ulPWMFrequency = 8000;
//
// Get the number of PWM clocks in a 8 KHz period.
//
g_ulPWMClock = PWM_CLOCK / 8000;
//
// Done with this PWM frequency.
//
break;
}
//
// The PWM frequency is 12.5 KHz.
//
case FLAG_PWM_FREQUENCY_12K:
{
//
// Set the PWM frequency variable.
//
g_ulPWMFrequency = 12500;
//
// Get the number of PWM clocks in a 12.5 KHz period.
//
g_ulPWMClock = PWM_CLOCK / 12500;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -