| /* Copyright (c) 2013 Daniel Gilbert, loglow@gmail.com |
| |
| Permission is hereby granted, free of charge, to any person obtaining a copy of |
| this software and associated documentation files (the "Software"), to deal in the |
| Software without restriction, including without limitation the rights to use, copy, |
| modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, |
| and to permit persons to whom the Software is furnished to do so, subject to the |
| following conditions: |
| |
| The above copyright notice and this permission notice shall be included in all |
| copies or substantial portions of the Software. |
| |
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, |
| INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A |
| PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT |
| HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
| OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE |
| SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ |
| |
| |
| #include "IntervalTimer.h" |
| |
| |
| // ------------------------------------------------------------ |
| // static class variables need to be reiterated here before use |
| // ------------------------------------------------------------ |
| bool IntervalTimer::PIT_enabled; |
| bool IntervalTimer::PIT_used[]; |
| IntervalTimer::ISR IntervalTimer::PIT_ISR[]; |
| |
| |
| |
| // ------------------------------------------------------------ |
| // these are the ISRs (Interrupt Service Routines) that get |
| // called by each PIT timer when it fires. they're defined here |
| // so that they can auto-clear themselves and so the user can |
| // specify a custom ISR and reassign it as needed |
| // ------------------------------------------------------------ |
| void pit0_isr() { PIT_TFLG0 = 1; IntervalTimer::PIT_ISR[0](); } |
| void pit1_isr() { PIT_TFLG1 = 1; IntervalTimer::PIT_ISR[1](); } |
| void pit2_isr() { PIT_TFLG2 = 1; IntervalTimer::PIT_ISR[2](); } |
| void pit3_isr() { PIT_TFLG3 = 1; IntervalTimer::PIT_ISR[3](); } |
| |
| |
| |
| // ------------------------------------------------------------ |
| // this function inits and starts the timer, using the specified |
| // function as a callback and the period provided. must be passed |
| // the name of a function taking no arguments and returning void. |
| // make sure this function can complete within the time allowed. |
| // attempts to allocate a timer using available resources, |
| // returning true on success or false in case of failure. |
| // period is specified as number of bus cycles |
| // ------------------------------------------------------------ |
| bool IntervalTimer::beginCycles(ISR newISR, uint32_t newValue) { |
| |
| // if this interval timer is already running, stop it |
| if (status == TIMER_PIT) { |
| stop_PIT(); |
| status = TIMER_OFF; |
| } |
| // store callback pointer |
| myISR = newISR; |
| |
| // attempt to allocate this timer |
| if (allocate_PIT(newValue)) status = TIMER_PIT; |
| else status = TIMER_OFF; |
| |
| // check for success and return |
| if (status != TIMER_OFF) return true; |
| return false; |
| |
| } |
| |
| |
| // ------------------------------------------------------------ |
| // stop the timer if it's currently running, using its status |
| // to determine what hardware resources the timer may be using |
| // ------------------------------------------------------------ |
| void IntervalTimer::end() { |
| if (status == TIMER_PIT) stop_PIT(); |
| status = TIMER_OFF; |
| } |
| |
| |
| |
| // ------------------------------------------------------------ |
| // enables the PIT clock bit, the master PIT reg, and sets flag |
| // ------------------------------------------------------------ |
| void IntervalTimer::enable_PIT() { |
| SIM_SCGC6 |= SIM_SCGC6_PIT; |
| PIT_MCR = 0; |
| PIT_enabled = true; |
| } |
| |
| |
| |
| // ------------------------------------------------------------ |
| // disables the master PIT reg, the PIT clock bit, and unsets flag |
| // ------------------------------------------------------------ |
| void IntervalTimer::disable_PIT() { |
| PIT_MCR = 1; |
| SIM_SCGC6 &= ~SIM_SCGC6_PIT; |
| PIT_enabled = false; |
| } |
| |
| |
| |
| // ------------------------------------------------------------ |
| // enables the PIT clock if not already enabled, then checks to |
| // see if any PITs are available for use. if one is available, |
| // it's initialized and started with the specified value, and |
| // the function returns true, otherwise it returns false |
| // ------------------------------------------------------------ |
| bool IntervalTimer::allocate_PIT(uint32_t newValue) { |
| |
| // enable clock to the PIT module if necessary |
| if (!PIT_enabled) enable_PIT(); |
| |
| // check for an available PIT, and if so, start it |
| for (uint8_t id = 0; id < NUM_PIT; id++) { |
| if (!PIT_used[id]) { |
| PIT_id = id; |
| start_PIT(newValue); |
| PIT_used[id] = true; |
| return true; |
| } |
| } |
| |
| // no PIT available |
| return false; |
| |
| } |
| |
| |
| |
| // ------------------------------------------------------------ |
| // configuters a PIT's registers, function pointer, and enables |
| // interrupts, effectively starting the timer upon completion |
| // ------------------------------------------------------------ |
| void IntervalTimer::start_PIT(uint32_t newValue) { |
| |
| // point to the correct registers |
| PIT_LDVAL = &PIT_LDVAL0 + PIT_id * 4; |
| PIT_TCTRL = &PIT_TCTRL0 + PIT_id * 4; |
| IRQ_PIT_CH = IRQ_PIT_CH0 + PIT_id; |
| |
| // point to the correct PIT ISR |
| PIT_ISR[PIT_id] = myISR; |
| |
| // write value to register and enable interrupt |
| *PIT_TCTRL = 0; |
| *PIT_LDVAL = newValue; |
| *PIT_TCTRL = 3; |
| NVIC_ENABLE_IRQ(IRQ_PIT_CH); |
| |
| } |
| |
| |
| |
| // ------------------------------------------------------------ |
| // stops an active PIT by disabling its interrupt, writing to |
| // its control register, and freeing up its state for future use. |
| // also, if no PITs remain in use, disables the core PIT clock |
| // ------------------------------------------------------------ |
| void IntervalTimer::stop_PIT() { |
| |
| // disable interrupt and PIT |
| NVIC_DISABLE_IRQ(IRQ_PIT_CH); |
| *PIT_TCTRL = 0; |
| |
| // free PIT for future use |
| PIT_used[PIT_id] = false; |
| |
| // check if we're still using any PIT |
| for (uint8_t id = 0; id < NUM_PIT; id++) { |
| if (PIT_used[id]) return; |
| } |
| |
| // none used, disable PIT clock |
| disable_PIT(); |
| |
| } |
| |