Added pin descriptions for NUCLEO-F303RE. Tested
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