1
0
mirror of https://github.com/DCC-EX/CommandStation-EX.git synced 2025-03-14 18:13:09 +01:00

Add high accuracy timing for STM32, on pins D3 and D6.

This commit is contained in:
Neil McKechnie 2023-02-17 16:04:21 +00:00
parent 10cd580061
commit 9797a0fd2d

View File

@ -1,4 +1,5 @@
/* /*
* © 2023 Neil McKechnie
* © 2022 Paul M. Antoine * © 2022 Paul M. Antoine
* © 2021 Mike S * © 2021 Mike S
* © 2021 Harald Barth * © 2021 Harald Barth
@ -50,13 +51,13 @@ HardwareSerial Serial1(PG9, PG14); // Rx=PG9, Tx=PG14 -- D0, D1 - F412ZG/F446ZE
#endif #endif
INTERRUPT_CALLBACK interruptHandler=0; INTERRUPT_CALLBACK interruptHandler=0;
// Let's use STM32's timer #11 until disabused of this notion // Let's use STM32's timer #2 which supports hardware pulse generation on pins D3 and D6
// Timer #11 is used for "servo" library, but as DCC-EX is not using // (accurate timing, independent of the latency of interrupt handling).
// this libary, we should be free and clear. // Pin D3 is driven by TIM2 channel 2 and D6 is TIM2 channel 3.
HardwareTimer timer(TIM11); HardwareTimer timer(TIM2);
// Timer IRQ handler // Timer IRQ handler
void Timer11_Handler() { void Timer_Handler() {
interruptHandler(); interruptHandler();
} }
@ -67,9 +68,8 @@ void DCCTimer::begin(INTERRUPT_CALLBACK callback) {
// adc_set_sample_rate(ADC_SAMPLETIME_480CYCLES); // adc_set_sample_rate(ADC_SAMPLETIME_480CYCLES);
timer.pause(); timer.pause();
timer.setPrescaleFactor(1); timer.setPrescaleFactor(1);
// timer.setOverflow(CLOCK_CYCLES * 2);
timer.setOverflow(DCC_SIGNAL_TIME, MICROSEC_FORMAT); timer.setOverflow(DCC_SIGNAL_TIME, MICROSEC_FORMAT);
timer.attachInterrupt(Timer11_Handler); timer.attachInterrupt(Timer_Handler);
timer.refresh(); timer.refresh();
timer.resume(); timer.resume();
@ -77,20 +77,42 @@ void DCCTimer::begin(INTERRUPT_CALLBACK callback) {
} }
bool DCCTimer::isPWMPin(byte pin) { bool DCCTimer::isPWMPin(byte pin) {
//TODO: SAMD whilst this call to digitalPinHasPWM will reveal which pins can do PWM, // Timer 2 Channel 2 controls pin D3, and Timer2 Channel 3 controls D6.
// there's no support yet for High Accuracy, so for now return false // Enable the appropriate timer channel.
// return digitalPinHasPWM(pin); switch (pin) {
return false; case 3:
timer.setMode(2, TIMER_OUTPUT_COMPARE_INACTIVE, D3);
return true;
case 6:
timer.setMode(3, TIMER_OUTPUT_COMPARE_INACTIVE, D6);
return true;
default:
return false;
}
} }
void DCCTimer::setPWM(byte pin, bool high) { void DCCTimer::setPWM(byte pin, bool high) {
// TODO: High Accuracy mode is not supported as yet, and may never need to be // Set the timer so that, at the next counter overflow, the requested
(void) pin; // pin state is activated automatically before the interrupt code runs.
(void) high; switch (pin) {
case 3:
if (high)
TIM2->CCMR1 = (TIM2->CCMR1 & ~TIM_CCMR1_OC2M_Msk) | TIM_CCMR1_OC2M_0;
else
TIM2->CCMR1 = (TIM2->CCMR1 & ~TIM_CCMR1_OC2M_Msk) | TIM_CCMR1_OC2M_1;
break;
case 6:
if (high)
TIM2->CCMR2 = (TIM2->CCMR2 & ~TIM_CCMR2_OC3M_Msk) | TIM_CCMR2_OC3M_0;
else
TIM2->CCMR2 = (TIM2->CCMR2 & ~TIM_CCMR2_OC3M_Msk) | TIM_CCMR2_OC3M_1;
break;
}
} }
void DCCTimer::clearPWM() { void DCCTimer::clearPWM() {
return; timer.setMode(2, TIMER_OUTPUT_COMPARE_INACTIVE, NC);
timer.setMode(3, TIMER_OUTPUT_COMPARE_INACTIVE, NC);
} }
void DCCTimer::getSimulatedMacAddress(byte mac[6]) { void DCCTimer::getSimulatedMacAddress(byte mac[6]) {