📜 ⬆️ ⬇️

We create an original gift with the help of chemistry, physics and electronics: part 3

The third, final part of my article on the creation of a luminous crystal souvenir. It describes the software part, that is, the ATTiny13 microcontroller firmware from the glorious kind of AVRok.


Step 4: develop the firmware



The firmware for the device is quite trivial, but you should pay attention to the following points:
1) This microcontroller has only one 8-bit timer, so you have to use it simultaneously for both PWM and sensor polling, switching back and forth.
2) To reduce power consumption, it would be nice to immerse the device in a dream when no one touches it.
')
Here with the second point there was some hitch. Maybe I’ll say something you already know, but it may well be that it will save you from a long search for a glitch in the firmware. After transferring the microcontroller to the Power Down low power mode, it can be awakened only by an external interrupt (which we don’t have anywhere in the circuit) or a watchdog timer.
But the fact is that the watchdog timer can work in two modes: reload on overflow and interrupt on overflow (yes, there is still a third mode, a combination of the two previous ones). And, as it turned out, in the overload mode on overflow, it does not take the controller out of sleep mode, at least the circuit has flatly refused to work like that. Therefore, we will use exactly the overflow interrupt mode.

And one more note: if you assemble such a device and test the “shed”, remember the following:
The sensor will show a much larger capacity if you touch, say, the minus of the battery when testing with your second hand. I stuck with this, because during testing I pressed the wires to the battery with my hands, so I set an overly large threshold.

The general approach to the implementation is as follows: the device can be in one of six states:
1) The diode is off, waiting for a response from the sensor. In this state, we periodically check the sensor, after which we fall asleep for about a second, which will ensure low power consumption
2) After the first response from the sensor, we no longer fall asleep, wait about 300 ms to make sure that this is not a hindrance, and recheck the sensor. If the sensor is not active, then return to state 1. Otherwise, go to state 3.
3) Switch the timer to the PWM mode and slowly increase the duty cycle, then we glow until 4 seconds pass and we go to state 4
4) Switch the timer from PWM to touch mode, check the sensor to determine when the device is put in place, when the sensor becomes inactive, go to state 5
5) We are waiting for another 15 seconds, recheck the sensor. If active, then return to state 4. Otherwise, go to state 6.
6) Slowly extinguish, while periodically switching the timer from PWM mode to sensor mode and re-checking its activity. If the sensor has become active again, then go to state 3, again starting to flare up. Otherwise, when the duty cycle reached 0, again switch to mode 1.

Below I will set out the code with comments. Because I had to hurry with the code in order to have time for the new year, there may well be completely non-optimal moments, please do not kick for them)
However, this is a fully working and debugged code. Takes about 70-80 percent of flash memory tinky.

#define F_CPU 9600000UL #include<avr/io.h> #include<util/delay.h> #include<avr/wdt.h> #include<avr/sleep.h> #include<avr/interrupt.h> //  enum DEV_MODE{M_WAITING_SENSOR, //      M_SENSOR_RECHECK, //    M_GLOW, // M_GLOW_AND_CHECK, //    M_GLOW_AND_RECKECK, //,   M_FADE}; // unsigned char SensorHi=0; //,        unsigned short Delay=0; //      unsigned short PWM=0; //  unsigned char Mode=0; //  void SetTimer(char Mod) //          { if(Mod) //1,   { TCCR0A=0x00; TCCR0B=0x00; TCNT0=0x00; } else //0,  { TCCR0A=0x83; TCCR0B=0x00; TCNT0=0x00; } } void Recalibrate() //  { cli(); //    ,   ) SetTimer(1); DDRB&= 0b11110111; PORTB|=0b00010000; TCCR0B=0x01; while(!(PINB&0b00001000)); TCCR0B=0x00; DDRB|= 0b00001000; PORTB&= 0b11101111; SensorHi=TCNT0+3; //   +3   ,    sei(); //   } unsigned char CheckSensor() { cli(); SetTimer(1); DDRB&= 0b11110111; PORTB|=0b00010000; TCCR0B=0x01; while(!(PINB&0b00001000)); TCCR0B=0x00; DDRB|= 0b00001000; PORTB&= 0b11101111; unsigned char Time=TCNT0; sei(); if(Time>SensorHi) return 0xFF; return 0x00; } ISR(SIG_WATCHDOG_TIMEOUT) //     { __asm__ __volatile__("nop"); //     } int main() { ACSR = 0b1000000; //   DDRB = 0b00011001; SetTimer(1); //      Mode= M_WAITING_SENSOR; sei(); PORTB=0b00000001; //    4  for(char i=0;i<40;i++) //  ,    { _delay_ms(20); _delay_ms(20); _delay_ms(20); _delay_ms(20); _delay_ms(20); } PORTB=0b00000000; //  Recalibrate(); // _delay_ms(20); while(1) { switch(Mode) { case M_WAITING_SENSOR: if(CheckSensor()) { Mode= M_SENSOR_RECHECK; PWM=0x00; } else { //       //    ,      //          sei(); __asm__ __volatile__("in r16, 0x21"); __asm__ __volatile__("ori r16, 0b00011000"); __asm__ __volatile__("out 0x21 ,r16"); __asm__ __volatile__("ldi r16, 0b01000111"); __asm__ __volatile__("out 0x21 ,r16"); __asm__ __volatile__("ldi r16 ,0b00110000"); __asm__ __volatile__("out 0x35 ,r16"); __asm__ __volatile__("sleep"); //     1 ,   } break; case M_SENSOR_RECHECK: cli(); Delay++; if(Delay>0x0010) { if(!CheckSensor()) { Mode= M_WAITING_SENSOR; Delay=0x0000; } else { Delay=0x0000; Mode= M_GLOW; SetTimer(0); PWM=0x00; OCR0A=PWM; TCCR0B|=0x01; } } break; case M_GLOW: Delay++; if((PWM<0xFF)&&(Delay%2==0)) // **,  Delay%2 PWM++; if(Delay>0x0200) //  { SetTimer(1); PORTB =0b00000001; //    . 1 Mode= M_GLOW_AND_CKECK; Delay=0x0000; } break; case M_GLOW_AND_CKECK: if(!CheckSensor()) { Mode=M_GLOW_AND_RECKECK; PWM=0xFF; } break; case M_GLOW_AND_RECKECK: Delay++; if(Delay>0x0500) // ,        { //    20 if(CheckSensor()) { Mode= M_GLOW_AND_CKECK; Delay=0x0000; } else { Delay=0x0000; Mode=M_FADE; PWM=0xFF; OCR0A=PWM; SetTimer(0); TCCR0B=0x01; } } break; case M_FADE: Delay++; if(Delay%5==0) //   Delay   { TCCR0B=0x00; PORTB = 0b00000000; if(CheckSensor()) { Mode=0x02; //  -    Delay=0x0000; } SetTimer(0); TCCR0B=0x01; } if((PWM>0)&&(Delay%2==0)) //  PWM--; if(Delay>0x0200) //  { SetTimer(1); PORTB=0b00000000; Mode= M_WAITING_SENSOR; Delay=0x0000; } break; } OCR0A=PWM; _delay_ms(20); } } 


Conclusion



That's all - the technical work is still there: fix the firmware in SMD ATTiny13, solder the SMD resistor to its two pins (perhaps you have enough space in the stand for non-SMD components, but my place was critical), moving away from its wire as a sensor to the foil side of the stand, solder the leads of the LED to the microcontroller and the common wire, plus from the battery, connect to the power supply of the microcontroller, and minus to the common wire and close the stand with a lid.

In my device, I divided the stand into two parts, at the top I placed the circuit from which I took the power and sensor wires, and sealed it with a plastic partition. He put a wide spring on it under the minus of the battery and a curved plate on top under the plus. Thus, after the stand is closed with a lid, the plates are pressed against the battery and the circuit turns on, giving me 4 seconds to remove my hands before calibration. If this is not done, the device will consider that the capacity of the inactive sensor is equal to the capacity of the active one, and it will be impossible to ignite it without recalibrating.

Judging by my measurements, the circuit consumes 0.02 mA in idle mode and about 12 mA at full brightness of the diode. Even if the multimeter showed an inaccurate result and was mistaken by an order of magnitude, the energy in the battery should be enough for about half a year of idle time, which is quite a good result.

With due diligence, you can make a similar device of any size, and as a salt, you can take a substance of a different color and grow a red or yellow crystal. All in your hands)

Part 1
Part 2

Source: https://habr.com/ru/post/111671/


All Articles