Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependents: qonly_controller foc-ed_in_the_bot_compact foc-ed_in_the_bot_compact CurrentModeSine ... more
Fork of FastPWM by
Revision 4:a7b9f778c4b4, committed 2013-08-13
- 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
--- /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
