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.
Dependencies: mbed FastIO FastPWM USBDevice
Fork of Pinscape_Controller by
IRRemote/IRPwmOut.h@77:0b96f6867312, 2017-03-17 (annotated)
- Committer:
- mjr
- Date:
- Fri Mar 17 22:02:08 2017 +0000
- Revision:
- 77:0b96f6867312
- Child:
- 100:1ff35c07217c
New memory pool management; keeping old ones as #ifdefs for now for reference.
Who changed what in which revision?
| User | Revision | Line number | New contents of line |
|---|---|---|---|
| mjr | 77:0b96f6867312 | 1 | // IRPwmOut - KL25Z ONLY |
| mjr | 77:0b96f6867312 | 2 | // |
| mjr | 77:0b96f6867312 | 3 | // This is a lightweight reimplementation of PwmOut for KL25Z only. |
| mjr | 77:0b96f6867312 | 4 | // It's basically a bare-metal version of the parts of PwmOut we |
| mjr | 77:0b96f6867312 | 5 | // need. There are two main differences from the base class: |
| mjr | 77:0b96f6867312 | 6 | // |
| mjr | 77:0b96f6867312 | 7 | // - Since we're KL25Z only, we can be less abstract about the |
| mjr | 77:0b96f6867312 | 8 | // hardware register interface, allowing us to store fewer |
| mjr | 77:0b96f6867312 | 9 | // internal pointers. This makes our memory footprint smaller. |
| mjr | 77:0b96f6867312 | 10 | // |
| mjr | 77:0b96f6867312 | 11 | // - We allow for higher precision in setting the PWM period for |
| mjr | 77:0b96f6867312 | 12 | // short cycles, by setting the TPM pre-scaler to 1 and using |
| mjr | 77:0b96f6867312 | 13 | // fractional microsecond counts. The KL25Z native clock rate |
| mjr | 77:0b96f6867312 | 14 | // is 48MHz, which translates to 20ns ticks - meaning we can get |
| mjr | 77:0b96f6867312 | 15 | // .02us precision in our PWM periods. The base mbed library |
| mjr | 77:0b96f6867312 | 16 | // sets the pre-scaler to 64, allowing only 1.33us precision. |
| mjr | 77:0b96f6867312 | 17 | // The reason it does this is the tradeoff of precision against |
| mjr | 77:0b96f6867312 | 18 | // maximum cycle length. The KL25Z TPM has a 16-bit counter, |
| mjr | 77:0b96f6867312 | 19 | // so the longest cycle is 65536 * one clock. With pre-scaling |
| mjr | 77:0b96f6867312 | 20 | // at 1, this is a maximum 1.365ms cycle, whereas it's 87ms with |
| mjr | 77:0b96f6867312 | 21 | // the pre-scaler at the mbed setting of 64. That's a good |
| mjr | 77:0b96f6867312 | 22 | // default for most applications; for our purposes, though, |
| mjr | 77:0b96f6867312 | 23 | // we're always working with cycles in the 35kHz to 40kHz range, |
| mjr | 77:0b96f6867312 | 24 | // so our cycles are all sub-millisecond. |
| mjr | 77:0b96f6867312 | 25 | |
| mjr | 77:0b96f6867312 | 26 | #ifndef _IRPWMOUT_H_ |
| mjr | 77:0b96f6867312 | 27 | #define _IRPWMOUT_H_ |
| mjr | 77:0b96f6867312 | 28 | |
| mjr | 77:0b96f6867312 | 29 | #include <mbed.h> |
| mjr | 77:0b96f6867312 | 30 | #include <pinmap.h> |
| mjr | 77:0b96f6867312 | 31 | #include <PeripheralPins.h> |
| mjr | 77:0b96f6867312 | 32 | #include <clk_freqs.h> |
| mjr | 77:0b96f6867312 | 33 | |
| mjr | 77:0b96f6867312 | 34 | class IRPwmOut |
| mjr | 77:0b96f6867312 | 35 | { |
| mjr | 77:0b96f6867312 | 36 | public: |
| mjr | 77:0b96f6867312 | 37 | IRPwmOut(PinName pin) |
| mjr | 77:0b96f6867312 | 38 | { |
| mjr | 77:0b96f6867312 | 39 | // determine the channel |
| mjr | 77:0b96f6867312 | 40 | PWMName pwm = (PWMName)pinmap_peripheral(pin, PinMap_PWM); |
| mjr | 77:0b96f6867312 | 41 | MBED_ASSERT(pwm != (PWMName)NC); |
| mjr | 77:0b96f6867312 | 42 | unsigned int port = (unsigned int)pin >> PORT_SHIFT; |
| mjr | 77:0b96f6867312 | 43 | unsigned int tpm_n = (pwm >> TPM_SHIFT); |
| mjr | 77:0b96f6867312 | 44 | this->ch_n = (pwm & 0xFF); |
| mjr | 77:0b96f6867312 | 45 | |
| mjr | 77:0b96f6867312 | 46 | // get the system clock rate |
| mjr | 77:0b96f6867312 | 47 | if (mcgpllfll_frequency()) { |
| mjr | 77:0b96f6867312 | 48 | SIM->SOPT2 |= SIM_SOPT2_TPMSRC(1); // Clock source: MCGFLLCLK or MCGPLLCLK |
| mjr | 77:0b96f6867312 | 49 | pwm_clock = mcgpllfll_frequency() / 1000000.0f; |
| mjr | 77:0b96f6867312 | 50 | } else { |
| mjr | 77:0b96f6867312 | 51 | SIM->SOPT2 |= SIM_SOPT2_TPMSRC(2); // Clock source: ExtOsc |
| mjr | 77:0b96f6867312 | 52 | pwm_clock = extosc_frequency() / 1000000.0f; |
| mjr | 77:0b96f6867312 | 53 | } |
| mjr | 77:0b96f6867312 | 54 | |
| mjr | 77:0b96f6867312 | 55 | // enable the clock gate on the port (PTx) and TPM module |
| mjr | 77:0b96f6867312 | 56 | SIM->SCGC5 |= 1 << (SIM_SCGC5_PORTA_SHIFT + port); |
| mjr | 77:0b96f6867312 | 57 | SIM->SCGC6 |= 1 << (SIM_SCGC6_TPM0_SHIFT + tpm_n); |
| mjr | 77:0b96f6867312 | 58 | |
| mjr | 77:0b96f6867312 | 59 | // fix the pre-scaler at divide-by-1 for maximum precision |
| mjr | 77:0b96f6867312 | 60 | const uint32_t clkdiv = 0; |
| mjr | 77:0b96f6867312 | 61 | |
| mjr | 77:0b96f6867312 | 62 | // set up the TPM registers: counter enabled, pre-scaler as set above, |
| mjr | 77:0b96f6867312 | 63 | // no interrupts, high 'true', edge aligned |
| mjr | 77:0b96f6867312 | 64 | tpm = (TPM_Type *)(TPM0_BASE + 0x1000 * tpm_n); |
| mjr | 77:0b96f6867312 | 65 | tpm->SC = TPM_SC_CMOD(1) | TPM_SC_PS(clkdiv); |
| mjr | 77:0b96f6867312 | 66 | tpm->CONTROLS[ch_n].CnSC = (TPM_CnSC_MSB_MASK | TPM_CnSC_ELSB_MASK); |
| mjr | 77:0b96f6867312 | 67 | |
| mjr | 77:0b96f6867312 | 68 | // clear the count register to turn off the output |
| mjr | 77:0b96f6867312 | 69 | tpm->CNT = 0; |
| mjr | 77:0b96f6867312 | 70 | |
| mjr | 77:0b96f6867312 | 71 | // default to 1ms period |
| mjr | 77:0b96f6867312 | 72 | period_us(1000.0f); |
| mjr | 77:0b96f6867312 | 73 | printf("IRPwmOut, SC=%08lx, CnSC=%08lx\r\n", |
| mjr | 77:0b96f6867312 | 74 | tpm->SC, tpm->CONTROLS[ch_n].CnSC); |
| mjr | 77:0b96f6867312 | 75 | |
| mjr | 77:0b96f6867312 | 76 | // wire the pinout |
| mjr | 77:0b96f6867312 | 77 | pinmap_pinout(pin, PinMap_PWM); |
| mjr | 77:0b96f6867312 | 78 | } |
| mjr | 77:0b96f6867312 | 79 | |
| mjr | 77:0b96f6867312 | 80 | float read() |
| mjr | 77:0b96f6867312 | 81 | { |
| mjr | 77:0b96f6867312 | 82 | float v = float(tpm->CONTROLS[ch_n].CnV)/float(tpm->MOD + 1); |
| mjr | 77:0b96f6867312 | 83 | return v > 1.0f ? 1.0f : v; |
| mjr | 77:0b96f6867312 | 84 | } |
| mjr | 77:0b96f6867312 | 85 | |
| mjr | 77:0b96f6867312 | 86 | void write(float val) |
| mjr | 77:0b96f6867312 | 87 | { |
| mjr | 77:0b96f6867312 | 88 | // do the glitch-free write |
| mjr | 77:0b96f6867312 | 89 | glitchFreeWrite(val); |
| mjr | 77:0b96f6867312 | 90 | |
| mjr | 77:0b96f6867312 | 91 | // Reset the counter. This is a workaround for a hardware problem |
| mjr | 77:0b96f6867312 | 92 | // on the KL25Z, namely that the CnV register can only be written |
| mjr | 77:0b96f6867312 | 93 | // once per PWM cycle. Any subsequent attempt to write it in the |
| mjr | 77:0b96f6867312 | 94 | // same cycle will be lost. Resetting the counter forces the end |
| mjr | 77:0b96f6867312 | 95 | // of the cycle and makes the register writable again. This isn't |
| mjr | 77:0b96f6867312 | 96 | // an ideal workaround because it causes visible brightness glitching |
| mjr | 77:0b96f6867312 | 97 | // if the caller writes new values repeatedly, such as when fading |
| mjr | 77:0b96f6867312 | 98 | // lights in or out. |
| mjr | 77:0b96f6867312 | 99 | tpm->CNT = 0; |
| mjr | 77:0b96f6867312 | 100 | } |
| mjr | 77:0b96f6867312 | 101 | |
| mjr | 77:0b96f6867312 | 102 | // Write a new value without forcing the current PWM cycle to end. |
| mjr | 77:0b96f6867312 | 103 | // This results in glitch-free writing during fades or other series |
| mjr | 77:0b96f6867312 | 104 | // of rapid writes, BUT with the giant caveat that the caller MUST NOT |
| mjr | 77:0b96f6867312 | 105 | // write another value before the current PWM cycle ends. Doing so |
| mjr | 77:0b96f6867312 | 106 | // will cause the later write to be lost. Callers using this must |
| mjr | 77:0b96f6867312 | 107 | // take care, using mechanisms of their own, to limit writes to once |
| mjr | 77:0b96f6867312 | 108 | // per PWM cycle. |
| mjr | 77:0b96f6867312 | 109 | void glitchFreeWrite(float val) |
| mjr | 77:0b96f6867312 | 110 | { |
| mjr | 77:0b96f6867312 | 111 | // limit to 0..1 range |
| mjr | 77:0b96f6867312 | 112 | val = (val < 0.0f ? 0.0f : val > 1.0f ? 1.0f : val); |
| mjr | 77:0b96f6867312 | 113 | |
| mjr | 77:0b96f6867312 | 114 | // Write the duty cycle register. The argument value is a duty |
| mjr | 77:0b96f6867312 | 115 | // cycle on a normalized 0..1 scale; for the hardware, we need to |
| mjr | 77:0b96f6867312 | 116 | // renormalize to the 0..MOD scale, where MOD is the cycle length |
| mjr | 77:0b96f6867312 | 117 | // in clock counts. |
| mjr | 77:0b96f6867312 | 118 | tpm->CONTROLS[ch_n].CnV = (uint32_t)((float)(tpm->MOD + 1) * val); |
| mjr | 77:0b96f6867312 | 119 | } |
| mjr | 77:0b96f6867312 | 120 | |
| mjr | 77:0b96f6867312 | 121 | void period_us(float us) |
| mjr | 77:0b96f6867312 | 122 | { |
| mjr | 77:0b96f6867312 | 123 | if (tpm->SC == (TPM_SC_CMOD(1) | TPM_SC_PS(0)) |
| mjr | 77:0b96f6867312 | 124 | && tpm->CONTROLS[ch_n].CnSC == (TPM_CnSC_MSB_MASK | TPM_CnSC_ELSB_MASK)) |
| mjr | 77:0b96f6867312 | 125 | printf("period_us ok\r\n"); |
| mjr | 77:0b96f6867312 | 126 | else |
| mjr | 77:0b96f6867312 | 127 | printf("period_us regs changed??? %08lx, %08lx\r\n", |
| mjr | 77:0b96f6867312 | 128 | tpm->SC, tpm->CONTROLS[ch_n].CnSC); |
| mjr | 77:0b96f6867312 | 129 | |
| mjr | 77:0b96f6867312 | 130 | float dc = read(); |
| mjr | 77:0b96f6867312 | 131 | tpm->MOD = (uint32_t)(pwm_clock * (float)us) - 1; |
| mjr | 77:0b96f6867312 | 132 | write(dc); |
| mjr | 77:0b96f6867312 | 133 | } |
| mjr | 77:0b96f6867312 | 134 | |
| mjr | 77:0b96f6867312 | 135 | protected: |
| mjr | 77:0b96f6867312 | 136 | // hardware register base, and TPM channel number we're on |
| mjr | 77:0b96f6867312 | 137 | TPM_Type *tpm; |
| mjr | 77:0b96f6867312 | 138 | uint8_t ch_n; |
| mjr | 77:0b96f6867312 | 139 | |
| mjr | 77:0b96f6867312 | 140 | // global clock rate - store statically |
| mjr | 77:0b96f6867312 | 141 | static float pwm_clock; |
| mjr | 77:0b96f6867312 | 142 | }; |
| mjr | 77:0b96f6867312 | 143 | |
| mjr | 77:0b96f6867312 | 144 | #endif |
