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
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 */
Generated on Wed Jul 13 2022 03:30:10 by 1.7.2