/**************************************************************************/ /*! @file timer16.c @author K. Townsend (microBuilder.eu) @section DESCRIPTION Generic code for both 16-bit timers. @warning 16-bit timers are limited to roughly ~0.91ms (or 910uS) on a system running at 72MHz since: @code 1 mS = CFG_CPU_CCLK / 1000 = 72000000 / 1000 = 72000 'ticks' @endcode Meaning that 0xFFFF (65535) 'ticks' = 0.910208 milliseconds or 910 microseconds. @section Example @code #include "/core/cpu/cpu.h" #include "/core/timer16/timer16.h" // Instantiated in timer16.h extern volatile uint32_t timer16_0_counter; ... cpuInit(); // Initialise timer0 with a delay of 0xFFFF, which will cause the // timer interrupt to fire every 65535 ticks and increment // timer16_0_counter by 1 timer16Init(0, 0xFFFF); // Enable the timer timer16Enable(0); // At this point timer16_0_counter should start incrementing by 1 // every 65535 ticks @endcode @section LICENSE Software License Agreement (BSD License) Copyright (c) 2010, microBuilder SARL All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /**************************************************************************/ #include "timer16.h" volatile uint32_t timer16_0_counter = 0; volatile uint32_t timer16_1_counter = 0; #ifdef CFG_PWM volatile uint32_t pwmCounter = 0; extern volatile uint32_t pwmMaxPulses; // See drivers/pwm/pwm.c #endif /**************************************************************************/ /*! @brief Causes a blocking delay for the specified number of clock ticks. @note The exact duration of this delay depends on the speed of the system clock, but it will invariably be short because of the 16-bit limitation. For example, on a system with a 72MHz clock, a 1mS delay would be equal to 72,000 ticks, which is already above the maximum 16-bit value of 65,535. Thus, the maximum delay measured in mS with a 72MHz clock is ~0.91mS. @param[in] timerNum The 16-bit timer to user (0..1) @param[in] delayInTicks The number of clock ticks to delay (0..65534) @section Example @code #include "/core/cpu/cpu.h" #include "/core/timer16/timer16.h" int main(void) { cpuInit(); // Initialise timer 0 ... delay is provided but not used here timer16Init(0, 0xFFFF); // Enable the timer timer16Enable(0); while(1) { // Cause blocking delay for 36000 ticks (0.5mS @ 72MHz) // Note: The delay must be 65534 or less (16-bit value) timer16DelayTicks(0, 36000); } } @endcode */ /**************************************************************************/ void timer16DelayTicks(uint8_t timerNum, uint16_t delayInTicks) { // ToDo: Verify incoming value if (timerNum == 0) { /* Reset the timer */ TMR_TMR16B0TCR = TMR_TMR16B0TCR_COUNTERRESET_ENABLED; /* Set the prescaler to zero */ TMR_TMR16B0PR = 0x00; TMR_TMR16B0MR0 = delayInTicks; /* Reset all interrupts */ TMR_TMR16B0IR = TMR_TMR16B0IR_MASK_ALL; /* Stop timer on match (MR0) */ TMR_TMR16B0MCR = TMR_TMR16B0MCR_MR0_STOP_ENABLED; /* Start timer */ TMR_TMR16B0TCR = TMR_TMR16B0TCR_COUNTERENABLE_ENABLED; /* Wait until the delay time has elapsed */ while (TMR_TMR16B0TCR & TMR_TMR16B0TCR_COUNTERENABLE_ENABLED); } else if (timerNum == 1) { /* Reset the timer */ TMR_TMR16B1TCR = TMR_TMR16B1TCR_COUNTERRESET_ENABLED; /* Set the prescaler to zero */ TMR_TMR16B1PR = 0x00; TMR_TMR16B1MR0 = delayInTicks; /* Reset all interrupts */ TMR_TMR16B1IR = TMR_TMR16B1IR_MASK_ALL; /* Stop timer on match (MR0) */ TMR_TMR16B1MCR = TMR_TMR16B1MCR_MR0_STOP_ENABLED; /* Start timer */ TMR_TMR16B1TCR = TMR_TMR16B1TCR_COUNTERENABLE_ENABLED; /* Wait until the delay time has elapsed */ while (TMR_TMR16B1TCR & TMR_TMR16B1TCR_COUNTERENABLE_ENABLED); } return; } /**************************************************************************/ /*! @brief Causes a blocking delay for the specified number of microseconds @warning The maximum delay in uS will depend on the clock speed, but running at 72MHz the maximum delay (MR = 0xFFFF) would be 910uS (0xFFFF / 72 = 910), or 0.91 milliseconds. @param[in] timerNum The 16-bit timer to user (0..1) @param[in] delayInUs The number of microseconds to wait @section Example @code #include "/core/cpu/cpu.h" #include "/core/timer16/timer16.h" int main(void) { cpuInit(); // Initialise timer 0 ... delay is provided but not used here timer16Init(0, 0xFFFF); // Enable the timer timer16Enable(0); while(1) { // Cause blocking delay for 500 microseconds (0.5mS) timer16DelayUS(0, 500); } } @endcode */ /**************************************************************************/ void timer16DelayUS(uint8_t timerNum, uint16_t delayInUS) { // ToDo: Check if the appropriate timer is enabled first? if (timerNum == 0) { /* Reset the timer */ TMR_TMR16B0TCR = TMR_TMR16B0TCR_COUNTERRESET_ENABLED; /* Set the prescaler to zero */ TMR_TMR16B0PR = 0x00; TMR_TMR16B0MR0 = delayInUS * ((CFG_CPU_CCLK/SCB_SYSAHBCLKDIV)/1000000); /* Reset all interrupts */ TMR_TMR16B0IR = TMR_TMR16B0IR_MASK_ALL; /* Stop timer on match (MR0) */ TMR_TMR16B0MCR = TMR_TMR16B0MCR_MR0_STOP_ENABLED; /* Start timer */ TMR_TMR16B0TCR = TMR_TMR16B0TCR_COUNTERENABLE_ENABLED; /* Wait until the delay time has elapsed */ while (TMR_TMR16B0TCR & TMR_TMR16B0TCR_COUNTERENABLE_ENABLED); } else if (timerNum == 1) { /* Reset the timer */ TMR_TMR16B1TCR = TMR_TMR16B1TCR_COUNTERRESET_ENABLED; /* Set the prescaler to zero */ TMR_TMR16B1PR = 0x00; TMR_TMR16B1MR0 = delayInUS * ((CFG_CPU_CCLK/SCB_SYSAHBCLKDIV)/1000000); /* Reset all interrupts */ TMR_TMR16B1IR = TMR_TMR16B1IR_MASK_ALL; /* Stop timer on match (MR0) */ TMR_TMR16B1MCR = TMR_TMR16B1MCR_MR0_STOP_ENABLED; /* Start timer */ TMR_TMR16B1TCR = TMR_TMR16B1TCR_COUNTERENABLE_ENABLED; /* Wait until the delay time has elapsed */ while (TMR_TMR16B1TCR & TMR_TMR16B1TCR_COUNTERENABLE_ENABLED); } return; } /**************************************************************************/ /*! @brief Interrupt handler for 16-bit timer 0 */ /**************************************************************************/ void TIMER16_0_IRQHandler(void) { /* Clear the interrupt flag */ TMR_TMR16B0IR = TMR_TMR16B0IR_MR0; /* Increment timer counter by 1 (it will automatically roll back to 0) */ timer16_0_counter++; return; } /**************************************************************************/ /*! @brief Interrupt handler for 16-bit timer 1 */ /**************************************************************************/ void TIMER16_1_IRQHandler(void) { /* Clear the interrupt flag */ TMR_TMR16B1IR = TMR_TMR16B1IR_MR0; /* Increment timer counter by 1 (it will automatically roll back to 0) */ timer16_1_counter++; #ifdef CFG_PWM /* Check if the PWM output should be disabled after pwmMaxPulses pulses */ /* See "drivers/pwm/pwm.c" */ if (TMR_TMR16B1IR & TMR_TMR16B1IR_MR3) { /* Clear the interrupt flag */ TMR_TMR16B1IR = TMR_TMR16B1IR_MR3; if (pwmMaxPulses > 0) { pwmCounter++; if (pwmCounter == pwmMaxPulses) { /* Disable interrupt on MR3 */ TMR_TMR16B1MCR &= ~(TMR_TMR16B1MCR_MR3_INT_MASK); /* Disable Timer */ TMR_TMR16B1TCR &= ~(TMR_TMR16B1TCR_COUNTERENABLE_MASK); /* Reset the counter variables */ pwmCounter = 0; pwmMaxPulses = 0; } } } #endif return; } /**************************************************************************/ /*! @brief Enables the specified timer @param[in] timerNum The 16-bit timer to enable (0..1) */ /**************************************************************************/ void timer16Enable(uint8_t timerNum) { if ( timerNum == 0 ) { TMR_TMR16B0TCR = TMR_TMR16B0TCR_COUNTERENABLE_ENABLED; } else if (timerNum == 1) { TMR_TMR16B1TCR = TMR_TMR16B1TCR_COUNTERENABLE_ENABLED; } return; } /**************************************************************************/ /*! @brief Disables the specified timer @param[in] timerNum The 16-bit timer to disable (0..1) */ /**************************************************************************/ void timer16Disable(uint8_t timerNum) { if ( timerNum == 0 ) { TMR_TMR16B0TCR = TMR_TMR16B0TCR_COUNTERENABLE_DISABLED; } else if (timerNum == 1) { TMR_TMR16B1TCR = TMR_TMR16B1TCR_COUNTERENABLE_DISABLED; } return; } /**************************************************************************/ /*! @brief Resets the specified timer @param[in] timerNum The 16-bit timer to reset (0..1) */ /**************************************************************************/ void timer16Reset(uint8_t timerNum) { uint32_t regVal; if ( timerNum == 0 ) { regVal = TMR_TMR16B0TCR; regVal |= TMR_TMR16B0TCR_COUNTERRESET_ENABLED; TMR_TMR16B0TCR = regVal; } else if (timerNum == 1) { regVal = TMR_TMR16B1TCR; regVal |= TMR_TMR16B1TCR_COUNTERRESET_ENABLED; TMR_TMR16B1TCR = regVal; } return; } /**************************************************************************/ /*! @brief Initialises the specified 16-bit timer, sets the timer interval, resets the timer, and configures the interrupt handler. Initialises a 16-bit timer with the supplied timer interval (the amount of time that passes between each timer 'tick'). Every time that this interval elapses, the timer's interrupt will be fired and the appropriate counter variable will be incremented by one (For example, with CT16B0, 'timer16_0_counter' would be incremented). @param[in] timerNum The 16-bit timer to initiliase (0..1) @param[in] timerInterval The number of clock 'ticks' between resets (0..65534) @warning Care needs to be taken when configuring the timers since the pins are all multiplexed with other peripherals. This code is provided as a starting point, but it will need to be adjusted according to your own situation and pin/peripheral requirements */ /**************************************************************************/ void timer16Init(uint8_t timerNum, uint16_t timerInterval) { // If timerInterval is invalid, use the default value if (timerInterval < 1) { timerInterval = TIMER16_DEFAULTINTERVAL; } if ( timerNum == 0 ) { /* Enable the clock for CT16B0 */ SCB_SYSAHBCLKCTRL |= (SCB_SYSAHBCLKCTRL_CT16B0); /* The physical pins associated with CT16B0 are not enabled by default in order to avoid conflicts with other peripherals. Pin 0.10 (CT16B0_MAT2), for example, can not be used while debugging with a hardware debugger. If you require one or more of these pins, simply uncomment the code below */ /* Configure PIO0.2 as Timer0_16 CAP0 */ // IOCON_PIO0_2 &= ~IOCON_PIO0_2_FUNC_MASK; // IOCON_PIO0_2 |= IOCON_PIO0_2_FUNC_CT16B0_CAP0; /* Configure PIO0.8 as Timer0_16 MAT0 */ // IOCON_PIO0_8 &= ~IOCON_PIO0_8_FUNC_MASK; // IOCON_PIO0_8 |= IOCON_PIO0_8_FUNC_CT16B0_MAT0; /* Configure PIO0.9 as Timer0_16 MAT1 */ // IOCON_PIO0_9 &= ~IOCON_PIO0_9_FUNC_MASK; // IOCON_PIO0_9 |= IOCON_PIO0_9_FUNC_CT16B0_MAT1; /* Configure PIO0.10 as Timer0_16 MAT3 */ // IOCON_JTAG_TCK_PIO0_10 &= ~IOCON_JTAG_TCK_PIO0_10_FUNC_MASK; // IOCON_JTAG_TCK_PIO0_10 |= IOCON_JTAG_TCK_PIO0_10_FUNC_CT16B0_MAT2; timer16_0_counter = 0; TMR_TMR16B0MR0 = timerInterval; /* Configure match control register to raise an interrupt and reset on MR0 */ TMR_TMR16B0MCR = (TMR_TMR16B0MCR_MR0_INT_ENABLED | TMR_TMR16B0MCR_MR0_RESET_ENABLED); /* Enable the TIMER0 interrupt */ NVIC_EnableIRQ(TIMER_16_0_IRQn); } else if ( timerNum == 1 ) { /* Enable the clock for CT16B1 */ SCB_SYSAHBCLKCTRL |= (SCB_SYSAHBCLKCTRL_CT16B1); /* The physical pins associated with CT16B0 are not enabled by default in order to avoid conflicts with other peripherals. Pin 0.10 (CT16B0_MAT2), for example, can not be used while debugging with a hardware debugger. If you require one or more of these pins, simply uncomment the code below */ /* Configure PIO1.8 as Timer1_16 CAP0 */ // IOCON_PIO1_8 &= ~IOCON_PIO1_8_FUNC_MASK; // IOCON_PIO1_8 |= IOCON_PIO1_8_FUNC_CT16B1_CAP0; /* Configure PIO1.9 as Timer1_16 MAT0 */ // IOCON_PIO1_9 &= ~IOCON_PIO1_9_FUNC_MASK; // IOCON_PIO1_9 |= IOCON_PIO1_9_FUNC_CT16B1_MAT0; /* Configure PIO1.10 as Timer1_16 MAT1 */ // IOCON_PIO1_10 &= ~IOCON_PIO1_10_FUNC_MASK; // IOCON_PIO1_10 |= IOCON_PIO1_10_FUNC_CT16B1_MAT1; timer16_1_counter = 0; TMR_TMR16B1MR0 = timerInterval; /* Configure match control register to raise an interrupt and reset on MR0 */ TMR_TMR16B1MCR = (TMR_TMR16B1MCR_MR0_INT_ENABLED | TMR_TMR16B1MCR_MR0_RESET_ENABLED); /* Enable the TIMER1 Interrupt */ NVIC_EnableIRQ(TIMER_16_1_IRQn); } return; }