Skip to content

Instantly share code, notes, and snippets.

@Lukelectro
Created June 10, 2025 16:12
Show Gist options
  • Save Lukelectro/77fa07637c20d20e6db11c76731620c7 to your computer and use it in GitHub Desktop.
Save Lukelectro/77fa07637c20d20e6db11c76731620c7 to your computer and use it in GitHub Desktop.
Sourcecode van de firmware van een kookwekker die ik in oktober 2011 maakte. Kan dus 14 jaar oude domme fouten bevatten. https://www.circuitsonline.net/forum/view/message/1312121#1312121
// kookwekker. Definitieve versie. Zo zuinig mogelijk. Dus slapen, wakker worden op button interrupt om tijd in te stellen,
// aftellen met de displays uit op een knipperend puntje na, displays alleen aan bij tijd instellen.
// fuses instellen op 1Mhz clock (8Mhz intern/8, of evt. ext. kristal). Zet BOR en WDT uit als ze dat niet al zijn.
// BOR uit om sleep power consumtion lager te krijgen.
/* TODO:
- nameten wat verbruik in sleep is. (1uA voor de tinny2313, maar doet de periferie?)
*/
/* verschillen met dev. versie:
- display inverteren, evenals mux. aansturing. (is CC ipv CA dit keer.)
-bugfix: in zeldzame gevallen bleef het displaypuntje branden in sleepmode. Nu niet meer.
- extra feature: wekker loopt max. 2 minuten af en gaat daarna vanzelf uit.
*/
#include <avr/io.h>
#include <stdint.h>
#include <avr/sleep.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#define BUZZ 0 //buzzer portd.0
#define SW 3 //switch portd.3
#define DISP0 1 //dispmux0 portd.1
#define DISP1 2 //dispmux1 portd.2
volatile uint8_t minuten=0, tienminuten=0, tellertje=0, tijd0vlag=1, dispvlag=0;
// (Tien)minuten om de tijd bij te houden,
// tellertje om displays om en om te multiplexen / puntje te laten knipperen.
// tijd0vlag wordt gebruikt om door te geven dat de tijd 0 is. In de main naar minuten en tienminuten kijken terwijl ze
// in een ISR aangepast kunnen worden werkt niet goed, ik moet zowel minuten als tienminuten testen. Als de minuten 0 is, en
// daarna komt een interrupt die tienminuten 0 maakt, en de test voor de minuten zit voor de interrupt en die voor de tienminuten
// na de interrupt, dan heb ik een probleem. Want dan testen ze beiden 0 terwijl dat niet zo is.
// Dat verklaart waarom -af en toe- de wekker al bij 9 bij 0 denkt te zijn...
// Dispvlag wordt gebruikt om in de display multiplex ISR te kunnen kiezen tussen cijfers weergeven, of, zuiniger: een knipperend puntje.
ISR(TIMER1_COMPA_vect){ // timer interrupt voor: 't aftellen
if(minuten>0)
minuten--;
else if(tienminuten>0){
minuten = 9;
tienminuten--;
}
if((minuten|tienminuten)==0) tijd0vlag=1; // Als na deze wijzigingen de tijd 0 is, moet de wekker meteen aflopen.
// en niet pas een minuut later, wat zou gebeuren als ik dit in een else zet.
}
ISR(INT1_vect){ // button interrupt voor: wake-up from sleep.
TIMSK &= ~(1<<OCIE1A); // even stoppen met aftellen.
minuten=1;
tijd0vlag=0;
TCNT1 = 0; // netjes opnieuw beginnen met aftellen bij 0.
TIMSK |= (1<<OCIE1A); // en weer doorgaan met aftellen.
GIMSK&=~(1<<7); // deze interrupt disablen. Wordt weer enabled voor sleep.
};
ISR(TIMER0_OVF_vect){ // timer interrupt voor: display multiplexen
TCNT0=128; //preloaden op de helft. Dus 122,6x per seconde ipv 61,3
tellertje++;
if(dispvlag==1){
if((tellertje&(1<<0))==1){ // laatste bit van toggle bepaald welk display aangestuurd wordt.
PORTD &= ~( (1<<DISP0) | (1<<DISP1) ); // beide displays uit
switch(tienminuten){
case 0: PORTB=0b00111111; break; // 0 weergeven
case 1: PORTB=0b00001001; break; // 1 weergeven
case 2: PORTB=0b10100111; break; // 2 weergeven
case 3: PORTB=0b10001111; break; // 3 weergeven
case 4: PORTB=0b10011001; break; // 4 weergeven
case 5: PORTB=0b10011110; break; // 5 weergeven
case 6: PORTB=0b10111110; break; // 6 weergeven
case 7: PORTB=0b00001011; break; // 7 weergeven
case 8: PORTB=0b10111111; break; // 8 weergeven
case 9: PORTB=0b10011111; break; // 9 weergeven
default:PORTB=0b10110110; break; // E weergeven
}
PORTD |= (1<<DISP0); // disp0 aan.
}
else
{
PORTD &=~((1<<DISP0)|(1<<DISP1)); // beiden uit
switch(minuten){
case 0: PORTB=0b00111111; break; // 0 weergeven
case 1: PORTB=0b00001001; break; // 1 weergeven
case 2: PORTB=0b10100111; break; // 2 weergeven
case 3: PORTB=0b10001111; break; // 3 weergeven
case 4: PORTB=0b10011001; break; // 4 weergeven
case 5: PORTB=0b10011110; break; // 5 weergeven
case 6: PORTB=0b10111110; break; // 6 weergeven
case 7: PORTB=0b00001011; break; // 7 weergeven
case 8: PORTB=0b10111111; break; // 8 weergeven
case 9: PORTB=0b10011111; break; // 9 weergeven
default:PORTB=0b10110110; break; // E weergeven
}
PORTD |= (1<<DISP1); // Disp1 aan
}
}
else // als dispvlag 0 is dus.
{
PORTD &= ~( (1<<DISP0)|(1<<DISP1) ); // beide displays uit.
PORTD |= (1<<DISP1); // Disp1 aan.
if(tellertje>250) PORTB = 0b01000000; else PORTB=0; // dp aan gedurende 5/255de van de tijd. (ongeveer 40ms aan, per 2s)
}
}
int main(){
uint8_t i;
//init
ACSR = (1<<ACD); // analoge comperator disable.
//(Wordt al vanzelf disabled tijdens sleep, maar zo gebruikt 'ie ook niks in active mode)
PORTD = (1<<DISP0)|(1<<DISP1); // beginnen met displays uit
DDRA = 255; // porta (die 2 pinnen) outputs. (Kwestie van geen zwevende ingangen, die stroom slurpen, hebben)
DDRB = 255; // portB all outputs (display zit hier)
DDRD = ~(1<<SW); // portD outputs op de switch na.
//timer settings:
//OCR1A = 15626; //timer1, 16 bits. prescale 64, 15624tiks per seconde.
OCR1A = 58593; // met prescale 1024: 58592,75 per minuut :)
// count=((Fcpu/presc)/Fwanted)-1
TCCR1A = 0b00000000;
//TCCR1B = 0b00001011; // timer 1CTC, clkIO/64
TCCR1B = 0b00001101; // timer 1 CTC, clkIO/1024
TCCR0B = 0b00000011; // timer 0 (8 bits) klokken uit ckio/64.
TIMSK = (1<<OCIE1A)|(1<<TOIE0); //output compare 1A interupt enable en timer0 overflow enable (displays muxen)
sei(); // global interrupt enable
do{
if(tijd0vlag==1){ // als de tijd op 0 staat.
uint8_t timeout=0;
do{
// piepje totdat er op toets gedrukt wordt, gedurende max 2 minuten.
for(i=0;i<255;i++){
PORTD|=(1<<BUZZ);
_delay_us(500);
PORTD&=~(1<<BUZZ);
_delay_us(500);
}
_delay_ms(500);
timeout++; // elke 501ms iets bij optellen, na 240 zijn dat ongeveer 2 minuten.
}while( ((PIND& (1<<SW) )!=0)&&(timeout<240) ); // do-while herhalen zolang de switch niet is ingedrukt
// en er ook nog geen minuut om is. (dus totdat ofwel de minuut om is ofwel de switch wordt ingedrukt; de morgan)
PORTD&=~((1<<DISP0)|(1<<DISP1)); //displays uit
while( (PIND& (1<<SW))==0) _delay_ms(50); // wachten tot switch weer is losgelaten
// let op: in de tussentijd kan er een display interrupt komen die de displays weer aanzet.
// in zeldzame gevallen blijft dan het puntje branden in sleepmode. Dat is ongewenst, het slurpt de batterij langzaam leeg.
// deze bug is nu gefixed.
// naar sleepmode gaan:
cli(); // voor de zekerheid, onderstaande mag afaik niet overal onderbroken worden, dus geen interrupts toestaan.
PORTD&=~((1<<DISP0)|(1<<DISP1)); //displays zeker weten uit.
MCUCR|=(1<<SM1)|(1<<SM0); // selecteer "power down" sleep mode.
GIMSK=(1<<7); // externe interupt 0 (switch) enablen.
sleep_enable(); // slaapmode mogelijk maken
// sleep_bod_disable(); // dit kan de attiny2313 niet. Zet het handmatig uit in de fuses.
sei(); // interrupts weer aan, eerstvolgende instructie wordt nog uitgevoerd zonder interrupts.
sleep_cpu();
sleep_disable(); // nu CPU weer wakker is sleepmode-enable weer uitzetten.
}
// pas als de knop lang ingedrukt is minuut bijtellen. Anders slechts display even aan.
if((PIND&(1<<SW))==0){
dispvlag = 1; // cijfers weergeven
_delay_ms(1500); // tijde gevel om af te lezen / wachten of knop nog vastgehouden wordt
while((PIND&(1<<SW))==0){ // indien vastgehouden (of weer opnieuw ingedrukt)
TIMSK &= ~(1<<OCIE1A); // even stoppen met aftellen.
tijd0vlag=0; // tijd0vlag weer resetten bij instellen nieuwe tijd.
if(minuten<9) minuten++; // tijd instellen
else if(tienminuten<7){ // tot 79 minuten
tienminuten ++;
minuten=0;
}
else{ // anders weer op 0 beginnen.
minuten = 0;
tienminuten = 0;
tijd0vlag = 1; // Zodat er niet nog een minuut gewacht moet worden voor de wekker afloopt en uitgezet kan worden.
}
_delay_ms(400); // knop vasthouden voor meer minuten. (Is dat een Chinlisisme? "Hold Button For More Minutes" klinkt Chinglish)
TCNT1 = 0; // netjes opnieuw beginnen met aftellen als er een nieuwe tijd is ingesteld.
}
TIMSK |= (1<<OCIE1A); // doorgaan met aftellen. (tijdens instellen wordt aftellen gestopt)
_delay_ms(2000); // delay om nog wat langer cijfers weer te geven
dispvlag = 0; // en weer terug naar alleen een punt weergeven.
}
}while(1);
};
:1000000026C03EC06BC03CC03CC03AC083C038C0B4
:1000100037C036C035C034C033C032C031C030C044
:100020002FC02EC02DC09BC09CC09DC09EC09FC035
:10003000A0C0A1C0A2C0A3C0A4C0B6C0B7C0B8C071
:10004000B9C0BAC0BBC0BCC0BDC0BEC0BFC0112417
:100050001FBECFEDCDBF10E0A0E6B0E0E4E3F3E0DB
:1000600002C005900D92A236B107D9F710E0A2E6C2
:10007000B0E001C01D92A636B107E1F7C1D058C16A
:10008000BFCF1F920F920FB60F9211248F939F93A1
:1000900080916200882331F08091620081508093CA
:1000A00062000CC080916300882341F089E0809356
:1000B00062008091630081508093630090916200A0
:1000C00080916300892B19F481E0809360009F91F7
:1000D0008F910F900FBE0F901F9018951F920F9247
:1000E0000FB60F9211248F9389B78F7B89BF81E060
:1000F00080936200109260001DBC1CBC89B78064B4
:1001000089BF8BB78F778BBF8F910F900FBE0F90EA
:100110001F9018951F920F920FB60F9211248F9374
:10012000EF93FF9380E882BF809164008F5F80939C
:10013000640080916500813009F04EC080916400B8
:1001400080FF25C082B3897F82BB80916300E82F46
:10015000F0E0EA30F105B8F4ED5EFF4F09948FE36B
:1001600013C089E011C087EA0FC08FE80DC089E98C
:100170000BC08EE909C08EEB07C08BE005C08FEB8A
:1001800003C08FE901C086EB88BB919A31C082B36E
:10019000897F82BB80916200E82FF0E0EA30F105B0
:1001A000B8F4E35EFF4F09948FE313C089E011C0F8
:1001B00087EA0FC08FE80DC089E90BC08EE909C03E
:1001C0008EEB07C08BE005C08FEB03C08FE901C049
:1001D00086EB88BB929A0CC082B3897F82BB929ACD
:1001E000809164008B3F18F080E488BB01C018BA8E
:1001F000FF91EF918F910F900FBE0F901F90189568
:1002000080E888B986E082BB8FEF8ABB87BB87EF27
:1002100081BB81EE94EE9BBD8ABD1FBC8DE08EBD7F
:1002200083E083BF82E489BF7894A6EA49E150E085
:1002300064ED70E3B0E831E080916000813009F056
:1002400032C020E090E0909A8A2F8A95F1F790983A
:100250008A2F8A95F1F79F5F9F3FA9F788E893E17E
:10026000FA013197F1F70197D9F7839B03C02F5F0C
:10027000203F41F782B3897F82BB03C0CB01019746
:10028000F1F7839BFBCFF89482B3897F82BB85B75C
:10029000806585BFBBBF85B7806285BF7894889530
:1002A00085B78F7D85BF8399C7CF3093650088E977
:1002B0009AE3FA013197F1F70197D9F72AC089B784
:1002C0008F7B89BF1092600080916200893030F48A
:1002D000809162008F5F8093620012C08091630002
:1002E000873040F4809163008F5F809363001092A9
:1002F000620006C0109262001092630030936000AA
:1003000080EA9FE0FA013197F1F70197D9F71DBC18
:100310001CBC839BD4CF89B7806489BF80E29EE4F4
:10032000FA013197F1F70197D9F71092650084CF60
:04033000F894FFCF6F
:020334000100C6
:00000001FF
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment