Sunday, November 30, 2025

0000 0001 0000 1000

Competition Giveaway

This week I make a lovely candle from a cheap solar light - then give it away!

Here is the code for the candle as promised:

/*
  Candle with Three PWM

  Pseudo-random flickering to simulate a candle. Output is
  via 3xPWM channels, variables can be changed to
  alter the simulation

  Sun 30 Nov 2025 19:53:23 AEDT

*/

#include <stdint.h>
#include <stdlib.h>
#include "../device.h"
#include "../easy-pdk/calibrate.h"
#include "../auto_sysclock.h"
#include "../delay.h"
#include <stdbool.h>

#define LED4_BIT 4
#define LED0_BIT 0
#define LED3_BIT 3

bool debug = false;

uint16_t myrand = 2901;  // happy birthday

uint8_t slowcounter = 0;
uint8_t medcounter = 0;
uint8_t fastcounter = 0;
uint8_t slowstart = 0;
uint8_t slowend = 0;
uint8_t medstart = 0;
uint8_t medend = 0;
uint8_t faststart = 0;
uint8_t fastend = 0;
uint8_t faster = 0;

// cutoff percentages for types of flicker
uint8_t percentnormal = 82;  // cutoff for normal/calm
uint8_t percentsputter = 20; // cutoff for sputtering/normal

uint8_t flickdelay = 40;        // initial speed of flicker
uint8_t flickdelaysputter = 9;  // "sputtering" activity
uint8_t flickdelaynormal = 40;  // "normal" activity
uint8_t flickdelaycalm = 95;    // "calm" activity
uint8_t choosearray = 1;        // normal waves

int delaycounter = 50;
int delaydelay = 20;

uint8_t waves[9][4] = {
  {4, 6, 40, 50} ,    // sputter flicker waveslow
  {6, 8, 50, 80} ,   // sputter flicker wavemed
  {8, 10, 110, 130} ,   // sputter flicker wavefast
  {15, 25, 60, 100} ,    // normal flicker waveslow
  {10, 25, 110, 140} ,   // normal flicker wavemed
  {20, 25, 100, 120} ,   // normal flicker wavefast
  {40, 60, 100, 140} ,    // calm flicker waveslow
  {50, 70, 120, 160} ,   // calm flicker wavemed
  {70, 80, 140, 180}   // calm flicker wavefast
};

bool fastup = true;
bool slowup = true;
bool medup = true;

void mydelay(uint8_t counter) {

  for (uint8_t thiscount = 0; thiscount <= counter; thiscount++) {
    _delay_us(1);
  }
}

uint16_t gimmerand(uint16_t small, uint16_t big) {
  myrand ^= (myrand << 13);
  myrand ^= (myrand >> 9);
  myrand ^= (myrand << 7);
  if (abs(myrand) % 13 == 0) {
    myrand = myrand - 23;
  }
  if (abs(myrand) % 17 == 0) {
    myrand = myrand + 11;
  }
  return abs(myrand) % 23 * (big - small) / 23 + small;
}

void getnewslow(uint8_t whicharray) {
  slowstart = gimmerand(waves[whicharray][0], waves[whicharray][1]);
  slowend = gimmerand(waves[whicharray][2], waves[whicharray][3]);
}

void getnewmed(uint8_t whicharray) {
  medstart = gimmerand(waves[whicharray+1][0], waves[whicharray+1][1]);
  medend = gimmerand(waves[whicharray+1][2], waves[whicharray+1][3]);
}

void getnewfast(uint8_t whicharray) {
  faststart = gimmerand(waves[whicharray+2][0], waves[whicharray+2][1]);
  fastend = gimmerand(waves[whicharray+2][2], waves[whicharray+2][3]);
  faster = gimmerand(2, 6);
}

void myblink(uint8_t howmany) {
  for (uint8_t blinkcount = 1; blinkcount <= howmany; blinkcount++) {
    PA = 0b00000000;
    _delay_ms(10);
    PA = 0b00100000;
    _delay_ms(10);
  }
}

// Main program
void main() {

  _delay_ms(100); // settle

  if (debug) {
    PAC |= (1 << 5);
    myblink(5);
  }

  // see datasheet
  PWMG1DTL = 0x00;
  PWMG1DTH = 0x00;
  PWMG1CUBL = 0xff;
  PWMG1CUBH = 0xff;
  PWMG1C = 0b10100111;
  PWMG1S = 0b00000000;

  PWMG0DTL = 0x00;
  PWMG0DTH = 0x00;
  PWMG0CUBL = 0xff;
  PWMG0CUBH = 0xff;
  PWMG0C = 0b10100111;
  PWMG0S = 0b00000000;

  PWMG2DTL = 0x00;
  PWMG2DTH = 0x00;
  PWMG2CUBL = 0xff;
  PWMG2CUBH = 0xff;
  PWMG2C = 0b10100111;
  PWMG2S = 0b00000000;

  getnewfast(choosearray);
  getnewslow(choosearray);
  getnewmed(choosearray);
  slowcounter = slowstart;
  fastcounter = faststart;
  medcounter = medstart;

  PAC |= (1 << LED4_BIT) | (1 << LED0_BIT) | (1 << LED3_BIT);

  // Main processing loop
  while (1) {

    // ramp up slow
    if (slowup) {
      slowcounter++;
      if (slowcounter > slowend) { // ramp finished so switch boolean
        slowup = !slowup;
      }
    }
    else {
      // ramp down slow
      slowcounter--;
      if (slowcounter < slowstart) { // ramp finished so switch boolean
        slowup = !slowup;
        getnewslow(choosearray);
      }
    }

    // ramp up med
    if (medup) {
      medcounter++;
      if (medcounter > medend) { // ramp finished so switch boolean
        medup = !medup;
      }
    }
    else {
      // ramp down med
      medcounter--;
      if (medcounter < medstart) { // ramp finished so switch boolean
        medup = !medup;
        getnewmed(choosearray);
      }
    }

    // ramp up fast
    if (fastup) {
      fastcounter = fastcounter + faster;
      if (fastcounter > fastend) { // ramp finished so switch boolean
        fastup = !fastup;
      }
    }
    else {
      // ramp down fast
      fastcounter = fastcounter - faster;
      if (fastcounter < faststart) { // ramp finished so switch boolean
        fastup = !fastup;
        getnew...and here is the video for your enjoyment...

vid
fast(choosearray);
      }
    }
    // delay + a re-purposed random for ramp speeds
    mydelay(flickdelay + faster);
    delaycounter = delaycounter - 1;

    if (delaycounter == 0) {
      delaycounter = gimmerand(1, 100);
      if (delaycounter > percentnormal) { // calm
        if (debug) myblink(3);
        flickdelay = flickdelaycalm;
        choosearray = 6;
        delaycounter = 100-delaycounter;
      }
      else if (delaycounter > percentsputter) { // "normal"
        if (debug) myblink(2);
        flickdelay = flickdelaynormal;
        choosearray = 3;
      }
      else { // sputtering
        if (debug) myblink(1);
        flickdelay = flickdelaysputter;
        choosearray = 0;
      }
    delaycounter = delaycounter * delaydelay;
    }

    PWMG2DTL = slowcounter & 255;
    PWMG2DTH = slowcounter;
    PWMG0DTL = fastcounter & 255;
    PWMG0DTH = fastcounter;
    PWMG1DTL = medcounter & 255;
    PWMG1DTH = medcounter;

  }
}

// Startup code - Setup/calibrate system clock
unsigned char _sdcc_external_startup(void) {

  AUTO_INIT_SYSCLOCK();
  AUTO_CALIBRATE_SYSCLOCK(TARGET_VDD_MV);

  return 0;
}

...and here is the video for your enjoyment! Good luck in the competition.