Pinscape fork (KL25Z)

Dependents:   Pinscape_Controller_V2_arnoz Pinscape_Controller_V2

Fork of FastPWM by Erik -

Files at this revision

API Documentation at this revision

Comitter:
Sissors
Date:
Sun Jan 01 14:37:55 2017 +0000
Parent:
32:10e2e171f430
Child:
35:25e9500598ae
Commit message:
Fixed issue where STM32 PWM on MCUs with high clock frequency could be a factor of two too fast

Changed in this revision

Device/FastPWM_STM_TIM.cpp Show annotated file Show diff for this revision Revisions of this file
--- a/Device/FastPWM_STM_TIM.cpp	Tue Sep 06 20:17:21 2016 +0000
+++ b/Device/FastPWM_STM_TIM.cpp	Sun Jan 01 14:37:55 2017 +0000
@@ -6,22 +6,39 @@
 
 typedef __IO uint32_t* CHANNEL_P_T;
 
-#define PWM_CHANNEL     (**(CHANNEL_P_T*)fast_obj)
-#define PWM_TIMER       ((TIM_TypeDef*)_pwm.pwm)
+typedef struct  {
+    CHANNEL_P_T channel;
+    uint32_t clk_prescaler;
+} fastpwm_struct;
+
+#define PWM_CHANNEL         ((((fastpwm_struct*)fast_obj)->channel)) 
+#define PWM_CLK_PRESCALER   ((((fastpwm_struct*)fast_obj)->clk_prescaler))
+#define PWM_TIMER           ((TIM_TypeDef*)_pwm.pwm)
 
 #if defined(TARGET_STM32F0) || defined (TARGET_STM32F1) || defined (TARGET_STM32L1)
 extern __IO uint32_t* getChannel(TIM_TypeDef* pwm, PinName pin);
 #endif
 
 void FastPWM::initFastPWM( void ) {
-    fast_obj = new (CHANNEL_P_T);
-    
+    fast_obj = new fastpwm_struct;
     #if defined(TARGET_STM32F0) || defined (TARGET_STM32F1) || defined (TARGET_STM32L1)
-    *(CHANNEL_P_T*)fast_obj = getChannel(PWM_TIMER, _pwm.pin);
+    PWM_CHANNEL = getChannel(PWM_TIMER, _pwm.pin);
     #else
-    *(CHANNEL_P_T*)fast_obj = &PWM_TIMER->CCR1 + _pwm.channel - 1; 
+    PWM_CHANNEL = (&PWM_TIMER->CCR1 + _pwm.channel - 1); 
     #endif
     
+    // Depending on the timer and the internal bus it is connected to, each STM timer
+    // can have a fixed prescaler from the clock, especially the faster devices.
+    // In order not to have to hardcode this in, we use knowledge that mbed lib sets
+    // default period to 20ms to reverse engineer the prescaler from this. 
+    uint32_t current_hz = SystemCoreClock / (PWM_TIMER->PSC + 1) / (PWM_TIMER->ARR+1);
+    PWM_CLK_PRESCALER = (current_hz + 1) / 50;  //50Hz is magic number it should be, +1 is to handle possible rounding errors in mbed setup
+    
+    //Sanity check in case a target does something different
+    if ( (PWM_CLK_PRESCALER == 0 ) || (PWM_CLK_PRESCALER > 16)) {
+        PWM_CLK_PRESCALER = 1;
+    }
+    
     //Enable PWM period syncing for glitch free result
     PWM_TIMER->CR1 |= TIM_CR1_ARPE;
     
@@ -29,7 +46,7 @@
 }
 
 void FastPWM::pulsewidth_ticks( uint32_t ticks ) {
-    PWM_CHANNEL = ticks;    
+    *PWM_CHANNEL = ticks;    
 }
 
 void FastPWM::period_ticks( uint32_t ticks ) {
@@ -41,15 +58,18 @@
 }
 
 uint32_t FastPWM::setPrescaler(uint32_t reqScale) {
-    if (reqScale == 0)
+    if (reqScale == 0) {
         //Return prescaler
-        return PWM_TIMER->PSC + 1;
-    if (reqScale > (uint32_t)(1<<16))
-        reqScale = 1<<16;
+        return (PWM_TIMER->PSC + 1) * PWM_CLK_PRESCALER;
+    }
+    if (reqScale > (uint32_t)(PWM_CLK_PRESCALER<<16)) {
+        reqScale = PWM_CLK_PRESCALER<<16;
+    }
     //Else set prescaler, we have to substract one from reqScale since a 0 in PCVAL is prescaler of 1
-    PWM_TIMER->PSC = reqScale - 1;
+    //Take into account PWM_CLK_PRESCALER, we need to make sure reqScale is always rounded up
+    PWM_TIMER->PSC = (reqScale + PWM_CLK_PRESCALER - 1)/PWM_CLK_PRESCALER - 1;
 
-    return reqScale;
+    return setPrescaler(0);
 }
 
 #endif
\ No newline at end of file