// Rizeni otacek ventilatoru pro Raspberry Pi s vyuzitim ATtiny 85 // pomoci I2C komunikace pres PWM modulaci napajeciho napeti pro ventilator // // Datum: 5.10.2016 // Velikost prelozeneho kodu: 2226 Bajtu /* Komunikacni cast sestavena na zaklade techto knihoven a prikladu: /* ATtiny85 as an I2C Slave BroHogan 1/12/11 * Example of ATtiny I2C slave receiving and sending data to an Arduino master. * Credit and thanks to Don Blake for his usiTwiSlave code. * More on TinyWireS usage - see TinyWireS.h * https://github.com/rambo/TinyWire */ /* // Zapojeni: //============= // ATtiny85 // +-\_/-+ // nezapojeno (RESET) - PB5 1| |8 Vcc 3,3V - Raspberry Pi 3,3V // LED "STOP ALARM" (ventilator je zadreny) - PB3 2| |7 PB2 SCL - Raspberry Pi (SCL) // cidlo otaceni (HALL nebo OPTO) - PB4 3| |6 PB1 OC0B (PWM) - ventilator - spinany pres N-FET (nebo vstup do servisniho rezimu) // Raspberry Pi GND - GND 4| |5 PB0 SDA - Raspberry Pi (SDA) // +-----+ // Popis funkce: //=============== // Ventilator je ovladany z Raspberry Pi pomoci I2C sbernice. Jeho vykon je mozne plynule ridit od 0% do 100% pres PWM. // Po stejne komunikacni lince je mozne v RasPi prijimat informace o aktualnim stavu ventilatoru. // Adresu I2C lze zmenit v sekci "setup()" - defaultne je nastavena na hodnotu 0x60. // // Pro vyber pozadovane informace nebo pro ovladani vykonu slouzi tyto povely v Pythonu: // bus.write_byte(0x60, 1) # prepne ATtiny tak, ze bude odesilat informaci o otackach ventilatoru // bus.write_byte(0x60, 2) # prepne ATtiny tak, ze bude odesilat informaci o stavu vystupu STOP ALARM // bus.write_byte(0x60, 3) # prepne ATtiny tak, ze bude odesilat informaci o naposledy prijatem prikazu pro zmenu vykonu motoru (naposled prijate PWM) // bus.write_byte(0x60, 4) # zapne funkci pro zpozdene vypnuti (1 minutu toci ventilatorem na 75% a pak se automaticky ventilator zastavi) // // bus.write_byte(0x60, 0) # nastavi PWM na 0% (zastavi motor) // bus.write_byte(0x60, 127) # nastavi PWM na 50% (polovicni vykon do motoru) // bus.write_byte(0x60, 255) # nastavi PWM na 100% (spusti motor na plno) // (jakekoliv odeslane cislo v rozsahu 4 az 255 nastavi prislusny vykon PWM) // // Pozadovana informace se z ATtiny v Pythonu ziska prikazem: // i2c_data = bus.read_byte(0x60) // // V pripade, ze se nastavuje vyssi vykon ventilatoru, nez je aktualni vykon, provede se automaticky kratkodobe (0.3 sek.) zvyseni vykonu na 100%. // Tento vykonovy impulz slouzi k tomu, aby se prekonaly setrvacnosti a podarilo se ventilator roztocit. Pak se PWM snizi na pozadovanou hodnotu. // Kdyz se nastavuje nizsi, nez aktualni vykon, k tomuto impulzu nedochazi. // // Pri mereni otacek muze dojit k nekolika pripadum: // - Vystupem je cislo 255. // V tom pripade jsou otacky tak vysoke, ze se nestaci zpracovavat, nebo je v signalu od cidla vyznamne ruseni. // - Vystupem je cislo 0. // To znamena, ze se ventilator toci velice pomalu, nebo dokonce stoji. // - Vystupem je cislo v rozsahu 1 az 254. // Na prevod tohoto cisla na skutecne otacky za minutu je treba pouzit vzorec: // // rpm = 10 * cislo // // Vzorec plati pri cidle, ktere dava 2 impulzy na otacku (pri pouziti vnitrniho magnetickeho krouzku ventilatoru) // Pokud cidlo otacek dava pouze 1 impulz na otacku, musi se tento vysledek jeste vynasobit 2. // // // Vystup "STOP ALARM" (pin PB3) slouzi jako signalizace zadreni ventilatoru. // Aktivuje se jen v pripade, ze se ma ventilator otacet, ale neotaci se. Napriklad je nastaveny velice maly vykon. // Pokud ma ale ventilator nastaveny vykon na 0%, tak se sice netoci, ale STOP ALARM neni aktivni. // // Pri prepnuti na odesilani stavu STOP ALARMU mohou byt vystupem pouze 3 cisla: // 10 ... ventilator se normalne otaci (PB3 je ve stavu LOW) // 20 ... ventilator stoji, i kdyz se do nej posila nejaky vykon (je zadreny) (PB3 je ve stavu HIGH) // 30 ... ventilator stoji, kvuli tomu, ze se do nej nepousti zadny vykon (PB3 je ve stavu LOW) // // Pokud je pri zapnuti napajeni pin PB1 (FET tranzistor) pripojeny k Vcc, vstoupi se do servisniho rezimu. // Servisni rezim slouzi k otestovani spravneho mechanickeho nastaveni cidla otaceni. // Pri otaceni vrtule ventilatoru pak LED na PB3 kopiruje logicky stav cidla na PB4. // Ze servisniho rezimu je mozne vystoupit pouze resetem (vypnutim napajeni). // // // Nastaveni FUSE bajtu v ATtiny85: // LOW 0xE2 // HIGH 0xDF // EXTEND 0xFF */ //----------------------------------------------------------------------------------------------- #include "TinyWireS.h" // knihovna pro ATtiny I2C Slave #ifndef TWI_RX_BUFFER_SIZE // nastaveni bufferu podle prikladu #define TWI_RX_BUFFER_SIZE ( 1 ) #endif volatile byte pwm; // Globalni promenna pro sirku generovanych impulzu (0 az 255). volatile byte stav_LED = 30; // Globalni promenna pro signalizaci, jestli je ventilator zadreny (20), jestli se toci (10), nebo jestli motor stoji zamerne (30). volatile unsigned long sirka = 0; // Sirka impulzu z cidla otaceni. volatile byte vystupni_velicina = 1; // Jaky udaj se bude odesilat zpatky po I2C (defaultne sirka impulzu). volatile byte aktualni_pwm = 0; // Pomocna promenna pro odesilani aktualne nastaveneho PWM zpatky po I2C komunikaci. // --------------------------------------------------------------- // Tenhle podprogram reaguje na Pythoni prikaz: // bus.write_byte(0x60, prikaz) // Kdyz je prikaz 0, nebo v rozsahu 4 az 255, nastavi se podle prijate hodnoty sirka PWM generovanych impulzu. // Kdyz je prikaz 1, prepne se na odesilani otacek ventilatoru (0 az 254) pro 0 az 2540 ot/min, nebo 255 = chyba mereni (moc rychle otacky). // Kdyz je prikaz 2, prepne se na odesilani stavu LED (STOP ALARM) (10 = nesviti (toci); 20 = sviti (zadreno); 30 = nesviti, protoze je vetilator vypnuty). // Kdyz je prikaz 3, prepne se na odesilani naposled prijate hodnoty PWM (0, nebo cislo v rozsahu 4 az 255). // Kdyz je prikaz 4, minutu toci ventilatorem na 50% vykonu a pak se motor zastavi void receiveEvent(uint8_t howMany) { byte vstup = TinyWireS.receive(); // Uloz cislo, ktere prislo pres I2C, do promenne. if (vstup == 1) // Prepne na odesilani otacek ventilatoru po I2C. { vystupni_velicina = 1; // Otacky (0 = stoji ..... 254 = 2540ot/min ; 255 = moc rychle impulzy, nebo ruseni). } if (vstup == 2) // Prepne na odesilani stavu LED po I2C. { vystupni_velicina = 2; // Stav STOP ALARMU (10 = motor bezi; 20 = motor je zadreny; 30 = motor zastaven umyslne). } if (vstup == 3) // Prepne na odesilani aktualniho PWM po I2C. { vystupni_velicina = 3; // Hodnota PWM (0 az 255 - s vyjimkou hodnot 1, 2, 3). } if ( vstup == 0 ) // Pri prikazu pro zastaveni motoru se jen nastavi sirka PWM na 0%. { OCR0B = 255; // Nastaveni PWM sirky generovanych impulzu na 0%. aktualni_pwm = 0; // Pomocna promenna pro ukladani naposled prijate hodnoty PWM } if (vstup == 4) // Zpozdene vypnuti ventilatoru { OCR0B = 64 ; // ... na minutu se do ventilatoru pusti 75% vykonu for (byte i = 0; i < 60 ; i++) { tws_delay(48000); // ... pauza 60 x 1 sekunda (cislo 48000 odpovida asi 1 sekunde) } OCR0B = 255 ; // ... po minute vypne ventilator pwm = 0; aktualni_pwm = 0; } if ( vstup > 4) // Nastaveni nejakeho jineho PWM vykonu, nez je 0% { if (vstup > aktualni_pwm) // Kdyz se ma rychlost otaceni ventilatoru proti aktualnimu stavu zvetsit ... { OCR0B = 0 ; // ... na chvili se do nej pusti plny vykon (kvuli prekonani setrvacnosti) ... tws_delay(16000); // ... a pockat asi 0,3 sekundy (cislo 48000 odpovida asi 1 sekunde) } pwm = 255 - vstup; // Uprava cisla aby pro registr OCR0B platilo: 0=plny vykon ; 255=STOP. OCR0B = pwm; // Nastaveni PWM sirky generovanych impulzu. aktualni_pwm = vstup; // Pomocna promenna pro ukladani naposled prijate hodnoty PWM. } } // --------------------------------------------------------------- // Tenhle podprogram reaguje na Pythoni prikaz: // i2c_data = bus.read_byte(0x60) // Odesle zpatky do RasPi hodnotu pozadovane veliciny void requestEvent() { if (vystupni_velicina == 1) { byte rpm10; // Po I2C se bude odesilat hodnota, ktera zhruba odpovida desetine otacek ventilatoru za minutu. if (sirka == 0 ) // Kdyz ventilator stoji (sirka = 0) ... { rpm10 = 0; // ... nastavi se odesilana hodnota "underload" (0). } else // Kdyz se ventilator toci (prichazeji nejake impulzy), dochazi k vypoctu otacek za minutu. { // ---------------------- // Prepocte sirku impulzu v mikrosekundach na otacky ventilatoru se dvema poly. // Vypocet byl pomoci mereni odladen tak, aby v temer celem rozsahu otacek nebyla chyba vetsi nez 10%. // rpm = 10 * ((975000 / sirka impulzu) - 7) // Napriklad pro dvoumagnetovy ventilator by melo byt pri skutecnych otackach 1200 ot/min vystupem cislo 120. // Pro ventilator s jednim impulzem na otacku je pri stejnych otackach vystupem cislo 60. rpm10 = (975000 / sirka); // vypocet if (rpm10 > 7) { rpm10 = rpm10 - 7; // korekce } else { rpm10 = 0; } // ---------------------- } if (sirka < 3800 && sirka > 0) // kdyz se ventilator toci tak rychle (vic nez 2560 ot/min), ze je sirka impulzu moc kratka ... { rpm10 = 255; // ... nastavi se odesilana hodnota na "overload" (255). } TinyWireS.send(rpm10); // Odesle zpatky do RasPi prepoctenou hodnotu otacek za minutu } if (vystupni_velicina == 2) { TinyWireS.send(stav_LED); // Odesle zpatky do RasPi stav LED (10, 20, nebo 30). } if (vystupni_velicina == 3) { TinyWireS.send(aktualni_pwm); // Odesle zpatky do RasPi aktualni hodnotu PWM. } } // -------------------------- S T A R T ------------------------------------- void setup() { //---------------------------------------------------- // Vstup do servisniho rezimu pripojenim PB1 (FET) do stavu HIGH pri zapnuti napajeni (zaroven se tim spusti ventilator na 100%). // Pri normalnim startu je na PB1 ridici elektroda (Gate) FET tranzistoru, ktera je pomoci Pull-Down odporu udrzovana v logicke "0". DDRB = 0b00001000; // Nastaveni smeru signalu na portu PB1 (FET) na vstup a PB3 (LED) na vystup PORTB = 0b00000000; // Zadne Pull-Upy, LED zhasnout if ((PINB & 0b00000010) == 0b00000010) // Kdyz je pri zapnuti napajeni PB1 v "HIGH", vstoupi se do servisniho rezimu. { while (true) // Z nekonecne smycky se da dostat jen vypnutim napajeni. { if ((PINB & 0b00010000) == 0b00000000) // Kdyz je cidle (PB4) "0" ... { PORTB &= 0b11110111; // ... zhasne LED na PB3. } else // Kdyz je cidle (PB4) "1" ... { PORTB |= 0b00001000; // ... rozsviti LED na PB3. } } } // Konec servisniho rezimu. //---------------------------------------------------- DDRB = 0b00001010; // Nastaveni smeru signalu na portu PB1 (PWM - kanal B) a PB3 (LED signalizace: STOP ALARM) na vystup. PORTB = 0b00000000; // Pri magnetickem cidle otaceni se Pull-Up na PB4 nepripoji (vystupem cidla je analogove napeti). TinyWireS.begin(0x60); // Nastaveni adresy obvodu ATtiny85 pro I2C komunikaci. TinyWireS.onReceive(receiveEvent); // Podprogram pro prijem prikazu z RasPi. TinyWireS.onRequest(requestEvent); // Podprogram pro odesilani pozadovanych udaju do RasPi. TCCR0A = 0b11110011; // Rezim FAST PWM; vystup se nastavi do '0' pri dosazeni pozadovaneho cisla. TCCR0B = 0b00000001; // FAST PWM, delic zakladni frekvence na 36kHz (pro prescaler='001b'). OCR0B = 255; // Pri startu programu nastavit PWM na sirku 0% (zastavit ventilator). } // --------------------------------------------------------------- void loop() { TinyWireS_stop_check(); if (aktualni_pwm > 0) // Kdyz by se mel ventilator tocit: { sirka = pulseIn(4,HIGH,150000); // Po dobu asi 3/4 sekundy testuje, jestli se na PB4 objevi HIGH impulz. (cislo 200000 odpovida asi 1 sekunde) if (sirka == 0) // Kdyz behem teto doby neprijde ani jeden kompletni impulz (od nabezne do sestupne hrany) ... { PORTB |= 0b00001000; // ... rozsviti LED na PB3 - ostatni hodnoty jsou beze zmeny. stav_LED = 20; // Znacka "Ventilator je zadreny". } else // Kdyz od cidla nejake impulzy prichazeji ... { PORTB &= 0b11110111; // ... zhasne LED na PB3 - ostatni hodnoty jsou beze zmeny. stav_LED = 10; // Znacka "Ventilator se normalne toci". } } else // Kdyz je PWM 0% (motor byl zamerne zastaven) ... { PORTB &= 0b11110111; // ... zhasne LED na PB3 - ostatni hodnoty jsou beze zmeny. stav_LED = 30; // Znacka "Motor stoji umyslne (PWM je 0%)". sirka = 0; // Pri zamernem zastaveni je sirka impulzu 0 (zadne impulzy nechodi). } }