🐛 Fix STM32 set_pwm_duty (#23125)

This commit is contained in:
Mike La Spina 2021-11-23 14:05:50 -06:00 committed by Scott Lahteine
parent 184fc36a08
commit 5f08864d1f
7 changed files with 96 additions and 60 deletions

View file

@ -114,16 +114,19 @@ byte MarlinSPI::transfer(uint8_t _data) {
return rxData; return rxData;
} }
__STATIC_INLINE void LL_SPI_EnableDMAReq_RX(SPI_TypeDef *SPIx) { SET_BIT(SPIx->CR2, SPI_CR2_RXDMAEN); }
__STATIC_INLINE void LL_SPI_EnableDMAReq_TX(SPI_TypeDef *SPIx) { SET_BIT(SPIx->CR2, SPI_CR2_TXDMAEN); }
uint8_t MarlinSPI::dmaTransfer(const void *transmitBuf, void *receiveBuf, uint16_t length) { uint8_t MarlinSPI::dmaTransfer(const void *transmitBuf, void *receiveBuf, uint16_t length) {
const uint8_t ff = 0xFF; const uint8_t ff = 0xFF;
//if ((hspi->Instance->CR1 & SPI_CR1_SPE) != SPI_CR1_SPE) //only enable if disabled //if (!LL_SPI_IsEnabled(_spi.handle)) // only enable if disabled
__HAL_SPI_ENABLE(&_spi.handle); __HAL_SPI_ENABLE(&_spi.handle);
if (receiveBuf) { if (receiveBuf) {
setupDma(_spi.handle, _dmaRx, DMA_PERIPH_TO_MEMORY, true); setupDma(_spi.handle, _dmaRx, DMA_PERIPH_TO_MEMORY, true);
HAL_DMA_Start(&_dmaRx, (uint32_t)&(_spi.handle.Instance->DR), (uint32_t)receiveBuf, length); HAL_DMA_Start(&_dmaRx, (uint32_t)&(_spi.handle.Instance->DR), (uint32_t)receiveBuf, length);
SET_BIT(_spi.handle.Instance->CR2, SPI_CR2_RXDMAEN); /* Enable Rx DMA Request */ LL_SPI_EnableDMAReq_RX(_spi.handle.Instance); // Enable Rx DMA Request
} }
// check for 2 lines transfer // check for 2 lines transfer
@ -136,7 +139,7 @@ uint8_t MarlinSPI::dmaTransfer(const void *transmitBuf, void *receiveBuf, uint16
if (transmitBuf) { if (transmitBuf) {
setupDma(_spi.handle, _dmaTx, DMA_MEMORY_TO_PERIPH, mincTransmit); setupDma(_spi.handle, _dmaTx, DMA_MEMORY_TO_PERIPH, mincTransmit);
HAL_DMA_Start(&_dmaTx, (uint32_t)transmitBuf, (uint32_t)&(_spi.handle.Instance->DR), length); HAL_DMA_Start(&_dmaTx, (uint32_t)transmitBuf, (uint32_t)&(_spi.handle.Instance->DR), length);
SET_BIT(_spi.handle.Instance->CR2, SPI_CR2_TXDMAEN); /* Enable Tx DMA Request */ LL_SPI_EnableDMAReq_TX(_spi.handle.Instance); // Enable Tx DMA Request
} }
if (transmitBuf) { if (transmitBuf) {
@ -160,7 +163,7 @@ uint8_t MarlinSPI::dmaSend(const void * transmitBuf, uint16_t length, bool minc)
setupDma(_spi.handle, _dmaTx, DMA_MEMORY_TO_PERIPH, minc); setupDma(_spi.handle, _dmaTx, DMA_MEMORY_TO_PERIPH, minc);
HAL_DMA_Start(&_dmaTx, (uint32_t)transmitBuf, (uint32_t)&(_spi.handle.Instance->DR), length); HAL_DMA_Start(&_dmaTx, (uint32_t)transmitBuf, (uint32_t)&(_spi.handle.Instance->DR), length);
__HAL_SPI_ENABLE(&_spi.handle); __HAL_SPI_ENABLE(&_spi.handle);
SET_BIT(_spi.handle.Instance->CR2, SPI_CR2_TXDMAEN); /* Enable Tx DMA Request */ LL_SPI_EnableDMAReq_TX(_spi.handle.Instance); // Enable Tx DMA Request
HAL_DMA_PollForTransfer(&_dmaTx, HAL_DMA_FULL_TRANSFER, HAL_MAX_DELAY); HAL_DMA_PollForTransfer(&_dmaTx, HAL_DMA_FULL_TRANSFER, HAL_MAX_DELAY);
HAL_DMA_Abort(&_dmaTx); HAL_DMA_Abort(&_dmaTx);
// DeInit objects // DeInit objects

View file

@ -27,37 +27,67 @@
#include "../../inc/MarlinConfig.h" #include "../../inc/MarlinConfig.h"
#include "timers.h" #include "timers.h"
void set_pwm_duty(const pin_t pin, const uint16_t v, const uint16_t v_size/*=255*/, const bool invert/*=false*/) { // Array to support sticky frequency sets per timer
if (!PWM_PIN(pin)) return; // Don't proceed if no hardware timer static uint16_t timer_freq[TIMER_NUM];
void set_pwm_duty(const pin_t pin, const uint16_t v, const uint16_t v_size/*=255*/, const bool invert/*=false*/) {
if (!PWM_PIN(pin)) return; // Don't proceed if no hardware timer
bool needs_freq;
PinName pin_name = digitalPinToPinName(pin); PinName pin_name = digitalPinToPinName(pin);
TIM_TypeDef *Instance = (TIM_TypeDef *)pinmap_peripheral(pin_name, PinMap_PWM); TIM_TypeDef *Instance = (TIM_TypeDef *)pinmap_peripheral(pin_name, PinMap_PWM);
HardwareTimer *HT;
TimerModes_t previousMode;
uint16_t adj_val = Instance->ARR * v / v_size; uint16_t value = v;
if (invert) adj_val = Instance->ARR - adj_val; if (invert) value = v_size - value;
switch (get_pwm_channel(pin_name)) {
case TIM_CHANNEL_1: LL_TIM_OC_SetCompareCH1(Instance, adj_val); break; uint32_t index = get_timer_index(Instance);
case TIM_CHANNEL_2: LL_TIM_OC_SetCompareCH2(Instance, adj_val); break;
case TIM_CHANNEL_3: LL_TIM_OC_SetCompareCH3(Instance, adj_val); break; if (HardwareTimer_Handle[index] == nullptr) {
case TIM_CHANNEL_4: LL_TIM_OC_SetCompareCH4(Instance, adj_val); break; HardwareTimer_Handle[index]->__this = new HardwareTimer((TIM_TypeDef *)pinmap_peripheral(pin_name, PinMap_PWM));
needs_freq = true; // The instance must be new set the default frequency of PWM_FREQUENCY
} }
HT = (HardwareTimer *)(HardwareTimer_Handle[index]->__this);
uint32_t channel = STM_PIN_CHANNEL(pinmap_function(pin_name, PinMap_PWM));
previousMode = HT->getMode(channel);
if (previousMode != TIMER_OUTPUT_COMPARE_PWM1)
HT->setMode(channel, TIMER_OUTPUT_COMPARE_PWM1, pin);
if (needs_freq) {
if (timer_freq[index] == 0 ) { // If the timer is unconfigured and no freq is set then default PWM_FREQUENCY.
timer_freq[index] = PWM_FREQUENCY;
set_pwm_frequency(pin_name, timer_freq[index]); // Set the frequency and save the value to the assigned index no.
}
}
// Note the resolution is sticky here, the input can be upto 16 bits and that would require RESOLUTION_16B_COMPARE_FORMAT (16)
// If such a need were to manifest then we would need to calc the resolution based on the v_size parameter and add code for it.
HT->setCaptureCompare(channel, value, RESOLUTION_8B_COMPARE_FORMAT); // Sets the duty, the calc is done in the library :)
pinmap_pinout(pin_name, PinMap_PWM); // Make sure the pin output state is set.
if (previousMode != TIMER_OUTPUT_COMPARE_PWM1) HT->resume();
} }
#if NEEDS_HARDWARE_PWM void set_pwm_frequency(const pin_t pin, int f_desired) {
if (!PWM_PIN(pin)) return; // Don't proceed if no hardware timer
HardwareTimer *HT;
PinName pin_name = digitalPinToPinName(pin);
TIM_TypeDef *Instance = (TIM_TypeDef *)pinmap_peripheral(pin_name, PinMap_PWM); // Get HAL timer instance
void set_pwm_frequency(const pin_t pin, int f_desired) { uint32_t index = get_timer_index(Instance);
if (!PWM_PIN(pin)) return; // Don't proceed if no hardware timer
PinName pin_name = digitalPinToPinName(pin); // Protect used timers
TIM_TypeDef *Instance = (TIM_TypeDef *)pinmap_peripheral(pin_name, PinMap_PWM); // Get HAL timer instance if (index == TEMP_TIMER_NUM || index == STEP_TIMER_NUM
#if PULSE_TIMER_NUM != STEP_TIMER_NUM
|| index == PULSE_TIMER_NUM
#endif
) return;
LOOP_S_L_N(i, 0, NUM_HARDWARE_TIMERS) // Protect used timers if (HardwareTimer_Handle[index] == nullptr) // If frequency is set before duty we need to create a handle here.
if (timer_instance[i] && timer_instance[i]->getHandle()->Instance == Instance) HardwareTimer_Handle[index]->__this = new HardwareTimer((TIM_TypeDef *)pinmap_peripheral(pin_name, PinMap_PWM));
return; HT = (HardwareTimer *)(HardwareTimer_Handle[index]->__this);
timer_freq[index] = f_desired; // Save the last frequency so duty will not set the default for this timer number.
pwm_start(pin_name, f_desired, 0, RESOLUTION_8B_COMPARE_FORMAT); HT->setOverflow(f_desired, HERTZ_FORMAT);
} }
#endif
#endif // HAL_STM32 #endif // HAL_STM32

View file

@ -161,11 +161,11 @@ uint32_t TFT_SPI::ReadID(uint16_t Reg) {
for (i = 0; i < 4; i++) { for (i = 0; i < 4; i++) {
#if TFT_MISO_PIN != TFT_MOSI_PIN #if TFT_MISO_PIN != TFT_MOSI_PIN
//if (hspi->Init.Direction == SPI_DIRECTION_2LINES) { //if (hspi->Init.Direction == SPI_DIRECTION_2LINES) {
while ((SPIx.Instance->SR & SPI_FLAG_TXE) != SPI_FLAG_TXE) {} while (!__HAL_SPI_GET_FLAG(&SPIx, SPI_FLAG_TXE)) {}
SPIx.Instance->DR = 0; SPIx.Instance->DR = 0;
//} //}
#endif #endif
while ((SPIx.Instance->SR & SPI_FLAG_RXNE) != SPI_FLAG_RXNE) {} while (!__HAL_SPI_GET_FLAG(&SPIx, SPI_FLAG_RXNE)) {}
Data = (Data << 8) | SPIx.Instance->DR; Data = (Data << 8) | SPIx.Instance->DR;
} }
@ -195,8 +195,8 @@ bool TFT_SPI::isBusy() {
void TFT_SPI::Abort() { void TFT_SPI::Abort() {
// Wait for any running spi // Wait for any running spi
while ((SPIx.Instance->SR & SPI_FLAG_TXE) != SPI_FLAG_TXE) {} while (!__HAL_SPI_GET_FLAG(&SPIx, SPI_FLAG_TXE)) {}
while ((SPIx.Instance->SR & SPI_FLAG_BSY) == SPI_FLAG_BSY) {} while ( __HAL_SPI_GET_FLAG(&SPIx, SPI_FLAG_BSY)) {}
// First, abort any running dma // First, abort any running dma
HAL_DMA_Abort(&DMAtx); HAL_DMA_Abort(&DMAtx);
// DeInit objects // DeInit objects
@ -214,8 +214,8 @@ void TFT_SPI::Transmit(uint16_t Data) {
SPIx.Instance->DR = Data; SPIx.Instance->DR = Data;
while ((SPIx.Instance->SR & SPI_FLAG_TXE) != SPI_FLAG_TXE) {} while (!__HAL_SPI_GET_FLAG(&SPIx, SPI_FLAG_TXE)) {}
while ((SPIx.Instance->SR & SPI_FLAG_BSY) == SPI_FLAG_BSY) {} while ( __HAL_SPI_GET_FLAG(&SPIx, SPI_FLAG_BSY)) {}
if (TFT_MISO_PIN != TFT_MOSI_PIN) if (TFT_MISO_PIN != TFT_MOSI_PIN)
__HAL_SPI_CLEAR_OVRFLAG(&SPIx); // Clear overrun flag in 2 Lines communication mode because received is not read __HAL_SPI_CLEAR_OVRFLAG(&SPIx); // Clear overrun flag in 2 Lines communication mode because received is not read

View file

@ -264,7 +264,10 @@ void analogWrite(pin_t pin, int pwm_val8); // PWM only! mul by 257 in maple!?
#define PLATFORM_M997_SUPPORT #define PLATFORM_M997_SUPPORT
void flashFirmware(const int16_t); void flashFirmware(const int16_t);
#define HAL_CAN_SET_PWM_FREQ // This HAL supports PWM Frequency adjustment #ifndef PWM_FREQUENCY
#define PWM_FREQUENCY 1000 // Default PWM Frequency
#endif
#define HAL_CAN_SET_PWM_FREQ // This HAL supports PWM Frequency adjustment
/** /**
* set_pwm_frequency * set_pwm_frequency
@ -278,5 +281,6 @@ void set_pwm_frequency(const pin_t pin, int f_desired);
* Set the PWM duty cycle of the provided pin to the provided value * Set the PWM duty cycle of the provided pin to the provided value
* Optionally allows inverting the duty cycle [default = false] * Optionally allows inverting the duty cycle [default = false]
* Optionally allows changing the maximum size of the provided value to enable finer PWM duty control [default = 255] * Optionally allows changing the maximum size of the provided value to enable finer PWM duty control [default = 255]
* The timer must be pre-configured with set_pwm_frequency() if the default frequency is not desired.
*/ */
void set_pwm_duty(const pin_t pin, const uint16_t v, const uint16_t v_size=255, const bool invert=false); void set_pwm_duty(const pin_t pin, const uint16_t v, const uint16_t v_size=255, const bool invert=false);

View file

@ -30,40 +30,40 @@
void set_pwm_duty(const pin_t pin, const uint16_t v, const uint16_t v_size/*=255*/, const bool invert/*=false*/) { void set_pwm_duty(const pin_t pin, const uint16_t v, const uint16_t v_size/*=255*/, const bool invert/*=false*/) {
if (!PWM_PIN(pin)) return; if (!PWM_PIN(pin)) return;
timer_dev *timer = PIN_MAP[pin].timer_device; timer_dev *timer = PIN_MAP[pin].timer_device;
if (!(timer->regs.bas->SR & TIMER_CR1_CEN)) // Ensure the timer is enabled
set_pwm_frequency(pin, PWM_FREQUENCY);
uint16_t max_val = timer->regs.bas->ARR * v / v_size; uint16_t max_val = timer->regs.bas->ARR * v / v_size;
if (invert) max_val = v_size - max_val; if (invert) max_val = v_size - max_val;
pwmWrite(pin, max_val); pwmWrite(pin, max_val);
} }
#if NEEDS_HARDWARE_PWM void set_pwm_frequency(const pin_t pin, int f_desired) {
if (!PWM_PIN(pin)) return; // Don't proceed if no hardware timer
void set_pwm_frequency(const pin_t pin, int f_desired) { timer_dev *timer = PIN_MAP[pin].timer_device;
if (!PWM_PIN(pin)) return; // Don't proceed if no hardware timer uint8_t channel = PIN_MAP[pin].timer_channel;
timer_dev *timer = PIN_MAP[pin].timer_device; // Protect used timers
uint8_t channel = PIN_MAP[pin].timer_channel; if (timer == get_timer_dev(TEMP_TIMER_NUM)) return;
if (timer == get_timer_dev(STEP_TIMER_NUM)) return;
#if PULSE_TIMER_NUM != STEP_TIMER_NUM
if (timer == get_timer_dev(PULSE_TIMER_NUM)) return;
#endif
// Protect used timers if (!(timer->regs.bas->SR & TIMER_CR1_CEN)) // Ensure the timer is enabled
if (timer == get_timer_dev(TEMP_TIMER_NUM)) return; timer_init(timer);
if (timer == get_timer_dev(STEP_TIMER_NUM)) return;
#if PULSE_TIMER_NUM != STEP_TIMER_NUM
if (timer == get_timer_dev(PULSE_TIMER_NUM)) return;
#endif
if (!(timer->regs.bas->SR & TIMER_CR1_CEN)) // Ensure the timer is enabled timer_set_mode(timer, channel, TIMER_PWM);
timer_init(timer); uint16_t preload = 255; // Lock 255 PWM resolution for high frequencies
int32_t prescaler = (HAL_TIMER_RATE) / (preload + 1) / f_desired - 1;
timer_set_mode(timer, channel, TIMER_PWM); if (prescaler > 65535) { // For low frequencies increase prescaler
uint16_t preload = 255; // Lock 255 PWM resolution for high frequencies prescaler = 65535;
int32_t prescaler = (HAL_TIMER_RATE) / (preload + 1) / f_desired - 1; preload = (HAL_TIMER_RATE) / (prescaler + 1) / f_desired - 1;
if (prescaler > 65535) { // For low frequencies increase prescaler
prescaler = 65535;
preload = (HAL_TIMER_RATE) / (prescaler + 1) / f_desired - 1;
}
if (prescaler < 0) return; // Too high frequency
timer_set_reload(timer, preload);
timer_set_prescaler(timer, prescaler);
} }
if (prescaler < 0) return; // Too high frequency
timer_set_reload(timer, preload);
timer_set_prescaler(timer, prescaler);
}
#endif // NEEDS_HARDWARE_PWM
#endif // __STM32F1__ #endif // __STM32F1__

View file

@ -345,7 +345,7 @@ static bool longName2DosName(const char *longName, char *dosName) {
hdma->DmaBaseAddress->IFCR = (DMA_ISR_GIF1 << hdma->ChannelIndex); hdma->DmaBaseAddress->IFCR = (DMA_ISR_GIF1 << hdma->ChannelIndex);
SET_BIT(hdma->ErrorCode, HAL_DMA_ERROR_TE); // Update error code SET_BIT(hdma->ErrorCode, HAL_DMA_ERROR_TE); // Update error code
hdma->State= HAL_DMA_STATE_READY; // Change the DMA state hdma->State = HAL_DMA_STATE_READY; // Change the DMA state
__HAL_UNLOCK(hdma); // Process Unlocked __HAL_UNLOCK(hdma); // Process Unlocked
return HAL_ERROR; return HAL_ERROR;
} }

View file

@ -96,7 +96,6 @@
#else #else
#define FAST_PWM_FAN // STM32 Variant allow TIMER2 Hardware PWM #define FAST_PWM_FAN // STM32 Variant allow TIMER2 Hardware PWM
#define FAST_PWM_FAN_FREQUENCY 31400 // This frequency allow a good range, fan starts at 3%, half noise at 50% #define FAST_PWM_FAN_FREQUENCY 31400 // This frequency allow a good range, fan starts at 3%, half noise at 50%
#define NEEDS_HARDWARE_PWM 1
#define FAN_MIN_PWM 5 #define FAN_MIN_PWM 5
#define FAN_MAX_PWM 255 #define FAN_MAX_PWM 255
#endif #endif