Things that remain constant whichever Mbed LPC1768 is used: peripheral definitions; leds; memory layout; main processor clock. Note that watchdog, default and hard fault handlers; peripheral power and clocks; and the vector table will need to be set up for each project.

Dependents:   test-lpc1768 oldheating gps motorhome ... more

Aim

To program the MBED LPC1768 using just the online compiler and the UM10360.pdf manual.
As a hobbyist I want to do the fun stuff - programming the various peripherals to make them do what I wanted them to do - unfortunately the Mbed OS libraries have done all that while leaving the mundane for me to do.
The Mbed OS libraries have to cope with multiple compilers, multiple targets and all the peripherals even if not used; making them large and putting many layers between you and the registers of the LPC1768.
We, on he other hand, can limit ourselves to a single compiler (the online ARM CC compiler), one target (the MBED LPC1768), and those few peripherals we need which, together with limiting some abilities such as dynamic allocation of interrupt vectors, allows us to dramatically reduces the number of files required and means we can specify and understand each and every bit of our application.

Technical

Bit Banding

Use bit-banding to access the individual bits by mapping each bit in the region base (GPIO 0x2000 0000 or peripheral 0x4000 0000) to a word in the region alias (GPIO 0x2200 0000 or peripheral 0x4200 0000).
The formula is therefore ALIAS + ((((registerAddress - BASE) << 3) + BIT) << 2)) which calculates the number of bits above the base [bytes * 8 plus bit number]; converts it to the number of bytes [ *4] and adds that to the alias.
Writing with bit banding still involves a read modify write cycle; the difference is that the cycle and bit twiddling is managed in hardware and is atomic.
For GPIO writes it is better to use the SET and CLR registers without bit banding to eliminate the surplus read.

Startup code

From the ARM Information Center:

  • __main calls __scatterload then calls __rt_entry
  • __scatterload goes through the region table and initializes the various execution-time regions
  • __rt_entry calls __user_setup_stackheap to initialise the stack and heap, then calls __rt_lib_init to initialise the C library sub systems and finally calls the user-level main

Files used

These are the files listed roughly in the order you might create them. Files you would not normally touch are in the lpc1768 library to make them easier to maintain, while files you would normally modify to suit your application are in the main folder.
lpc1768 contains static files for:

  • startup - memory layout in a scatter file and a routine to calculate the stack and heap locations
  • bitband - macros for each of the two bit band regions
  • system - set up the PLL to take the CPU clock from 12 to 96MHz
  • gpio - addresses of the various GPIO registers
  • led - leds 1 to 4: get, toggle and set
  • serialpc - the serial link to the PC using uart0
  • firmware - contains routines to handle the uploading of new firmware without having to plug into the usb port
  • semihost - contains routines to do various jobs such as MAC, reset and local file handling
  • fault - saves the location of a fault across restarts in a small area left aside in the scatter file
  • handlers - hard fault and default handlers: other handlers are defined in the module that uses them
  • watchdog - watchdog handler

main contains files to modify for:

  • vectors - a simple list of handler addresses without any code
  • periphs - a list of peripherals to power up and their clock dividers
  • main - the start up routine which calls periphs, system, and led initialisation then loops flashing an led

startup.sct

Contains the scatter file where the different memory regions are defined for the linker.
Derived from targets/TARGET_NXP/TARGET_LPC176X/device/TOOLCHAIN_ARM_STD/LPC1768.sct.
I have removed the NIVT and IAP reserved areas by not moving the vector table into ram (therefore have to specify the handlers at link time: they cannot be dynamically allocated) and by not using In Application Programming.
The .CRPSection, together with a line in startup.c ensures that the value -1 is stored in location 0x2FC thereby setting code read protection to none.
The following sections are specified:

  • RESET - used in vectors.S
  • .CRPSection - used in startup.c to ensure that Code Read Protection is set to none
  • InRoot$$Sections - used for code which must execute at its load address
  • AHBSRAM0 - used in the log module for use as buffer; normally dedicated to USB if you use it
  • AHBSRAM1 - used in the net module for the ethernet buffers
  • fault - Leave 4 bytes for the fault code at 20083FFC
  • CANRAM - normally dedicated to Controller Area Network if you use it

startup.sct

#! armcc -E

#define MBED_APP_START 0x00000000

#define MBED_APP_SIZE 0x80000

LR_IROM1 MBED_APP_START MBED_APP_SIZE ; load region size_region
{   
  ER_IROM0 MBED_APP_START 0x2FC    ; load address = execution address
  {
    *.o (RESET, +First)
    .ANY (+RO)
  }
  ER_CRP (MBED_APP_START + 0x2FC) FIXED 4
  {
    *.o (.CRPSection)
  }
  ER_IROM1 (MBED_APP_START + (0x2FC + 4)) FIXED (MBED_APP_SIZE - (0x2FC + 4))
  {
    *(InRoot$$Sections)
    .ANY (+RO)
  }
  RW_IRAM1 0x10000000 0x8000    ; RW and ZI
  {
   .ANY (+RW +ZI)
  }
  RW_IRAM2 0x2007C000 0x4000    ; RW data, USB RAM used by the Log module
  {
   .ANY (AHBSRAM0)
  }
  RW_IRAM3 0x20080000 0x3FFC    ; RW data, ETH RAM used by the Net module. Leave 4 bytes for the fault code at 20083FFC
  {
   .ANY (AHBSRAM1)
  }
  RW_IRAM4 0x40038000 0x0800    ; RW data, CAN RAM
  {
   .ANY (CANRAM)
  }
}

startup.c

Contains the c function __user_setup_stackheap which is called from __main to calculate the stack and heap positions prior to main being called .
Derived from platform/mbed_retarget.cpp.

startup.c

#include <stdint.h>
#include <rt_misc.h>

__attribute__ ((section (".CRPSection"), used)) const long crpKey = -1; //Don't use Code Read Protection

extern char Image$$RW_IRAM1$$ZI$$Limit[];

__value_in_regs struct __initial_stackheap __user_setup_stackheap(uint32_t R0, uint32_t R1, uint32_t R2, uint32_t R3)
{
    uint32_t zi_limit = (uint32_t)Image$$RW_IRAM1$$ZI$$Limit;
    uint32_t sp_limit = __current_sp();

    zi_limit = (zi_limit + 7) & ~0x7;    // ensure zi_limit is 8-byte aligned

    struct __initial_stackheap r;
    r.heap_base = zi_limit;
    r.heap_limit = sp_limit;
    return r;
}

vectors.S

Contains the vector table specified in assembly; there is no code here: just pointers
Derived from targets/TARGET_NXP/TARGET_LPC176X/device/TOOLCHAIN_ARM_STD/startup_LPC17xx.S. There are two difference from Mbed:

  • I have not specified a ResetHandler or SystemInit function but have just put __main directly into the table and called SystemInit from main: there is no longer assembly code for a reset handler which calls SystemInit then __main.
  • I define the handlers in the module they are used (the default and hard fault handlers have been given their own module) and put the name directly into the table: they are no longer weak references which default to an infinite loop if not defined.

To define an interrupt handler in a given application:

  • Define the handler (void parameters and returns void) in its module using the attribute __irq
  • Add the name of the handler to the list of IMPORTs
  • Replace 'DefaultHandler' on the relevant line of __Vectors with the name of the handler

vectors.S

__initial_sp        EQU     0x10008000  ; Top of RAM from LPC1768

                PRESERVE8
                THUMB

; Vector Table Mapped to Address 0 at Reset

                AREA    RESET, DATA, READONLY
                EXPORT  __Vectors
                IMPORT  __main
                IMPORT  DefaultHandler
                IMPORT  HardFaultHandler
                IMPORT  WatchdogHandler

__Vectors       DCD     __initial_sp              ; Top of Stack
                DCD     __main                    ; Reset Handler
                DCD     DefaultHandler            ; NMI Handler
                DCD     HardFaultHandler          ; Hard Fault Handler
                DCD     DefaultHandler            ; MPU Fault Handler
                DCD     DefaultHandler            ; Bus Fault Handler
                DCD     DefaultHandler            ; Usage Fault Handler
                DCD     0                         ; Reserved
                DCD     0                         ; Reserved
                DCD     0                         ; Reserved
                DCD     0                         ; Reserved
                DCD     DefaultHandler            ; SVCall Handler
                DCD     DefaultHandler            ; Debug Monitor Handler
                DCD     0                         ; Reserved
                DCD     DefaultHandler            ; PendSV Handler
                DCD     DefaultHandler            ; SysTick Handler

                ; External Interrupts
                DCD     WatchdogHandler           ; Watchdog Timer
                DCD     DefaultHandler            ; Timer0
                DCD     DefaultHandler            ; Timer1
                DCD     DefaultHandler            ; Timer2
                DCD     DefaultHandler            ; Timer3
                DCD     DefaultHandler            ; UART0
                DCD     DefaultHandler            ; UART1
                DCD     DefaultHandler            ; UART2
                DCD     DefaultHandler            ; UART3
                DCD     DefaultHandler            ; PWM1
                DCD     DefaultHandler            ; I2C0
                DCD     DefaultHandler            ; I2C1
                DCD     DefaultHandler            ; I2C2
                DCD     DefaultHandler            ; SPI
                DCD     DefaultHandler            ; SSP0
                DCD     DefaultHandler            ; SSP1
                DCD     DefaultHandler            ; PLL0 Lock (Main PLL)
                DCD     DefaultHandler            ; Real Time Clock
                DCD     DefaultHandler            ; External Interrupt 0
                DCD     DefaultHandler            ; External Interrupt 1
                DCD     DefaultHandler            ; External Interrupt 2
                DCD     DefaultHandler            ; External Interrupt 3
                DCD     DefaultHandler            ; A/D Converter
                DCD     DefaultHandler            ; Brown-Out Detect
                DCD     DefaultHandler            ; USB
                DCD     DefaultHandler            ; CAN
                DCD     DefaultHandler            ; General Purpose DMA
                DCD     DefaultHandler            ; I2S
                DCD     DefaultHandler            ; Ethernet
                DCD     DefaultHandler            ; Repetitive Interrupt Timer
                DCD     DefaultHandler            ; Motor Control PWM
                DCD     DefaultHandler            ; Quadrature Encoder Interface
                DCD     DefaultHandler            ; PLL1 Lock (USB PLL)

                ALIGN
                END

bitband.h

bitband.h

#define ALIAS4 0x42000000
#define BASE4  0x40000000
#define BIT_BAND4(ADDR_PAR, BIT_PAR) *((volatile unsigned *)(ALIAS4 + ((((ADDR_PAR - BASE4) << 3) + BIT_PAR) << 2)))

periphs.c

Power up, select the clock divider and select the pins for the peripherals you need; before the PLL is enabled.

periphs.c

#define PCONP    (*((volatile unsigned *) 0x400FC0C4))
#define PCLKSEL0 (*((volatile unsigned *) 0x400FC1A8))
#define PCLKSEL1 (*((volatile unsigned *) 0x400FC1AC))
#define PINSEL0  (*((volatile unsigned *) 0x4002C000))
#define PINSEL1  (*((volatile unsigned *) 0x4002C004))
#define PINSEL2  (*((volatile unsigned *) 0x4002C008))
#define PINSEL3  (*((volatile unsigned *) 0x4002C00C))


void PeriphsInit (void)
{
    //Peripheral power - Table 46
    PCONP  = 0;
    PCONP |= 1 <<  1; //TIMER0
    PCONP |= 1 <<  3; //UART0
    PCONP |= 1 <<  4; //UART1
    PCONP |= 1 <<  9; //RTC
    PCONP |= 1 << 10; //SSP1
    PCONP |= 1 << 15; //GPIO
    PCONP |= 1 << 30; //ENET                 

    //Peripheral clocks; default is 00 to divide by 4; specify 01 to divide by 1
    PCLKSEL0  = 0;
    PCLKSEL0 |= 1 <<  2;  //TIM0
    PCLKSEL0 |= 1 <<  6;  //UART0
    PCLKSEL0 |= 1 <<  8;  //UART1
    PCLKSEL0 |= 1 << 20;  //SSP1

    //Pin functions table 80.
    PINSEL0  = 0;
    PINSEL0 |= 1U <<  4; //P0.02 01 TXD0  UART0
    PINSEL0 |= 1U <<  6; //P0.03 01 RXD0  UART0
    PINSEL0 |= 2U << 14; //P0.07 10 SCK1   SSP1
    PINSEL0 |= 2U << 16; //P0.08 10 MISO1  SSP1
    PINSEL0 |= 2U << 18; //P0.09 10 MOSI1  SSP1
    PINSEL0 |= 1U << 30; //P0.15 01 TXD1  UART1
    
    PINSEL1  = 0;
    PINSEL1 |= 1U <<  0; //P0.16 01 RXD1  UART1
    
    PINSEL2  = 0;
    PINSEL2 |= 1U <<  0; //P1.00 01 ENET_TXD0
    PINSEL2 |= 1U <<  2; //P1.01 01 ENET_TXD1
    PINSEL2 |= 1U <<  8; //P1.04 01 ENET_TX_EN
    PINSEL2 |= 1U << 16; //P1.08 01 ENET_CRS
    PINSEL2 |= 1U << 18; //P1.09 01 ENET_RXD0
    PINSEL2 |= 1U << 20; //P1.10 01 ENET_RXD1
    PINSEL2 |= 1U << 28; //P1.14 01 ENET_RX_ER
    PINSEL2 |= 1U << 30; //P1.15 01 ENET_REF_CLK
    
    PINSEL3  = 0;
    PINSEL3 |= 1U <<  0; //P1.16 01 ENET_MDC
    PINSEL3 |= 1U <<  2; //P1.17 01 ENET_MDIO
}

system.c

Sets up the flash wait states, the main oscillator and finally starts the PLL to move from 12MHz to 96MHz. The peripheral clocks have to be set before the PLL is enabled.

system.c

#include "bitband.h"

//Addresses
#define   FLASHCFG_ADDR 0x400FC000 // Flash Accelerator Configuration Register; controls flash access timing R/W 0x303A
#define    CCLKCFG_ADDR 0x400FC104 // CPU Clock Configuration Register R/W 0
#define  CLKSRCSEL_ADDR 0x400FC10C // Clock Source Select Register R/W 0 
#define        SCS_ADDR 0x400FC1A0 // System Control and Status R/W 0
#define    PLL0CON_ADDR 0x400FC080 // PLL0 Control Register R/W 0
#define    PLL0CFG_ADDR 0x400FC084 // PLL0 Configuration Register R/W 0
#define   PLL0STAT_ADDR 0x400FC088 // PLL0 Status Register RO 0
#define   PLL0FEED_ADDR 0x400FC08C // PLL0 Feed Register WO NA
#define  USBCLKCFG_ADDR 0x400FC108 // USB Clock Configuration Register R/W 0
#define  CLKOUTCFG_ADDR 0x400FC1C8 // Clock Output Configuration Register R/W 0

//Registers
#define   FLASHCFG *((volatile unsigned *)  FLASHCFG_ADDR)
#define    CCLKCFG *((volatile unsigned *)   CCLKCFG_ADDR)
#define  CLKSRCSEL *((volatile unsigned *) CLKSRCSEL_ADDR)
#define    PLL0CFG *((volatile unsigned *)   PLL0CFG_ADDR)
#define   PLL0FEED *((volatile unsigned *)  PLL0FEED_ADDR)
#define  USBCLKCFG *((volatile unsigned *) USBCLKCFG_ADDR)
#define  CLKOUTCFG *((volatile unsigned *) CLKOUTCFG_ADDR)

//Bits
#define PLLE0    BIT_BAND4( PLL0CON_ADDR,  0)
#define PLLC0    BIT_BAND4( PLL0CON_ADDR,  1)
#define PLOCK0   BIT_BAND4(PLL0STAT_ADDR, 26) //Reflects the PLL0 Lock status.
#define OSCRANGE BIT_BAND4(     SCS_ADDR,  4)
#define OSCEN    BIT_BAND4(     SCS_ADDR,  5)
#define OSCSTAT  BIT_BAND4(     SCS_ADDR,  6)

void SystemInit()
{
    //Flash config
    FLASHCFG &= ~(0xF << 12); // b15:12 0b0100 Flash accesses use 5 CPU clocks. Use for up to 100 MHz CPU clock.
    FLASHCFG |=   0x4 << 12 ;

    //Enable the main oscillator and wait for it to be ready
    OSCRANGE = 0;                  //Main Oscillator Range Select: 0= 1 MHz to 20 MHz; 1= 15 MHz to 25 MHz
    OSCEN    = 1;                  //Main Oscillator Enable
    while (!OSCSTAT) __nop();      //Wait for Main Oscillator to be ready
    
    // bit 7:0 CCLKSEL: Divide value for the CPU clock (CCLK) from the PLL0 output.
    CCLKCFG   = 2;                 // 2 == divide by 3
    
    //PLL0 Clock Source Select
    CLKSRCSEL = 1;                 //01 selects the main oscillator
    
    //PLLO Configuration
    PLL0CFG   = 11;                //MSEL0=11 == M=12 == multiply by 24; NSEL=0 == divide by 1
    PLL0FEED  = 0xAA;
    PLL0FEED  = 0x55;
    
    //PLL0 Control
    PLLE0     = 1;                 // PLL0 Enable
    PLL0FEED  = 0xAA;
    PLL0FEED  = 0x55;
    while (!PLOCK0) __nop();       // Wait for PLL to be locked
    PLLC0     = 1;                 // PLL0 Connect
    PLL0FEED  = 0xAA;
    PLL0FEED  = 0x55;

    //USB Clock divider
    USBCLKCFG = 5;                 // 5 == divide by 6
    
    //Clock Output
    CLKOUTCFG = 0;                 //No clock output
}

gpio.h

gpio.h

#define FIO0DIR_ADDR 0x2009C000
#define FIO1DIR_ADDR 0x2009C020
#define FIO2DIR_ADDR 0x2009C040

#define FIO0PIN_ADDR 0x2009C014
#define FIO1PIN_ADDR 0x2009C034
#define FIO2PIN_ADDR 0x2009C054

#define FIO0SET_ADDR 0x2009C018
#define FIO1SET_ADDR 0x2009C038
#define FIO2SET_ADDR 0x2009C058

#define FIO0CLR_ADDR 0x2009C01C
#define FIO1CLR_ADDR 0x2009C03C
#define FIO2CLR_ADDR 0x2009C05C

#define FIO0SET(BIT_PAR) *((volatile unsigned *) FIO0SET_ADDR) = 1U << BIT_PAR
#define FIO1SET(BIT_PAR) *((volatile unsigned *) FIO1SET_ADDR) = 1U << BIT_PAR
#define FIO2SET(BIT_PAR) *((volatile unsigned *) FIO2SET_ADDR) = 1U << BIT_PAR

#define FIO0CLR(BIT_PAR) *((volatile unsigned *) FIO0CLR_ADDR) = 1U << BIT_PAR
#define FIO1CLR(BIT_PAR) *((volatile unsigned *) FIO1CLR_ADDR) = 1U << BIT_PAR
#define FIO2CLR(BIT_PAR) *((volatile unsigned *) FIO2CLR_ADDR) = 1U << BIT_PAR

#define ALIAS2 0x22000000
#define BASE2  0x20000000
#define BIT_BAND2(ADDR_PAR, BIT_PAR) *((volatile unsigned *)(ALIAS2 + ((((ADDR_PAR - BASE2) << 3) + BIT_PAR) << 2)))

#define FIO0PIN(BIT_PAR) BIT_BAND2(FIO0PIN_ADDR, BIT_PAR)
#define FIO1PIN(BIT_PAR) BIT_BAND2(FIO1PIN_ADDR, BIT_PAR)
#define FIO2PIN(BIT_PAR) BIT_BAND2(FIO2PIN_ADDR, BIT_PAR)

#define FIO0DIR(BIT_PAR) BIT_BAND2(FIO0DIR_ADDR, BIT_PAR)
#define FIO1DIR(BIT_PAR) BIT_BAND2(FIO1DIR_ADDR, BIT_PAR)
#define FIO2DIR(BIT_PAR) BIT_BAND2(FIO2DIR_ADDR, BIT_PAR)

led.c

led.c

#include <stdbool.h>
#include "bitband.h"
#include "gpio.h"

#define LED1BIT 18
#define LED2BIT 20
#define LED3BIT 21
#define LED4BIT 23

#define LED1DIR BIT_BAND2(FIO1DIR_ADDR, LED1BIT)
#define LED2DIR BIT_BAND2(FIO1DIR_ADDR, LED2BIT)
#define LED3DIR BIT_BAND2(FIO1DIR_ADDR, LED3BIT)
#define LED4DIR BIT_BAND2(FIO1DIR_ADDR, LED4BIT)

#define LED1PIN BIT_BAND2(FIO1PIN_ADDR, LED1BIT)
#define LED2PIN BIT_BAND2(FIO1PIN_ADDR, LED2BIT)
#define LED3PIN BIT_BAND2(FIO1PIN_ADDR, LED3BIT)
#define LED4PIN BIT_BAND2(FIO1PIN_ADDR, LED4BIT)

#define LED1SET FIO1SET = 1U << LED1BIT
#define LED1CLR FIO1CLR = 1U << LED1BIT

#define LED2SET FIO1SET = 1U << LED2BIT
#define LED2CLR FIO1CLR = 1U << LED2BIT

#define LED3SET FIO1SET = 1U << LED3BIT
#define LED3CLR FIO1CLR = 1U << LED3BIT

#define LED4SET FIO1SET = 1U << LED4BIT
#define LED4CLR FIO1CLR = 1U << LED4BIT

void LedInit()
{
    LED1DIR = 1;
    LED2DIR = 1;
    LED3DIR = 1;
    LED4DIR = 1;
}
void Led1Set(bool value) { if (value) LED1SET; else LED1CLR; }
void Led2Set(bool value) { if (value) LED2SET; else LED2CLR; }
void Led3Set(bool value) { if (value) LED3SET; else LED3CLR; }
void Led4Set(bool value) { if (value) LED4SET; else LED4CLR; }

void Led1Tgl() { if (LED1PIN) LED1CLR; else LED1SET; }
void Led2Tgl() { if (LED2PIN) LED2CLR; else LED2SET; }
void Led3Tgl() { if (LED3PIN) LED3CLR; else LED3SET; }
void Led4Tgl() { if (LED4PIN) LED4CLR; else LED4SET; }

bool Led1Get() { return LED1PIN; }
bool Led2Get() { return LED2PIN; }
bool Led3Get() { return LED3PIN; }
bool Led4Get() { return LED4PIN; }

handlers.c

Contains those handlers which are not defined elsewhere in code. Any handler needs to be defined with the __irq attribute.
The hard fault handler is configured to just flash led 4; this leaves the other leds available to help locate the source of the hard fault: typically something like an array overflow or accessing a peripheral that has not been powered up. Faults are saved in the fault module for later debugging

handlers.c

#include <stdint.h>

#include "fault.h"
#include "led.h"

__irq void DefaultHandler()
{
    FaultSave(FAULT_TYPE_DEFAULT);
    int value = 0;
    int count = 0;
    while (1)
    {
        count++;
        if (count > 10000000)
        {
            value = !value;
            Led1Set( value);
            Led2Set(!value);
            Led3Set(!value);
            Led4Set( value);
            count = 0;
        }
    }
}
__irq void HardFaultHandler()
{
    FaultSave(FAULT_TYPE_HARD);
    int value = 0;
    int count = 0;
    while (1)
    {
        count++;
        if (count > 10000000)
        {
            value = !value;
            Led4Set(value);
            count = 0;
        }
    }
}

main.c

Calls PeriphsInit to power up and select the peripheral clocks, then SystemInit and LedInit. After that you can initialise anything else then go into an infinite loop as this routine never exits.

main.c

#include <stdint.h>
#include <stdbool.h>

#include "periphs.h"
#include "system.h"
#include "led.h"

int main()
{
    PeriphsInit();
    SystemInit();
    LedInit();
    
    int count = 0;
    while (true)
    {
        count++;
        if (count > 10000000)
        {
            Led1Set(!Led1Get());
            count = 0;
        }
    }
}
Committer:
andrewboyson
Date:
Fri Jun 03 11:51:13 2022 +0000
Revision:
71:2f8af23950de
Parent:
70:3ae450c74c5e
Added can module

Who changed what in which revision?

UserRevisionLine numberNew contents of line
andrewboyson 51:fb18aa3ec115 1 #include <stdlib.h>
andrewboyson 51:fb18aa3ec115 2 #include <stdio.h>
andrewboyson 51:fb18aa3ec115 3 #include <string.h>
andrewboyson 51:fb18aa3ec115 4 #include <stdbool.h>
andrewboyson 51:fb18aa3ec115 5 #include <time.h>
andrewboyson 51:fb18aa3ec115 6
andrewboyson 51:fb18aa3ec115 7 #include "time64.h"
andrewboyson 70:3ae450c74c5e 8 #include "tm.h"
andrewboyson 51:fb18aa3ec115 9
andrewboyson 51:fb18aa3ec115 10
andrewboyson 51:fb18aa3ec115 11 static bool isLeapYear(int year)
andrewboyson 51:fb18aa3ec115 12 {
andrewboyson 51:fb18aa3ec115 13 year += 1900;
andrewboyson 51:fb18aa3ec115 14 bool leapYear = !(year & 0x3);
andrewboyson 51:fb18aa3ec115 15 if (year >= 2100)
andrewboyson 51:fb18aa3ec115 16 {
andrewboyson 51:fb18aa3ec115 17 if (year % 100 == 0) leapYear = false;
andrewboyson 51:fb18aa3ec115 18 if (year % 400 == 0) leapYear = true;
andrewboyson 51:fb18aa3ec115 19 }
andrewboyson 51:fb18aa3ec115 20 return leapYear;
andrewboyson 51:fb18aa3ec115 21
andrewboyson 51:fb18aa3ec115 22 }
andrewboyson 69:18d8b1bd2952 23 int TmMonthLength(int year, int month)
andrewboyson 51:fb18aa3ec115 24 {
andrewboyson 51:fb18aa3ec115 25 static char monthlengths[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
andrewboyson 51:fb18aa3ec115 26 int daysInMonth = monthlengths[month];
andrewboyson 51:fb18aa3ec115 27 if (month == 1 && isLeapYear(year)) daysInMonth++; //February is month 1 of months 0 to 11
andrewboyson 51:fb18aa3ec115 28 return daysInMonth;
andrewboyson 51:fb18aa3ec115 29 }
andrewboyson 51:fb18aa3ec115 30 static bool isDst(int year, int month, int dayOfMonth, int dayOfWeek, int hours)
andrewboyson 51:fb18aa3ec115 31 {
andrewboyson 51:fb18aa3ec115 32 //Find the last Sunday in the month
andrewboyson 69:18d8b1bd2952 33 int lastDayOfMonth = TmMonthLength(year, month);
andrewboyson 51:fb18aa3ec115 34 int daysToEndOfMonth = lastDayOfMonth - dayOfMonth;
andrewboyson 51:fb18aa3ec115 35 int dayOfWeekOfLastDayOfMonth = (dayOfWeek + daysToEndOfMonth) % 7;
andrewboyson 51:fb18aa3ec115 36 int lastSundayDayOfMonth = lastDayOfMonth - dayOfWeekOfLastDayOfMonth;
andrewboyson 51:fb18aa3ec115 37
andrewboyson 51:fb18aa3ec115 38 //Check each month
andrewboyson 51:fb18aa3ec115 39 if (month <= 1) return false; //Jan, Feb
andrewboyson 51:fb18aa3ec115 40 if (month == 2) //Mar - DST true after 1am UTC on the last Sunday in March
andrewboyson 51:fb18aa3ec115 41 {
andrewboyson 51:fb18aa3ec115 42 if (dayOfMonth < lastSundayDayOfMonth) return false;
andrewboyson 51:fb18aa3ec115 43 if (dayOfMonth == lastSundayDayOfMonth) return hours >= 1;
andrewboyson 51:fb18aa3ec115 44 if (dayOfMonth > lastSundayDayOfMonth) return true;
andrewboyson 51:fb18aa3ec115 45 }
andrewboyson 51:fb18aa3ec115 46 if (month >= 3 && month <= 8) return true; //Apr, May, Jun, Jul, Aug, Sep
andrewboyson 51:fb18aa3ec115 47 if (month == 9) //Oct - DST false after 1am UTC on the last Sunday in October
andrewboyson 51:fb18aa3ec115 48 {
andrewboyson 51:fb18aa3ec115 49 if (dayOfMonth < lastSundayDayOfMonth) return true;
andrewboyson 51:fb18aa3ec115 50 if (dayOfMonth == lastSundayDayOfMonth) return hours < 1;
andrewboyson 51:fb18aa3ec115 51 if (dayOfMonth > lastSundayDayOfMonth) return false;
andrewboyson 51:fb18aa3ec115 52 }
andrewboyson 51:fb18aa3ec115 53 if (month >= 10) return false; //Nov, Dec
andrewboyson 51:fb18aa3ec115 54 return false;
andrewboyson 51:fb18aa3ec115 55 }
andrewboyson 51:fb18aa3ec115 56 static void calculateDayOfYearAndWeek(int thisYear, int thisMonth, int thisMonthDay, int* pDayOfYear, int* pDayOfWeek)
andrewboyson 51:fb18aa3ec115 57 {
andrewboyson 51:fb18aa3ec115 58 int dayOfYear = 0; //1 Jan is day 0
andrewboyson 51:fb18aa3ec115 59 int dayOfWeek = 4; //1 Jan 1970 is a Thursday
andrewboyson 51:fb18aa3ec115 60
andrewboyson 51:fb18aa3ec115 61 //Add days of each whole year
andrewboyson 51:fb18aa3ec115 62 for (int y = 70; y < thisYear; y++)
andrewboyson 51:fb18aa3ec115 63 {
andrewboyson 51:fb18aa3ec115 64 int lengthOfYear = isLeapYear(y) ? 366 : 365;
andrewboyson 51:fb18aa3ec115 65 dayOfWeek += lengthOfYear;
andrewboyson 51:fb18aa3ec115 66 }
andrewboyson 51:fb18aa3ec115 67
andrewboyson 51:fb18aa3ec115 68 //Add days of each whole month
andrewboyson 51:fb18aa3ec115 69 for (int m = 0; m < thisMonth; m++)
andrewboyson 51:fb18aa3ec115 70 {
andrewboyson 69:18d8b1bd2952 71 int lengthOfMonth = TmMonthLength(thisYear, m);
andrewboyson 51:fb18aa3ec115 72 dayOfYear += lengthOfMonth;
andrewboyson 51:fb18aa3ec115 73 dayOfWeek += lengthOfMonth;
andrewboyson 51:fb18aa3ec115 74 }
andrewboyson 51:fb18aa3ec115 75
andrewboyson 51:fb18aa3ec115 76 //Add days of part month
andrewboyson 51:fb18aa3ec115 77 thisMonthDay--; //thisMonthDay is 01 to 31 where we need 00 to 30
andrewboyson 51:fb18aa3ec115 78 dayOfYear += thisMonthDay;
andrewboyson 51:fb18aa3ec115 79 dayOfWeek += thisMonthDay;
andrewboyson 51:fb18aa3ec115 80
andrewboyson 51:fb18aa3ec115 81 //Update the day of year and day of week parts of the struct tm
andrewboyson 51:fb18aa3ec115 82 *pDayOfYear = dayOfYear; // 0 --> 365
andrewboyson 51:fb18aa3ec115 83 *pDayOfWeek = dayOfWeek % 7; // 0 --> 6
andrewboyson 51:fb18aa3ec115 84 }
andrewboyson 51:fb18aa3ec115 85 static void normalise(int* pHours, int* pDayOfWeek, int* pDayOfMonth, int* pMonth, int * pDayOfYear, int* pYear)
andrewboyson 51:fb18aa3ec115 86 {
andrewboyson 51:fb18aa3ec115 87 if (*pHours > 23)
andrewboyson 51:fb18aa3ec115 88 {
andrewboyson 51:fb18aa3ec115 89 *pHours -= 24;
andrewboyson 51:fb18aa3ec115 90 ++*pDayOfWeek;
andrewboyson 51:fb18aa3ec115 91 if (*pDayOfWeek > 6) *pDayOfWeek = 0;
andrewboyson 51:fb18aa3ec115 92 ++*pDayOfYear;
andrewboyson 51:fb18aa3ec115 93 ++*pDayOfMonth;
andrewboyson 69:18d8b1bd2952 94 if (*pDayOfMonth > TmMonthLength(*pYear, *pMonth))
andrewboyson 51:fb18aa3ec115 95 {
andrewboyson 51:fb18aa3ec115 96 ++*pMonth;
andrewboyson 51:fb18aa3ec115 97 if (*pMonth > 11)
andrewboyson 51:fb18aa3ec115 98 {
andrewboyson 51:fb18aa3ec115 99 ++*pYear;
andrewboyson 51:fb18aa3ec115 100 *pDayOfYear = 0;
andrewboyson 51:fb18aa3ec115 101 *pMonth = 0;
andrewboyson 51:fb18aa3ec115 102 }
andrewboyson 51:fb18aa3ec115 103 *pDayOfMonth = 1;
andrewboyson 51:fb18aa3ec115 104 }
andrewboyson 51:fb18aa3ec115 105 }
andrewboyson 51:fb18aa3ec115 106
andrewboyson 51:fb18aa3ec115 107 if (*pHours < 0)
andrewboyson 51:fb18aa3ec115 108 {
andrewboyson 51:fb18aa3ec115 109 *pHours += 24;
andrewboyson 51:fb18aa3ec115 110 --*pDayOfWeek;
andrewboyson 51:fb18aa3ec115 111 if (*pDayOfWeek < 0) *pDayOfWeek = 6;
andrewboyson 51:fb18aa3ec115 112 --*pDayOfYear;
andrewboyson 51:fb18aa3ec115 113 --*pDayOfMonth;
andrewboyson 51:fb18aa3ec115 114 if (*pDayOfMonth < 1)
andrewboyson 51:fb18aa3ec115 115 {
andrewboyson 51:fb18aa3ec115 116 --*pMonth;
andrewboyson 51:fb18aa3ec115 117 if (*pMonth < 0)
andrewboyson 51:fb18aa3ec115 118 {
andrewboyson 51:fb18aa3ec115 119 --*pYear;
andrewboyson 51:fb18aa3ec115 120 *pDayOfYear = isLeapYear(*pYear) ? 365 : 364;
andrewboyson 51:fb18aa3ec115 121 *pMonth = 11;
andrewboyson 51:fb18aa3ec115 122 }
andrewboyson 69:18d8b1bd2952 123 *pDayOfMonth = TmMonthLength(*pYear, *pMonth);
andrewboyson 51:fb18aa3ec115 124 }
andrewboyson 51:fb18aa3ec115 125 }
andrewboyson 51:fb18aa3ec115 126 }
andrewboyson 51:fb18aa3ec115 127 static void addYears(int* pYear, int* pDayOfWeek, int* pDaysLeft)
andrewboyson 51:fb18aa3ec115 128 {
andrewboyson 51:fb18aa3ec115 129 while(1)
andrewboyson 51:fb18aa3ec115 130 {
andrewboyson 51:fb18aa3ec115 131 //See if it is a leap year
andrewboyson 51:fb18aa3ec115 132 int leapYear = isLeapYear(*pYear);
andrewboyson 51:fb18aa3ec115 133
andrewboyson 51:fb18aa3ec115 134 //Find the number of days in this year
andrewboyson 51:fb18aa3ec115 135 int daysInYear = leapYear ? 366 : 365;
andrewboyson 51:fb18aa3ec115 136
andrewboyson 51:fb18aa3ec115 137 //Stop if this is the final year
andrewboyson 51:fb18aa3ec115 138 if (*pDaysLeft < daysInYear) break;
andrewboyson 51:fb18aa3ec115 139
andrewboyson 51:fb18aa3ec115 140 //Calculate the current day of the week at the start of the year
andrewboyson 51:fb18aa3ec115 141 *pDayOfWeek += leapYear ? 2 : 1;
andrewboyson 51:fb18aa3ec115 142 if (*pDayOfWeek >= 7) *pDayOfWeek -= 7;
andrewboyson 51:fb18aa3ec115 143
andrewboyson 51:fb18aa3ec115 144 //Move on to the next year
andrewboyson 51:fb18aa3ec115 145 *pDaysLeft -= daysInYear;
andrewboyson 51:fb18aa3ec115 146 ++*pYear;
andrewboyson 51:fb18aa3ec115 147 }
andrewboyson 51:fb18aa3ec115 148 }
andrewboyson 51:fb18aa3ec115 149 static void addMonths(int year, int* pMonth, int* pDaysLeft)
andrewboyson 51:fb18aa3ec115 150 {
andrewboyson 51:fb18aa3ec115 151 while(1)
andrewboyson 51:fb18aa3ec115 152 {
andrewboyson 69:18d8b1bd2952 153 int daysInMonth = TmMonthLength(year, *pMonth);
andrewboyson 51:fb18aa3ec115 154
andrewboyson 51:fb18aa3ec115 155 //Stop if this is the last month
andrewboyson 51:fb18aa3ec115 156 if (*pDaysLeft < daysInMonth) break;
andrewboyson 51:fb18aa3ec115 157
andrewboyson 51:fb18aa3ec115 158 //Move onto next month
andrewboyson 51:fb18aa3ec115 159 *pDaysLeft -= daysInMonth;
andrewboyson 51:fb18aa3ec115 160 ++*pMonth;
andrewboyson 51:fb18aa3ec115 161 }
andrewboyson 51:fb18aa3ec115 162 }
andrewboyson 51:fb18aa3ec115 163 static void timeToTm(time64 t, struct tm* ptm, bool local)
andrewboyson 51:fb18aa3ec115 164 {
andrewboyson 51:fb18aa3ec115 165 //Extract the seconds, minutes, hours and days from the time64 t
andrewboyson 51:fb18aa3ec115 166 lldiv_t divres;
andrewboyson 51:fb18aa3ec115 167 divres = lldiv( t, 60); int seconds = divres.rem;
andrewboyson 51:fb18aa3ec115 168 divres = lldiv(divres.quot, 60); int minutes = divres.rem;
andrewboyson 51:fb18aa3ec115 169 divres = lldiv(divres.quot, 24); int hours = divres.rem;
andrewboyson 51:fb18aa3ec115 170 int daysLeft = divres.quot;
andrewboyson 51:fb18aa3ec115 171
andrewboyson 51:fb18aa3ec115 172 //Add a year at a time while there is more than a year of days left
andrewboyson 51:fb18aa3ec115 173 int year = 70; //Unix epoch is 1970
andrewboyson 51:fb18aa3ec115 174 int dayOfWeek = 4; //1 Jan 1970 is a Thursday
andrewboyson 51:fb18aa3ec115 175 addYears(&year, &dayOfWeek, &daysLeft);
andrewboyson 51:fb18aa3ec115 176
andrewboyson 51:fb18aa3ec115 177 //Days left contains the days left from the start (1 Jan) of the current year
andrewboyson 51:fb18aa3ec115 178 int dayOfYear = daysLeft;
andrewboyson 51:fb18aa3ec115 179 dayOfWeek += daysLeft;
andrewboyson 51:fb18aa3ec115 180 dayOfWeek %= 7;
andrewboyson 51:fb18aa3ec115 181
andrewboyson 51:fb18aa3ec115 182 //Add a month at a time while there is more than a month of days left
andrewboyson 51:fb18aa3ec115 183 int month = 0;
andrewboyson 51:fb18aa3ec115 184 addMonths(year, &month, &daysLeft);
andrewboyson 51:fb18aa3ec115 185
andrewboyson 51:fb18aa3ec115 186 //Days left contains the days left from the start (1st) of the current month
andrewboyson 51:fb18aa3ec115 187 int dayOfMonth = daysLeft + 1;
andrewboyson 51:fb18aa3ec115 188
andrewboyson 70:3ae450c74c5e 189 //Work out if Daylight Saving Time applies
andrewboyson 70:3ae450c74c5e 190 int dst = isDst(year, month, dayOfMonth, dayOfWeek, hours);
andrewboyson 70:3ae450c74c5e 191
andrewboyson 51:fb18aa3ec115 192 //Deal with local time offsets
andrewboyson 51:fb18aa3ec115 193 if (local)
andrewboyson 70:3ae450c74c5e 194 {
andrewboyson 51:fb18aa3ec115 195 //Adjust for the timezone
andrewboyson 70:3ae450c74c5e 196 hours += dst ? TM_DST_OFFSET : TM_STD_OFFSET;
andrewboyson 51:fb18aa3ec115 197 normalise(&hours, &dayOfWeek, &dayOfMonth, &month, &dayOfYear, &year);
andrewboyson 51:fb18aa3ec115 198 }
andrewboyson 51:fb18aa3ec115 199
andrewboyson 51:fb18aa3ec115 200 //Set up the broken time TM structure
andrewboyson 51:fb18aa3ec115 201 ptm->tm_sec = seconds; // 00 --> 59
andrewboyson 51:fb18aa3ec115 202 ptm->tm_min = minutes; // 00 --> 59
andrewboyson 51:fb18aa3ec115 203 ptm->tm_hour = hours; // 00 --> 23
andrewboyson 51:fb18aa3ec115 204 ptm->tm_mday = dayOfMonth; // 01 --> 31
andrewboyson 51:fb18aa3ec115 205 ptm->tm_mon = month; // 00 --> 11
andrewboyson 51:fb18aa3ec115 206 ptm->tm_year = year; // Years since 1900
andrewboyson 51:fb18aa3ec115 207 ptm->tm_wday = dayOfWeek; // 0 --> 6 where 0 == Sunday
andrewboyson 51:fb18aa3ec115 208 ptm->tm_yday = dayOfYear; // 0 --> 365
andrewboyson 51:fb18aa3ec115 209 ptm->tm_isdst = dst; // +ve if DST, 0 if not DSTime, -ve if the information is not available. Note that 'true' evaluates to +1.
andrewboyson 51:fb18aa3ec115 210 }
andrewboyson 51:fb18aa3ec115 211
andrewboyson 51:fb18aa3ec115 212
andrewboyson 51:fb18aa3ec115 213 void TmUtcFromTime64(time64 time, struct tm* ptm)
andrewboyson 51:fb18aa3ec115 214 {
andrewboyson 51:fb18aa3ec115 215 timeToTm(time, ptm, false);
andrewboyson 51:fb18aa3ec115 216 }
andrewboyson 51:fb18aa3ec115 217 void TmLocalFromTime64(time64 time, struct tm* ptm)
andrewboyson 51:fb18aa3ec115 218 {
andrewboyson 51:fb18aa3ec115 219 timeToTm(time, ptm, true);
andrewboyson 51:fb18aa3ec115 220 }
andrewboyson 51:fb18aa3ec115 221 time64 TmUtcToTime64(struct tm* ptm)
andrewboyson 51:fb18aa3ec115 222 {
andrewboyson 51:fb18aa3ec115 223 time64 days = 0;
andrewboyson 51:fb18aa3ec115 224
andrewboyson 51:fb18aa3ec115 225 for (int y = 70; y < ptm->tm_year; y++) days += isLeapYear(y) ? 366 : 365;
andrewboyson 51:fb18aa3ec115 226
andrewboyson 51:fb18aa3ec115 227 days += ptm->tm_yday;
andrewboyson 51:fb18aa3ec115 228
andrewboyson 51:fb18aa3ec115 229 return days * 86400 +
andrewboyson 51:fb18aa3ec115 230 ptm->tm_hour * 3600 +
andrewboyson 51:fb18aa3ec115 231 ptm->tm_min * 60 +
andrewboyson 51:fb18aa3ec115 232 ptm->tm_sec;
andrewboyson 51:fb18aa3ec115 233 }
andrewboyson 51:fb18aa3ec115 234
andrewboyson 51:fb18aa3ec115 235
andrewboyson 51:fb18aa3ec115 236 int TmSecondsBetween(struct tm* ptmLater, struct tm* ptmEarlier)
andrewboyson 51:fb18aa3ec115 237 {
andrewboyson 51:fb18aa3ec115 238 int days = 0;
andrewboyson 51:fb18aa3ec115 239
andrewboyson 51:fb18aa3ec115 240 if (ptmLater->tm_year > ptmEarlier->tm_year) for (int y = ptmEarlier->tm_year; y < ptmLater->tm_year; y++) days += isLeapYear(y) ? 366 : 365;
andrewboyson 51:fb18aa3ec115 241 else for (int y = ptmEarlier->tm_year; y > ptmLater->tm_year; y--) days -= isLeapYear(y) ? 366 : 365;
andrewboyson 51:fb18aa3ec115 242
andrewboyson 51:fb18aa3ec115 243 days += ptmLater->tm_yday - ptmEarlier->tm_yday;
andrewboyson 51:fb18aa3ec115 244
andrewboyson 51:fb18aa3ec115 245 return days * 86400 +
andrewboyson 51:fb18aa3ec115 246 (ptmLater->tm_hour - ptmEarlier->tm_hour) * 3600 +
andrewboyson 51:fb18aa3ec115 247 (ptmLater->tm_min - ptmEarlier->tm_min ) * 60 +
andrewboyson 51:fb18aa3ec115 248 (ptmLater->tm_sec - ptmEarlier->tm_sec );
andrewboyson 51:fb18aa3ec115 249 }
andrewboyson 51:fb18aa3ec115 250
andrewboyson 51:fb18aa3ec115 251 void TmUtcToLocal(struct tm* ptm)
andrewboyson 51:fb18aa3ec115 252 {
andrewboyson 51:fb18aa3ec115 253 //Establish DST
andrewboyson 51:fb18aa3ec115 254 ptm->tm_isdst = isDst(ptm->tm_year, ptm->tm_mon, ptm->tm_mday, ptm->tm_wday, ptm->tm_hour);
andrewboyson 51:fb18aa3ec115 255
andrewboyson 51:fb18aa3ec115 256 //Adjust for the timezone
andrewboyson 70:3ae450c74c5e 257 ptm->tm_hour += ptm->tm_isdst ? TM_DST_OFFSET : TM_STD_OFFSET;
andrewboyson 51:fb18aa3ec115 258 normalise(&ptm->tm_hour, &ptm->tm_wday, &ptm->tm_mday, &ptm->tm_mon, &ptm->tm_yday, &ptm->tm_year);
andrewboyson 51:fb18aa3ec115 259 }
andrewboyson 51:fb18aa3ec115 260
andrewboyson 51:fb18aa3ec115 261 void TmFromAsciiDateTime(const char* pDate, const char* pTime, struct tm* ptm) // Convert compile time to system time
andrewboyson 51:fb18aa3ec115 262 {
andrewboyson 51:fb18aa3ec115 263 //__DATE__ The string constant contains eleven characters and looks like "Feb 12 1996". If the day of the month is less than 10, it is padded with a space on the left.
andrewboyson 51:fb18aa3ec115 264 char month[5];
andrewboyson 51:fb18aa3ec115 265 sscanf(pDate, "%s %d %d", month, &ptm->tm_mday, &ptm->tm_year); ptm->tm_year -= 1900;
andrewboyson 51:fb18aa3ec115 266
andrewboyson 51:fb18aa3ec115 267 // Find where month is in month_names. Deduce month value.
andrewboyson 51:fb18aa3ec115 268 static const char month_names[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
andrewboyson 51:fb18aa3ec115 269 ptm->tm_mon = (strstr(month_names, month) - month_names) / 3;
andrewboyson 51:fb18aa3ec115 270
andrewboyson 51:fb18aa3ec115 271 //__TIME__ The string constant contains eight characters and looks like "23:59:01".
andrewboyson 51:fb18aa3ec115 272 sscanf(pTime, "%2d %*c %2d %*c %2d", &ptm->tm_hour, &ptm->tm_min, &ptm->tm_sec);
andrewboyson 51:fb18aa3ec115 273
andrewboyson 51:fb18aa3ec115 274 //Fill the day of week and the day of year part of the tm structure
andrewboyson 51:fb18aa3ec115 275 calculateDayOfYearAndWeek(ptm->tm_year, ptm->tm_mon, ptm->tm_mday, &ptm->tm_yday, &ptm->tm_wday);
andrewboyson 51:fb18aa3ec115 276 }
andrewboyson 51:fb18aa3ec115 277 void TmFromInteger(int year, int month, int mday, int hour, int min, int sec, struct tm* ptm)
andrewboyson 51:fb18aa3ec115 278 {
andrewboyson 51:fb18aa3ec115 279 ptm->tm_year = year - 1900;
andrewboyson 51:fb18aa3ec115 280 ptm->tm_mon = month - 1;
andrewboyson 51:fb18aa3ec115 281 ptm->tm_mday = mday;
andrewboyson 51:fb18aa3ec115 282 calculateDayOfYearAndWeek(ptm->tm_year, ptm->tm_mon, ptm->tm_mday, &ptm->tm_yday, &ptm->tm_wday);
andrewboyson 51:fb18aa3ec115 283 ptm->tm_hour = hour;
andrewboyson 51:fb18aa3ec115 284 ptm->tm_min = min;
andrewboyson 51:fb18aa3ec115 285 ptm->tm_sec = sec;
andrewboyson 51:fb18aa3ec115 286 }
andrewboyson 51:fb18aa3ec115 287 void TmIncrement(struct tm* ptm)
andrewboyson 51:fb18aa3ec115 288 {
andrewboyson 51:fb18aa3ec115 289 ptm->tm_sec++;
andrewboyson 51:fb18aa3ec115 290 if (ptm->tm_sec > 59)
andrewboyson 51:fb18aa3ec115 291 {
andrewboyson 51:fb18aa3ec115 292 ptm->tm_sec = 0;
andrewboyson 51:fb18aa3ec115 293 ptm->tm_min++;
andrewboyson 51:fb18aa3ec115 294 }
andrewboyson 51:fb18aa3ec115 295 if (ptm->tm_min > 59)
andrewboyson 51:fb18aa3ec115 296 {
andrewboyson 51:fb18aa3ec115 297 ptm->tm_min = 0;
andrewboyson 51:fb18aa3ec115 298 ptm->tm_hour++;
andrewboyson 51:fb18aa3ec115 299 }
andrewboyson 51:fb18aa3ec115 300 if (ptm->tm_hour > 23)
andrewboyson 51:fb18aa3ec115 301 {
andrewboyson 51:fb18aa3ec115 302 ptm->tm_hour = 0;
andrewboyson 51:fb18aa3ec115 303 ptm->tm_wday++;
andrewboyson 51:fb18aa3ec115 304 if (ptm->tm_wday > 6) ptm->tm_wday = 0;
andrewboyson 51:fb18aa3ec115 305 ptm->tm_yday++;
andrewboyson 51:fb18aa3ec115 306 ptm->tm_mday++;
andrewboyson 69:18d8b1bd2952 307 if (ptm->tm_mday > TmMonthLength(ptm->tm_year, ptm->tm_mon))
andrewboyson 51:fb18aa3ec115 308 {
andrewboyson 51:fb18aa3ec115 309 ptm->tm_mon++;
andrewboyson 51:fb18aa3ec115 310 if (ptm->tm_mon > 11)
andrewboyson 51:fb18aa3ec115 311 {
andrewboyson 51:fb18aa3ec115 312 ptm->tm_year++;
andrewboyson 51:fb18aa3ec115 313 ptm->tm_yday = 0;
andrewboyson 51:fb18aa3ec115 314 ptm->tm_mon = 0;
andrewboyson 51:fb18aa3ec115 315 }
andrewboyson 51:fb18aa3ec115 316 ptm->tm_mday = 1;
andrewboyson 51:fb18aa3ec115 317 }
andrewboyson 51:fb18aa3ec115 318 }
andrewboyson 51:fb18aa3ec115 319 }