mirror of
https://github.com/DCC-EX/CommandStation-EX.git
synced 2024-12-27 14:21:25 +01:00
PWM pin implementation
This commit is contained in:
parent
9d5f579847
commit
75ab2ab533
65
DCCTimer.cpp
65
DCCTimer.cpp
@ -25,6 +25,21 @@
|
||||
* DCCEX works on a single timer interrupt at a regular 58uS interval.
|
||||
* The DCCWaveform class generates the signals to the motor shield
|
||||
* 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"
|
||||
@ -35,13 +50,14 @@ INTERRUPT_CALLBACK interruptHandler=0;
|
||||
|
||||
#ifdef ARDUINO_ARCH_MEGAAVR
|
||||
// Arduino unoWifi Rev2 and nanoEvery architectire
|
||||
|
||||
void DCCTimer::begin(INTERRUPT_CALLBACK callback) {
|
||||
interruptHandler=callback;
|
||||
noInterrupts();
|
||||
ADC0.CTRLC = (ADC0.CTRLC & 0b00110000) | 0b01000011; // speed up analogRead sample time
|
||||
TCB0.CTRLB = TCB_CNTMODE_INT_gc & ~TCB_CCMPEN_bm; // timer compare mode with output disabled
|
||||
TCB0.CTRLA = TCB_CLKSEL_CLKDIV2_gc; // 8 MHz ~ 0.125 us
|
||||
TCB0.CCMP = CLOCK_CYCLES -1; // 1 tick less for timer reset
|
||||
TCB0.CCMP = CLOCK_CYCLES -1; // 1 tick less for timer reset
|
||||
TCB0.INTFLAGS = TCB_CAPT_bm; // clear interrupt request flag
|
||||
TCB0.INTCTRL = TCB_CAPT_bm; // Enable the interrupt
|
||||
TCB0.CNT = 0;
|
||||
@ -55,12 +71,29 @@ INTERRUPT_CALLBACK interruptHandler=0;
|
||||
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]) {
|
||||
memcpy(mac,&SIGROW.SERNUM0,6); // serial number
|
||||
memcpy(mac,(void *) &SIGROW.SERNUM0,6); // serial number
|
||||
}
|
||||
|
||||
#else
|
||||
// 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) {
|
||||
interruptHandler=callback;
|
||||
noInterrupts();
|
||||
@ -76,6 +109,34 @@ INTERRUPT_CALLBACK interruptHandler=0;
|
||||
// ISR called by timer interrupt every 58uS
|
||||
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>
|
||||
void DCCTimer::getSimulatedMacAddress(byte mac[6]) {
|
||||
for (byte i=0; i<6; i++) mac[i]=boot_signature_byte_get(0x0E + i);
|
||||
|
@ -8,6 +8,8 @@ class DCCTimer {
|
||||
public:
|
||||
static void begin(INTERRUPT_CALLBACK interrupt);
|
||||
static void getSimulatedMacAddress(byte mac[6]);
|
||||
static bool isPWMPin(byte pin);
|
||||
static void setPWM(byte pin, bool high);
|
||||
private:
|
||||
};
|
||||
|
||||
|
@ -39,6 +39,9 @@ void DCCWaveform::begin(MotorDriver * mainDriver, MotorDriver * progDriver) {
|
||||
progTripValue = progDriver->mA2raw(TRIP_CURRENT_PROG); // need only calculate once hence static
|
||||
mainTrack.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);
|
||||
}
|
||||
|
||||
|
@ -18,6 +18,7 @@
|
||||
*/
|
||||
#include <Arduino.h>
|
||||
#include "MotorDriver.h"
|
||||
#include "DCCTimer.h"
|
||||
#include "DIAG.h"
|
||||
|
||||
#define setHIGH(fastpin) *fastpin.inout |= fastpin.maskHIGH
|
||||
@ -25,6 +26,7 @@
|
||||
#define isHIGH(fastpin) (*fastpin.inout & fastpin.maskHIGH)
|
||||
#define isLOW(fastpin) (!isHIGH(fastpin))
|
||||
|
||||
bool MotorDriver::usePWM=false;
|
||||
|
||||
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) {
|
||||
@ -68,6 +70,11 @@ MotorDriver::MotorDriver(byte power_pin, byte signal_pin, byte signal_pin2, int8
|
||||
rawCurrentTripValue=(int)(trip_milliamps / sense_factor);
|
||||
}
|
||||
|
||||
bool MotorDriver::isPWMCapable() {
|
||||
return (!dualSignal) && DCCTimer::isPWMPin(signalPin);
|
||||
}
|
||||
|
||||
|
||||
void MotorDriver::setPower(bool on) {
|
||||
if (on) {
|
||||
// toggle brake before turning power on - resets overcurrent error
|
||||
@ -94,17 +101,21 @@ void MotorDriver::setBrake(bool on) {
|
||||
}
|
||||
|
||||
void MotorDriver::setSignal( bool high) {
|
||||
if (high) {
|
||||
setHIGH(fastSignalPin);
|
||||
if (dualSignal) setLOW(fastSignalPin2);
|
||||
if (usePWM) {
|
||||
DCCTimer::setPWM(signalPin,high);
|
||||
}
|
||||
else {
|
||||
setLOW(fastSignalPin);
|
||||
if (dualSignal) setHIGH(fastSignalPin2);
|
||||
if (high) {
|
||||
setHIGH(fastSignalPin);
|
||||
if (dualSignal) setLOW(fastSignalPin2);
|
||||
}
|
||||
else {
|
||||
setLOW(fastSignalPin);
|
||||
if (dualSignal) setHIGH(fastSignalPin2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int MotorDriver::getCurrentRaw() {
|
||||
int current = analogRead(currentPin);
|
||||
if (faultPin != UNUSED_PIN && isLOW(fastFaultPin) && isHIGH(fastPowerPin))
|
||||
|
@ -44,7 +44,9 @@ class MotorDriver {
|
||||
inline int getRawCurrentTripValue() {
|
||||
return rawCurrentTripValue;
|
||||
}
|
||||
|
||||
bool isPWMCapable();
|
||||
static bool usePWM;
|
||||
|
||||
private:
|
||||
void getFastPin(const FSH* type,int pin, bool input, FASTPIN & result);
|
||||
void getFastPin(const FSH* type,int pin, FASTPIN & result) {
|
||||
|
Loading…
Reference in New Issue
Block a user