init
[CYBERHEART] / heart.c
1 /*
2  * CyberHeart, AVR ATtiny10 cellular automation micro-engine
3  * Copyright © 2021 Dmitry Shalnoff [interplaymedium.org]
4  * Licensed under the Apache License, Version 2.0
5 */
6
7 #define F_CPU 8000000UL         // define CPU speed. actual clock speed is set using CLKPSR prescaler (see main()
8
9 // ---------------------- main settings ------------------------
10
11 // #define UART 1               // UART on PB1, for debugging purposes. due the lack of memory, LED PWM and watchdog delay will be disabled
12
13 #define RULE            30      // rule 22 look better, 30 gives shorter sequences and looks like giving longer 'tales' (test needed)
14 #define LEN             8       // perimeter of the universe (bytes)
15 #define LOW_STAT_THRS   6       // 6    // 13   // low threshold for silent 'messaging', to make LED more "talkative" :) Check statistics for your RULE and change it
16
17 #define BATT_LOW_LIMIT  231     // 231  // ADC battery meseurement limit for 'charging' message, 255 - minimum
18
19 // -------------------------------------------------------------
20
21 #include <avr/io.h>
22 #include <util/delay.h>
23 #include <avr/interrupt.h>
24 #include <avr/pgmspace.h>
25
26 #include <avr/sleep.h>
27 #include <util/atomic.h>
28 #include <avr/wdt.h>
29
30 #define ADC PB0
31 #define SND PB1
32 #define LED PB2
33
34 #define EXP_LEN         10      // EXP, LED PWM glowing, number of steps
35
36 uint8_t         uni[ LEN ];     // 1D compact manifold universe :)
37
38 // ---------------------- some useful defines -------------------
39
40 #define false           0
41 #define true            1
42
43 #define output_low(port,pin) port &= ~(1<<pin)
44 #define output_high(port,pin) port |= (1<<pin)
45 #define set_input(portdir,pin) portdir &= ~(1<<pin)
46 #define set_output(portdir,pin) portdir |= (1<<pin)
47
48 #define invert(port,pin) port ^= (1<<pin)
49
50 #define bit(x) (1 << (x))
51
52 // ---------------------- simple UART TX -----------------------
53
54 #ifdef UART 
55
56 void put_char( uint8_t c ){
57
58         c = ~c;
59         output_low( PORTB, SND );
60         for( uint8_t i = 10; i; i-- ){          // 10 bits
61
62                 _delay_us( 1000000 / 9600 );    // bit duration / (8MHz, 9600, 104 us) Delay 832 cycles
63
64                 if( c & 1 ) output_low( PORTB, SND ); else output_high( PORTB, SND );   // data bit 0 / data bit 1 or stop bit
65                 c >>= 1;
66         }
67
68
69 void put_str_P(PGM_P str){
70         static PGM_P s;
71         s=str;
72         while (pgm_read_byte(s)) put_char( pgm_read_byte(s++) );
73 }
74
75 void put_num( uint8_t num ){
76
77         uint8_t div = 1;
78
79         for (div = 1; num / div >= 10 ; div *= 10);
80
81         do {
82                 put_char( 48 + (( num / div ) % 10 )); 
83                 div /= 10;
84         } while ( div > 0 );
85
86 }
87
88 #endif 
89
90 // ---------------------- LED PWM ------------------------------
91
92 #ifndef UART
93
94 #define INT_PWM_DISABLE ({ TIMSK0 &= ~((1<<OCIE0A) | (1<<TOIE0)); })    // compare interrupt off
95 #define INT_PWM_ENABLE  ({ TIMSK0 |= ((1<<OCIE0A) | (1<<TOIE0)); })     // compare interrupt on
96
97 ISR ( TIM0_COMPA_vect ){
98         output_low(PORTB, LED);
99 }
100
101 ISR ( TIM0_OVF_vect ){
102 //      if (OCR0A != 0) 
103         output_high(PORTB, LED);
104 }
105
106 void ledPWMInint(){
107
108         // Timer0 in mode 14, fast PWM with ICR0 as top.
109         // Enable OC0A and OC0B, lower mode bits
110 //      TCCR0A = (1<<COM0A1) | (1<<COM0B1) | (1<<WGM01);
111         TCCR0A = (1<<WGM01);
112         TIMSK0 = (1<<OCIE0A);                           // Eneable interrupt (since I'm uisng PB2, I can't use integrated pin :( )
113 //      TIMSK0 = (1<<OCIE0A) | (1<<OCIE0B);             // Eneable interrupt (since I'm uisng PB2, I can't use integrated pin :( ) + second interrupt for beeper
114         ICR0 = 1000;                                    // Set top to 1000
115         TCCR0B = (1<<CS01)  | (1<<WGM03) | (1<<WGM02);  // Start timer with prescaler 1:8 and set upper mode bits. WGM0 = 1110 Fast PWM ICR0 TOP TOP
116 }
117
118 #endif
119
120 // ---------------------- read ADC -----------------------------
121
122
123 void readADC(){
124
125         PRR   &= ~(1<<PRADC);                                           // power on ADC
126         ADMUX = 0;                                                      // channel selection (PB0) MUX1 = 0, MUX0 = 0
127 //      ADMUX = (1<<MUX0);
128         _delay_ms(30);
129         ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);      // enable ADC + set prescaler 128
130         ADCSRA |= (1<<ADSC);                                            // start convertion (in single mode)
131         while ((ADCSRA & (1<<ADIF))==0);                                // waiting for conversion completes flag, see ADCL register for result
132         ADCSRA = (1<<ADIF);                                             // clear flag
133
134         ADCSRA &= ~(1<<ADEN);                                           // disable ADC (for sleep mode) 
135         PRR |= (1<<PRADC);                                              // power off ADC
136 }
137
138 // ---------------------- cell automation tools -----------------
139
140 void shift_left( uint8_t shift) {
141
142         while (shift--) {
143
144                 uint8_t carry = 0;
145                 uint8_t i = LEN - 1;
146
147                 do {
148                         uint8_t next = (uni[i] & 0x80) ? 1 : 0;
149                         uni[i] = carry | (uni[i] << 1);
150                         carry = next;
151
152                 } while ( i-- > 0 );
153
154                 uni[ LEN - 1 ] |= carry ;
155
156         }
157 }   
158
159 // ---------------------- for power down delay -----------------
160
161 EMPTY_INTERRUPT(WDT_vect); // clearing the flag (wake up by WDT interrupt)
162
163 // ====================== MAIN =================================
164
165 int main(void) {
166
167         // Set CPU speed by setting clock prescaler
168
169         CCP = 0xD8; // signature to unlock ptrotection of CLKPSR
170         CLKPSR = 0; // sets the clock division factor
171
172         // disable digital inputs (for ADC and so)
173
174         DIDR0 = 0b00001111;
175
176         // IO and interrupts init
177
178         cli();
179 #ifndef UART
180         ledPWMInint(); // must be before DDR setup
181         INT_PWM_ENABLE;
182 #endif
183         set_input(DDRB, ADC);   // input
184         output_low(PORTB, ADC); // no pull-up
185
186         set_output(DDRB, LED);  
187         set_output(DDRB, SND);  
188
189         sei();
190
191
192 #ifdef UART 
193 //      put_str_P( PSTR("^__.__^\n") );
194 //      put_str_P( PSTR("\n .:::.   .:::.\n:::::::.:::::::\n:::::::::::::::\n':::::::::::::'\n  ':::::::::'\n    ':::::'\n      ':'\n"));
195 #endif  
196
197         uint8_t         i = LEN-1, tmp, triade;
198         uint8_t         cnt = 0, cntMax = 0;
199 #ifndef UART 
200         uint8_t         j, k, m;
201 #endif
202
203         // ----------- initial randomizer -----------------
204
205         while( i-- ) {
206                 readADC();
207                 uni[ i ] = pgm_read_byte( uni[ i+1 ] ) + ADCL;
208         }
209
210         // ------------ cell automata -------------
211
212         while (1) {
213
214                 tmp             = uni[ 0 ];
215
216                 for (i=0; i < LEN*8; i++){
217
218                         // new cell calculation
219                 
220                         if ( i > LEN * 8 - 4 ) triade = (tmp >> (LEN*8 - i) ); else triade = uni[ 0 ];
221                         if ( RULE & bit(7 & triade) ) uni[ 0 ] |= bit(2); else uni[ 0 ] &= ~bit(2);
222 #ifdef UART 
223                         put_char( (( uni[ 0 ] >> 2) & 1) ? '*' : ' ');
224 #endif
225
226                         // long sequence check          
227
228 //                      if ( (( uni[ 0 ] >> 2) & 1) == (( uni[ 0 ] >> 3) & 1) ) { 
229                         if ( (( uni[ 0 ] >> 2) & 0b11) == 0b00 ) { 
230
231                                 cnt++;
232
233 #ifdef UART 
234
235                                 if ( cnt > cntMax ) {
236                                         cntMax = cnt;
237                                         put_num( cntMax );
238                                 }
239 #else
240
241                                 readADC();
242
243                                 // message LED + SND
244
245                                 if (  ( cnt >= cntMax ) || cnt > LOW_STAT_THRS || ADCL >= BATT_LOW_LIMIT ) { 
246
247                                         // ------------- glow --------------
248
249                                         OCR0A = 1;
250                                         m = EXP_LEN;
251
252                                         while ( m-- ){
253                                                 OCR0A *= 2;
254                                                 _delay_ms(60);
255                                         }
256
257                                         readADC();
258
259                                         if ( cnt > cntMax || ADCL >= BATT_LOW_LIMIT ) {
260
261                                                 if ( ADCL < BATT_LOW_LIMIT ) {
262                                                         cntMax = cnt; 
263                                                 } else {
264                                                         cnt = 100; // batery low message
265                                                 }
266
267                                                 // playing current epoch byte sequence as score or 'battery low' message
268
269                                                 do {
270                                                         m = uni[ cnt % LEN ] % 5;
271                                                         j = 255;
272
273                                                         while ( j-- ){
274                                                                 invert(PORTB, SND);
275
276                                                                 k = 30*(m + 1);
277
278                                                                 while ( k-- ) { //500ns
279
280                                                                         asm volatile (
281                                                                         "    rjmp 1f    \n"
282                                                                         "1:  rjmp 2f    \n"
283                                                                         "2:     \n"
284                                                                         );
285                                                                 }
286                                                         }
287
288                                                         _delay_ms(60);
289
290                                                 } while ( cnt-- );
291                                         }
292
293                                         m = EXP_LEN;
294
295                                         while ( m-- ){ 
296                                                 OCR0A /= 2;
297                                                 _delay_ms(60);
298                                         }
299
300                                         // ---------------------------------
301
302                                 }
303
304 #endif
305
306                         } else {
307                                 cnt = 0;
308                         }
309                         
310                         shift_left( 1 );
311                 }
312
313                 invert(PORTB, SND);
314
315 #ifdef UART 
316                 put_char( ' ' );
317                 put_num( cntMax );
318                 shift_left( LEN*8-1 ); // 1 bit right shift, not really required if you don't visualize it, just for beauty of canonical view
319 #endif
320
321                 // ---------------------------------------- 
322
323 #ifdef UART 
324                 readADC();
325                 put_str_P( PSTR(" DAC: ") );
326                 put_num( ADCL );
327                 put_str_P( PSTR("\n") );
328 #endif
329
330                 // ------------- delay --------------------
331
332 //              _delay_ms(1000);
333
334                 if ( ( ADCL < BATT_LOW_LIMIT ) ) {      // disabled on charging message 
335
336                         wdt_reset();
337                         wdt_enable( WDTO_8S ); // <-- delay 
338                         WDTCSR |= (1<<WDIE);
339
340                         set_sleep_mode(SLEEP_MODE_PWR_DOWN);
341                         sleep_enable();
342
343                         NONATOMIC_BLOCK( NONATOMIC_RESTORESTATE ) {
344                                 sleep_cpu();
345                                 wdt_disable();
346                         }
347                         sleep_disable();
348
349                         // re-init 
350
351                         DIDR0 = 0b00001111; // disable digital inputs (for ADC)
352                         set_input(DDRB, ADC);   // input
353                         output_low(PORTB, ADC); // no pull-up
354
355                 }
356
357                 // ----------------------------------------
358
359         }
360 }
361
Contact me: dev (at) shalnoff (dot) com
PGP fingerprint: A6B8 3B23 6013 F18A 0C71 198B 83D8 C64D 917A 5717