Batty

From RevSpace
Revision as of 13:58, 9 October 2019 by Glu (talk | contribs) (→‎Source)
Jump to navigation Jump to search

Batty: Tiny bat detector kit voor het weekend van de wetenschap

De sourcecode, gerber files en het schema komen hier binnenkort te staan. Waarschijnlijk wordt er nog een tweede ronde PCB's besteld. De kits komen dan in de automaat te liggen.

Source
/*
 * t402code.c
 *
 * Created: 01/10/2019 15:22:39
 * Author : Wilenzo
 */ 

#define F_CPU 10000000UL // 20 MHz clock speed / 2 prescaler

#include <avr/io.h>
#include <util/delay_basic.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>

//Pins
#define UAMP  1
#define BUTN  2
#define AOUT  3
#define NMIC  6
#define PMIC  7

//Port
#define FALLING       0x03
#define INPUT_DISABLE 0x04
#define PULLUPEN      0x08

//Analog Comparator
#define HYS_00        0x01 
#define HYS_10        0x03
#define HYS_25        0x05
#define HYS_50        0x07
#define AC_OFF        0x00
#define NEG_EDGE      0x20

//Global variables
volatile uint8_t cntAC = 0;  //AC call counter, for frequency division
volatile uint8_t toAC  = 1;  //time-out flag for AC, silences output when active
volatile uint8_t fDiv  = 16; //Division factor for frequency of microphone 32kHz->2kHz after reset, must never be lower than 2!
volatile uint8_t Zzz   = 0;  //Sleep trigger

//Every low to high transition a counter is increased, except when a previous time-out is detected, then only the time-out is cleared.
ISR(AC0_AC_vect){
    AC0_STATUS |= 0x01;
    if (!(toAC)) ++cntAC; else toAC = 0; 
    TCB0_CNT = 0;
}

//Timeout! Signal used to disable sound generation is setting cntAC to max.
ISR(TCB0_INT_vect){
    TCB0_INTFLAGS |= 0x01;
    toAC = 1;
    cntAC = 0xFF;
}

//Wake-up from deep sleep or button pressed. Turn on RTC if it was turned off, else change division factor of frequency
ISR(PORTA_PORT_vect){
    if (RTC_CTRLA & 0x01){
        // Already running: Change division factor, selectable are: 2, 4, 8, 16, 32, 64 and 128
        while(RTC_STATUS & 0x02);
        RTC_CNT = 0;
        fDiv <<= 1; 
        if (fDiv == 0) fDiv = 2; 
        if (fDiv > 4) TCB0_CCMP = 1000; else TCB0_CCMP = 2000; //Select 10kHz or 5kHz lower detection limit (for divisions 2 and 4).
    } else {
        // Wake from sleep
        sleep_disable();
        Zzz = 0;
        while(RTC_STATUS & 0x01);
        RTC_CTRLA |= 0x01;
    }

    //Wait a while, then wait for release, wait more: poor man's debounce function
    _delay_loop_2(0);
    _delay_loop_2(0);    
    _delay_loop_2(0);    
    while(!(PORTA_IN & 1<<BUTN));
    _delay_loop_2(0);
        
    //Turn on microphone amplifier, clear interrupt flags
    PORTA_OUTSET = (1<<UAMP);
    PORTA_INTFLAGS = 0xFF;
    RTC_INTFLAGS |= 0x02;
}

//Auto power off after 5 minutes of button inactivity
ISR(RTC_CNT_vect){
    RTC_INTFLAGS |= 0x02;
    Zzz = 1;
}

//Init I/O, timers, interrupts etc...
void init(void){
    	//Set clock to 10MHz, should work from 2.7V and up.
      _PROTECTED_WRITE(CLKCTRL_MCLKCTRLA, CLKCTRL_CLKSEL_OSC20M_gc);
      _PROTECTED_WRITE(CLKCTRL_MCLKCTRLB, CLKCTRL_PEN_bm | CLKCTRL_PDIV_2X_gc);

      //CPU: Round robin interrupt handling
      CPUINT_CTRLA = CPUINT_LVL0RR_bm;

      //Port configuration
      PORTA_DIR = (1<<AOUT)|(1<<UAMP);
      PORTA_OUT = 0;
      PORTA_PIN1CTRL = INPUT_DISABLE;
      PORTA_PIN2CTRL = PULLUPEN | FALLING;
      PORTA_PIN3CTRL = INPUT_DISABLE;
      PORTA_PIN6CTRL = INPUT_DISABLE;
      PORTA_PIN7CTRL = INPUT_DISABLE;
      
      //Analog comparator
      AC0_CTRLA    = HYS_00 | NEG_EDGE; //0mV hysteresis, negative edge interrupt, on.
      AC0_MUXCTRLA = 0;                 //Normal pos/neg IO pins are used
      AC0_INTCTRL  = 0x01;              //Negative edge interrupt on

      //Timer B (time-out check)
      TCB0_CCMP    = 1000; // 10000Hz interrupts, for ignoring lower frequencies, higher number => lower frequency
      TCB0_CTRLA   = 0x01; // Counter on, peripheral speed (=> 10MHz)
      TCB0_INTCTRL = 0x01; // Interrupt on

      //RTC, used for auto power off 
      while(RTC_STATUS & 0x08);
      RTC_CMP      = 300;  // About 5 minutes of listening time before the unit is turned off 
      while(RTC_STATUS & 0x02);
      RTC_CNT      = 0;
      RTC_INTCTRL  = 0x02; // Compare interrupt
      RTC_CLKSEL   = 0x01; // Set clock rate to 1024Hz
      while(RTC_STATUS & 0x01);
      RTC_CTRLA    = 0x51; // Turn RTC on, with 1024 prescaling factor.

      //Sleep to power down mode (deep)
      SLPCTRL_CTRLA = 0x04;

      //Enable interrupts
      sei();
}



int main(void)
{
    init();

    while (1) 
    {
        //Check sleepiness
        if (Zzz) {
            //beep
            PORTA_DIRSET = (1<<AOUT);
            for (uint16_t a=50; a<255; ++a){
              _delay_loop_2(a<<5);
              PORTA_OUTTGL = (1<<AOUT);
            }

            //Turn off all outputs, RTC too and reset RTC
            PORTA_OUT = 0;
            while(RTC_STATUS & 0x01);
            RTC_CTRLA &= 0xFE; 
            while(RTC_STATUS & 0x02);
            RTC_CNT   = 0;            
            
            //Sleep (and unsleep after wake to be sure)
            sleep_enable();
            sleep_cpu();
            sleep_disable();
        }

        //Auto turn off underflow protection
        if(RTC_CNT > 300) RTC_CNT = 300;

        //Check if output pin should be toggled or be turned off. This produces "sound".
        if (fDiv < 2) fDiv = 2;
        if (cntAC >= (fDiv>>1)){
            if (cntAC == 0xFF){
                //Off
                PORTA_DIRCLR = (1<<AOUT);
                PORTA_OUTCLR = (1<<AOUT);
            } else {
                //Toggle
                cntAC -= (fDiv>>1);
                PORTA_DIRSET = (1<<AOUT);
                PORTA_OUTTGL = (1<<AOUT);
            }
        }
    }
}