added prescaler for 16 bit pwm in LPC1347 target

Fork of mbed-dev by mbed official

Committer:
<>
Date:
Fri Sep 02 15:07:44 2016 +0100
Revision:
144:ef7eb2e8f9f7
Parent:
0:9b334a45a8ff
This updates the lib to the mbed lib v125

Who changed what in which revision?

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