mbed library sources

Dependents:   Nucleo_blink_led

Fork of mbed-src by mbed official

Files at this revision

API Documentation at this revision

Comitter:
mbed_official
Date:
Tue Feb 24 13:45:08 2015 +0000
Parent:
479:7e81235899b0
Child:
481:ca51ab3eed5a
Commit message:
Synchronized with git revision 1b2a62100ab444910759144c041dcdd45f3dc7c6

Full URL: https://github.com/mbedmicro/mbed/commit/1b2a62100ab444910759144c041dcdd45f3dc7c6/

LPC81x - Update us_ticker.c, using MRT

Changed in this revision

targets/cmsis/TARGET_NXP/TARGET_LPC81X/LPC8xx.h Show annotated file Show diff for this revision Revisions of this file
targets/hal/TARGET_NXP/TARGET_LPC81X/device.h Show annotated file Show diff for this revision Revisions of this file
targets/hal/TARGET_NXP/TARGET_LPC81X/objects.h Show annotated file Show diff for this revision Revisions of this file
targets/hal/TARGET_NXP/TARGET_LPC81X/pwmout_api.c Show annotated file Show diff for this revision Revisions of this file
targets/hal/TARGET_NXP/TARGET_LPC81X/us_ticker.c Show annotated file Show diff for this revision Revisions of this file
--- a/targets/cmsis/TARGET_NXP/TARGET_LPC81X/LPC8xx.h	Sat Feb 21 15:00:07 2015 +0000
+++ b/targets/cmsis/TARGET_NXP/TARGET_LPC81X/LPC8xx.h	Tue Feb 24 13:45:08 2015 +0000
@@ -368,23 +368,46 @@
 } LPC_WKT_TypeDef;
 /*@}*/ /* end of group LPC8xx_WKT */
 
-
 /*------------- Multi-Rate Timer(MRT) --------------------------------------------------*/
-typedef struct {
-__IO uint32_t INTVAL;
-__IO uint32_t TIMER;
-__IO uint32_t CTRL;
-__IO uint32_t STAT;
-} MRT_Channel_cfg_Type;
-
-typedef struct {
-  MRT_Channel_cfg_Type Channel[4];
-   uint32_t Reserved0[1];
-  __IO uint32_t IDLE_CH;
-  __IO uint32_t IRQ_FLAG;
+//New, Copied from lpc824
+/**
+  * @brief Multi-Rate Timer (MRT) (MRT)
+  */
+typedef struct {                                    /*!< (@ 0x40004000) MRT Structure                                          */
+  __IO uint32_t  INTVAL0;                           /*!< (@ 0x40004000) MRT0 Time interval value register. This value
+                                                         is loaded into the TIMER0 register.                                   */
+  __I  uint32_t  TIMER0;                            /*!< (@ 0x40004004) MRT0 Timer register. This register reads the
+                                                         value of the down-counter.                                            */
+  __IO uint32_t  CTRL0;                             /*!< (@ 0x40004008) MRT0 Control register. This register controls
+                                                         the MRT0 modes.                                                       */
+  __IO uint32_t  STAT0;                             /*!< (@ 0x4000400C) MRT0 Status register.                                  */
+  __IO uint32_t  INTVAL1;                           /*!< (@ 0x40004010) MRT0 Time interval value register. This value
+                                                         is loaded into the TIMER0 register.                                   */
+  __I  uint32_t  TIMER1;                            /*!< (@ 0x40004014) MRT0 Timer register. This register reads the
+                                                         value of the down-counter.                                            */
+  __IO uint32_t  CTRL1;                             /*!< (@ 0x40004018) MRT0 Control register. This register controls
+                                                         the MRT0 modes.                                                       */
+  __IO uint32_t  STAT1;                             /*!< (@ 0x4000401C) MRT0 Status register.                                  */
+  __IO uint32_t  INTVAL2;                           /*!< (@ 0x40004020) MRT0 Time interval value register. This value
+                                                         is loaded into the TIMER0 register.                                   */
+  __I  uint32_t  TIMER2;                            /*!< (@ 0x40004024) MRT0 Timer register. This register reads the
+                                                         value of the down-counter.                                            */
+  __IO uint32_t  CTRL2;                             /*!< (@ 0x40004028) MRT0 Control register. This register controls
+                                                         the MRT0 modes.                                                       */
+  __IO uint32_t  STAT2;                             /*!< (@ 0x4000402C) MRT0 Status register.                                  */
+  __IO uint32_t  INTVAL3;                           /*!< (@ 0x40004030) MRT0 Time interval value register. This value
+                                                         is loaded into the TIMER0 register.                                   */
+  __I  uint32_t  TIMER3;                            /*!< (@ 0x40004034) MRT0 Timer register. This register reads the
+                                                         value of the down-counter.                                            */
+  __IO uint32_t  CTRL3;                             /*!< (@ 0x40004038) MRT0 Control register. This register controls
+                                                         the MRT0 modes.                                                       */
+  __IO uint32_t  STAT3;                             /*!< (@ 0x4000403C) MRT0 Status register.                                  */
+  __I  uint32_t  RESERVED0[45];
+  __I  uint32_t  IDLE_CH;                           /*!< (@ 0x400040F4) Idle channel register. This register returns
+                                                         the number of the first idle channel.                                 */
+  __IO uint32_t  IRQ_FLAG;                          /*!< (@ 0x400040F8) Global interrupt flag register                         */
 } LPC_MRT_TypeDef;
 
-
 /*------------- Universal Asynchronous Receiver Transmitter (USART) -----------*/
 /** @addtogroup LPC8xx_UART LPC8xx Universal Asynchronous Receiver/Transmitter
   @{
--- a/targets/hal/TARGET_NXP/TARGET_LPC81X/device.h	Sat Feb 21 15:00:07 2015 +0000
+++ b/targets/hal/TARGET_NXP/TARGET_LPC81X/device.h	Tue Feb 24 13:45:08 2015 +0000
@@ -29,7 +29,7 @@
 #define DEVICE_SERIAL_FC        1
 
 #define DEVICE_I2C              1
-#define DEVICE_I2CSLAVE         0
+#define DEVICE_I2CSLAVE         1
 
 #define DEVICE_SPI              1
 #define DEVICE_SPISLAVE         1
@@ -40,7 +40,7 @@
 
 #define DEVICE_ETHERNET         0
 
-#define DEVICE_PWMOUT           0
+#define DEVICE_PWMOUT           1
 
 #define DEVICE_SEMIHOST         0
 #define DEVICE_LOCALFILESYSTEM  0
--- a/targets/hal/TARGET_NXP/TARGET_LPC81X/objects.h	Sat Feb 21 15:00:07 2015 +0000
+++ b/targets/hal/TARGET_NXP/TARGET_LPC81X/objects.h	Tue Feb 24 13:45:08 2015 +0000
@@ -43,6 +43,11 @@
     unsigned char spi_n;
 };
 
+struct pwmout_s {
+     LPC_SCT_TypeDef* pwm;
+     uint32_t pwm_ch;
+};
+
 #include "gpio_object.h"
 
 #ifdef __cplusplus
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/targets/hal/TARGET_NXP/TARGET_LPC81X/pwmout_api.c	Tue Feb 24 13:45:08 2015 +0000
@@ -0,0 +1,226 @@
+/* mbed Microcontroller Library
+ * Copyright (c) 2006-2013 ARM Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "mbed_assert.h"
+#include "pwmout_api.h"
+#include "cmsis.h"
+#include "pinmap.h"
+#include "mbed_error.h"
+
+// Ported from LPC824 and adapted.
+
+#if DEVICE_PWMOUT
+
+#define PWM_IRQn     SCT_IRQn
+
+// Bit flags for used SCT Outputs
+static unsigned char sct_used = 0;
+static int sct_inited = 0;
+
+// Find available output channel
+// Max number of PWM outputs is 4 on LPC812
+static int get_available_sct() {
+   int i;
+       
+// Find available output channel 0..3
+// Also need one Match register per channel
+    for (i = 0; i < CONFIG_SCT_nOU; i++) {
+//    for (i = 0; i < 4; i++) {
+        if ((sct_used & (1 << i)) == 0)
+            return i;
+    }
+    return -1;
+}
+
+// Any Port pin may be used for PWM.
+// Max number of PWM outputs is 4
+void pwmout_init(pwmout_t* obj, PinName pin) {
+    MBED_ASSERT(pin != (uint32_t)NC);
+
+    int sct_n = get_available_sct();
+    if (sct_n == -1) {
+        error("No available SCT Output");
+    }
+
+    sct_used |= (1 << sct_n);
+
+    obj->pwm =  (LPC_SCT_TypeDef*)LPC_SCT;
+    obj->pwm_ch = sct_n;
+
+    LPC_SCT_TypeDef* pwm = obj->pwm;
+
+    // Init SCT on first use
+    if (! sct_inited) {
+      sct_inited = 1;
+
+      // Enable the SCT clock
+      LPC_SYSCON->SYSAHBCLKCTRL |= (1 << 8);
+
+      // Clear peripheral reset the SCT:
+      LPC_SYSCON->PRESETCTRL |=  (1 << 8);
+
+      // Two 16-bit counters, autolimit (ie reset on Match_0)
+      pwm->CONFIG &= ~(0x1);
+      pwm->CONFIG |= (1 << 17);
+
+      // halt and clear the counter
+      pwm->CTRL_L |= (1 << 2) | (1 << 3);
+
+      // System Clock (30 Mhz) -> Prescaler -> us_ticker (1 MHz)
+      pwm->CTRL_L &= ~(0x7F << 5);
+      pwm->CTRL_L |= (((SystemCoreClock/1000000 - 1) & 0x7F) << 5);
+
+      pwm->EVENT[0].CTRL  = (1 << 12) | 0;                     // Event_0 on Match_0
+      pwm->EVENT[0].STATE = 0xFFFFFFFF;                        // All states
+    
+      // unhalt the counter:
+      //    - clearing bit 2 of the CTRL register
+      pwm->CTRL_L &= ~(1 << 2);
+      
+      // Not using IRQs 
+      //NVIC_SetVector(PWM_IRQn, (uint32_t)pwm_irq_handler);
+      //NVIC_EnableIRQ(PWM_IRQn);    
+    }
+
+    // LPC81x has only one SCT and 4 Outputs
+    // LPC82x has only one SCT and 6 Outputs
+    // LPC1549 has 4 SCTs and 16 Outputs    
+    switch(sct_n) {
+        case 0:
+            // SCTx_OUT0
+            LPC_SWM->PINASSIGN[6] &= ~0xFF000000;
+            LPC_SWM->PINASSIGN[6] |= (pin << 24);
+            break;
+        case 1:
+            // SCTx_OUT1
+            LPC_SWM->PINASSIGN[7] &= ~0x000000FF;
+            LPC_SWM->PINASSIGN[7] |= (pin);
+            break;
+        case 2:
+            // SCTx_OUT2
+            LPC_SWM->PINASSIGN[7] &= ~0x0000FF00;
+            LPC_SWM->PINASSIGN[7] |= (pin << 8);
+            break;
+        case 3:
+            // SCTx_OUT3
+            LPC_SWM->PINASSIGN[7] &= ~0x00FF0000;
+            LPC_SWM->PINASSIGN[7] |= (pin << 16);
+            break;
+        default:
+            break;
+    }
+
+    pwm->EVENT[sct_n + 1].CTRL  = (1 << 12) | (sct_n + 1);  // Event_n on Match_n
+    pwm->EVENT[sct_n + 1].STATE = 0xFFFFFFFF;               // All states
+
+    pwm->OUT[sct_n].SET = (1 << 0);                         // All PWM channels are SET on Event_0
+    pwm->OUT[sct_n].CLR = (1 << (sct_n + 1));               // PWM ch is CLRed on Event_(ch+1)
+   
+    // default to 20ms: standard for servos, and fine for e.g. brightness control
+    pwmout_period_ms(obj, 20);   // 20ms period
+    pwmout_write    (obj, 0.0);  //  0ms pulsewidth, dutycycle 0
+}
+
+void pwmout_free(pwmout_t* obj) {
+    // PWM channel is now free
+    sct_used &= ~(1 << obj->pwm_ch);
+    
+    // Disable the SCT clock when all channels free
+    if (sct_used == 0) {  
+      LPC_SYSCON->SYSAHBCLKCTRL &= ~(1 << 8);
+      sct_inited = 0;
+    };  
+}
+
+// Set new dutycycle (0.0 .. 1.0)
+void pwmout_write(pwmout_t* obj, float value) {
+    //value is new dutycycle
+    if (value < 0.0f) {
+        value = 0.0;
+    } else if (value > 1.0f) {
+        value = 1.0;
+    }
+     
+    // Match_0 is PWM period. Compute new endtime of pulse for current channel
+    uint32_t t_off = (uint32_t)((float)(obj->pwm->MATCHREL[0].L) * value);
+    obj->pwm->MATCHREL[(obj->pwm_ch) + 1].L = t_off; // New endtime
+}
+
+// Get dutycycle (0.0 .. 1.0)
+float pwmout_read(pwmout_t* obj) {
+    uint32_t t_period = obj->pwm->MATCHREL[0].L;
+
+    //Sanity check
+    if (t_period == 0) {
+     return 0.0;
+    };
+     
+    uint32_t t_off  = obj->pwm->MATCHREL[(obj->pwm_ch) + 1].L;
+    float v = (float)t_off/(float)t_period;
+    //Sanity check    
+    return (v > 1.0f) ? (1.0f) : (v);
+}
+
+// Set the PWM period, keeping the duty cycle the same (for this channel only!).
+void pwmout_period(pwmout_t* obj, float seconds){
+    pwmout_period_us(obj, seconds * 1000000.0f);
+}
+
+// Set the PWM period, keeping the duty cycle the same (for this channel only!).
+void pwmout_period_ms(pwmout_t* obj, int ms) {
+    pwmout_period_us(obj, ms * 1000);
+}
+
+// Set the PWM period, keeping the duty cycle the same (for this channel only!).
+void pwmout_period_us(pwmout_t* obj, int us) {
+    
+    uint32_t t_period = obj->pwm->MATCHREL[0].L;  // Current PWM period
+    obj->pwm->MATCHREL[0].L = (uint64_t)us;       // New PWM period
+
+    //Keep the dutycycle for the new PWM period
+    //Should really do this for all active channels!!
+    //This problem exists in all mbed libs.
+
+    //Sanity check
+    if (t_period == 0) {
+      return;
+//      obj->pwm->MATCHREL[(obj->pwm_ch) + 1].L = 0; // New endtime for this channel     
+    }
+    else {    
+      uint32_t t_off  = obj->pwm->MATCHREL[(obj->pwm_ch) + 1].L;
+      float v = (float)t_off/(float)t_period;
+      obj->pwm->MATCHREL[(obj->pwm_ch) + 1].L = (uint64_t)((float)us * (float)v); // New endtime for this channel
+    }   
+}
+
+
+//Set pulsewidth
+void pwmout_pulsewidth(pwmout_t* obj, float seconds) {
+    pwmout_pulsewidth_us(obj, seconds * 1000000.0f);
+}
+
+//Set pulsewidth
+void pwmout_pulsewidth_ms(pwmout_t* obj, int ms){
+    pwmout_pulsewidth_us(obj, ms * 1000);
+}
+
+//Set pulsewidth
+void pwmout_pulsewidth_us(pwmout_t* obj, int us) {
+
+//Should add Sanity check to make sure pulsewidth < period!
+    obj->pwm->MATCHREL[(obj->pwm_ch) + 1].L = (uint64_t)us; // New endtime for this channel
+}
+
+#endif
--- a/targets/hal/TARGET_NXP/TARGET_LPC81X/us_ticker.c	Sat Feb 21 15:00:07 2015 +0000
+++ b/targets/hal/TARGET_NXP/TARGET_LPC81X/us_ticker.c	Tue Feb 24 13:45:08 2015 +0000
@@ -17,77 +17,105 @@
 #include "us_ticker_api.h"
 #include "PeripheralNames.h"
 
-#define US_TICKER_TIMER_IRQn     SCT_IRQn
+//New, using MRT instead of SCT, needed to free up SCT for PWM
+//Ported from LPC824 libs
+static int us_ticker_inited = 0;
+unsigned int ticker_fullcount_us;
+unsigned long int ticker_expired_count_us = 0;
+int MRT_Clock_MHz;
 
-int us_ticker_inited = 0;
+#define US_TICKER_TIMER_IRQn     MRT_IRQn
 
 void us_ticker_init(void) {
-    if (us_ticker_inited) return;
+    
+    if (us_ticker_inited)
+        return;
+
     us_ticker_inited = 1;
     
-    // Enable the SCT clock
-    LPC_SYSCON->SYSAHBCLKCTRL |= (1 << 8);
-    
-    // Clear peripheral reset the SCT:
-    LPC_SYSCON->PRESETCTRL |= (1 << 8);
-    
-    // Unified counter (32 bits)
-    LPC_SCT->CONFIG |= 1;
+    // Calculate MRT clock value (MRT has no prescaler)
+    MRT_Clock_MHz = (SystemCoreClock / 1000000);
+    // Calculate fullcounter value in us (MRT has 31 bits and clock is 30 MHz) 
+    ticker_fullcount_us = 0x80000000UL/MRT_Clock_MHz;
+
+    // Enable the MRT clock
+    LPC_SYSCON->SYSAHBCLKCTRL |= (1 << 10);
+
+    // Clear peripheral reset the MRT
+    LPC_SYSCON->PRESETCTRL |= (1 << 7);
+
+    // Force load interval value (Bit 0-30 is interval value, Bit 31 is Force Load bit)     
+    LPC_MRT->INTVAL0 = 0xFFFFFFFFUL;
+    // Enable Ch0 interrupt, Mode 0 is Repeat Interrupt
+    LPC_MRT->CTRL0 = (0x0 << 1) | (0x1 << 0);
+
+    // Force load interval value (Bit 0-30 is interval value, Bit 31 is Force Load bit)     
+    LPC_MRT->INTVAL1 = 0x80000000UL;
+    // Disable ch1 interrupt, Mode 0 is Repeat Interrupt
+    LPC_MRT->CTRL1 = (0x0 << 1) | (0x0 << 0);
     
-    // halt and clear the counter
-    LPC_SCT->CTRL_L |= (1 << 2) | (1 << 3);
-    
-    // System Clock (12)MHz -> us_ticker (1)MHz
-    LPC_SCT->CTRL_L |= ((SystemCoreClock/1000000 - 1) << 5);
-    
-    // unhalt the counter:
-    //    - clearing bit 2 of the CTRL register
-    LPC_SCT->CTRL_L &= ~(1 << 2);
-    
+    // Set MRT interrupt vector
     NVIC_SetVector(US_TICKER_TIMER_IRQn, (uint32_t)us_ticker_irq_handler);
     NVIC_EnableIRQ(US_TICKER_TIMER_IRQn);
 }
 
+//TIMER0 is used for us ticker and timers (Timer, wait(), wait_us() etc)
 uint32_t us_ticker_read() {
+    
     if (!us_ticker_inited)
         us_ticker_init();
-    
-    return LPC_SCT->COUNT_U;
+
+    // Generate ticker value
+    // MRT source clock is SystemCoreClock (30MHz) and MRT is a 31-bit countdown timer
+    // Calculate expected value using current count and number of expired times to mimic a 32bit timer @ 1 MHz 
+    //
+    // ticker_expired_count_us
+    // The variable ticker_expired_count_us keeps track of the number of 31bits overflows (counted by TIMER0) and
+    // corrects that back to us counts.
+    //
+    // (0x7FFFFFFFUL - LPC_MRT->TIMER0)/MRT_Clock_MHz
+    // The counter is a 31bit downcounter from 7FFFFFFF so correct to actual count-up value and correct
+    // for 30 counts per us.
+    //
+    // Added up these 2 parts result in current us time returned as 32 bits.
+    return (0x7FFFFFFFUL - LPC_MRT->TIMER0)/MRT_Clock_MHz + ticker_expired_count_us;            
 }
 
+//TIMER1 is used for Timestamped interrupts (Ticker(), Timeout())
 void us_ticker_set_interrupt(timestamp_t timestamp) {
-    // halt the counter: 
-    //    - setting bit 2 of the CTRL register
-    LPC_SCT->CTRL_L |= (1 << 2);
     
-    // set timestamp in compare register
-    LPC_SCT->MATCH[0].U = (uint32_t)timestamp;
-    
-    // unhalt the counter:
-    //    - clearing bit 2 of the CTRL register
-    LPC_SCT->CTRL_L &= ~(1 << 2);
+    // MRT source clock is SystemCoreClock (30MHz) and MRT is a 31-bit countdown timer    
+    // Force load interval value (Bit 0-30 is interval value, Bit 31 is Force Load bit)
+    // Note: The MRT has less counter headroom available than the typical mbed 32bit timer @ 1 MHz.
+    //       The calculated counter interval until the next timestamp will be truncated and an
+    //       'early' interrupt will be generated in case the max required count interval exceeds
+    //       the available 31 bits space. However, the mbed us_ticker interrupt handler will 
+    //       check current time against the next scheduled timestamp and simply re-issue the
+    //       same interrupt again when needed. The calculated counter interval will now be smaller.
+    LPC_MRT->INTVAL1 = (((timestamp - us_ticker_read()) * MRT_Clock_MHz) | 0x80000000UL);
     
-    // if events are not enabled, enable them
-    if (!(LPC_SCT->EVEN & 0x01)) {
-        
-        // comb mode = match only
-        LPC_SCT->EVENT[0].CTRL = (1 << 12);
-        
-        // ref manual:
-        //   In simple applications that do not 
-        //   use states, write 0x01 to this 
-        //   register to enable an event
-        LPC_SCT->EVENT[0].STATE |= 0x1;
-        
-        // enable events
-        LPC_SCT->EVEN |= 0x1;
+    // Enable interrupt 
+    LPC_MRT->CTRL1 |= 1;
+}
+
+//Disable Timestamped interrupts triggered by TIMER1
+void us_ticker_disable_interrupt() {
+    //Timer1 for Timestamped interrupts (31 bits downcounter @ SystemCoreClock)    
+    LPC_MRT->CTRL1 &= ~1;
+}
+
+void us_ticker_clear_interrupt() {
+    
+    //Timer1 for Timestamped interrupts (31 bits downcounter @ SystemCoreClock)
+    if (LPC_MRT->STAT1 & 1)
+        LPC_MRT->STAT1 = 1;
+    
+    //Timer0 for us counter (31 bits downcounter @ SystemCoreClock)
+    if (LPC_MRT->STAT0 & 1) {
+        LPC_MRT->STAT0 = 1;
+        // ticker_expired_count_us = (ticker_expired * 0x80000000UL) / MRT_Clock_MHz
+        // The variable ticker_expired_count_us keeps track of the number of 31bits overflows (counted by TIMER0) and
+        // the multiplication/division corrects that back to us counts.
+        ticker_expired_count_us += ticker_fullcount_us;
     }
 }
-
-void us_ticker_disable_interrupt(void) {
-    LPC_SCT->EVEN &= ~1;
-}
-
-void us_ticker_clear_interrupt(void) {
-    LPC_SCT->EVFLAG = 1;
-}