Mahmood ul Hassan
Published © CC BY-NC

Energy Efficient LED Lights with Adaptive Brightness Control

Energy efficient, IoT-enabled lighting system with adaptive brightness control to complement the daylight instead of simply ON/OFF.

AdvancedFull instructions providedOver 1 day515

Things used in this project

Hardware components

Spresense boards (main & extension)
Sony Spresense boards (main & extension)
×1
ESP8266 ESP-12E
Espressif ESP8266 ESP-12E
×1
APDS9930
×1
OCTAL BUS TRANSCEIVER 74LS245IC
You can use another buffer IC instead of this.
×1
LED Light
×1
DC-DC Buck converter
×1
L9110 motor driver board
×1
PIR Motion Sensor (generic)
PIR Motion Sensor (generic)
×1
Sharp PC817 Opto-isolator IC
×1
Breadboard (generic)
Breadboard (generic)
×1
Resistor 10k ohm
Resistor 10k ohm
×1
Resistor 220 ohm
Resistor 220 ohm
×1
Jumper wires (generic)
Jumper wires (generic)
×1
Male-Header 36 Position 1 Row- Long (0.1")
Male-Header 36 Position 1 Row- Long (0.1")
Optional
×1
12V/15V 1A power supply
choose power supply according to LED light (most >1W LED lights need ~10V).
×1

Software apps and online services

Arduino IDE
Arduino IDE
Adafruit IO
IFTTT Google Assistant
MATLAB
MATLAB
opensource alternative GNU Octave will do the same job. https://www.gnu.org/software/octave/

Hand tools and fabrication machines

Hot glue gun (generic)
Hot glue gun (generic)
Soldering iron (generic)
Soldering iron (generic)

Story

Read more

Schematics

Circuit Diagram

Sony Spresense apk file

The apk file of my code.
Simply Upload it on Sony Spresense and test my project

Code

Main

Arduino
#include <Arduino.h>
#include <Wire.h>
#include <APDS9930.h>
#include <SDHCI.h>
#include <GNSS.h>
#include <U8g2lib.h>
#include <U8x8lib.h>
#include <sdk/chip/cxd56_rtc.h>
#include "sdk/cxd56_rtc.h"

static SpGnss Gnss;                   /**< SpGnss object */

/**
 * @enum ParamSat
 * @brief Satellite system
 */
enum ParamSat {
  eSatGps,            /**< GPS                     World wide coverage  */
  eSatGlonass,        /**< GLONASS                 World wide coverage  */
  eSatGpsSbas,        /**< GPS+SBAS                North America        */
  eSatGpsGlonass,     /**< GPS+Glonass             World wide coverage  */
  eSatGpsQz1c,        /**< GPS+QZSS_L1CA           East Asia & Oceania  */
  eSatGpsGlonassQz1c, /**< GPS+Glonass+QZSS_L1CA   East Asia & Oceania  */
  eSatGpsQz1cQz1S,    /**< GPS+QZSS_L1CA+QZSS_L1S  Japan                */
};

/* Set this parameter depending on your current region. */
static enum ParamSat satType =  eSatGpsGlonass;

bool rtc_status = false;
timespec rtcTime;
time_t timeSinceEpoch = 0;
void setup_rtc();
void printTime(struct timespec *tp, char *msg);
String print2digits(int number);

SDClass SD;
File root;

U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);
static unsigned char light_on_logo[] = {
 0x00,0x00,0x80,0x01,0x84,0x21,0x08,0x10,0x90,0x09,0xe0,0x07,
 0x20,0x04,0xb6,0x6d,0xb6,0x6d,0x20,0x04,0xe0,0x07,0x90,0x09,
 0x08,0x10,0x84,0x21,0x80,0x01,0x00,0x00};
static unsigned char light_off_logo[] = {
 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x01,0xe0,0x07,
 0x20,0x04,0xb0,0x0d,0xb0,0x0d,0x20,0x04,0xe0,0x07,0x80,0x01,
 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
static unsigned char rtc_logo[] = {
 0xff,0xff,0x89,0x91,0x01,0x80,0x03,0xd0,0x01,0x88,0x01,0x84,
 0x01,0x82,0x83,0xc1,0x83,0xc1,0x01,0x82,0x01,0x84,0x01,0x80,
 0x03,0xc0,0x01,0x80,0x89,0x91,0xff,0xff};

APDS9930 apds = APDS9930();
float ambient_light = 0; // can also be an unsigned long
float max_light = 100;

#define PWM_LED_PIN 9
#define PIR_PIN 8

void showLEDLevel(uint8_t led_level);
uint8_t led_level=0;
bool pir_status=false;
volatile uint8_t pir_delay=0;
volatile uint8_t pir_user_delay=10;
bool pir_sw();

void pir_ISR(){
  delayMicroseconds(20);
  if(pir_delay<pir_user_delay){
    pir_delay=pir_user_delay;
  }
}

void setup() {
  pinMode(PWM_LED_PIN, OUTPUT);
  pinMode(PIR_PIN, INPUT);
  attachInterrupt(PIR_PIN, pir_ISR, CHANGE);
  Serial.begin(115200);

  u8g2.begin();
  u8g2.clear();
  u8g2.setContrast(150);

  if (!SD.begin()) {
    Serial.println(F("SD card is not present"));
  }
  
  if (SD.beginUsbMsc()) {
    Serial.println(F("USB MSC Failure!"));
  } else {
    Serial.println(F("*** USB MSC Prepared! ***"));
    Serial.println(F("Insert SD and Connect Extension Board USB to PC."));
  }

  
  if ( apds.init() ) {
    Serial.println(F("APDS-9930 OK"));
  } else {
    Serial.println(F("APDS-9930 ERROR"));
  }
  
  // Start running the APDS-9930 light sensor (no interrupts)
  if ( apds.enableLightSensor(false) ) {
    Serial.println(F("ALS OK"));
  } else {
    Serial.println(F("ALS ERROR"));
  }

  Serial.println(up_rtc_initialize());

  int result;
  result = Gnss.begin();
  if (result != 0){
    Serial.println(F("Gnss begin error!!"));
  }
  else
  {
    Gnss.select(GPS);
    Gnss.select(GLONASS);

    result = Gnss.start(COLD_START);
    if (result != 0){
      Serial.println(F("Gnss start error!!"));
    }
    else{
      Serial.println(F("Gnss setup OK"));
    }
  }
}

float AL_control=0;
uint16_t led_pwm=0;
void loop() {  
  if ( !apds.readAmbientLightLux(ambient_light)){
    Serial.print(F("Error reading light values\n"));
  }
  else{
    Serial.print(F("Ambient: "));
    Serial.print(ambient_light);
    Serial.print(F(" PIR: "));
    Serial.print(digitalRead(PIR_PIN));
    Serial.print(F("\n"));
    if ( ambient_light > max_light ) {
       ambient_light = max_light;
    }
    AL_control=25;
    if(pir_sw()){
      apds.readAmbientLightLux(ambient_light);
      while(ambient_light<AL_control-0.5){
        analogWrite(PWM_LED_PIN, 1023-led_pwm);
        apds.readAmbientLightLux(ambient_light);
        led_pwm=led_pwm+1;
        if(led_pwm>1023) led_pwm=1023;
        delay(10);
      }
      apds.readAmbientLightLux(ambient_light);
      while(ambient_light>AL_control+0.5){
        analogWrite(PWM_LED_PIN, 1023-led_pwm);
        apds.readAmbientLightLux(ambient_light);
        if(led_pwm<700) led_pwm=700;
        led_pwm=led_pwm-1;
        delay(10);
      }
      led_level = map(led_pwm, 700, 1023, 0, 10);
    }
    else{
      analogWrite(PWM_LED_PIN, 1023);
      led_level=0;
    }
  }

  if (rtc_status){
    up_rtc_gettime(&rtcTime);
  }
  else{
    setup_rtc();
  }
  printTime(&rtcTime, "Time");
  delay(900);
}

void setup_rtc(){
  if(!timeSinceEpoch){
    if (Gnss.waitUpdate(-1)){
      SpNavData NavData;
      Gnss.getNavData(&NavData);
      if (NavData.time.year!=1980){
        struct tm tm;
        tm.tm_year = NavData.time.year-1900;
        tm.tm_mon = NavData.time.month-1;
        tm.tm_mday = NavData.time.day;
        tm.tm_hour = NavData.time.hour;
        tm.tm_min = NavData.time.minute;
        tm.tm_sec = NavData.time.sec;
        timeSinceEpoch = mktime(&tm);
      }      
    }
    if(timeSinceEpoch){
      rtcTime.tv_sec=timeSinceEpoch;
      rtcTime.tv_nsec=0;
      up_rtc_settime(&rtcTime);
      rtc_status=true;
    }
  }
  else{
    rtc_status=false;
  }
}

bool pir_sw(){
  if (pir_delay>0){
    pir_status=true;
    pir_delay--;
    Serial.println("PIR ON! Remaining sec ");
    Serial.println(pir_delay);
    return true;
  }
  else{
    pir_status=false;
    Serial.println("PIR OFF");
    return false;
  }
}

void printTime(struct timespec *tp, char *msg){
  struct tm tm;
  char rtcinfo[100];
  (void)gmtime_r(&tp->tv_sec, &tm);

  sprintf(rtcinfo,"%s:", msg);
  Serial.println(rtcinfo);
  sprintf(rtcinfo,"RTC Sec %u", tp->tv_sec);
  Serial.println(rtcinfo);
  sprintf(rtcinfo,"RTC nSec 0.%09u", tp->tv_nsec);
  Serial.println(rtcinfo);
  sprintf(rtcinfo,"%4d/%02d/%02d %02d:%02d:%02d",
          tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
          tm.tm_hour, tm.tm_min, tm.tm_sec);
  Serial.println(rtcinfo);

  String date_string="";
  date_string += print2digits(tm.tm_mday) + "/";
  date_string += print2digits(tm.tm_mon+1) + "/";
  date_string += print2digits(tm.tm_year+1900);
  String time_string="";
  time_string += print2digits(tm.tm_hour) + ":";
  time_string += print2digits(tm.tm_min) + ":";
  time_string += print2digits(tm.tm_sec);

  noInterrupts();
  u8g2.firstPage();
  do {
    display_indicators();
    u8g2.setCursor(33, 45);
    u8g2.setFont(u8g2_font_t0_15_tn);  // set the font for the terminal window
    u8g2.print(time_string);
    u8g2.setCursor(35, 63);
    u8g2.setFont(u8g2_font_6x10_tn);  // set the font for the terminal window
    u8g2.print(date_string);
  } while ( u8g2.nextPage() );
  interrupts();
}

String print2digits(int number) {
  String string ="";
  if (number < 10) {
    string ="0";
  }
  string+=number;
  return string;
}

void display_indicators(){
  u8g2.drawXBM(0, 0, 16, 16, (pir_status)?light_on_logo:light_off_logo);
  if(rtc_status)  u8g2.drawXBM(22, 0, 16, 16, rtc_logo);
  showLEDLevel(led_level);
}

void showLEDLevel(uint8_t led_level) {
  u8g2.drawFrame(45, 0, 83, 16);
  uint8_t box_offset=47;
  for (uint8_t i = 0; i < led_level; i++)
  {
    u8g2.drawBox(box_offset, 2, 7, 12);
    box_offset += 8;
  }
}


//  tm_sec The number of seconds after the minute, normally in the range 0 to 59, but can be up to 60 to allow for leap seconds.
//  tm_min The number of minutes after the hour, in the range 0 to 59.
//  tm_hour The number of hours past midnight, in the range 0 to 23.
//  tm_mday The day of the month, in the range 1 to 31.
//  tm_mon The number of months since January, in the range 0 to 11.
//  tm_year The number of years since 1900.
//  tm_wday The number of days since Sunday, in the range 0 to 6.
//  tm_yday The number of days since January 1, in the range 0 to 365. 

u8g2_fonts.c

Arduino
Change the u8g2_fonts.c code with this one otherwise you will get RAM overflow error
No preview (download only).

u8x8_fonts.c

Arduino
Delete all the content of u8x8_fonts.c file to avoid RAM overflow error.
/*
  u8x8_fonts.c
*/
#include "u8x8.h"

cxd56_rtc.c

Arduino
Add this file HERE to enable RTC in arduino
...\Arduino15\packages\SPRESENSE\tools\spresense-sdk\1.1.3\spresense\release\include\sdk
/****************************************************************************
 * bsp/src/cxd56_rtc.c
 *
 *   Copyright 2018 Sony Semiconductor Solutions Corporation
 *
 * 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 Sony Semiconductor Solutions Corporation 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 AND CONTRIBUTORS
 * "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 OWNER OR CONTRIBUTORS 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.
 *
 ****************************************************************************/

/************************************************************************************
 * Included Files
 ************************************************************************************/

#include <sdk/config.h>

#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <errno.h>

#include <nuttx/irq.h>
#include <nuttx/arch.h>

#include <arch/board/board.h>

#include "up_arch.h"
#include "cxd56_rtc.h"

#include "chip/cxd5602_memorymap.h"
#include "chip/cxd5602_backupmem.h"
#include "chip/cxd56_rtc.h"

/************************************************************************************
 * Pre-processor Definitions
 ************************************************************************************/
/* Configuration ********************************************************************/

#ifdef CONFIG_RTC_HIRES
#  ifndef CONFIG_RTC_FREQUENCY
#    define CONFIG_RTC_FREQUENCY 32768
#  endif
#  if CONFIG_RTC_FREQUENCY != 32768
#    error "Only lo-res CONFIG_RTC_FREQUENCY of 32.768kHz is supported"
#  endif
#else
#  ifndef CONFIG_RTC_FREQUENCY
#    define CONFIG_RTC_FREQUENCY 1
#  endif
#  if CONFIG_RTC_FREQUENCY != 1
#    error "Only lo-res CONFIG_RTC_FREQUENCY of 1Hz is supported"
#  endif
#endif

/* convert seconds to 64bit counter value running at 32kHz */

#define SEC_TO_CNT(sec) ((uint64_t)(((uint64_t)(sec)) << 15))

/* convert nano-seconds to 32kHz counter less than 1 second */

#define NSEC_TO_PRECNT(nsec) \
  (((nsec) / (NSEC_PER_SEC / CONFIG_RTC_FREQUENCY)) & 0x7fff)

#define MAGIC_RTC_SAVE (0x12aae190077a80ull)

/************************************************************************************
 * Private Types
 ************************************************************************************/

#ifdef CONFIG_RTC_ALARM
struct alm_cbinfo_s
{
  volatile alm_callback_t ac_cb; /* Client callback function */
  volatile FAR void *ac_arg;     /* Argument to pass with the callback function */
};
#endif

struct rtc_backup_s
{
  uint64_t magic;
  int64_t  reserved0;
  int64_t  offset;              /* offset time from RTC HW value */
  int64_t  reserved1;
};

/************************************************************************************
 * Private Data
 ************************************************************************************/

/* Callback to use when the alarm expires */

#ifdef CONFIG_RTC_ALARM
static struct alm_cbinfo_s g_alarmcb[RTC_ALARM_LAST];
#endif

/* Saved data for persistent RTC time */

static struct rtc_backup_s *g_rtc_save;

/************************************************************************************
 * Public Data
 ************************************************************************************/

volatile bool g_rtc_enabled = false;

/************************************************************************************
 * Private Functions
 ************************************************************************************/

/************************************************************************************
 * Name: rtc_dumptime
 *
 * Description:
 *    Dump RTC
 *
 * Input Parameters:
 *   None
 *
 * Returned Value:
 *   None
 *
 ************************************************************************************/

#ifdef CONFIG_DEBUG_RTC
static void rtc_dumptime(FAR const struct timespec *tp, FAR const char *msg)
{
  FAR struct tm tm;

  (void)gmtime_r(&tp->tv_sec, &tm);

  rtcinfo("%s:\n", msg);
  rtcinfo("RTC %u.%09u\n", tp->tv_sec, tp->tv_nsec);
  rtcinfo("%4d/%02d/%02d %02d:%02d:%02d\n",
          tm.tm_year, tm.tm_mon, tm.tm_mday,
          tm.tm_hour, tm.tm_min, tm.tm_sec);
}
#else
	#define rtc_dumptime(tp, msg)
#endif

/************************************************************************************
 * Name: cxd56_rtc_interrupt
 *
 * Description:
 *    RTC interrupt service routine
 *
 * Input Parameters:
 *   irq - The IRQ number that generated the interrupt
 *   context - Architecture specific register save information.
 *
 * Returned Value:
 *   Zero (OK) on success; A negated errno value on failure.
 *
 ************************************************************************************/

#ifdef CONFIG_RTC_ALARM
static int cxd56_rtc_interrupt(int irq, FAR void *context, FAR void *arg)
{
  FAR struct alm_cbinfo_s *cbinfo;
  alm_callback_t cb;
  FAR void *cb_arg;
  uint32_t source, clear;
  int id;
  int ret = OK;

  /* interrupt clear */

  source = getreg32(CXD56_RTC0_ALMFLG);

  if (source & RTCREG_ALM0_MASK)
    {
      id = RTC_ALARM0;
      clear = source & RTCREG_ALM0_MASK;
    }
  else if (source & RTCREG_ALM1_MASK)
    {
      id = RTC_ALARM1;
      clear = source & RTCREG_ALM1_MASK;
    }
  else if (source & RTCREG_ALM2_MASK)
    {
      id = RTC_ALARM2;
      clear = source & RTCREG_ALM2_MASK;
    }
  else
    {
      rtcerr("ERROR: Invalid ALARM\n");
      return ret;
    }
  putreg32(clear, CXD56_RTC0_ALMCLR);
  putreg32(0, CXD56_RTC0_ALMOUTEN(id));

  cbinfo = &g_alarmcb[id];

  if (cbinfo->ac_cb != NULL)
    {
      /* Alarm callback */

      cb = cbinfo->ac_cb;
      cb_arg = (FAR void*)cbinfo->ac_arg;

      cbinfo->ac_cb  = NULL;
      cbinfo->ac_arg = NULL;

      cb(cb_arg, id);
    }

  return 0;
}
#endif

/************************************************************************************
 * Public Functions
 ************************************************************************************/

/************************************************************************************
 * Name: up_rtc_initialize
 *
 * Description:
 *   Initialize the hardware RTC per the selected configuration.  This function is
 *   called once during the OS initialization sequence
 *
 * Input Parameters:
 *   None
 *
 * Returned Value:
 *   Zero (OK) on success; a negated errno on failure
 *
 ************************************************************************************/

int up_rtc_initialize(void)
{
#ifdef CONFIG_RTC_ALARM
  /* Configure RTC interrupt to catch overflow and alarm interrupts. */

  irq_attach(CXD56_IRQ_RTC0_A0, cxd56_rtc_interrupt, NULL);
  irq_attach(CXD56_IRQ_RTC0_A2, cxd56_rtc_interrupt, NULL);
  irq_attach(CXD56_IRQ_RTC_INT, cxd56_rtc_interrupt, NULL);
  up_enable_irq(CXD56_IRQ_RTC0_A0);
  up_enable_irq(CXD56_IRQ_RTC0_A2);
  up_enable_irq(CXD56_IRQ_RTC_INT);
#endif

  g_rtc_save = (struct rtc_backup_s*)BKUP->rtc_saved_data;

  /* If saved data is invalid, clear offset information */

  if (g_rtc_save->magic != MAGIC_RTC_SAVE)
    {
      g_rtc_save->offset = 0;
    }

  g_rtc_enabled = true;

  return OK;
}

/************************************************************************************
 * Name: up_rtc_time
 *
 * Description:
 *   Get the current time in seconds.  This is similar to the standard time()
 *   function.  This interface is only required if the low-resolution RTC/counter
 *   hardware implementation selected.  It is only used by the RTOS during
 *   initialization to set up the system time when CONFIG_RTC is set but neither
 *   CONFIG_RTC_HIRES nor CONFIG_RTC_DATETIME are set.
 *
 * Input Parameters:
 *   None
 *
 * Returned Value:
 *   The current time in seconds
 *
 ************************************************************************************/

#ifndef CONFIG_RTC_HIRES
time_t up_rtc_time(void)
{
  uint64_t count;

  count = cxd56_rtc_count();
  count += g_rtc_save->offset;
  count >>= 15; /* convert to 1sec resolution */

  return (time_t)count/CONFIG_RTC_FREQUENCY;
}
#endif

/************************************************************************************
 * Name: up_rtc_gettime
 *
 * Description:
 *   Get the current time from the high resolution RTC clock/counter.  This interface
 *   is only supported by the high-resolution RTC/counter hardware implementation.
 *   It is used to replace the system timer.
 *
 * Input Parameters:
 *   tp - The location to return the high resolution time value.
 *
 * Returned Value:
 *   Zero (OK) on success; a negated errno on failure
 *
 ************************************************************************************/

#ifdef CONFIG_RTC_HIRES
int up_rtc_gettime(FAR struct timespec *tp)
{
  uint64_t count;

  count = cxd56_rtc_count();
  count += g_rtc_save->offset;

  /* Then we can save the time in seconds and fractional seconds. */

  tp->tv_sec  = count / CONFIG_RTC_FREQUENCY;
  tp->tv_nsec = (count % CONFIG_RTC_FREQUENCY)*(NSEC_PER_SEC/CONFIG_RTC_FREQUENCY);

  rtc_dumptime(tp, "Getting time");

  return OK;
}
#endif

/************************************************************************************
 * Name: up_rtc_settime
 *
 * Description:
 *   Set the RTC to the provided time.  All RTC implementations must be able to
 *   set their time based on a standard timespec.
 *
 * Input Parameters:
 *   tp - the time to use
 *
 * Returned Value:
 *   Zero (OK) on success; a negated errno on failure
 *
 ************************************************************************************/

int up_rtc_settime(FAR const struct timespec *tp)
{
  irqstate_t flags;
  uint64_t count;

  flags = enter_critical_section();

#ifdef RTC_DIRECT_CONTROL
  /* wait until previous write request is completed */

  while (RTCREG_WREQ_BUSYA_MASK & getreg32(CXD56_RTC0_WRREGREQ));

  putreg32(tp->tv_sec, CXD56_RTC0_WRREGPOSTCNT);
  putreg32(NSEC_TO_PRECNT(tp->tv_nsec), CXD56_RTC0_WRREGPRECNT);

  putreg32(RTCREG_WREQ_BUSYA_MASK, CXD56_RTC0_WRREGREQ);

  /* wait until write request reflected */

  while (RTCREG_WREQ_BUSYB_MASK & getreg32(CXD56_RTC0_WRREGREQ));

#else
  /* Only save the difference from HW raw value */

  count = SEC_TO_CNT(tp->tv_sec) | NSEC_TO_PRECNT(tp->tv_nsec);
  g_rtc_save->offset = (int64_t)count - (int64_t)cxd56_rtc_count();
#endif

  leave_critical_section(flags);

  rtc_dumptime(tp, "Setting time");

  return OK;
}

/************************************************************************************
 * Name: cxd56_rtc_count
 *
 * Description:
 *   Get RTC raw counter value
 *
 * Returned Value:
 *   64bit counter value running at 32kHz
 *
 ************************************************************************************/

uint64_t cxd56_rtc_count(void)
{
  uint64_t val;
  irqstate_t flags;

  /*
   * The pre register is latched with reading the post rtcounter register,
   * so these registers always have to been read in the below order,
   * 1st post -> 2nd pre, and should be operated in atomic.
   */

  flags = enter_critical_section();

  val = (uint64_t)getreg32(CXD56_RTC0_RTPOSTCNT) << 15;
  val |= getreg32(CXD56_RTC0_RTPRECNT);

  leave_critical_section(flags);

  return val;
}

/************************************************************************************
 * Name: cxd56_rtc_almcount
 *
 * Description:
 *   Get RTC raw alarm counter value
 *
 * Returned Value:
 *   64bit alarm counter value running at 32kHz
 *
 ************************************************************************************/

#ifdef CONFIG_RTC_ALARM
uint64_t cxd56_rtc_almcount(void)
{
  uint64_t val;
  irqstate_t flags;

  flags = enter_critical_section();

  val = (uint64_t)getreg32(CXD56_RTC0_SETALMPOSTCNT(0)) << 15;
  val |= (getreg32(CXD56_RTC0_SETALMPRECNT(0)) & 0x7fff);

  leave_critical_section(flags);

  return val;
}
#endif

/************************************************************************************
 * Name: cxd56_rtc_setalarm
 *
 * Description:
 *   Set up an alarm.
 *
 * Input Parameters:
 *   alminfo - Information about the alarm configuration.
 *
 * Returned Value:
 *   Zero (OK) on success; a negated errno on failure
 *
 ************************************************************************************/

#ifdef CONFIG_RTC_ALARM
int cxd56_rtc_setalarm(FAR struct alm_setalarm_s *alminfo)
{
  FAR struct alm_cbinfo_s *cbinfo;
  irqstate_t flags;
  int ret = -EBUSY;
  int id;
  uint64_t count;

  ASSERT(alminfo != NULL);
  DEBUGASSERT(RTC_ALARM_LAST > alminfo->as_id);

  /* Set the alarm in hardware and enable interrupts */

  id = alminfo->as_id;
  cbinfo = &g_alarmcb[id];

  if (cbinfo->ac_cb == NULL)
    {
      /* The set the alarm */

      flags = enter_critical_section();

      cbinfo->ac_cb  = alminfo->as_cb;
      cbinfo->ac_arg = alminfo->as_arg;

      count = SEC_TO_CNT(alminfo->as_time.tv_sec) |
        NSEC_TO_PRECNT(alminfo->as_time.tv_nsec);

      count -= g_rtc_save->offset;

      /* wait until previous alarm request is completed */

      while (RTCREG_ASET_BUSY_MASK & getreg32(CXD56_RTC0_SETALMPRECNT(id)));

      putreg32((uint32_t)(count >> 15), CXD56_RTC0_SETALMPOSTCNT(id));
      putreg32((uint32_t)(count & 0x7fff), CXD56_RTC0_SETALMPRECNT(id));

      while (RTCREG_ALM_BUSY_MASK & getreg32(CXD56_RTC0_ALMOUTEN(id)));

      putreg32(RTCREG_ALM_EN_MASK | RTCREG_ALM_ERREN_MASK,
               CXD56_RTC0_ALMOUTEN(id));

      while (RTCREG_ALM_BUSY_MASK & getreg32(CXD56_RTC0_ALMOUTEN(id)));

      leave_critical_section(flags);

      rtc_dumptime(&alminfo->as_time, "New Alarm time");
      ret = OK;
    }

  return ret;
}
#endif

/************************************************************************************
 * Name: cxd56_rtc_cancelalarm
 *
 * Description:
 *   Cancel an alaram.
 *
 * Input Parameters:
 *  alarmid - Identifies the alarm to be cancelled
 *
 * Returned Value:
 *   Zero (OK) on success; a negated errno on failure
 *
 ************************************************************************************/

#ifdef CONFIG_RTC_ALARM
int cxd56_rtc_cancelalarm(enum alm_id_e alarmid)
{
  FAR struct alm_cbinfo_s *cbinfo;
  irqstate_t flags;
  int ret = -ENODATA;

  DEBUGASSERT(RTC_ALARM_LAST > alarmid);

  /* Set the alarm in hardware and enable interrupts */

  cbinfo = &g_alarmcb[alarmid];

  if (cbinfo->ac_cb != NULL)
    {
      /* Unset the alarm */

      flags = enter_critical_section();

      cbinfo->ac_cb = NULL;

      while (RTCREG_ALM_BUSY_MASK & getreg32(CXD56_RTC0_ALMOUTEN(alarmid)));

      putreg32(0, CXD56_RTC0_ALMOUTEN(alarmid));

      leave_critical_section(flags);

      ret = OK;
    }

  return ret;
}
#endif

cxd56_rtc.h

Arduino
Replace the existing cxd56_rtc.h code with this.
HERE: ...\Arduino15\packages\SPRESENSE\tools\spresense-sdk\1.1.3\spresense\release\include\sdk\
/****************************************************************************
 * bsp/src/cxd56_rtc.h
 *
 *   Copyright 2018 Sony Semiconductor Solutions Corporation
 *
 * 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 Sony Semiconductor Solutions Corporation 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 AND CONTRIBUTORS
 * "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 OWNER OR CONTRIBUTORS 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.
 *
 ****************************************************************************/

#ifndef __ARCH_ARM_SRC_CXD56XX_CXD56_RTC_H
#define __ARCH_ARM_SRC_CXD56XX_CXD56_RTC_H

/****************************************************************************
 * Included Files
 ****************************************************************************/

#include <sdk/config.h>
#include <time.h>
#include <nuttx/timers/rtc.h>

/****************************************************************************
 * Public Types
 ****************************************************************************/

#ifndef __ASSEMBLY__

#ifdef CONFIG_RTC_ALARM

/* The form of an alarm callback */

typedef CODE void (*alm_callback_t)(FAR void *arg, unsigned int alarmid);

enum alm_id_e
{
  RTC_ALARM0 = 0,           /* RTC ALARM 0 */
  RTC_ALARM_LAST,
  RTC_ALARM1 = 1,           /* RTC ALARM 1 */
  RTC_ALARM2,               /* RTC ALARM 2 (relative) */
};

/* Structure used to pass parmaters to set an alarm */

struct alm_setalarm_s
{
  int             as_id;    /* enum alm_id_e */
  struct timespec as_time;  /* Alarm expiration time */
  alm_callback_t  as_cb;    /* Callback (if non-NULL) */
  FAR void       *as_arg;   /* Argument for callback */
};

#endif /* CONFIG_RTC_ALARM */

/****************************************************************************
 * Public Data
 ****************************************************************************/

#undef EXTERN
#if defined(__cplusplus)
#define EXTERN extern "C"
extern "C"
{
#else
#define EXTERN extern
#endif

/****************************************************************************
 * Pre-processor Definitions
 ****************************************************************************/
#define CONFIG_DEBUG_RTC
#define CONFIG_RTC_HIRES
int up_rtc_initialize(void);
int up_rtc_settime(FAR const struct timespec *tp);
int up_rtc_settime(FAR const struct timespec *tp);

#ifdef CONFIG_DEBUG_RTC
	static void rtc_dumptime(FAR const struct timespec *tp, FAR const char *msg);
#else
	#define rtc_dumptime(tp, msg)
#endif

#ifdef CONFIG_RTC_ALARM
	static int cxd56_rtc_interrupt(int irq, FAR void *context, FAR void *arg);
#endif

#ifndef CONFIG_RTC_HIRES
	time_t up_rtc_time(void);
#endif

#ifdef CONFIG_RTC_HIRES
	int up_rtc_gettime(FAR struct timespec *tp);
#endif

/************************************************************************************
 * Name: cxd56_rtc_count
 *
 * Description:
 *   Get RTC raw counter value
 *
 * Returned Value:
 *   64bit counter value running at 32kHz
 *
 ************************************************************************************/

uint64_t cxd56_rtc_count(void);

/************************************************************************************
 * Name: cxd56_rtc_almcount
 *
 * Description:
 *   Get RTC raw alarm counter value
 *
 * Returned Value:
 *   64bit alarm counter value running at 32kHz
 *
 ************************************************************************************/

#ifdef CONFIG_RTC_ALARM
uint64_t cxd56_rtc_almcount(void);
#endif /* CONFIG_RTC_ALARM */

/************************************************************************************
 * Name: cxd56_rtc_setalarm
 *
 * Description:
 *   Set up an alarm.
 *
 * Input Parameters:
 *   alminfo - Information about the alarm configuration.
 *
 * Returned Value:
 *   Zero (OK) on success; a negated errno on failure
 *
 ************************************************************************************/

#ifdef CONFIG_RTC_ALARM
int cxd56_rtc_setalarm(FAR struct alm_setalarm_s *alminfo);
#endif /* CONFIG_RTC_ALARM */

/************************************************************************************
 * Name: cxd56_rtc_cancelalarm
 *
 * Description:
 *   Cancel an alaram.
 *
 * Input Parameters:
 *  alarmid - Identifies the alarm to be cancelled
 *
 * Returned Value:
 *   Zero (OK) on success; a negated errno on failure
 *
 ************************************************************************************/

#ifdef CONFIG_RTC_ALARM
int cxd56_rtc_cancelalarm(enum alm_id_e alarmid);
#endif /* CONFIG_RTC_ALARM */

/****************************************************************************
 * Name: cxd56_rtc_lowerhalf
 *
 * Description:
 *   Instantiate the RTC lower half driver for the STM32L4.  General usage:
 *
 *     #include <nuttx/timers/rtc.h>
 *     #include "stm32l4_rtc.h>
 *
 *     struct rtc_lowerhalf_s *lower;
 *     lower = stm32l4_rtc_lowerhalf();
 *     rtc_initialize(0, lower);
 *
 * Input Parameters:
 *   None
 *
 * Returned Value:
 *   On success, a non-NULL RTC lower interface is returned.  NULL is
 *   returned on any failure.
 *
 ****************************************************************************/

#ifdef CONFIG_RTC_DRIVER
FAR struct rtc_lowerhalf_s *cxd56_rtc_lowerhalf(void);
#endif /* CONFIG_RTC_DRIVER */

#undef EXTERN
#if defined(__cplusplus)
}
#endif
#endif /* __ASSEMBLY__ */
#endif /* __ARCH_ARM_SRC_CXD56XX_CXD56_RTC_H */

Credits

Mahmood ul Hassan

Mahmood ul Hassan

2 projects • 5 followers
Electronics Engineer with more than 7 years of experience in reverse engineering and test & measurement equipment designing

Comments