ATtiny402 Christmas tree with motion

23 LED Christmas Tree, Lights up with PIR and lasts for a long time on a Lipo battery.

Update 7/6/2023: I'm adding fading with PWM with a small analogWrite fuction. Currently under construction.

STL at: https://www.printables.com/model/633383-2022-christmas-gift-to-all-christmas-tree-with-mot

or at: https://cults3d.com/en/3d-model/home/2022-christmas-gift-to-all-christmas-tree-with-motion-sensor



PCB can be bought from Oshpark:

https://www.shermluge.com/electronics/attiny-202402/attiny402-firefly-remake-of-attiny85-firefly 

/*************************************************************************** * ATtiny402-PIR-Christmas-Tree-or-Meteor-with-sleep.c * * Currently at .16uA * With Motion sensor its 16-19uA * * Created: 4/1/2022 19:25:47 * Author : Sherman Stebbins * * To do: *  Maybe add analogWrite function in for fading. From 5pin PWM program. * */
#define F_CPU 10000000UL#include <avr/io.h>#include <util/delay.h>#include <avr/interrupt.h>#include <stdlib.h> //for rand(); works//#include "analogWrite.h" //Set up first then use.(work in progress)
#define PA PIN3_bm#define PB PIN6_bm#define PC PIN7_bm#define PD PIN1_bm
#define ModeMax 4//#define DEBG 
const uint8_t led_dir[12] = { ( PA | PB), //LED 0 1 ( PA | PB), //LED 0 1 ( PC | PA), //LED 2 3 ( PC | PA), //LED 2 3 ( PD | PA), //LED 4 5 ( PD | PA), //LED 4 5 ( PB | PC), //LED 6 7 ( PB | PC), //LED 6 7 ( PB | PD), //LED 8 9 ( PB | PD), //LED 8 9 ( PD | PC), //LED 10 11 ( PD | PC) }; //LED 10 11
const uint8_t led[12] = { ( PA ), //LED 0 ( PB ), //LED 1 ( PA ), //LED 2 ( PC ), //LED 3 ( PA ), //LED 4 ( PD ), //LED 5 ( PC ), //LED 6 ( PB ), //LED 7 ( PD ), //LED 8 ( PB ), //LED 9 ( PC ), //LED 10    ( PD) }; //LED 11
static volatile uint8_t sleepstate = 0;static volatile uint8_t mode = 0;uint8_t randLoops;

void gotosleep(void);void ledOn(uint8_t);void ledOff(void);void allLedOn(uint8_t);void rainDrop(uint8_t, uint8_t);void randRainDrops(uint8_t);void upDown(uint8_t, uint8_t);void randLeds(uint8_t);//void ledOnFade(uint8_t light,uint8_t brightness);//void allLedOnFade(uint8_t brightness);
int main(void){// long tt = random(10,20); PORTA.DIRSET = 0; for(uint8_t i=0;i<12;i++){ for (uint8_t k=0;k<10;k++){ ledOn(i); _delay_ms(1); ledOff(); _delay_ms(1); if(i>0){ ledOn(i-1); _delay_ms(1); ledOff(); _delay_ms(1); } } } // TCA0_init();// TCB0_init();
//pull up pin 2 and set it to trigger int on falling edge: PORTA.PIN2CTRL = PORT_ISC_RISING_gc; //this was my fix for changing current draw sei(); //Set enable interrupt //set sleep to power down //SLPCTRL.CTRLA = 0b00000101; //sherms way SLPCTRL.CTRLA = SLPCTRL_SMODE_PDOWN_gc | SLPCTRL_SEN_bm; //Microchip studio showed this gotosleep(); //sleep //asm("sleep"); //sleep
while (1){ randLoops = (rand() % 9)+6;//3; //random 3-12 loops for display switch(mode){ case 0: rainDrop(150,randLoops); break; case 1: randRainDrops(randLoops); break; case 2: upDown(150,randLoops); break; case 3: randLeds(randLoops); break; case 4: allLedOn(100); //about 1min per 100 (250 max). break; default: break; } #ifdef DEBG ledOn(mode); _delay_ms(50); ledOff(); #endif if(mode++ > ModeMax-1){ mode=0; } gotosleep(); }}

void rainDrop(uint8_t speed,  uint8_t loops){ ledOff(); for(uint8_t l=0;l<loops;l++){ for(uint8_t i=0;i<12;i++){ for (uint8_t k=0;k<speed;k++){ ledOn(i); _delay_us(50); ledOff(); _delay_us(20); if(i>0){ ledOn(i-1); _delay_us(50); ledOff(); _delay_us(20); } if(i>1){ ledOn(i-2); _delay_us(50); ledOff(); _delay_us(20); } } } for(int i=0;i<5;i++){ ledOn(11); _delay_ms(10); ledOff(); _delay_ms(10); } }}
void randRainDrops(uint8_t loops){ ledOff(); int randNum; for(uint8_t l=0;l<loops;l++){ randNum = (rand() % 205)+10; for(uint8_t i=0;i<12;i++){ for (int k=0;k<randNum;k++){ ledOn(i); _delay_us(50); ledOff(); _delay_us(50); if(i>0){ ledOn(i-1); _delay_us(50); ledOff(); _delay_us(50); } } } for(int i=0;i<5;i++){ ledOn(11); _delay_ms(10); ledOff(); _delay_ms(10); } }}
void upDown(uint8_t speed, uint8_t loops){ ledOff(); uint8_t j; for(uint8_t h=0;h<loops;h++){ for(uint8_t i=0;i<12;i++){ for (uint8_t k=0;k<speed;k++){ ledOn(i); _delay_us(50); ledOff(); _delay_us(20); } } for(uint8_t i=12;i>0;i--){ j=i-1; for (uint8_t k=0;k<speed;k++){ ledOn(j); _delay_us(50); ledOff(); _delay_us(20); } } }}
void randLeds(uint8_t loops){ uint8_t randLight; uint8_t randTime; for(uint8_t i=0;i<loops;i++){ for (uint8_t k=0;k<48;k++){ randLight = rand() % 12; ledOn(randLight); randTime = (rand() % 80) + 10; for(uint8_t l=0;l<randTime;l++){ _delay_us(200); } ledOff(); } }}
void ledOn(uint8_t light){ PORTA.DIRSET = led_dir[light]; PORTA.OUT = led[light];}
void allLedOn(uint8_t loops){ for (uint16_t j=0;j<loops*250;j++){ for(uint8_t k=0;k<15;k++){ for (uint8_t i = 0; i<12 ; i++) { ledOn(i); ledOff(); //try removing to see if it works better } } }}/*void ledOnFade(uint8_t light,uint8_t brightness){    PORTA.DIRSET = led_dir[light]; //PORTA.OUT = led[light]; analogWrite(led[light],brightness);}
void allLedOnFade(uint8_t brightness){ for(uint8_t k=0;k<15;k++){ for (uint8_t i = 0; i<12 ; i++) { ledOnFade(i,brightness); } }}*/
void ledOff(void){ PORTA.DIRCLR = (PA|PB|PC|PD);}
void gotosleep(void){ PORTA.DIRSET = 0b00000000;
PORTA.PIN3CTRL = PORT_PULLUPEN_bm; PORTA.PIN1CTRL = PORT_PULLUPEN_bm; PORTA.PIN7CTRL = PORT_PULLUPEN_bm; PORTA.PIN6CTRL = PORT_PULLUPEN_bm;
sei(); SLPCTRL.CTRLA = 0b00000101; //set power down and sleep enable asm("sleep"); //sleep //turn off pull ups after sleep PORTA.PIN3CTRL &= ~(PORT_PULLUPEN_bm); PORTA.PIN1CTRL &= ~(PORT_PULLUPEN_bm); PORTA.PIN7CTRL &= ~(PORT_PULLUPEN_bm); PORTA.PIN6CTRL &= ~(PORT_PULLUPEN_bm); // for analogWrite() work in progress. // TCA0_init();// TCB0_init();
}
ISR(PORTA_PORT_vect){// if(sleepstate){// sleepstate=0;// } //PORTA.INTFLAGS = 0x04; //clear int flag, from Ben Heck.. PORTA.INTFLAGS = PORT_INT2_bm; //not tested, got from iotn402.h}


/********************************************************************************************* * analogWrite.h * * for ATtiny 0-Series, might work with 1 or 2 * * Created: 4/25/2023 16:34:19 * Author : Sherman Stebbins */ 

#include <avr/io.h>



void TCB0_init (void);void TCA0_init(void);void analogWrite(uint8_t pin, uint8_t phase);
/********************************************************************************************* * analogWrite.c * *  * * Created: 4/25/2023 16:34:19 * Author : Sherman Stebbins */ 

#include "analogWrite.h"


/* EXAMPLE: switch (light){ case 0: analogWrite(6,240); //Changes PA6 break; case 1: analogWrite(3,20); //Changes PA3 break; case 2: analogWrite(1,10); //changes PA1 break; case 3: analogWrite(2,150); //changes PA2 break; case 4: analogWrite(7,200); //changes PA7 break; } */
void TCA0_init(void){ /* set waveform output on PORT A */// PORTMUX.TCAROUTEA = PORTMUX_TCA0_PORTA_gc; //TOTAL FAILURE FROM EXAMPLE PORTMUX.CTRLC = PORTMUX_TCA00_ALTERNATE_gc; // Move it to PA7 TOTALLY WOKED AND MOVED TO PA7 //added 4/20/23 WORKED AND ADDED 5TH CHANNEL /* enable split mode */ TCA0.SPLIT.CTRLD = TCA_SPLIT_SPLITM_bm; TCA0.SPLIT.CTRLB = TCA_SPLIT_HCMP0EN_bm /* enable compare channel 0 for the higher byte */ |TCA_SPLIT_LCMP0EN_bm | TCA_SPLIT_HCMP1EN_bm | TCA_SPLIT_LCMP1EN_bm  | TCA_SPLIT_LCMP2EN_bm | TCA_SPLIT_HCMP2EN_bm; /* TCA_SPLIT_LCMP0EN_bm;  enable compare channel 0 for the lower byte  "This is the mystry trying to get to a port. */ /* set the PWM frequencies and duty cycles */ // PA1 Low still not working to a port (UPDATE: WORKS GREAT NOW!!) TCA0.SPLIT.LPER = 0xFF; //PERIOD_EXAMPLE_VALUE_L //changes frequency
// (PA3): TCA0.SPLIT.HPER = 0xFF;//PERIOD_EXAMPLE_VALUE_H; TCA0.SPLIT.HCMP1 = 0xFF; // DUTY_CYCLE_EXAMPLE_VALUE_H; TCA0.SPLIT.HCMP2 = 0xFF; // DUTY_CYCLE_EXAMPLE_VALUE_H; TCA0.SPLIT.CTRLA = TCA_SPLIT_CLKSEL_DIV2_gc /* set clock source (sys_clk/16) */ | TCA_SPLIT_ENABLE_bm; /* start timer */}
//Pin PA6:void TCB0_init (void){ /* Load CCMP register with the period and duty cycle of the PWM */ TCB0.CCMP = 0x03AF; //first two digits adjust phase, second two fine tune frequency

/* Enable TCB0 and Divide CLK_PER by 2 */ TCB0.CTRLA |= TCB_ENABLE_bm; TCB0.CTRLA |= TCB_CLKSEL_CLKDIV2_gc; /* Enable Pin Output and configure TCB in 8-bit PWM mode */ TCB0.CTRLB |= TCB_CCMPEN_bm; TCB0.CTRLB |= TCB_CNTMODE_PWM8_gc;}
void analogWrite(uint8_t pin, uint8_t phase){ switch (pin){ case 6: TCB0.CCMPH = phase; //Changes PA6 //if (phase=0) break; case 3: TCA0.SPLIT.HCMP0 = phase; //Changes PA3 break; case 1: TCA0.SPLIT.LCMP1 = phase; //changes PA1 break; case 2: TCA0.SPLIT.LCMP2 = phase; //changes PA2 break; case 7: TCA0.SPLIT.LCMP0 = phase; //changes PA7 break; }}