mirror of
https://github.com/DCC-EX/CommandStation-EX.git
synced 2025-02-18 15:06:03 +01:00
PWM pin implementation
This commit is contained in:
parent
9d5f579847
commit
75ab2ab533
63
DCCTimer.cpp
63
DCCTimer.cpp
@ -25,6 +25,21 @@
|
|||||||
* DCCEX works on a single timer interrupt at a regular 58uS interval.
|
* DCCEX works on a single timer interrupt at a regular 58uS interval.
|
||||||
* The DCCWaveform class generates the signals to the motor shield
|
* The DCCWaveform class generates the signals to the motor shield
|
||||||
* based on this timer.
|
* based on this timer.
|
||||||
|
*
|
||||||
|
* If the motor drivers are BOTH configured to use the correct 2 pins for the architecture,
|
||||||
|
* (see isPWMPin() function. )
|
||||||
|
* then this allows us to use a hardware driven pin switching arrangement which is
|
||||||
|
* achieved by setting the duty cycle of the NEXT clock interrupt to 0% or 100% depending on
|
||||||
|
* the required pin state. (see setPWM())
|
||||||
|
* This is more accurate than the software interrupt but at the expense of
|
||||||
|
* limiting the choice of available pins.
|
||||||
|
* Fortunately, a standard motor shield on a Mega uses pins that qualify for PWM...
|
||||||
|
* Other shields may be jumpered to PWM pins or run directly using the software interrupt.
|
||||||
|
*
|
||||||
|
* Because the PWM-based waveform is effectively set half a cycle after the software version,
|
||||||
|
* it is not acceptable to drive the two tracks on different methiods or it would cause
|
||||||
|
* problems for <1 JOIN> etc.
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "DCCTimer.h"
|
#include "DCCTimer.h"
|
||||||
@ -35,6 +50,7 @@ INTERRUPT_CALLBACK interruptHandler=0;
|
|||||||
|
|
||||||
#ifdef ARDUINO_ARCH_MEGAAVR
|
#ifdef ARDUINO_ARCH_MEGAAVR
|
||||||
// Arduino unoWifi Rev2 and nanoEvery architectire
|
// Arduino unoWifi Rev2 and nanoEvery architectire
|
||||||
|
|
||||||
void DCCTimer::begin(INTERRUPT_CALLBACK callback) {
|
void DCCTimer::begin(INTERRUPT_CALLBACK callback) {
|
||||||
interruptHandler=callback;
|
interruptHandler=callback;
|
||||||
noInterrupts();
|
noInterrupts();
|
||||||
@ -55,12 +71,29 @@ INTERRUPT_CALLBACK interruptHandler=0;
|
|||||||
interruptHandler();
|
interruptHandler();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool DCCTimer::isPWMPin(byte pin) {
|
||||||
|
return false; // TODO what are the relevant pins?
|
||||||
|
}
|
||||||
|
|
||||||
|
void DCCTimer::setPWM(byte pin, bool high) {
|
||||||
|
// TODO what are the relevant pins?
|
||||||
|
}
|
||||||
|
|
||||||
void DCCTimer::getSimulatedMacAddress(byte mac[6]) {
|
void DCCTimer::getSimulatedMacAddress(byte mac[6]) {
|
||||||
memcpy(mac,&SIGROW.SERNUM0,6); // serial number
|
memcpy(mac,(void *) &SIGROW.SERNUM0,6); // serial number
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
// Arduino nano, uno, mega etc
|
// Arduino nano, uno, mega etc
|
||||||
|
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
|
||||||
|
#define TIMER1_A_PIN 11
|
||||||
|
#define TIMER1_B_PIN 12
|
||||||
|
#define TIMER1_C_PIN 13
|
||||||
|
#else
|
||||||
|
#define TIMER1_A_PIN 9
|
||||||
|
#define TIMER1_B_PIN 10
|
||||||
|
#endif
|
||||||
|
|
||||||
void DCCTimer::begin(INTERRUPT_CALLBACK callback) {
|
void DCCTimer::begin(INTERRUPT_CALLBACK callback) {
|
||||||
interruptHandler=callback;
|
interruptHandler=callback;
|
||||||
noInterrupts();
|
noInterrupts();
|
||||||
@ -76,6 +109,34 @@ INTERRUPT_CALLBACK interruptHandler=0;
|
|||||||
// ISR called by timer interrupt every 58uS
|
// ISR called by timer interrupt every 58uS
|
||||||
ISR(TIMER1_OVF_vect){ interruptHandler(); }
|
ISR(TIMER1_OVF_vect){ interruptHandler(); }
|
||||||
|
|
||||||
|
// Alternative pin manipulation via PWM control.
|
||||||
|
bool DCCTimer::isPWMPin(byte pin) {
|
||||||
|
return pin==TIMER1_A_PIN
|
||||||
|
|| pin==TIMER1_B_PIN
|
||||||
|
#ifdef TIMER1_C_PIN
|
||||||
|
|| pin==TIMER1_C_PIN
|
||||||
|
#endif
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DCCTimer::setPWM(byte pin, bool high) {
|
||||||
|
if (pin==TIMER1_A_PIN) {
|
||||||
|
TCCR1A |= _BV(COM1A1);
|
||||||
|
OCR1A= high?1024:0;
|
||||||
|
}
|
||||||
|
else if (pin==TIMER1_B_PIN) {
|
||||||
|
TCCR1A |= _BV(COM1B1);
|
||||||
|
OCR1B= high?1024:0;
|
||||||
|
}
|
||||||
|
#ifdef TIMER1_C_PIN
|
||||||
|
else if (pin==TIMER1_C_PIN) {
|
||||||
|
TCCR1A |= _BV(COM1C1);
|
||||||
|
OCR1C= high?1024:0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#include <avr/boot.h>
|
#include <avr/boot.h>
|
||||||
void DCCTimer::getSimulatedMacAddress(byte mac[6]) {
|
void DCCTimer::getSimulatedMacAddress(byte mac[6]) {
|
||||||
for (byte i=0; i<6; i++) mac[i]=boot_signature_byte_get(0x0E + i);
|
for (byte i=0; i<6; i++) mac[i]=boot_signature_byte_get(0x0E + i);
|
||||||
|
@ -8,6 +8,8 @@ class DCCTimer {
|
|||||||
public:
|
public:
|
||||||
static void begin(INTERRUPT_CALLBACK interrupt);
|
static void begin(INTERRUPT_CALLBACK interrupt);
|
||||||
static void getSimulatedMacAddress(byte mac[6]);
|
static void getSimulatedMacAddress(byte mac[6]);
|
||||||
|
static bool isPWMPin(byte pin);
|
||||||
|
static void setPWM(byte pin, bool high);
|
||||||
private:
|
private:
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -39,6 +39,9 @@ void DCCWaveform::begin(MotorDriver * mainDriver, MotorDriver * progDriver) {
|
|||||||
progTripValue = progDriver->mA2raw(TRIP_CURRENT_PROG); // need only calculate once hence static
|
progTripValue = progDriver->mA2raw(TRIP_CURRENT_PROG); // need only calculate once hence static
|
||||||
mainTrack.setPowerMode(POWERMODE::OFF);
|
mainTrack.setPowerMode(POWERMODE::OFF);
|
||||||
progTrack.setPowerMode(POWERMODE::OFF);
|
progTrack.setPowerMode(POWERMODE::OFF);
|
||||||
|
MotorDriver::usePWM= mainDriver->isPWMCapable() && progDriver->isPWMCapable();
|
||||||
|
if (MotorDriver::usePWM) DIAG(F("\nWaveform using PWM pins for accuracy."));
|
||||||
|
else DIAG(F("\nWaveform accuracy limited by signal pin configuration."));
|
||||||
DCCTimer::begin(DCCWaveform::interruptHandler);
|
DCCTimer::begin(DCCWaveform::interruptHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include "MotorDriver.h"
|
#include "MotorDriver.h"
|
||||||
|
#include "DCCTimer.h"
|
||||||
#include "DIAG.h"
|
#include "DIAG.h"
|
||||||
|
|
||||||
#define setHIGH(fastpin) *fastpin.inout |= fastpin.maskHIGH
|
#define setHIGH(fastpin) *fastpin.inout |= fastpin.maskHIGH
|
||||||
@ -25,6 +26,7 @@
|
|||||||
#define isHIGH(fastpin) (*fastpin.inout & fastpin.maskHIGH)
|
#define isHIGH(fastpin) (*fastpin.inout & fastpin.maskHIGH)
|
||||||
#define isLOW(fastpin) (!isHIGH(fastpin))
|
#define isLOW(fastpin) (!isHIGH(fastpin))
|
||||||
|
|
||||||
|
bool MotorDriver::usePWM=false;
|
||||||
|
|
||||||
MotorDriver::MotorDriver(byte power_pin, byte signal_pin, byte signal_pin2, int8_t brake_pin,
|
MotorDriver::MotorDriver(byte power_pin, byte signal_pin, byte signal_pin2, int8_t brake_pin,
|
||||||
byte current_pin, float sense_factor, unsigned int trip_milliamps, byte fault_pin) {
|
byte current_pin, float sense_factor, unsigned int trip_milliamps, byte fault_pin) {
|
||||||
@ -68,6 +70,11 @@ MotorDriver::MotorDriver(byte power_pin, byte signal_pin, byte signal_pin2, int8
|
|||||||
rawCurrentTripValue=(int)(trip_milliamps / sense_factor);
|
rawCurrentTripValue=(int)(trip_milliamps / sense_factor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool MotorDriver::isPWMCapable() {
|
||||||
|
return (!dualSignal) && DCCTimer::isPWMPin(signalPin);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void MotorDriver::setPower(bool on) {
|
void MotorDriver::setPower(bool on) {
|
||||||
if (on) {
|
if (on) {
|
||||||
// toggle brake before turning power on - resets overcurrent error
|
// toggle brake before turning power on - resets overcurrent error
|
||||||
@ -94,6 +101,10 @@ void MotorDriver::setBrake(bool on) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MotorDriver::setSignal( bool high) {
|
void MotorDriver::setSignal( bool high) {
|
||||||
|
if (usePWM) {
|
||||||
|
DCCTimer::setPWM(signalPin,high);
|
||||||
|
}
|
||||||
|
else {
|
||||||
if (high) {
|
if (high) {
|
||||||
setHIGH(fastSignalPin);
|
setHIGH(fastSignalPin);
|
||||||
if (dualSignal) setLOW(fastSignalPin2);
|
if (dualSignal) setLOW(fastSignalPin2);
|
||||||
@ -102,9 +113,9 @@ void MotorDriver::setSignal( bool high) {
|
|||||||
setLOW(fastSignalPin);
|
setLOW(fastSignalPin);
|
||||||
if (dualSignal) setHIGH(fastSignalPin2);
|
if (dualSignal) setHIGH(fastSignalPin2);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int MotorDriver::getCurrentRaw() {
|
int MotorDriver::getCurrentRaw() {
|
||||||
int current = analogRead(currentPin);
|
int current = analogRead(currentPin);
|
||||||
if (faultPin != UNUSED_PIN && isLOW(fastFaultPin) && isHIGH(fastPowerPin))
|
if (faultPin != UNUSED_PIN && isLOW(fastFaultPin) && isHIGH(fastPowerPin))
|
||||||
|
@ -44,6 +44,8 @@ class MotorDriver {
|
|||||||
inline int getRawCurrentTripValue() {
|
inline int getRawCurrentTripValue() {
|
||||||
return rawCurrentTripValue;
|
return rawCurrentTripValue;
|
||||||
}
|
}
|
||||||
|
bool isPWMCapable();
|
||||||
|
static bool usePWM;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void getFastPin(const FSH* type,int pin, bool input, FASTPIN & result);
|
void getFastPin(const FSH* type,int pin, bool input, FASTPIN & result);
|
||||||
|
Loading…
Reference in New Issue
Block a user