An I/O controller for virtual pinball machines: accelerometer nudge sensing, analog plunger input, button input encoding, LedWiz compatible output controls, and more.

Dependencies:   mbed FastIO FastPWM USBDevice

Fork of Pinscape_Controller by Mike R

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers IRPwmOut.h Source File

IRPwmOut.h

00001 // This class is not currently used - it's been replaced by IRTransmitter
00002 // and NewPwm.
00003 #if 0
00004 
00005 // IRPwmOut - KL25Z ONLY
00006 //
00007 // This is a lightweight reimplementation of PwmOut for KL25Z only.
00008 // It's basically a bare-metal version of the parts of PwmOut we
00009 // need.  There are two main differences from the base class:
00010 //
00011 // - Since we're KL25Z only, we can be less abstract about the
00012 //   hardware register interface, allowing us to store fewer
00013 //   internal pointers.  This makes our memory footprint smaller.
00014 //
00015 // - We allow for higher precision in setting the PWM period for
00016 //   short cycles, by setting the TPM pre-scaler to 1 and using
00017 //   fractional microsecond counts.  The KL25Z native clock rate
00018 //   is 48MHz, which translates to 20ns ticks - meaning we can get
00019 //   .02us precision in our PWM periods.  The base mbed library 
00020 //   sets the pre-scaler to 64, allowing only 1.33us precision.
00021 //   The reason it does this is the tradeoff of precision against
00022 //   maximum cycle length.  The KL25Z TPM has a 16-bit counter,
00023 //   so the longest cycle is 65536 * one clock.  With pre-scaling
00024 //   at 1, this is a maximum 1.365ms cycle, whereas it's 87ms with
00025 //   the pre-scaler at the mbed setting of 64.  That's a good
00026 //   default for most applications; for our purposes, though,
00027 //   we're always working with cycles in the 35kHz to 40kHz range,
00028 //   so our cycles are all sub-millisecond.
00029 
00030 #ifndef _IRPWMOUT_H_
00031 #define _IRPWMOUT_H_
00032 
00033 #include <mbed.h>
00034 #include <pinmap.h>
00035 #include <PeripheralPins.h>
00036 #include <clk_freqs.h>
00037 
00038 class IRPwmOut
00039 {
00040 public:
00041     IRPwmOut(PinName pin)
00042     {
00043         // determine the channel
00044         PWMName pwm = (PWMName)pinmap_peripheral(pin, PinMap_PWM);
00045         MBED_ASSERT(pwm != (PWMName)NC);
00046         unsigned int port  = (unsigned int)pin >> PORT_SHIFT;
00047         unsigned int tpm_n = (pwm >> TPM_SHIFT);
00048         this->ch_n  = (pwm & 0xFF);
00049         
00050         // get the system clock rate    
00051         if (mcgpllfll_frequency()) {
00052             SIM->SOPT2 |= SIM_SOPT2_TPMSRC(1); // Clock source: MCGFLLCLK or MCGPLLCLK
00053             pwm_clock = mcgpllfll_frequency() / 1000000.0f;
00054         } else {
00055             SIM->SOPT2 |= SIM_SOPT2_TPMSRC(2); // Clock source: ExtOsc
00056             pwm_clock = extosc_frequency() / 1000000.0f;
00057         }
00058         
00059         // enable the clock gate on the port (PTx) and TPM module
00060         SIM->SCGC5 |= 1 << (SIM_SCGC5_PORTA_SHIFT + port);
00061         SIM->SCGC6 |= 1 << (SIM_SCGC6_TPM0_SHIFT + tpm_n);
00062 
00063         // fix the pre-scaler at divide-by-1 for maximum precision    
00064         const uint32_t clkdiv = 0;
00065 
00066         // set up the TPM registers: counter enabled, pre-scaler as set above,
00067         // no interrupts, high 'true', edge aligned
00068         tpm = (TPM_Type *)(TPM0_BASE + 0x1000 * tpm_n);
00069         tpm->SC = TPM_SC_CMOD(1) | TPM_SC_PS(clkdiv);
00070         tpm->CONTROLS[ch_n].CnSC = (TPM_CnSC_MSB_MASK | TPM_CnSC_ELSB_MASK);
00071     
00072         // clear the count register to turn off the output
00073         tpm->CNT = 0;
00074         
00075         // default to 1ms period
00076         period_us(1000.0f);
00077         //printf("IRPwmOut,  SC=%08lx, CnSC=%08lx\r\n", tpm->SC, tpm->CONTROLS[ch_n].CnSC);
00078     
00079         // wire the pinout
00080         pinmap_pinout(pin, PinMap_PWM);
00081     }
00082     
00083     float read()
00084     {
00085         float v = float(tpm->CONTROLS[ch_n].CnV)/float(tpm->MOD + 1);
00086         return v > 1.0f ? 1.0f : v;
00087     }
00088     
00089     void write(float val)
00090     {
00091         // do the glitch-free write
00092         glitchFreeWrite(val);
00093         
00094         // Reset the counter.  This is a workaround for a hardware problem
00095         // on the KL25Z, namely that the CnV register can only be written
00096         // once per PWM cycle.  Any subsequent attempt to write it in the
00097         // same cycle will be lost.  Resetting the counter forces the end
00098         // of the cycle and makes the register writable again.  This isn't
00099         // an ideal workaround because it causes visible brightness glitching
00100         // if the caller writes new values repeatedly, such as when fading
00101         // lights in or out.
00102         tpm->CNT = 0;    
00103     }
00104 
00105     // Write a new value without forcing the current PWM cycle to end.
00106     // This results in glitch-free writing during fades or other series
00107     // of rapid writes, BUT with the giant caveat that the caller MUST NOT
00108     // write another value before the current PWM cycle ends.  Doing so
00109     // will cause the later write to be lost.  Callers using this must 
00110     // take care, using mechanisms of their own, to limit writes to once
00111     // per PWM cycle.
00112     void glitchFreeWrite(float val)
00113     {
00114         // limit to 0..1 range
00115         val = (val < 0.0f ? 0.0f : val > 1.0f ? 1.0f : val);
00116     
00117         // Write the duty cycle register.  The argument value is a duty
00118         // cycle on a normalized 0..1 scale; for the hardware, we need to
00119         // renormalize to the 0..MOD scale, where MOD is the cycle length 
00120         // in clock counts.  
00121         tpm->CONTROLS[ch_n].CnV = (uint32_t)((float)(tpm->MOD + 1) * val);
00122     }
00123     
00124     void period_us(float us)
00125     {
00126         // if (tpm->SC == (TPM_SC_CMOD(1) | TPM_SC_PS(0))
00127         //     && tpm->CONTROLS[ch_n].CnSC == (TPM_CnSC_MSB_MASK | TPM_CnSC_ELSB_MASK))
00128         //     printf("period_us ok\r\n");
00129         // else
00130         //     printf("period_us regs changed??? %08lx, %08lx\r\n", tpm->SC, tpm->CONTROLS[ch_n].CnSC);
00131 
00132         float dc = read();
00133         tpm->MOD = (uint32_t)(pwm_clock * (float)us) - 1;
00134         write(dc);
00135     }
00136     
00137 protected:
00138     // hardware register base, and TPM channel number we're on
00139     TPM_Type *tpm;
00140     uint8_t ch_n;
00141     
00142     // global clock rate - store statically
00143     static float pwm_clock;
00144 };
00145 
00146 #endif
00147 
00148 #endif /* 0 */