added prescaler for 16 bit pwm in LPC1347 target

Fork of mbed-dev by mbed official

Committer:
bogdanm
Date:
Thu Oct 01 15:25:22 2015 +0300
Revision:
0:9b334a45a8ff
Child:
144:ef7eb2e8f9f7
Initial commit on mbed-dev

Replaces mbed-src (now inactive)

Who changed what in which revision?

UserRevisionLine numberNew contents of line
bogdanm 0:9b334a45a8ff 1 /***************************************************************************//**
bogdanm 0:9b334a45a8ff 2 * @file us_ticker.c
bogdanm 0:9b334a45a8ff 3 *******************************************************************************
bogdanm 0:9b334a45a8ff 4 * @section License
bogdanm 0:9b334a45a8ff 5 * <b>(C) Copyright 2015 Silicon Labs, http://www.silabs.com</b>
bogdanm 0:9b334a45a8ff 6 *******************************************************************************
bogdanm 0:9b334a45a8ff 7 *
bogdanm 0:9b334a45a8ff 8 * Permission is granted to anyone to use this software for any purpose,
bogdanm 0:9b334a45a8ff 9 * including commercial applications, and to alter it and redistribute it
bogdanm 0:9b334a45a8ff 10 * freely, subject to the following restrictions:
bogdanm 0:9b334a45a8ff 11 *
bogdanm 0:9b334a45a8ff 12 * 1. The origin of this software must not be misrepresented; you must not
bogdanm 0:9b334a45a8ff 13 * claim that you wrote the original software.
bogdanm 0:9b334a45a8ff 14 * 2. Altered source versions must be plainly marked as such, and must not be
bogdanm 0:9b334a45a8ff 15 * misrepresented as being the original software.
bogdanm 0:9b334a45a8ff 16 * 3. This notice may not be removed or altered from any source distribution.
bogdanm 0:9b334a45a8ff 17 *
bogdanm 0:9b334a45a8ff 18 * DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Silicon Labs has no
bogdanm 0:9b334a45a8ff 19 * obligation to support this Software. Silicon Labs is providing the
bogdanm 0:9b334a45a8ff 20 * Software "AS IS", with no express or implied warranties of any kind,
bogdanm 0:9b334a45a8ff 21 * including, but not limited to, any implied warranties of merchantability
bogdanm 0:9b334a45a8ff 22 * or fitness for any particular purpose or warranties against infringement
bogdanm 0:9b334a45a8ff 23 * of any proprietary rights of a third party.
bogdanm 0:9b334a45a8ff 24 *
bogdanm 0:9b334a45a8ff 25 * Silicon Labs will not be liable for any consequential, incidental, or
bogdanm 0:9b334a45a8ff 26 * special damages, or any other relief, or for any claim by any third party,
bogdanm 0:9b334a45a8ff 27 * arising from your use of this Software.
bogdanm 0:9b334a45a8ff 28 *
bogdanm 0:9b334a45a8ff 29 ******************************************************************************/
bogdanm 0:9b334a45a8ff 30
bogdanm 0:9b334a45a8ff 31 #include <stddef.h>
bogdanm 0:9b334a45a8ff 32 #include "us_ticker_api.h"
bogdanm 0:9b334a45a8ff 33 #include "cmsis.h"
bogdanm 0:9b334a45a8ff 34 #include "mbed_assert.h"
bogdanm 0:9b334a45a8ff 35 #include "em_cmu.h"
bogdanm 0:9b334a45a8ff 36 #include "em_timer.h"
bogdanm 0:9b334a45a8ff 37 #include "device_peripherals.h"
bogdanm 0:9b334a45a8ff 38 #include "device.h"
bogdanm 0:9b334a45a8ff 39 #include "clocking.h"
bogdanm 0:9b334a45a8ff 40 #include "sleep_api.h"
bogdanm 0:9b334a45a8ff 41 #include "sleepmodes.h"
bogdanm 0:9b334a45a8ff 42
bogdanm 0:9b334a45a8ff 43 #define TIMER_LEAST_ACTIVE_SLEEPMODE EM1
bogdanm 0:9b334a45a8ff 44 /**
bogdanm 0:9b334a45a8ff 45 * Timer functions for microsecond ticker.
bogdanm 0:9b334a45a8ff 46 * mbed expects a 32-bit timer. Since the EFM32 only has 16-bit timers,
bogdanm 0:9b334a45a8ff 47 * the upper 16 bits are implemented in software.
bogdanm 0:9b334a45a8ff 48 */
bogdanm 0:9b334a45a8ff 49
bogdanm 0:9b334a45a8ff 50 static uint8_t us_ticker_inited = 0; // Is ticker initialized yet
bogdanm 0:9b334a45a8ff 51
bogdanm 0:9b334a45a8ff 52 static volatile uint32_t ticker_cnt = 0x2ff00; //Internal overflow count, used to extend internal 16-bit counter to (MHz * 32-bit)
bogdanm 0:9b334a45a8ff 53 static volatile uint16_t ticker_int_rem = 0; //Timer match value for user interrupt
bogdanm 0:9b334a45a8ff 54 static volatile uint32_t ticker_int_cnt = 0; //Amount of overflows until user interrupt
bogdanm 0:9b334a45a8ff 55 static volatile uint8_t ticker_freq_mhz = 0; //Frequency of timer in MHz
bogdanm 0:9b334a45a8ff 56
bogdanm 0:9b334a45a8ff 57 void us_ticker_irq_handler_internal(void)
bogdanm 0:9b334a45a8ff 58 {
bogdanm 0:9b334a45a8ff 59 /* Check for user interrupt expiration */
bogdanm 0:9b334a45a8ff 60 if (TIMER_IntGet(US_TICKER_TIMER) & TIMER_IF_CC0) {
bogdanm 0:9b334a45a8ff 61 if (ticker_int_rem > 0) {
bogdanm 0:9b334a45a8ff 62 TIMER_CompareSet(US_TICKER_TIMER, 0, ticker_int_rem);
bogdanm 0:9b334a45a8ff 63 ticker_int_rem = 0;
bogdanm 0:9b334a45a8ff 64 TIMER_IntClear(US_TICKER_TIMER, TIMER_IF_CC0);
bogdanm 0:9b334a45a8ff 65 } else if (ticker_int_cnt > 0) {
bogdanm 0:9b334a45a8ff 66 ticker_int_cnt--;
bogdanm 0:9b334a45a8ff 67 TIMER_IntClear(US_TICKER_TIMER, TIMER_IF_CC0);
bogdanm 0:9b334a45a8ff 68 } else {
bogdanm 0:9b334a45a8ff 69 us_ticker_irq_handler();
bogdanm 0:9b334a45a8ff 70 }
bogdanm 0:9b334a45a8ff 71 }
bogdanm 0:9b334a45a8ff 72
bogdanm 0:9b334a45a8ff 73 /* Handle timer overflow */
bogdanm 0:9b334a45a8ff 74 if (TIMER_IntGet(US_TICKER_TIMER) & TIMER_IF_OF) {
bogdanm 0:9b334a45a8ff 75 ticker_cnt++;
bogdanm 0:9b334a45a8ff 76 if(ticker_cnt >= (((uint32_t)ticker_freq_mhz) << 16)) ticker_cnt = 0;
bogdanm 0:9b334a45a8ff 77 TIMER_IntClear(US_TICKER_TIMER, TIMER_IF_OF);
bogdanm 0:9b334a45a8ff 78 }
bogdanm 0:9b334a45a8ff 79 }
bogdanm 0:9b334a45a8ff 80
bogdanm 0:9b334a45a8ff 81 void us_ticker_init(void)
bogdanm 0:9b334a45a8ff 82 {
bogdanm 0:9b334a45a8ff 83 if (us_ticker_inited) {
bogdanm 0:9b334a45a8ff 84 return;
bogdanm 0:9b334a45a8ff 85 }
bogdanm 0:9b334a45a8ff 86 us_ticker_inited = 1;
bogdanm 0:9b334a45a8ff 87
bogdanm 0:9b334a45a8ff 88 /* Enable clock for TIMERs */
bogdanm 0:9b334a45a8ff 89 CMU_ClockEnable(US_TICKER_TIMER_CLOCK, true);
bogdanm 0:9b334a45a8ff 90
bogdanm 0:9b334a45a8ff 91 /* Clear TIMER counter value */
bogdanm 0:9b334a45a8ff 92 TIMER_CounterSet(US_TICKER_TIMER, 0);
bogdanm 0:9b334a45a8ff 93
bogdanm 0:9b334a45a8ff 94 /* Get frequency of clock in MHz for scaling ticks to microseconds */
bogdanm 0:9b334a45a8ff 95 ticker_freq_mhz = (REFERENCE_FREQUENCY / 1000000);
bogdanm 0:9b334a45a8ff 96 MBED_ASSERT(ticker_freq_mhz > 0);
bogdanm 0:9b334a45a8ff 97
bogdanm 0:9b334a45a8ff 98 /*
bogdanm 0:9b334a45a8ff 99 * Calculate maximum prescaler that gives at least 1 MHz frequency, while keeping clock as an integer multiple of 1 MHz.
bogdanm 0:9b334a45a8ff 100 * Example: 14 MHz => prescaler = 1 (i.e. DIV2), ticker_freq_mhz = 7;
bogdanm 0:9b334a45a8ff 101 * 24 MHz => prescaler = 3 (i.e. DIV8), ticker_freq_mhz = 3;
bogdanm 0:9b334a45a8ff 102 * 48 MHz => prescaler = 4 (i.e. DIV16), ticker_freq_mhz = 3;
bogdanm 0:9b334a45a8ff 103 * Limit prescaling to maximum prescaler value, which is 10 (DIV1024).
bogdanm 0:9b334a45a8ff 104 */
bogdanm 0:9b334a45a8ff 105 uint32_t prescaler = 0;
bogdanm 0:9b334a45a8ff 106 while((ticker_freq_mhz & 1) == 0 && prescaler <= 10) {
bogdanm 0:9b334a45a8ff 107 ticker_freq_mhz = ticker_freq_mhz >> 1;
bogdanm 0:9b334a45a8ff 108 prescaler++;
bogdanm 0:9b334a45a8ff 109 }
bogdanm 0:9b334a45a8ff 110
bogdanm 0:9b334a45a8ff 111 /* Set prescaler */
bogdanm 0:9b334a45a8ff 112 US_TICKER_TIMER->CTRL = (US_TICKER_TIMER->CTRL & ~_TIMER_CTRL_PRESC_MASK) | (prescaler << _TIMER_CTRL_PRESC_SHIFT);
bogdanm 0:9b334a45a8ff 113
bogdanm 0:9b334a45a8ff 114 /* Select Compare Channel parameters */
bogdanm 0:9b334a45a8ff 115 TIMER_InitCC_TypeDef timerCCInit = TIMER_INITCC_DEFAULT;
bogdanm 0:9b334a45a8ff 116 timerCCInit.mode = timerCCModeCompare;
bogdanm 0:9b334a45a8ff 117
bogdanm 0:9b334a45a8ff 118 /* Configure Compare Channel 0 */
bogdanm 0:9b334a45a8ff 119 TIMER_InitCC(US_TICKER_TIMER, 0, &timerCCInit);
bogdanm 0:9b334a45a8ff 120
bogdanm 0:9b334a45a8ff 121 /* Enable interrupt vector in NVIC */
bogdanm 0:9b334a45a8ff 122 TIMER_IntEnable(US_TICKER_TIMER, TIMER_IEN_OF);
bogdanm 0:9b334a45a8ff 123 NVIC_SetVector(US_TICKER_TIMER_IRQ, (uint32_t) us_ticker_irq_handler_internal);
bogdanm 0:9b334a45a8ff 124 NVIC_EnableIRQ(US_TICKER_TIMER_IRQ);
bogdanm 0:9b334a45a8ff 125
bogdanm 0:9b334a45a8ff 126 /* Set top value */
bogdanm 0:9b334a45a8ff 127 TIMER_TopSet(US_TICKER_TIMER, 0xFFFF);
bogdanm 0:9b334a45a8ff 128
bogdanm 0:9b334a45a8ff 129 /* Start TIMER */
bogdanm 0:9b334a45a8ff 130 TIMER_Enable(US_TICKER_TIMER, true);
bogdanm 0:9b334a45a8ff 131 }
bogdanm 0:9b334a45a8ff 132
bogdanm 0:9b334a45a8ff 133 uint32_t us_ticker_read()
bogdanm 0:9b334a45a8ff 134 {
bogdanm 0:9b334a45a8ff 135 uint32_t volatile countH_old, countH, countL;
bogdanm 0:9b334a45a8ff 136
bogdanm 0:9b334a45a8ff 137 if (!us_ticker_inited) {
bogdanm 0:9b334a45a8ff 138 us_ticker_init();
bogdanm 0:9b334a45a8ff 139 }
bogdanm 0:9b334a45a8ff 140
bogdanm 0:9b334a45a8ff 141 /* Avoid jumping in time by reading high bits twice */
bogdanm 0:9b334a45a8ff 142 do {
bogdanm 0:9b334a45a8ff 143 countH_old = ticker_cnt;
bogdanm 0:9b334a45a8ff 144 /* If the counter overflowed while in the IRQ handler for the CC0 interrupt,
bogdanm 0:9b334a45a8ff 145 * it hasn't had time to update ticker_cnt yet. Take this into account here. */
bogdanm 0:9b334a45a8ff 146 if (TIMER_IntGet(US_TICKER_TIMER) & TIMER_IF_OF) {
bogdanm 0:9b334a45a8ff 147 countH_old += 1;
bogdanm 0:9b334a45a8ff 148 }
bogdanm 0:9b334a45a8ff 149 countL = US_TICKER_TIMER->CNT;
bogdanm 0:9b334a45a8ff 150 countH = ticker_cnt;
bogdanm 0:9b334a45a8ff 151 /* If the counter overflowed while in the IRQ handler for the CC0 interrupt,
bogdanm 0:9b334a45a8ff 152 * it hasn't had time to update ticker_cnt yet. Take this into account here. */
bogdanm 0:9b334a45a8ff 153 if (TIMER_IntGet(US_TICKER_TIMER) & TIMER_IF_OF) {
bogdanm 0:9b334a45a8ff 154 countH += 1;
bogdanm 0:9b334a45a8ff 155 }
bogdanm 0:9b334a45a8ff 156 } while (countH_old != countH);
bogdanm 0:9b334a45a8ff 157
bogdanm 0:9b334a45a8ff 158 /* Merge upper (mhz * 16-bit) and lower 16-bit into 64bit */
bogdanm 0:9b334a45a8ff 159 uint64_t count = ((uint64_t)countH << 16) | (uint64_t)countL;
bogdanm 0:9b334a45a8ff 160 /* Divide by ticker_freq_mhz to get 32-bit 1MHz timestamp */
bogdanm 0:9b334a45a8ff 161 return (count / ticker_freq_mhz);
bogdanm 0:9b334a45a8ff 162 }
bogdanm 0:9b334a45a8ff 163
bogdanm 0:9b334a45a8ff 164 void us_ticker_set_interrupt(timestamp_t timestamp)
bogdanm 0:9b334a45a8ff 165 {
bogdanm 0:9b334a45a8ff 166 int32_t delta = 0, ts = timestamp, time = us_ticker_read();
bogdanm 0:9b334a45a8ff 167
bogdanm 0:9b334a45a8ff 168 if((US_TICKER_TIMER->IEN & TIMER_IEN_CC0) == 0) {
bogdanm 0:9b334a45a8ff 169 //Timer was disabled, but is going to be enabled. Set sleep mode.
bogdanm 0:9b334a45a8ff 170 blockSleepMode(TIMER_LEAST_ACTIVE_SLEEPMODE);
bogdanm 0:9b334a45a8ff 171 }
bogdanm 0:9b334a45a8ff 172 TIMER_IntDisable(US_TICKER_TIMER, TIMER_IEN_CC0);
bogdanm 0:9b334a45a8ff 173
bogdanm 0:9b334a45a8ff 174 delta = ts - time;
bogdanm 0:9b334a45a8ff 175 if(delta <= ticker_freq_mhz) {
bogdanm 0:9b334a45a8ff 176 delta = ticker_freq_mhz;
bogdanm 0:9b334a45a8ff 177 timestamp = us_ticker_read() + 0x100;
bogdanm 0:9b334a45a8ff 178 }
bogdanm 0:9b334a45a8ff 179
bogdanm 0:9b334a45a8ff 180 /* Multiply by ticker_freq_mhz to get clock ticks */
bogdanm 0:9b334a45a8ff 181 delta *= ticker_freq_mhz;
bogdanm 0:9b334a45a8ff 182 /* Overflowing this doesn't matter, since we only need the lower 16 bits */
bogdanm 0:9b334a45a8ff 183 ts *= ticker_freq_mhz;
bogdanm 0:9b334a45a8ff 184
bogdanm 0:9b334a45a8ff 185 /* Split delta between timers */
bogdanm 0:9b334a45a8ff 186 ticker_int_cnt = (((uint64_t)delta) >> 16) & 0xFFFFFFFF;
bogdanm 0:9b334a45a8ff 187 ticker_int_rem = ts & 0xFFFF;
bogdanm 0:9b334a45a8ff 188
bogdanm 0:9b334a45a8ff 189 /* Set compare channel 0 to (current position + lower 16 bits of delta).
bogdanm 0:9b334a45a8ff 190 * If lower 16 bits is a small number, we a do one compare of (current + lower 16 + 0x8000)
bogdanm 0:9b334a45a8ff 191 * and then one of (current + lower 16). Else, we simply use (current + lower 16).
bogdanm 0:9b334a45a8ff 192 *
bogdanm 0:9b334a45a8ff 193 * When time from lower 16 bits have elapsed, run complete cycles with ticker_int_rem as
bogdanm 0:9b334a45a8ff 194 * reference ticker_int_cnt times. */
bogdanm 0:9b334a45a8ff 195 if ((delta & 0xFFFF) < 0x8000 && ticker_int_cnt > 0) {
bogdanm 0:9b334a45a8ff 196 TIMER_CompareSet(US_TICKER_TIMER, 0, ticker_int_rem + 0x8000);
bogdanm 0:9b334a45a8ff 197 ticker_int_cnt--;
bogdanm 0:9b334a45a8ff 198 } else {
bogdanm 0:9b334a45a8ff 199 TIMER_CompareSet(US_TICKER_TIMER, 0, ticker_int_rem);
bogdanm 0:9b334a45a8ff 200 ticker_int_rem = 0;
bogdanm 0:9b334a45a8ff 201 }
bogdanm 0:9b334a45a8ff 202 TIMER_IntClear(US_TICKER_TIMER, TIMER_IFC_CC0);
bogdanm 0:9b334a45a8ff 203 TIMER_IntEnable(US_TICKER_TIMER, TIMER_IEN_CC0);
bogdanm 0:9b334a45a8ff 204 }
bogdanm 0:9b334a45a8ff 205
bogdanm 0:9b334a45a8ff 206 void us_ticker_disable_interrupt(void)
bogdanm 0:9b334a45a8ff 207 {
bogdanm 0:9b334a45a8ff 208 if((US_TICKER_TIMER->IEN & TIMER_IEN_CC0) != 0) {
bogdanm 0:9b334a45a8ff 209 //Timer was enabled, but is going to get disabled. Clear sleepmode.
bogdanm 0:9b334a45a8ff 210 unblockSleepMode(TIMER_LEAST_ACTIVE_SLEEPMODE);
bogdanm 0:9b334a45a8ff 211 }
bogdanm 0:9b334a45a8ff 212 /* Disable compare channel interrupts */
bogdanm 0:9b334a45a8ff 213 TIMER_IntDisable(US_TICKER_TIMER, TIMER_IEN_CC0);
bogdanm 0:9b334a45a8ff 214 }
bogdanm 0:9b334a45a8ff 215
bogdanm 0:9b334a45a8ff 216 void us_ticker_clear_interrupt(void)
bogdanm 0:9b334a45a8ff 217 {
bogdanm 0:9b334a45a8ff 218 /* Clear compare channel interrupts */
bogdanm 0:9b334a45a8ff 219 TIMER_IntClear(US_TICKER_TIMER, TIMER_IFC_CC0);
bogdanm 0:9b334a45a8ff 220 }