/

Fork of FastPWM by Erik -

Files at this revision

API Documentation at this revision

Comitter:
Sissors
Date:
Tue Aug 13 16:54:06 2013 +0000
Parent:
3:3094d3806cfc
Child:
5:2812f0a115f7
Commit message:
v2.0
; Added KL25Z/LPC11u24 support
; Added prescalers
; Direct tick changes

Changed in this revision

Device/FastPWM_KL25Z.cpp Show annotated file Show diff for this revision Revisions of this file
Device/FastPWM_LPC11U24.cpp Show annotated file Show diff for this revision Revisions of this file
Device/FastPWM_LPC1768.cpp Show annotated file Show diff for this revision Revisions of this file
FastPWM.cpp Show diff for this revision Revisions of this file
FastPWM.h Show annotated file Show diff for this revision Revisions of this file
FastPWM_common.cpp Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Device/FastPWM_KL25Z.cpp	Tue Aug 13 16:54:06 2013 +0000
@@ -0,0 +1,56 @@
+#ifdef TARGET_KL25Z
+
+#include "FastPWM.h"
+
+volatile uint32_t *TPM_SC;
+
+void FastPWM::initFastPWM( void ) {
+    bits = 16;
+    
+    //Yes this is ugly, yes I should feel bad about it
+    TPM_SC = _pwm.MOD - 2;
+}
+
+void FastPWM::pulsewidth_ticks( uint32_t ticks ) {
+    *(_pwm.CnV) = ticks;
+    *_pwm.CNT = 0;  //Not yet sure why this is needed!
+}
+
+void FastPWM::period_ticks( uint32_t ticks ) {
+    *(_pwm.MOD) = ticks;
+    *_pwm.CNT = 0;
+}
+
+uint32_t FastPWM::getPeriod( void ) {
+    return *(_pwm.MOD);
+}
+
+uint32_t FastPWM::setPrescaler(uint32_t reqScale) {
+    const char prescalers[] = {1, 2, 4, 8, 16, 32, 64, 128};
+    
+    //If prescaler is 0, return current one
+    if (reqScale == 0)
+        return (prescalers[(*TPM_SC) & 0x07]);
+    
+    uint32_t retval = 0;
+    char bin;
+    
+    for (bin = 0; bin<8; bin++) {
+        retval = prescalers[bin];
+        if (retval >= reqScale)
+            break;
+    }
+    
+    //Clear lower 5 bits, write new value:
+    char clockbits = *TPM_SC & (3<<3);
+    
+    //For some reason clearing them takes some effort
+    while ((*TPM_SC & 0x1F) != 0)
+        *TPM_SC &= ~0x1F;
+        
+    
+    *TPM_SC |= bin + clockbits;
+    
+    return retval;   
+}
+#endif
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Device/FastPWM_LPC11U24.cpp	Tue Aug 13 16:54:06 2013 +0000
@@ -0,0 +1,80 @@
+#ifdef TARGET_LPC11U24
+
+#include "FastPWM.h"
+
+
+volatile uint32_t *PWM_MR;
+LPC_CTxxBx_Type *pwm_obj;
+
+typedef struct {
+    uint8_t timer;
+    uint8_t mr;
+} timer_mr;
+ 
+static timer_mr pwm_timer_map[11] = {
+    {0, 0}, {0, 1}, {0, 2},
+    {1, 0}, {1, 1},
+    {2, 0}, {2, 1}, {2, 2},
+    {3, 0}, {3, 1}, {3, 2},
+};
+ 
+static LPC_CTxxBx_Type *Timers[4] = {
+    LPC_CT16B0, LPC_CT16B1,
+    LPC_CT32B0, LPC_CT32B1
+};
+
+
+void FastPWM::initFastPWM( void ) {
+    //Sadly the 11u24 pwm object does not store match register/pwm object for some reason
+    //It recalculates it everytime, we just do it once because we are awesome
+    timer_mr tid = pwm_timer_map[_pwm.pwm];
+    pwm_obj = Timers[tid.timer];
+    PWM_MR = &pwm_obj->MR[tid.mr];
+    
+    if (tid.timer < 2)
+        //16-bit timer
+        bits = 16;
+    else
+        //32-bit timer
+        bits = 32;
+    
+    printf("Bits = %d, PWM = %d, MR = %d\n\r", bits, tid.timer, tid.mr);
+    
+}
+
+void FastPWM::pulsewidth_ticks( uint32_t ticks ) {
+    pwm_obj->TCR = 0x02;
+    *PWM_MR = pwm_obj->MR3 - ticks;  //They inverted PWM on the 11u24
+    pwm_obj->TCR = 0x01; 
+}
+
+void FastPWM::period_ticks( uint32_t ticks ) {
+    pwm_obj->TCR = 0x02;
+    pwm_obj->MR3 = ticks;
+    pwm_obj->TCR = 0x01;   
+}
+
+uint32_t FastPWM::getPeriod( void ) {
+    return pwm_obj->MR3;
+}
+
+uint32_t FastPWM::setPrescaler(uint32_t reqScale) {
+    //If 32-bit, disable auto-scaling, return 1
+    if (bits == 32) {
+        dynamicPrescaler = false;
+        return 1;
+    }
+    
+    //Else 16-bit timer:
+    if (reqScale == 0)
+        //Return prescaler
+        return pwm_obj->PR + 1;
+    if (reqScale > (uint32_t)(1<<16))
+        reqScale = 1<<16;
+    //Else set prescaler, we have to substract one from reqScale since a 0 in PCVAL is prescaler of 1
+    pwm_obj->PR = reqScale - 1;
+
+    return reqScale;
+}
+
+#endif
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Device/FastPWM_LPC1768.cpp	Tue Aug 13 16:54:06 2013 +0000
@@ -0,0 +1,34 @@
+#ifdef TARGET_LPC1768
+
+#include "FastPWM.h"
+
+void FastPWM::initFastPWM( void ) {
+    //Set clock source
+    LPC_SC->PCLKSEL0|=1<<12;
+    bits = 32;
+}
+
+void FastPWM::pulsewidth_ticks( uint32_t ticks ) {
+    *(_pwm.MR) = ticks;
+    LPC_PWM1->LER |= 1 << _pwm.pwm;
+}
+
+void FastPWM::period_ticks( uint32_t ticks ) {
+    LPC_PWM1->MR0 = ticks;
+    LPC_PWM1->LER |= 1 << 0;
+}
+
+uint32_t FastPWM::getPeriod( void ) {
+    return LPC_PWM1->MR0;
+}
+
+//Maybe implemented later, but needing to change the prescaler for a 32-bit
+//timer used in PWM mode is kinda unlikely.
+//If you really need to do it, rejoice, you can make it run so slow a period is over 40,000 year
+uint32_t FastPWM::setPrescaler(uint32_t reqScale) {
+    //Disable dynamic prescaling
+    dynamicPrescaler = false;
+    
+    return 1;
+}
+#endif
\ No newline at end of file
--- a/FastPWM.cpp	Wed Jul 25 07:14:39 2012 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,93 +0,0 @@
-#include "FastPWM.h"
-
-FastPWM::FastPWM(PinName pin) : PWMObject(pin){
-    //Set clock source
-    LPC_SC->PCLKSEL0|=1<<12;
-    
-    _duty=0;
-    _period=0.02;
-    if (pin==p26||pin==LED1) {
-        PWMUnit=1;
-        MR=&LPC_PWM1->MR1;
-        }
-    else if (pin==p25||pin==LED2){
-        PWMUnit=2;
-        MR=&LPC_PWM1->MR2;
-        }
-    else if (pin==p24||pin==LED3){
-        PWMUnit=3;
-        MR=&LPC_PWM1->MR3;
-        }
-    else if (pin==p23||pin==LED4){
-        PWMUnit=4;
-        MR=&LPC_PWM1->MR4;
-        }
-    else if (pin==p22){
-        PWMUnit=5;
-        MR=&LPC_PWM1->MR5;
-        }
-    else if (pin==p21){
-        PWMUnit=6;
-        MR=&LPC_PWM1->MR6;
-        }
-    else
-        error("No hardware PWM pin\n\r");
-    
-    period(_period);
-}
-
-void FastPWM::period(double seconds) {
-    LPC_PWM1->MR0 = (unsigned int) (seconds * (double)F_CLK);
-    LPC_PWM1->LER |= 1;
-    _period = seconds;
-    pulsewidth(_duty*_period);
-}
-
-void FastPWM::period_ms(int ms) {
-    period((double)ms/1000.0);
-}
-
-void FastPWM::period_us(int us) {
-    period((double)us/1000000.0);
-}
-
-void FastPWM::period_us(double us) {
-    period(us/1000000.0);
-}
-
-void FastPWM::pulsewidth(double seconds) {
-    *MR=(unsigned int) (seconds * (double)F_CLK);
-    LPC_PWM1->LER |= 1<<PWMUnit;
-    _duty=seconds/_period;
-}
-
-void FastPWM::pulsewidth_ms(int ms) {
-    pulsewidth((double)ms/1000.0);
-}
-
-void FastPWM::pulsewidth_us(int us) {
-    pulsewidth((double)us/1000000.0);
-}
-
-void FastPWM::pulsewidth_us(double us) {
-    pulsewidth(us/1000000.0);
-}
-
-void FastPWM::write(double duty) {
-    _duty=duty;
-    pulsewidth(duty*_period);
-}
-
-double FastPWM::read( void ) {
-    return _duty;
-    }
-    
-FastPWM & FastPWM::operator= (double value) {
-    write(value);
-    return(*this);
-    }
-    
-FastPWM::operator double() {
-    return _duty;
-}
-    
\ No newline at end of file
--- a/FastPWM.h	Wed Jul 25 07:14:39 2012 +0000
+++ b/FastPWM.h	Tue Aug 13 16:54:06 2013 +0000
@@ -17,14 +17,9 @@
 
 #include "mbed.h"
 
-
 #ifndef FASTPWM_H
 #define FASTPWM_H
 
-#ifndef F_CLK
-#define F_CLK   96000000
-#endif
-
 /** Library that allows faster and/or higher resolution PWM output
   *
   * Library can directly replace standard mbed PWM library. Only limitation is that the maximum PWM period is four times shorter
@@ -33,19 +28,16 @@
   *
   * Contrary to the default mbed library, this library takes doubles instead of floats. The compiler will autocast if needed,
   * but do take into account it is done for a reason, your accuracy will otherwise be limitted by the floating point precision.
-  *
-  * In your program you can define F_CLK if you use a different clock frequency than the default one. 
-  *
-  * Only works on LPC1768 for now. If you want support for the other one, send a PM and I will have a look, but I cannot even compile for it.
   */
-class FastPWM {
+class FastPWM : public PwmOut {
 public:
     /**
     * Create a FastPWM object connected to the specified pin
     *
     * @param pin - PWM pin to connect to
+    * @param prescaler - Clock prescaler, -1 is dynamic (default), 0 is bit random, everything else normal
     */
-    FastPWM(PinName pin);
+    FastPWM(PinName pin, int prescaler = -1);
     
     /**
     * Set the PWM period, specified in seconds (double), keeping the duty cycle the same.
@@ -68,6 +60,14 @@
     void period_us(double us);
     
     /**
+    * Set the PWM period, specified in clock ticks, keeping _pulse width_ the same.
+    *
+    * This function can be used if low overhead is required. Do take into account the result is
+    * board (clock frequency) dependent, and this does not keep an equal duty cycle!
+    */
+    void period_ticks(uint32_t ticks);
+    
+    /**
     * Set the PWM pulsewidth, specified in seconds (double), keeping the period the same.
     */
     void pulsewidth(double seconds);
@@ -88,6 +88,14 @@
     void pulsewidth_us(double us);
     
     /**
+    * Set the PWM period, specified in clock ticks, keeping the period the same.
+    *
+    * This function can be used if low overhead is required. Do take into account the result is
+    * board (clock frequency) dependent!
+    */
+    void pulsewidth_ticks(uint32_t ticks);
+    
+    /**
     * Set the ouput duty-cycle, specified as a percentage (double)
     *
     * @param duty - A double value representing the output duty-cycle, specified as a percentage.  The value should lie between 0.0 (representing on 0%) and 1.0 (representing on 100%).
@@ -111,13 +119,33 @@
     */
     operator double();
     
+    /**
+    * Set the PWM prescaler
+    *
+    * The period of all PWM pins on the same PWM unit have to be reset after using this!
+    *
+    * @param value - The required prescaler. Special values: 0 = lock current prescaler, -1 = use dynamic prescaler
+    * @param return - The prescaler which was set (can differ from requested prescaler if not possible)
+    */
+    int prescaler(int value);
+    
 private:
-    PwmOut PWMObject;
+    void initFastPWM(void);
+    
+    uint32_t setPrescaler( uint32_t reqScale );
+    int calcPrescaler(uint64_t clocks);
+    uint32_t getPeriod( void );
+    
+    void updateTicks( uint32_t prescaler );
+    uint32_t bits;
+    
     double _duty;
-    double _period;
-    unsigned int PWMUnit;
     
-    __IO uint32_t *MR;
+    double dticks, dticks_us;
+    int iticks_ms, iticks_us;
+    
+    bool dynamicPrescaler;
+    
 
 };
 #endif
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/FastPWM_common.cpp	Tue Aug 13 16:54:06 2013 +0000
@@ -0,0 +1,106 @@
+#include "FastPWM.h"
+
+FastPWM::FastPWM(PinName pin, int prescaler) : PwmOut(pin) {
+    initFastPWM();
+    this->prescaler(prescaler);
+    
+    //Set duty cycle on 0%, period on 20ms
+    _duty=0; 
+    period(0.02);
+    
+
+}
+
+void FastPWM::period(double seconds) {
+    if (dynamicPrescaler)
+        calcPrescaler((uint64_t)(seconds * (double) SystemCoreClock));
+
+     period_ticks(seconds * dticks + 0.5);
+    pulsewidth_ticks(getPeriod() * _duty);
+}
+
+void FastPWM::period_ms(int ms) {
+    if (dynamicPrescaler)
+        calcPrescaler(ms * (SystemCoreClock / 1000));
+        
+    period_ticks(ms * iticks_ms);
+    pulsewidth_ticks(getPeriod() * _duty);
+}
+
+void FastPWM::period_us(int us) {
+    if (dynamicPrescaler)
+        calcPrescaler(us * (SystemCoreClock / 1000000));
+    
+    period_ticks(us * iticks_us);
+    pulsewidth_ticks(getPeriod() * _duty);
+}
+
+void FastPWM::period_us(double us) {
+    if (dynamicPrescaler)
+        calcPrescaler((uint64_t)(us * (double)(SystemCoreClock / 1000000)));
+        
+    period_ticks(us * dticks_us + 0.5);
+    pulsewidth_ticks(getPeriod() * _duty);
+}
+
+void FastPWM::pulsewidth(double seconds) {
+    pulsewidth_ticks(seconds * dticks + 0.5);
+}
+
+void FastPWM::pulsewidth_ms(int ms) {
+    pulsewidth_ticks(ms * iticks_ms);
+}
+
+void FastPWM::pulsewidth_us(int us) {
+    pulsewidth_ticks(us * iticks_us);
+}
+
+void FastPWM::pulsewidth_us(double us) {
+    pulsewidth_ticks(us * dticks_us + 0.5);
+}
+
+void FastPWM::write(double duty) {
+    _duty=duty;
+    pulsewidth_ticks(duty*getPeriod());
+}
+
+double FastPWM::read( void ) {
+    return _duty;
+    }
+    
+FastPWM & FastPWM::operator= (double value) {
+    write(value);
+    return(*this);
+    }
+    
+FastPWM::operator double() {
+    return _duty;
+}
+
+int FastPWM::prescaler(int value) {
+    int retval;
+    if (value == -1) {
+        dynamicPrescaler = true;
+        value = 0;
+    }
+    else
+        dynamicPrescaler = false;
+    
+    retval = setPrescaler(value);
+    updateTicks(retval);
+    return retval;
+}
+
+void FastPWM::updateTicks( uint32_t prescaler ) {
+    dticks = SystemCoreClock / (double)prescaler;
+    dticks_us = SystemCoreClock / (double)prescaler / 1000000.0f;
+    iticks_ms = SystemCoreClock / prescaler / 1000;
+    iticks_us = SystemCoreClock / prescaler / 1000000;
+}
+
+int FastPWM::calcPrescaler(uint64_t clocks) {
+    uint32_t scale = (clocks >> bits) + 1;
+    uint32_t retval = setPrescaler(scale);
+    updateTicks(retval);
+    return retval;
+}     
\ No newline at end of file