A board support package for the LPC4088 Display Module.

Dependencies:   DM_HttpServer DM_USBHost

Dependents:   lpc4088_displaymodule_emwin lpc4088_displaymodule_demo_sphere sampleGUI sampleEmptyGUI ... more

Fork of DMSupport by EmbeddedArtists AB

Files at this revision

API Documentation at this revision

Comitter:
embeddedartists
Date:
Mon Nov 04 14:32:50 2019 +0000
Parent:
41:e06e764ff4fd
Commit message:
More updates related to mbed OS 5

Changed in this revision

Bios/BiosLoader.cpp Show annotated file Show diff for this revision Revisions of this file
DMBoard.cpp Show annotated file Show diff for this revision Revisions of this file
DMBoard.h Show annotated file Show diff for this revision Revisions of this file
DM_HttpServer.lib Show annotated file Show diff for this revision Revisions of this file
DM_USBHost.lib Show annotated file Show diff for this revision Revisions of this file
FileSystems/MCIFileSystem.cpp Show annotated file Show diff for this revision Revisions of this file
FileSystems/MCIFileSystem.h Show annotated file Show diff for this revision Revisions of this file
FileSystems/QSPIFileSystem.cpp Show annotated file Show diff for this revision Revisions of this file
FileSystems/QSPIFileSystem.h Show annotated file Show diff for this revision Revisions of this file
Memory/InternalEEPROM.cpp Show annotated file Show diff for this revision Revisions of this file
dm_rtc.cpp Show annotated file Show diff for this revision Revisions of this file
dm_rtc.h Show annotated file Show diff for this revision Revisions of this file
--- a/Bios/BiosLoader.cpp	Wed Oct 23 06:59:29 2019 +0000
+++ b/Bios/BiosLoader.cpp	Mon Nov 04 14:32:50 2019 +0000
@@ -222,12 +222,7 @@
   DMBoard::BoardError err = DMBoard::Ok;
   if (!_initialized) {
     do {
-      
-      // With mbed OS 5 the MPU has been enabled and by default execution from
-      // RAM is not allowed. Calling this function to allow execution since
-      // the BIOS code will be executed from RAM.
-      mbed_mpu_manager_lock_ram_execution();
-      
+            
       // Get the display bios from the DMBoard. DMBoard will have verified it
       // and will keep it in RAM so there is no need to copy it.
       uint8_t* p = NULL;
--- a/DMBoard.cpp	Wed Oct 23 06:59:29 2019 +0000
+++ b/DMBoard.cpp	Mon Nov 04 14:32:50 2019 +0000
@@ -26,7 +26,9 @@
 
 #if defined(DM_BOARD_ENABLE_MEASSURING_PINS)
   #include "meas.h"
-#endif        
+#endif      
+
+#include "dm_rtc.h"  
 
 /******************************************************************************
  * Configuration Compatibility Control
@@ -74,7 +76,8 @@
 DMBoard::DMBoard() : 
     _initialized(false),
 #if defined(DM_BOARD_USE_MCI_FS)
-    _mcifs("mci", P4_16),
+    _mcifs(P4_16),
+    _mciFatFs("mci"),
 #endif
 #if defined(DM_BOARD_USE_QSPI_FS)
     _qspifs("qspi"),
@@ -101,6 +104,17 @@
   BoardError err = Ok;
   if (!_initialized) {
     do {
+      
+      /* 
+       * By default mbed OS 5 comes with the MPU enabled and prevents execution
+       * from RAM and writes to ROM. We need to be able to execute from EAM
+       * (e.g. the BIOS functionality) and also write to ROM area (we are e.g.
+       * using the EEPROM peripheral in the LPC4088 which has an address space
+       * within the ROM area).
+       */      
+      mbed_mpu_manager_lock_ram_execution();
+      mbed_mpu_manager_lock_rom_write();
+      
       // Turn off the buzzer
       _buzzer.period_ms(1);
       _buzzer = 0;
@@ -129,6 +143,10 @@
       freopen("/null", "w", stdout);
 #endif
 
+#if defined(DM_BOARD_USE_MCI_FS)
+    _mciFatFs.mount(&_mcifs);
+#endif
+
 #if defined(DM_BOARD_USE_QSPI) || defined(DM_BOARD_USE_QSPI_FS)
       if (SPIFI::instance().init() != SPIFI::Ok) {
         err = SpifiError;
@@ -156,6 +174,12 @@
         break;
       }
 #endif
+      /*
+       * With mbed OS 5 the rtc driver in target/hal has been disabled
+       * Adding an external rtc driver instead.
+       */
+      attach_rtc(&dm_read_rtc, &dm_write_rtc, &dm_init_rtc, &dm_isenabled_rtc);
+
       _initialized = true;
     } while(0);
   }
--- a/DMBoard.h	Wed Oct 23 06:59:29 2019 +0000
+++ b/DMBoard.h	Mon Nov 04 14:32:50 2019 +0000
@@ -36,6 +36,18 @@
   #include "Registry.h"
 #endif
 
+#if defined(DM_BOARD_USE_USB_HOST) && defined(DM_BOARD_USE_ETHERNET)
+/*
+ * It is not possible to use USB Host and Ethernet at the same time due to a
+ * conflict in memory areas.
+ *
+ * In OS 5 memory area AHBSRAM0 is used by the EMAC driver (lpc17_emac.cpp) and
+ * AHBSRAM1 is used by LWIP as heap (lwip_sys_arch.c). The USB host 
+ * implementation is using AHBSRAM1 (USBHALHost.cpp). 
+ */
+#error "Cannot have both USB Host and Ethernet enabled at the same time. See DMBoard.h"
+#endif
+
 
 /**
  * Example of using the Board class:
@@ -181,6 +193,7 @@
 
 #if defined(DM_BOARD_USE_MCI_FS)
     MCIFileSystem _mcifs;
+    FATFileSystem _mciFatFs;
 #endif
 #if defined(DM_BOARD_USE_QSPI_FS)
     QSPIFileSystem _qspifs;
--- a/DM_HttpServer.lib	Wed Oct 23 06:59:29 2019 +0000
+++ b/DM_HttpServer.lib	Mon Nov 04 14:32:50 2019 +0000
@@ -1,1 +1,1 @@
-http://developer.mbed.org/teams/Embedded-Artists/code/DM_HttpServer/#c1c8276af541
+http://developer.mbed.org/teams/Embedded-Artists/code/DM_HttpServer/#9dcff8cf906a
--- a/DM_USBHost.lib	Wed Oct 23 06:59:29 2019 +0000
+++ b/DM_USBHost.lib	Mon Nov 04 14:32:50 2019 +0000
@@ -1,1 +1,1 @@
-http://developer.mbed.org/teams/Embedded-Artists/code/DM_USBHost/#f2d129436056
+http://developer.mbed.org/teams/Embedded-Artists/code/DM_USBHost/#819bbf04163b
--- a/FileSystems/MCIFileSystem.cpp	Wed Oct 23 06:59:29 2019 +0000
+++ b/FileSystems/MCIFileSystem.cpp	Mon Nov 04 14:32:50 2019 +0000
@@ -581,12 +581,11 @@
  * Public Functions
  *****************************************************************************/
 
-MCIFileSystem::MCIFileSystem(const char* name, PinName cd) :
-    FATFileSystem(name)
+MCIFileSystem::MCIFileSystem(PinName cd) : 
+    _init_ref_count(0), _is_initialized(false)
 {
     pUglyForIRQ = this;
 
-    _Stat = STA_NOINIT;
     memset(&_sdCardInfo, 0, sizeof(SDMMC_CARD_T));
     _eventReceived = false;
     _eventSuccess = false;
@@ -670,90 +669,131 @@
     _eventDmaChannel = GPDMA::instance().acquireChannel(mydmairq);
 }
 
-int MCIFileSystem::disk_initialize() {
+int MCIFileSystem::init() {
+    uint32_t val = core_util_atomic_incr_u32(&_init_ref_count, 1);
+        
+    if (val != 1) {
+        return BD_ERROR_OK;
+    }
 
-    debug_if(MCI_DBG, "mcifs:disk_initialize(), _Stat = %#x\n", _Stat);
+    debug_if(MCI_DBG, "mcifs:disk_initialize()\n");
 
     if (!cardInserted()) {
-      /* No card in the socket */
-      _Stat = STA_NODISK | STA_NOINIT;
+        debug("No card detected\r\n");
+        return BD_ERROR_DEVICE_ERROR;
     }
 
-    if (_Stat != STA_NOINIT) {
-        return _Stat;          /* card is already enumerated */
-    }
-
-    //rtc_init();
-
     /* Initialize the Card Data Strucutre */
     memset(&_sdCardInfo, 0, sizeof(SDMMC_CARD_T));
 
-    /* Reset */
-    _Stat = STA_NOINIT;
-
     /* Enumerate the card once detected. Note this function may block for a little while. */
     int ret = mci_Acquire();
     if (ret != 1) {
       debug("Card Acquire failed... got %d, but expected 1\r\n", ret);
-      return 1;//Stat;
+      return BD_ERROR_DEVICE_ERROR;
     }
 
-    _Stat &= ~STA_NOINIT;
-    return _Stat;
+    _is_initialized = true;
+    return BD_ERROR_OK;
 }
 
-int MCIFileSystem::disk_write(const uint8_t *buffer, uint64_t block_number, uint8_t count) {
-    debug_if(MCI_DBG, "mcifs:disk_write(%#x, %llu, %u), _Stat = %#x\n", (uint32_t)buffer, block_number, count, _Stat);
-    if (_Stat & STA_NOINIT) {
-        // not ready
-        return 1;
-    }
-    if (mci_WriteBlocks((void*)buffer, block_number, count) == SDC_RET_OK) {
-        return 0;
+int MCIFileSystem::deinit() {
+
+    if (!_is_initialized) {
+        return BD_ERROR_OK;
+    }    
+
+    uint32_t val = core_util_atomic_decr_u32(&_init_ref_count, 1);
+
+    if (val) {
+        return BD_ERROR_OK;
     }
 
-    return 1;
+    _is_initialized = false;
+    return BD_ERROR_OK;
 }
 
-int MCIFileSystem::disk_read(uint8_t *buffer, uint64_t block_number, uint8_t count) {
-    debug_if(MCI_DBG, "mcifs:disk_read(%#x, %llu, %u), _Stat = %#x\n", (uint32_t)buffer, block_number, count, _Stat);
-    if (_Stat & STA_NOINIT) {
-        // not ready
-        return _Stat;
+int MCIFileSystem::program(const void *buffer, bd_addr_t addr, bd_size_t size) {
+    ReturnCode status;
+    
+    debug_if(MCI_DBG, "mcifs:disk_write(%#x, %llu, %llu)\n", (uint32_t)buffer, addr, size);
+    
+    if (!_is_initialized) {
+        return BD_ERROR_DEVICE_ERROR;
     }
-    if (mci_ReadBlocks(buffer, block_number, count) == SDC_RET_OK) {
-        return 0;
+    
+    /* need to convert from number of bytes to blocks */
+    addr = addr / MMC_SECTOR_SIZE;
+    size = size / MMC_SECTOR_SIZE;
+    
+    status = mci_WriteBlocks((void*)buffer, addr, size);
+    if (status != SDC_RET_OK) {
+        return status;
     }
 
-    return 1;
+    return BD_ERROR_OK;
 }
 
-int MCIFileSystem::disk_status()
-{
-  debug_if(MCI_DBG, "mcifs:disk_status(), _Stat = %#x\n", _Stat);
-  return _Stat;
+int MCIFileSystem::read(void *buffer, bd_addr_t addr, bd_size_t size) {
+    ReturnCode status;
+    debug_if(MCI_DBG, "mcifs:disk_read(%#x, %llu, %llu)\n", (uint32_t)buffer, addr, size);
+    
+    if (!_is_initialized) {
+        return BD_ERROR_DEVICE_ERROR;
+    }
+    
+    /* need to convert from number of bytes to blocks */
+    addr = addr / MMC_SECTOR_SIZE;
+    size = size / MMC_SECTOR_SIZE;    
+    
+    status = mci_ReadBlocks(buffer, addr, size);
+    if (status != SDC_RET_OK) {
+        return status;
+    }
+
+    return BD_ERROR_OK;
 }
 
-int MCIFileSystem::disk_sync()
+int MCIFileSystem::sync()
 {
-  debug_if(MCI_DBG, "mcifs:disk_sync(), _Stat = %#x\n", _Stat);
-  uint32_t end = us_ticker_read() + 50*1000; // 50ms
-  while (us_ticker_read() < end)
-  {
-    if (mci_GetCardStatus() & R1_READY_FOR_DATA)
+    debug_if(MCI_DBG, "mcifs:disk_sync()\n");
+  
+    if (!_is_initialized) {
+        return BD_ERROR_OK;
+    }  
+  
+    uint32_t end = us_ticker_read() + 50*1000; // 50ms
+    while (us_ticker_read() < end)
     {
-      // card is ready
-      return 0;
+        if (mci_GetCardStatus() & R1_READY_FOR_DATA)
+        {
+            // card is ready
+            return BD_ERROR_OK;
+        }
     }
-  }
-  // timeout while waiting for card to get ready
-  return 1;
+    
+    // timeout while waiting for card to get ready
+    return SDC_RET_TIMEOUT;
 }
 
-uint64_t MCIFileSystem::disk_sectors()
+bd_size_t MCIFileSystem::get_read_size() const
+{
+    return MMC_SECTOR_SIZE;
+}
+
+bd_size_t MCIFileSystem::get_program_size() const
 {
-    debug_if(MCI_DBG, "mcifs:disk_sectors(), _Stat = %#x, returning %u\n", _Stat, _sdCardInfo.blocknr);
-    return _sdCardInfo.blocknr;
+    return MMC_SECTOR_SIZE;
+}
+
+bd_size_t MCIFileSystem::size() const
+{
+    return _sdCardInfo.block_len * MMC_SECTOR_SIZE;
+}
+
+const char *MCIFileSystem::get_type() const
+{
+    return "MCI";
 }
 
 void MCIFileSystem::mci_MCIIRQHandler()
@@ -1386,7 +1426,6 @@
 
   /* compute block length based on CSD response */
   _sdCardInfo.block_len = 1 << mci_GetBits(80, 83, _sdCardInfo.csd);
-
   if ((_sdCardInfo.card_type & CARD_TYPE_HC) && (_sdCardInfo.card_type & CARD_TYPE_SD)) {
     /* See section 5.3.3 CSD Register (CSD Version 2.0) of SD2.0 spec  an explanation for the calculation of these values */
     CSize = mci_GetBits(48, 63, (uint32_t *) _sdCardInfo.csd) + 1;
@@ -1778,6 +1817,7 @@
     // If the driver is having problems reading the card, adding a delay here
     // might help.
     //wait(0.01);
+    ThisThread::sleep_for(10);
   }
   
   if (_eventReceived && _eventSuccess) {
--- a/FileSystems/MCIFileSystem.h	Wed Oct 23 06:59:29 2019 +0000
+++ b/FileSystems/MCIFileSystem.h	Mon Nov 04 14:32:50 2019 +0000
@@ -48,24 +48,27 @@
  * }
  * @endcode
  */
-class MCIFileSystem : public FATFileSystem {
+class MCIFileSystem : public BlockDevice {
 public:
 
     /** Create the File System for accessing an SD/MMC Card using MCI
      *
-     * @param name The name used to access the virtual filesystem
      * @param cd   The pin connected to the CardDetect line
      */
-    MCIFileSystem(const char* name, PinName cd = P4_16);
+    MCIFileSystem(PinName cd = P4_16);
 
     virtual ~MCIFileSystem();
 
-    virtual int disk_initialize();
-    virtual int disk_status();
-    virtual int disk_read(uint8_t * buffer, uint64_t block_number, uint8_t count);
-    virtual int disk_write(const uint8_t * buffer, uint64_t block_number, uint8_t count);
-    virtual int disk_sync();
-    virtual uint64_t disk_sectors();
+    virtual int init();
+    virtual int deinit();
+
+    virtual int sync();
+    virtual int read(void * buffer, bd_addr_t addr, bd_size_t size);
+    virtual int program(const void * buffer, bd_addr_t addr, bd_size_t size);
+    virtual bd_size_t get_read_size() const;
+    virtual bd_size_t get_program_size() const;    
+    virtual bd_size_t size() const; 
+    virtual const char *get_type() const;
 
     void mci_MCIIRQHandler();
     void mci_DMAIRQHandler();
@@ -130,20 +133,20 @@
       uint16_t rca;                /*!< Relative address assigned to card */
       uint32_t speed;              /*!< Speed */
       uint32_t block_len;          /*!< Card sector size */
-      uint32_t device_size;        /*!< Device Size */
+      bd_size_t device_size;        /*!< Device Size */
       uint32_t blocknr;            /*!< Block Number */
       uint32_t clk_rate;           /*!< Clock rate */
     } SDMMC_CARD_T;
 
     typedef enum {
       SDC_RET_OK             =  0,
-      SDC_RET_CMD_FAILED     = -1,
-      SDC_RET_BAD_PARAMETERS = -2,
-      SDC_RET_BUS_NOT_IDLE   = -3,
-      SDC_RET_TIMEOUT        = -4,
-      SDC_RET_ERR_STATE      = -5,
-      SDC_RET_NOT_READY      = -6,
-      SDC_RET_FAILED         = -7,
+      SDC_RET_CMD_FAILED     = -5001,
+      SDC_RET_BAD_PARAMETERS = -5002,
+      SDC_RET_BUS_NOT_IDLE   = -5003,
+      SDC_RET_TIMEOUT        = -5004,
+      SDC_RET_ERR_STATE      = -5005,
+      SDC_RET_NOT_READY      = -5006,
+      SDC_RET_FAILED         = -5007,
     } ReturnCode;
 
     void initMCI();
@@ -192,8 +195,6 @@
     ReturnCode _readBlocks(uint32_t card_type, uint32_t startBlock, uint32_t blockNum) const;
     ReturnCode _writeBlocks(uint32_t card_type, uint32_t startBlock, uint32_t blockNum) const;
 
-
-    uint32_t _Stat;
     SDMMC_CARD_T _sdCardInfo;
 
     DigitalIn* _cardDetect;
@@ -201,6 +202,9 @@
     GPDMA::DMAChannels _eventDmaChannel;    /*!< DMA Channel used for transfer data */
     volatile bool _eventReceived;
     volatile bool _eventSuccess;
+    
+    uint32_t _init_ref_count;
+    bool _is_initialized;
 };
 
 #endif
--- a/FileSystems/QSPIFileSystem.cpp	Wed Oct 23 06:59:29 2019 +0000
+++ b/FileSystems/QSPIFileSystem.cpp	Mon Nov 04 14:32:50 2019 +0000
@@ -1238,8 +1238,9 @@
           // file is not in any sub folder so it is truly in the wanted dir
           nextTocIdx = possible + 1;
           strcpy(cur_entry.d_name, filename);
-          //return &cur_entry;
-          *ent = cur_entry;
+          
+          memcpy(ent->d_name, cur_entry.d_name, sizeof(cur_entry.d_name));
+          return 1;
         }
         
         // this is a file in a subfolder and should not be reported,
@@ -1262,13 +1263,14 @@
           char* pSlash = strchr(cur_entry.d_name, '/');
 //          pSlash++; //with ++ the returned dir name is "mydir/" without ++ "mydir" is returned
           *pSlash = '\0';
-          //return &cur_entry;
-          *ent = cur_entry;
+
+          memcpy(ent->d_name, cur_entry.d_name, sizeof(cur_entry.d_name));
+          return 1;
         }
       }
     }
   }
-  return NULL;
+  return 0;
 }
 
 void QSPIDirHandle::rewind() {
@@ -1293,7 +1295,7 @@
 //       ReadWrite - dictates where to read from, writes ignore it but
 //                   sets the position to the end afterwards
 //
-FileHandle *QSPIFileSystem::open(const char *filename, int flags)
+int QSPIFileSystem::open(FileHandle **file, const char *filename, int flags)
 {
     fresult res = qspifs_init();
 //     if (res == FS_OK) {
@@ -1308,6 +1310,7 @@
 //             res = FS_ERR_INVALID_PARAM;
 //         }
 //     }
+
     if (res == FS_OK) {
         if (strlen(filename) > HEADER_FNAME_STRLEN) {
             // Filename is too long
@@ -1331,11 +1334,14 @@
             res = qspifs_saveTOC();
         }
         if (res == FS_OK) {
-            return new QSPIFileHandle(&fh, flags);
+            *file = new QSPIFileHandle(&fh, flags);
         }
     }
-    debug_if(QSPI_DBG, "QSPIFS: Failed to open: %d\n", res);
-    return NULL;
+    if (res != FS_OK) {
+        debug_if(QSPI_DBG, "QSPIFS: Failed to open: %d\n", res);
+        return -res;
+    }
+    return 0;
 }
 
 int QSPIFileSystem::remove(const char *filename)
@@ -1399,27 +1405,29 @@
     return 0;
 }
 
-DirHandle *QSPIFileSystem::opendir(const char *name)
+int QSPIFileSystem::open(DirHandle **dir, const char *path)
 {
   if (isformatted()) {
-    if (*name == '\0') {
-        // opendir("/qspi/") will result in a call to this function with name=""
-        return QSPIDirHandle::openDir(name);
+    if (*path == '\0') {
+        // opendir("/qspi/") will result in a call to this function with path=""
+        *dir = QSPIDirHandle::openDir(path);
+        return 0;
     }
     
-    FileHandle* fh = open(name, O_RDONLY);
-    if (fh != NULL) {
+    FileHandle* fh;
+    if (open(&fh, path, O_RDONLY) == 0) {
       // Attempting to open a file as a dir
       delete fh;
-      return NULL;
+      return -1;
     }
     
-//       printf("opendir: name '%s'\n", name);
-    if (strlen(name) <= HEADER_DNAME_MAXLEN) {
-      return QSPIDirHandle::openDir(name);
+//       printf("opendir: path '%s'\n", path);
+    if (strlen(path) <= HEADER_DNAME_MAXLEN) {
+      *dir = QSPIDirHandle::openDir(path);
+      return 0;
     }
   }
-  return NULL;
+  return -1;
 }
 
 int QSPIFileSystem::mkdir(const char *name, mode_t mode)
--- a/FileSystems/QSPIFileSystem.h	Wed Oct 23 06:59:29 2019 +0000
+++ b/FileSystems/QSPIFileSystem.h	Mon Nov 04 14:32:50 2019 +0000
@@ -118,10 +118,12 @@
      */
     QSPIFileSystem(const char* name);
 
-    virtual FileHandle *open(const char *filename, int flags);
+    virtual int open(FileHandle **file, const char *filename, int flags);
+    virtual int open(DirHandle **dir, const char *path);
+
     virtual int remove(const char *filename);
     virtual int rename(const char *oldname, const char *newname);
-    virtual DirHandle *opendir(const char *name);
+
     virtual int mkdir(const char *name, mode_t mode);
 
     /** Creates a new file system on the QSPI flash.
--- a/Memory/InternalEEPROM.cpp	Wed Oct 23 06:59:29 2019 +0000
+++ b/Memory/InternalEEPROM.cpp	Mon Nov 04 14:32:50 2019 +0000
@@ -123,11 +123,7 @@
 void InternalEEPROM::init()
 {
   if (!_initialized) {
-    
-    // The EEPROM peripheral is in ROM address space. Must allow 
-    // writes to this address space.
-    mbed_mpu_manager_lock_rom_write();
-             
+                 
     // Bring EEPROM device out of power down mode
     powerUp();
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dm_rtc.cpp	Mon Nov 04 14:32:50 2019 +0000
@@ -0,0 +1,103 @@
+/*
+ *  Copyright 2019 Embedded Artists AB
+ *
+ *  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.
+ */
+ 
+
+/******************************************************************************
+ * Includes
+ *****************************************************************************/
+
+#include "mbed.h"
+#include "dm_rtc.h"
+#include "mbed_mktime.h"
+
+/******************************************************************************
+ * Defines and typedefs
+ *****************************************************************************/
+ 
+/******************************************************************************
+ * External global variables
+ *****************************************************************************/
+
+/******************************************************************************
+ * Local variables
+ *****************************************************************************/
+
+/******************************************************************************
+ * Local Functions
+ *****************************************************************************/
+
+
+/******************************************************************************
+ * Public Functions
+ *****************************************************************************/ 
+ 
+void dm_init_rtc(void)
+{
+    LPC_SC->PCONP |= 0x200; // Ensure power is on
+    LPC_RTC->CCR = 0x00;
+    
+    LPC_RTC->CCR |= 1 << 0; // Ensure the RTC is enabled    
+}
+
+time_t dm_read_rtc(void)
+{
+    // Setup a tm structure based on the RTC
+    struct tm timeinfo;
+    timeinfo.tm_sec = LPC_RTC->SEC;
+    timeinfo.tm_min = LPC_RTC->MIN;
+    timeinfo.tm_hour = LPC_RTC->HOUR;
+    timeinfo.tm_mday = LPC_RTC->DOM;
+    timeinfo.tm_mon = LPC_RTC->MONTH - 1;
+    timeinfo.tm_year = LPC_RTC->YEAR - 1900;
+    
+    // Convert to timestamp
+    time_t t;
+    if (_rtc_maketime(&timeinfo, &t, RTC_4_YEAR_LEAP_YEAR_SUPPORT) == false) {
+        return 0;
+    }
+    
+    return t;    
+}
+
+void dm_write_rtc(time_t t)
+{
+    // Convert the time in to a tm
+    struct tm timeinfo;
+    if (_rtc_localtime(t, &timeinfo, RTC_4_YEAR_LEAP_YEAR_SUPPORT) == false) {
+        return;
+    }
+
+    // Pause clock, and clear counter register (clears us count)
+    LPC_RTC->CCR |= 2;
+    
+    // Set the RTC
+    LPC_RTC->SEC = timeinfo.tm_sec;
+    LPC_RTC->MIN = timeinfo.tm_min;
+    LPC_RTC->HOUR = timeinfo.tm_hour;
+    LPC_RTC->DOM = timeinfo.tm_mday;
+    LPC_RTC->MONTH = timeinfo.tm_mon + 1;
+    LPC_RTC->YEAR = timeinfo.tm_year + 1900;
+    
+    // Restart clock
+    LPC_RTC->CCR &= ~((uint32_t)2);    
+}
+
+int dm_isenabled_rtc(void)
+{
+    return(((LPC_RTC->CCR) & 0x01) != 0);
+}
+
+ 
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dm_rtc.h	Mon Nov 04 14:32:50 2019 +0000
@@ -0,0 +1,25 @@
+/*
+ *  Copyright 2014 Embedded Artists AB
+ *
+ *  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.
+ */
+
+#ifndef DM_RTC_H
+#define DM_RTC_H
+
+void dm_init_rtc(void);
+time_t dm_read_rtc(void);
+void dm_write_rtc(time_t t);
+int dm_isenabled_rtc(void);
+
+#endif
\ No newline at end of file