2 * CyberHeart, AVR ATtiny10 cellular automation micro-engine
3 * Copyright © 2021 Dmitry Shalnoff [interplaymedium.org]
4 * Licensed under the Apache License, Version 2.0
7 #define F_CPU 8000000UL // define CPU speed. actual clock speed is set using CLKPSR prescaler (see main()
9 // ---------------------- main settings ------------------------
11 // #define UART 1 // UART on PB1, for debugging purposes. due the lack of memory, LED PWM and watchdog delay will be disabled
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
17 #define BATT_LOW_LIMIT 231 // 231 // ADC battery meseurement limit for 'charging' message, 255 - minimum
19 // -------------------------------------------------------------
22 #include <util/delay.h>
23 #include <avr/interrupt.h>
24 #include <avr/pgmspace.h>
26 #include <avr/sleep.h>
27 #include <util/atomic.h>
34 #define EXP_LEN 10 // EXP, LED PWM glowing, number of steps
36 uint8_t uni[ LEN ]; // 1D compact manifold universe :)
38 // ---------------------- some useful defines -------------------
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)
48 #define invert(port,pin) port ^= (1<<pin)
50 #define bit(x) (1 << (x))
52 // ---------------------- simple UART TX -----------------------
56 void put_char( uint8_t c ){
59 output_low( PORTB, SND );
60 for( uint8_t i = 10; i; i-- ){ // 10 bits
62 _delay_us( 1000000 / 9600 ); // bit duration / (8MHz, 9600, 104 us) Delay 832 cycles
64 if( c & 1 ) output_low( PORTB, SND ); else output_high( PORTB, SND ); // data bit 0 / data bit 1 or stop bit
69 void put_str_P(PGM_P str){
72 while (pgm_read_byte(s)) put_char( pgm_read_byte(s++) );
75 void put_num( uint8_t num ){
79 for (div = 1; num / div >= 10 ; div *= 10);
82 put_char( 48 + (( num / div ) % 10 ));
90 // ---------------------- LED PWM ------------------------------
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
97 ISR ( TIM0_COMPA_vect ){
98 output_low(PORTB, LED);
101 ISR ( TIM0_OVF_vect ){
103 output_high(PORTB, LED);
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);
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
120 // ---------------------- read ADC -----------------------------
125 PRR &= ~(1<<PRADC); // power on ADC
126 ADMUX = 0; // channel selection (PB0) MUX1 = 0, MUX0 = 0
127 // ADMUX = (1<<MUX0);
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
134 ADCSRA &= ~(1<<ADEN); // disable ADC (for sleep mode)
135 PRR |= (1<<PRADC); // power off ADC
138 // ---------------------- cell automation tools -----------------
140 void shift_left( uint8_t shift) {
148 uint8_t next = (uni[i] & 0x80) ? 1 : 0;
149 uni[i] = carry | (uni[i] << 1);
154 uni[ LEN - 1 ] |= carry ;
159 // ---------------------- for power down delay -----------------
161 EMPTY_INTERRUPT(WDT_vect); // clearing the flag (wake up by WDT interrupt)
163 // ====================== MAIN =================================
167 // Set CPU speed by setting clock prescaler
169 CCP = 0xD8; // signature to unlock ptrotection of CLKPSR
170 CLKPSR = 0; // sets the clock division factor
172 // disable digital inputs (for ADC and so)
176 // IO and interrupts init
180 ledPWMInint(); // must be before DDR setup
183 set_input(DDRB, ADC); // input
184 output_low(PORTB, ADC); // no pull-up
186 set_output(DDRB, LED);
187 set_output(DDRB, SND);
193 // put_str_P( PSTR("^__.__^\n") );
194 // put_str_P( PSTR("\n .:::. .:::.\n:::::::.:::::::\n:::::::::::::::\n':::::::::::::'\n ':::::::::'\n ':::::'\n ':'\n"));
197 uint8_t i = LEN-1, tmp, triade;
198 uint8_t cnt = 0, cntMax = 0;
203 // ----------- initial randomizer -----------------
207 uni[ i ] = pgm_read_byte( uni[ i+1 ] ) + ADCL;
210 // ------------ cell automata -------------
216 for (i=0; i < LEN*8; i++){
218 // new cell calculation
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);
223 put_char( (( uni[ 0 ] >> 2) & 1) ? '*' : ' ');
226 // long sequence check
228 // if ( (( uni[ 0 ] >> 2) & 1) == (( uni[ 0 ] >> 3) & 1) ) {
229 if ( (( uni[ 0 ] >> 2) & 0b11) == 0b00 ) {
235 if ( cnt > cntMax ) {
245 if ( ( cnt >= cntMax ) || cnt > LOW_STAT_THRS || ADCL >= BATT_LOW_LIMIT ) {
247 // ------------- glow --------------
259 if ( cnt > cntMax || ADCL >= BATT_LOW_LIMIT ) {
261 if ( ADCL < BATT_LOW_LIMIT ) {
264 cnt = 100; // batery low message
267 // playing current epoch byte sequence as score or 'battery low' message
270 m = uni[ cnt % LEN ] % 5;
278 while ( k-- ) { //500ns
300 // ---------------------------------
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
321 // ----------------------------------------
325 put_str_P( PSTR(" DAC: ") );
327 put_str_P( PSTR("\n") );
330 // ------------- delay --------------------
334 if ( ( ADCL < BATT_LOW_LIMIT ) ) { // disabled on charging message
337 wdt_enable( WDTO_8S ); // <-- delay
340 set_sleep_mode(SLEEP_MODE_PWR_DOWN);
343 NONATOMIC_BLOCK( NONATOMIC_RESTORESTATE ) {
351 DIDR0 = 0b00001111; // disable digital inputs (for ADC)
352 set_input(DDRB, ADC); // input
353 output_low(PORTB, ADC); // no pull-up
357 // ----------------------------------------