Showing posts with label interrupt. Show all posts
Showing posts with label interrupt. Show all posts

Friday, December 16, 2016

Measuring Wind Speed with an Anemometer and Arduino

In this video we look at how to measure wind speed using an anemometer and Arduino. This approach will work on both ARM and AVR based Arduinos.


//*****************Arduino anemometer sketch******************************
const byte interruptPin = 3; //anemomter input to digital pin
volatile unsigned long sTime = 0; //stores start time for wind speed calculation
unsigned long dataTimer = 0; //used to track how often to communicate data
volatile float pulseTime = 0; //stores time between one anemomter relay closing and the next
volatile float culPulseTime = 0; //stores cumulative pulsetimes for averaging
volatile bool start = true; //tracks when a new anemometer measurement starts
volatile unsigned int avgWindCount = 0; //stores anemometer relay counts for doing average wind speed
float aSetting = 60.0; //wind speed setting to signal alarm

void setup() {
  pinMode(13, OUTPUT); //setup LED pin to signal high wind alarm condition
  pinMode(interruptPin, INPUT_PULLUP); //set interrupt pin to input pullup
  attachInterrupt(interruptPin, anemometerISR, RISING); //setup interrupt on anemometer input pin, interrupt will occur whenever falling edge is detected
  dataTimer = millis(); //reset loop timer
}

void loop() {
 
  unsigned long rTime = millis();
  if((rTime - sTime) > 2500) pulseTime = 0; //if the wind speed has dropped below 1MPH than set it to zero
     
  if((rTime - dataTimer) > 1800){ //See if it is time to transmit
   
    detachInterrupt(interruptPin); //shut off wind speed measurement interrupt until done communication
    float aWSpeed = getAvgWindSpeed(culPulseTime,avgWindCount); //calculate average wind speed
    if(aWSpeed >= aSetting) digitalWrite(13, HIGH);   // high speed wind detected so turn the LED on
    else digitalWrite(13, LOW);   //no alarm so ensure LED is off
    culPulseTime = 0; //reset cumulative pulse counter
    avgWindCount = 0; //reset average wind count

    float aFreq = 0; //set to zero initially
    if(pulseTime > 0.0) aFreq = getAnemometerFreq(pulseTime); //calculate frequency in Hz of anemometer, only if pulsetime is non-zero
    float wSpeedMPH = getWindMPH(aFreq); //calculate wind speed in MPH, note that the 2.5 comes from anemometer data sheet
   
    Serial.begin(57600); //start serial monitor to communicate wind data
    Serial.println();
    Serial.println("...................................");
    Serial.print("Anemometer speed in Hz ");
    Serial.println(aFreq);
    Serial.print("Current wind speed is ");
    Serial.println(wSpeedMPH);
    Serial.print("Current average wind speed is ");
    Serial.println(aWSpeed);
    Serial.end(); //serial uses interrupts so we want to turn it off before we turn the wind measurement interrupts back on
   
    start = true; //reset start variable in case we missed wind data while communicating current data out
    attachInterrupt(digitalPinToInterrupt(interruptPin), anemometerISR, RISING); //turn interrupt back on
    dataTimer = millis(); //reset loop timer
  }
}

//using time between anemometer pulses calculate frequency of anemometer
float getAnemometerFreq(float pTime) { return (1/pTime); }
//Use anemometer frequency to calculate wind speed in MPH, note 2.5 comes from anemometer data sheet
float getWindMPH(float freq) { return (freq*2.5); }
//uses wind MPH value to calculate KPH
float getWindKPH(float wMPH) { return (wMPH*1.61); }
//Calculates average wind speed over given time period
float getAvgWindSpeed(float cPulse,int per) {
  if(per) return getWindMPH(getAnemometerFreq((float)(cPulse/per)));
  else return 0; //average wind speed is zero and we can't divide by zero
  }

//This is the interrupt service routine (ISR) for the anemometer input pin
//it is called whenever a falling edge is detected
void anemometerISR() {
  unsigned long cTime = millis(); //get current time
  if(!start) { //This is not the first pulse and we are not at 0 MPH so calculate time between pulses
   // test = cTime - sTime;
    pulseTime = (float)(cTime - sTime)/1000;
    culPulseTime += pulseTime; //add up pulse time measurements for averaging
    avgWindCount++; //anemomter went around so record for calculating average wind speed
  }
  sTime = cTime; //store current time for next pulse time calculation
  start = false; //we have our starting point for a wind speed measurement
}

Saturday, September 24, 2016

Reducing Power Consumption on Arduino Zero, MKR1000, or any SAMD21 Arduino Part 1

In this multiple part series we look at how to reduce power consumption for battery powered designs that utilize Arduino's with the Atmel SAMD21 MCU (Zero, MKR1000, etc). In part one we look at how to put the SAMD21 to sleep and wake it up with either the real time clock (RTC) or an external event on an input pin.



//***************Arduino Sketch from the video*********************.
//This code was used for a tutorial on the ForceTronics YouTube channel. It shows how to save power
//by putting Arduino's based on the SAMD21 MCU (MKR1000, Zero, etc) to sleep and how to wake them
//This code is public domain for anybody to use or modify

//#include "RTCZero.h"
#include <RTCZero.h>

/* Create an rtc object */
RTCZero rtc;

/* Change these values to set the current initial time */
const byte seconds = 0;
const byte minutes = 00;
const byte hours = 00;

/* Change these values to set the current initial date */
const byte day = 24;
const byte month = 9;
const byte year = 16;

void setup() 
{
  delay(5000); //delay so we can see normal current draw
   pinMode(LED_BUILTIN, OUTPUT); //set LED pin to output
  digitalWrite(LED_BUILTIN, LOW); //turn LED off

  rtc.begin(); //Start RTC library, this is where the clock source is initialized

  rtc.setTime(hours, minutes, seconds); //set time
  rtc.setDate(day, month, year); //set date

  rtc.setAlarmTime(00, 00, 10); //set alarm time to go off in 10 seconds
  
  //following two lines enable alarm, comment both out if you want to do external interrupt
  rtc.enableAlarm(rtc.MATCH_HHMMSS); //set alarm
  rtc.attachInterrupt(ISR); //creates an interrupt that wakes the SAMD21 which is triggered by a FTC alarm
  //comment out the below line if you are using RTC alarm for interrupt
 // extInterrupt(A1); //creates an interrupt source on external pin
  
  //puts SAMD21 to sleep
  rtc.standbyMode(); //library call
  //samSleep(); //function to show how call works
}

void loop() 
{
  //do nothing in main loop
}

//interrupt service routine (ISR), called when interrupt is triggered 
//executes after MCU wakes up
void ISR()
{
  digitalWrite(LED_BUILTIN, HIGH);
}


//function that sets up external interrupt
void extInterrupt(int interruptPin) {
  pinMode(interruptPin, INPUT_PULLUP);
  attachInterrupt(interruptPin, ISR, LOW);
}

//function to show how to put the 
void samSleep()
{
  // Set the sleep mode to standby
  SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
  // SAMD sleep
  __WFI();
}

//**********************Changed "begin" function from RTCZero Library**************
void RTCZero::begin(bool resetTime)
{
  uint16_t tmp_reg = 0;
  
  PM->APBAMASK.reg |= PM_APBAMASK_RTC; // turn on digital interface clock
  //config32kOSC();

  // If the RTC is in clock mode and the reset was
  // not due to POR or BOD, preserve the clock time
  // POR causes a reset anyway, BOD behaviour is?
  bool validTime = false;
  RTC_MODE2_CLOCK_Type oldTime;

  if ((!resetTime) && (PM->RCAUSE.reg & (PM_RCAUSE_SYST | PM_RCAUSE_WDT | PM_RCAUSE_EXT))) {
    if (RTC->MODE2.CTRL.reg & RTC_MODE2_CTRL_MODE_CLOCK) {

      validTime = true;
      oldTime.reg = RTC->MODE2.CLOCK.reg;
    }
  }
  // Setup clock GCLK2 with OSC32K divided by 32
  GCLK->GENDIV.reg = GCLK_GENDIV_ID(2)|GCLK_GENDIV_DIV(4);
  while (GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY)
    ;                                                         /*XOSC32K*/
  GCLK->GENCTRL.reg = (GCLK_GENCTRL_GENEN | GCLK_GENCTRL_SRC_OSCULP32K | GCLK_GENCTRL_ID(2) | GCLK_GENCTRL_DIVSEL );
  while (GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY)
    ;
  GCLK->CLKCTRL.reg = (uint32_t)((GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK2 | (RTC_GCLK_ID << GCLK_CLKCTRL_ID_Pos)));
  while (GCLK->STATUS.bit.SYNCBUSY)
    ;

  RTCdisable();

  RTCreset();

  tmp_reg |= RTC_MODE2_CTRL_MODE_CLOCK; // set clock operating mode
  tmp_reg |= RTC_MODE2_CTRL_PRESCALER_DIV1024; // set prescaler to 1024 for MODE2
  tmp_reg &= ~RTC_MODE2_CTRL_MATCHCLR; // disable clear on match
  
  //According to the datasheet RTC_MODE2_CTRL_CLKREP = 0 for 24h
  tmp_reg &= ~RTC_MODE2_CTRL_CLKREP; // 24h time representation

  RTC->MODE2.READREQ.reg &= ~RTC_READREQ_RCONT; // disable continuously mode

  RTC->MODE2.CTRL.reg = tmp_reg;
  while (RTCisSyncing())
    ;

  NVIC_EnableIRQ(RTC_IRQn); // enable RTC interrupt 
  NVIC_SetPriority(RTC_IRQn, 0x00);

  RTC->MODE2.INTENSET.reg |= RTC_MODE2_INTENSET_ALARM0; // enable alarm interrupt
  RTC->MODE2.Mode2Alarm[0].MASK.bit.SEL = MATCH_OFF; // default alarm match is off (disabled)
  
  while (RTCisSyncing())
    ;

  RTCenable();
  RTCresetRemove();

  // If desired and valid, restore the time value
  if ((!resetTime) && (validTime)) {
    RTC->MODE2.CLOCK.reg = oldTime.reg;
    while (RTCisSyncing())
      ;
  }

  _configured = true;
}

Sunday, July 24, 2016

Using the nRF24L01’s IRQ Pin to Generate an Interrupt with Arduino

In this video we look at how to use the pulse signal from an nRF24L01+ transceiver module's IRQ pin to trigger an interrupt on your Arduino.


Arduino code from video, receiver code first and then transmitter code:
#include <SPI.h> //Call SPI library so you can communicate with the nRF24L01+
#include <nRF24L01.h> //nRF2401 libarary found at https://github.com/tmrh20/RF24/
#include <RF24.h> //nRF2401 libarary found at https://github.com/tmrh20/RF24/
#include <avr/sleep.h> //library needed to use AVR based sleep API

const int pinCE = 9; //This pin is used to set the nRF24 to standby (0) or active mode (1)
const int pinCSN = 10; //This pin is used to tell the nRF24 whether the SPI communication is a command or message to send out
byte gotByte = 0; //used to store payload from transmit module
volatile int count = 0; //tracks the number of interrupts from IRQ
int pCount = 0; //tracks what last count value was so know when count has been updated
RF24 wirelessSPI(pinCE, pinCSN); // Declare object from nRF24 library (Create your wireless SPI) 
const uint64_t pAddress = 0xB00B1E5000LL;  //Create a pipe addresses for the 2 nodes to communicate over, the "LL" is for LongLong type

void setup()   {
  wirelessSPI.begin();  //Start the nRF24 module
  wirelessSPI.setAutoAck(1);                    // Ensure autoACK is enabled so rec sends ack packet to let you know it got the transmit packet payload
  wirelessSPI.enableAckPayload();         //allows you to include payload on ack packet
  wirelessSPI.maskIRQ(1,1,0);               //mask all IRQ triggers except for receive (1 is mask, 0 is no mask)
  wirelessSPI.setPALevel(RF24_PA_LOW); //Set power level to low, won't work well at higher levels (interfer with receiver)
  wirelessSPI.openReadingPipe(1,pAddress);      //open pipe o for recieving meassages with pipe address
  wirelessSPI.startListening();                 // Start listening for messages
  attachInterrupt(1, interruptFunction, FALLING);  //Create interrupt: 0 for pin 2 or 1 for pin 3, the name of the interrupt function or ISR, and condition to trigger interrupt
}

void loop() {

   if(pCount < count) { //If this is true it means count was interated and another interrupt occurred
       Serial.begin(57600);  //start serial to communicate process
       Serial.print("Receive packet number ");
       Serial.println(count); 
       Serial.end(); //have to end serial since it uses interrupts
       pCount = count; 
   }
}

//This is the function called when the interrupt occurs (pin 2 goes high)
//this is often referred to as the interrupt service routine or ISR
//This cannot take any input arguments or return anything
void interruptFunction() {
 count++; //up the receive counter
 while(wirelessSPI.available()) { //get data sent from transmit
       wirelessSPI.read( &gotByte, 1 ); //read one byte of data and store it in gotByte variable
 }

}

//********************Transmitter code**************************** #include <SPI.h> //Call SPI library so you can communicate with the nRF24L01+
#include <nRF24L01.h> //nRF2401 libarary found at https://github.com/tmrh20/RF24/
#include <RF24.h> //nRF2401 libarary found at https://github.com/tmrh20/RF24/

const int pinCE = 9; //This pin is used to set the nRF24 to standby (0) or active mode (1)
const int pinCSN = 10; //This pin is used to tell the nRF24 whether the SPI communication is a command or message to send out
byte counter = 1; //used to count the packets sent
RF24 wirelessSPI(pinCE, pinCSN); // Create your nRF24 object or wireless SPI connection
const uint64_t pAddress = 0xB00B1E5000LL;              // Radio pipe addresses for the 2 nodes to communicate.

void setup()  
{
  Serial.begin(57600);   //start serial to communicate process
  wirelessSPI.begin();            //Start the nRF24 module
  wirelessSPI.setAutoAck(1);                    // Ensure autoACK is enabled so rec sends ack packet to let you know it got the transmit packet payload
  wirelessSPI.enableAckPayload();               // Allow optional ack payloads
  wirelessSPI.setPALevel(RF24_PA_LOW);
  wirelessSPI.openWritingPipe(pAddress);        // pipe address that we will communicate over, must be the same for each nRF24 module
  wirelessSPI.stopListening();        //transmitter so stop listening for data
  randomSeed(analogRead(0));    //use random ADC value to seed random number algorithm
}

void loop() {
  delay(random(100,5000)); //Generate delay time between 100msec and 5 sec
  Serial.println("Sending packet"); 
  if (!wirelessSPI.write( &counter, 1 )){  //if the send fails let the user know over serial monitor
       Serial.println("packet delivery failed");  
  }
   Serial.println();   
}

Saturday, July 2, 2016

Advanced PWM for Arduino Zero or any Atmel SAMD21 Based Arduino Board


The Arduino PWM library leaves a lot to be desired since it really only scratches the surface on PWM capabilities built into today's MCUs. In this video we look at how to unlock some of the more advanced PWM features on any Arduino board based on the Atmel SAMD21 32 bit ARM MCU.



Click here to access the Atmel programming API from the video

Arduino code from video***********************************************************
//This was used for ForceTronics YouTube tutorial on generating PWM signals with SAMD21 based Arduinos
//This code is public domain and can be used by anyone at their own risk
//Some of this code was leveraged from MartinL on Arduino Forum http://forum.arduino.cc/index.php?topic=346731.5;wap2

//sets the period of the PWM signal, PWM period = wPer / gen clock rate 
volatile unsigned char wPer = 255;
//This variable is to generate the duty cycle of the PWM signal 0.5 --> 50%
volatile float pWMDC = .5;
//selects the gen clock for setting the waveform generator clock or sample rate
const unsigned char gClock = 4;
//sets the divide factor for the gen clk, 48MHz / 3 = 16MHz
const unsigned char dFactor = 3;

void setup() 

  pinMode(3, OUTPUT);
  analogReadResolution(8); //set the ADC resolution to match the PWM max resolution (0 to 255)
  
  REG_GCLK_GENDIV = GCLK_GENDIV_DIV(dFactor) |          // Divide the main clock down by some factor to get generic clock
                    GCLK_GENDIV_ID(gClock);            // Select Generic Clock (GCLK) 4
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization

  REG_GCLK_GENCTRL = GCLK_GENCTRL_IDC |           
                     GCLK_GENCTRL_GENEN |         // Enable GCLK4
                     GCLK_GENCTRL_SRC_DFLL48M |   // Set the 48MHz clock source
                     GCLK_GENCTRL_ID(gClock);          // Select GCLK4
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization

  // Enable the port multiplexer for the digital pin. Note commented out line is pin D7, other is D3
 // PORT->Group[g_APinDescription[7].ulPort].PINCFG[g_APinDescription[7].ulPin].bit.PMUXEN = 1;
  PORT->Group[g_APinDescription[3].ulPort].PINCFG[g_APinDescription[3].ulPin].bit.PMUXEN = 1;
  
   //Connect the TCC0 timer to digital output - port pins are paired odd PMUO and even PMUXE (note D7 is commented out and D3 is not)
  // PORT->Group[g_APinDescription[2].ulPort].PMUX[g_APinDescription[2].ulPin >> 1].reg = PORT_PMUX_PMUXO_F | PORT_PMUX_PMUXE_F; 
  PORT->Group[g_APinDescription[4].ulPort].PMUX[g_APinDescription[4].ulPin >> 1].reg = PORT_PMUX_PMUXO_F | PORT_PMUX_PMUXE_F;

  // Feed GCLK4 to TCC0 and TCC1
  REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN |         // Enable GCLK4 to TCC0 and TCC1
                     GCLK_CLKCTRL_GEN_GCLK4 |     // Select GCLK4
                     GCLK_CLKCTRL_ID_TCC0_TCC1;   // Feed GCLK4 to TCC0 and TCC1
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization

  //Set for Single slope PWM operation: timers or counters count up to TOP value and then repeat
  REG_TCC1_WAVE |= TCC_WAVE_WAVEGEN_NPWM;       // Reverse the output polarity on all TCC0 outputs
                   //TCC_WAVE_POL(0xF)      //this line inverts the output waveform
                   //TCC_WAVE_WAVEGEN_DSBOTH;    // Setup dual slope PWM on TCC0
  while (TCC1->SYNCBUSY.bit.WAVE);               // Wait for synchronization

  // Each timer counts up to a maximum or TOP value set by the PER register,
  // this determines the frequency of the PWM operation: 
  REG_TCC1_PER = wPer;         // This sets the rate or frequency of PWM signal. 
  while (TCC1->SYNCBUSY.bit.PER);                // Wait for synchronization
  
  // Set the PWM signal to output 50% duty cycle initially (0.5 x 255)
  REG_TCC1_CC1 = pWMDC*wPer;        
  while (TCC1->SYNCBUSY.bit.CC1);                // Wait for synchronization

  //enable interrupts
  REG_TCC1_INTENSET = TCC_INTENSET_OVF; //Set up interrupt at TOP of each PWM cycle
  enable_interrupts(); //enable in NVIC
  
  // Set prescaler and enable the outputs
  REG_TCC1_CTRLA |= TCC_CTRLA_PRESCALER_DIV1 |    // Divide GCLK4 by 1
                    TCC_CTRLA_ENABLE;             // Enable the TCC0 output
  while (TCC1->SYNCBUSY.bit.ENABLE);              // Wait for synchronization
}

void loop() { 

  //Put main code here
 }

//This function sets the interrupts priority to highest and then enables the PWM interrupt
void enable_interrupts() {
  NVIC_SetPriority(TCC1_IRQn, 0);    // Set the Nested Vector Interrupt Controller (NVIC) priority
  NVIC_EnableIRQ(TCC1_IRQn);
}

//This ISR is called at the end or TOP of each PWM cycle
void TCC1_Handler() {
    REG_TCC1_PER = analogRead(A1); //Get period from A1
    while (TCC1->SYNCBUSY.bit.PER);
    REG_TCC1_CC1 = (analogRead(A0)/255.0)*analogRead(A1); //calculate PWM using A0 reading and A1 current state
    while (TCC1->SYNCBUSY.bit.CC1);
    REG_TCC0_INTFLAG = TC_INTFLAG_OVF; //Need to reset interrupt
}

Monday, February 8, 2016

The Watchdog Timer on Arduino

In this video we take a look at the Watchdog Timer on Arduino and the three different ways to configure it. We show a simple example using the Watchdog Timer and you can find the code from the example below.



//**********************Arduino Code*******************************
#include <avr/wdt.h> //Watch dog timer functions
#include <EEPROM.h> //library for using EEPROM

// Pin 13 has an LED connected on most Arduino boards.
int led = 13;
volatile int wSetting = 1; //variable to store WDT setting, make it volatile since it will be used in ISR

void setup() {
  wdt_disable(); //Datasheet recommends disabling WDT right away in case of low probabibliy event
  pinMode(led, OUTPUT); //set up the LED pin to output
  pinMode(2,INPUT_PULLUP); //setup pin 2 for input since we will be using it with our button
  getSettings(); //start serial settings menu to choose WDT setting
  //setup the watchdog Timer based on the setting
  if(wSetting == 1) setWDT(0b01000000); //set for interrupt
  else if(wSetting == 2) setWDT(0b00001000); //set for reset
  else setWDT(0b01001000); //set for interrupt and then reset
}

void loop() {
  
  if(!digitalRead(2)) { //check if button to reset WDT was pressed
    digitalWrite(led, LOW);    // turn the LED off by making the voltage LOW
    wdt_reset(); //Reset the WDT 
  }
}

//This function gets the WDT setting from the user using Serial comm
void getSettings() {
  Serial.begin(57600);
  Serial.println("We just started up......");
  byte b; //variable to store interrupt / reset tracker
  if(EEPROM.get(10, b)) { //check if interrupt occurred before reset
    Serial.println("...and an interrupt occurred.....");
    b = 0; //reset variable
    EEPROM.put(10,b);
  }
  Serial.println("Select the WDT setting: 1 --> interrupt, 2 --> reset, 3 --> interrupt and reset");
  //wait for user to input setting selection
  while (!Serial.available()) { }
  wSetting = Serial.parseInt(); //get entry and ensure it is valid
  if(wSetting < 1 || wSetting > 3) {
      wSetting = 1;
   }
   Serial.print("WDT setting is ");
   Serial.println(wSetting);
   Serial.println();
   Serial.end(); //don't want to mix serial comm and interrupts together
}

/*
void setSleepInterval(byte timer) {
  sleep_enable(); //enable the sleep mode
  set_sleep_mode(SLEEP_MODE_PWR_DOWN); //set the type of sleep mode. Default is Idle. power down saves the most power
  ADCSRA &= ~(1<<ADEN); //Turn off ADC before going to sleep (set ADEN bit to 0). this saves even more power
  WDTCSR |= 0b00011000;    //Set the WDE bit and then clear it when set the prescaler, WDCE bit must be set if changing WDE bit   
  WDTCSR = 0b01000000 | timer; //This sets the WDT to the interval specified in the function argument
  wdt_reset(); //Reset the WDT 
  sleep_cpu(); //enter sleep mode. Next code that will be executed is the ISR when interrupt wakes Arduino from sleep
  sleep_disable(); //disable sleep mode
  ADCSRA |= (1<<ADEN); //Turn the ADC back on
}*/

//this function setups up and starts the watchdog timer
void setWDT(byte sWDT) {
   WDTCSR |= 0b00011000;
   WDTCSR = sWDT |  WDTO_2S; //Set WDT based user setting and for 2 second interval
   wdt_reset();
}


//This is the interrupt service routine for the WDT. It is called when the WDT times out and is in interrupt mode. 
//This ISR must be in your Arduino sketch or else the WDT will not work correctly
ISR (WDT_vect) 
{
  digitalWrite(led, HIGH); 
  if(wSetting == 3) {
    byte b = 1;
    EEPROM.put(10, b);
  }
  //wdt_disable();
}  // end of WDT_vect

Sunday, January 31, 2016

How to Reset Your Arduino from Code

In this video we talk about how to reset your Arduino from code with no hands. You can find the schematic and code from this video below.



***************Arduino Code from Video************************************
// Pin 13 has an LED connected on most Arduino boards.
int led = 13;
//create a variable for detecting what mode you are in. If used in interrupt must be "volatile"
/*volatile*/ bool nMode = 1;

void setup() {
  pinMode(4, OUTPUT); //set up pin 4, which is connected to base of transistor
  digitalWrite(4,LOW); //set to low so transistor is off and doesn't trigger reset
  pinMode(led, OUTPUT); //set up the LED pin to output
  Serial.begin(57600); //start serial comm
  Serial.println("We just started back up"); //print quick message so we know we just went through setup code
  Serial.println();
  Serial.end(); //need to shut off serial comm because it uses interrupts
  //Create interrupt: 0 for pin 2, the name of the interrupt function or ISR, and condition to trigger interrupt 
  attachInterrupt(0, interruptFunction, CHANGE); 
}

void loop() {
    digitalWrite(led, HIGH);   // turn the LED on (HIGH is the voltage level)
    delay(400);               // wait 
    digitalWrite(led, LOW);    // turn the LED off by making the voltage LOW
    delay(400);               // wait 
  /*
  noInterrupts(); //disables interrupts
  // critical, time-sensitive code here
  interrupts();//enables interrupts
  */
}

//This is the function called when the interrupt occurs (pin 2 goes high)
//this is often referred to as the interrupt service routine or ISR
//This cannot take any input arguments or return anything
void interruptFunction() {
  digitalWrite(4,HIGH); //with this variable set to 1 the LED will blink
 //detachInterrupt(0); this function call will turn the interrupt off
}

Thursday, October 1, 2015

Arduino Zero DAC Overview and Waveform Generator Example

In this video we take a look at the digital to analog converter (DAC) on the Arduino Zero. We will look at a simple example how to use the DAC and then we will look at a more complex example that turns the DAC into a pseudo waveform generator. You can find the code from this video below.


//***************ZeroDACExample Sketch*******************************
//This sketch provides an example on using the DAC on the Arduino Zero.
//It was used in a video tutorial on the ForceTronics YouTube Channel.
//This code is free and open for anybody to use and modify at their own risk

void setup()
{
  Serial.begin(57600); //start serial communication
  analogWriteResolution(10); //set the Arduino DAC for 10 bits of resolution (max)
  analogReadResolution(12); //set the ADC resolution to 12 bits, default is 10
  
  //Get user entered voltage, convert to DAC value, output DAC value
  analogWrite(A0,setDAC(getVoltValue()));
}

void loop()
{
  delay(2000);
  Serial.println();
  Serial.print("Measured voltage value is ");
  Serial.println(convertToVolt(analogRead(A1))); //Read value at ADC pin A1 and print it

}

//this function converts a user entered voltage value into a 10 bit DAC value 
int setDAC(float volt) {
//formula for calculating DAC output voltage Vdac = (dVal / 1023)*3.3V
return (int)((volt*1023)/3.3);
}

//this function gets the user entered DAC voltage value from serial monitor
float getVoltValue() {
float v = 0; //variable to store voltage 
Serial.println("Enter the voltage you want the DAC to output (range 0V to 3.3V)");
v = readParameter();
if (v < 0 || v > 3.3) v = 0; //check to make sure it is between 0 and 3.3V
Serial.print("DAC voltage set to ");
Serial.println(v);
Serial.println("Outputting DAC value........");
return v;
}

//waits for serial data and reads it in. This function reads in the parameters
// that are entered into the serial terminal
float readParameter() {
while(!Serial.available()); //Wait for user to enter setting
return Serial.parseFloat(); //get int that was entered on Serial monitor
}

//This function takes and ADC integer value (0 to 4095) and turns it into a voltage level. The input is the measured 12 bit ADC value.
float convertToVolt(int aVAL) {
return (((float)aVAL/4095)*3.3); //formula to convert ADC value to voltage reading
}

//***************ZeroWaveGen Sketch**************************************
//This sketch generates a sine wave on the Arduino Zero DAC based on user entered sample count and sample rate
//It was used in a tutorial video on the ForceTronics YouTube Channel. This code can be used and modified freely
//at the users own risk
volatile int sIndex; //Tracks sinewave points in array
int sampleCount = 100; // Number of samples to read in block
int *wavSamples; //array to store sinewave points
uint32_t sampleRate = 1000; //sample rate of the sine wave

void setup() {
  analogWriteResolution(10); //set the Arduino DAC for 10 bits of resolution (max)
  getSinParameters(); //get sinewave parameters from user on serial monitor
  
  /*Allocate the buffer where the samples are stored*/
  wavSamples = (int *) malloc(sampleCount * sizeof(int));
  genSin(sampleCount); //function generates sine wave
}

void loop() {
  sIndex = 0;   //Set to zero to start from beginning of waveform
  tcConfigure(sampleRate); //setup the timer counter based off of the user entered sample rate
  //loop until all the sine wave points have been played
  while (sIndex<sampleCount)
  { 
//start timer, once timer is done interrupt will occur and DAC value will be updated
    tcStartCounter(); 
  }
  //disable and reset timer counter
  tcDisable();
  tcReset();
}

//This function generates a sine wave and stores it in the wavSamples array
//The input argument is the number of points the sine wave is made up of
void genSin(int sCount) {
const float pi2 = 6.28; //2 x pi
float in; 

for(int i=0; i<sCount;i++) { //loop to build sine wave based on sample count
in = pi2*(1/(float)sCount)*(float)i; //calculate value in radians for sin()
wavSamples[i] = ((int)(sin(in)*511.5 + 511.5)); //Calculate sine wave value and offset based on DAC resolution 511.5 = 1023/2
}
}

//This function handles getting and setting the sine wave parameters from 
//the serial monitor. It is important to use the Serial.end() function
//to ensure it doesn't mess up the Timer counter interrupts later
void getSinParameters() {
Serial.begin(57600);
Serial.println("Enter number of points in sine wave (range 10 to 1000)");
sampleCount = readParameter();
if (sampleCount < 10 || sampleCount > 1000) sampleCount = 100;
Serial.print("Sample count set to ");
Serial.println(sampleCount);
Serial.println("Enter sample rate or samples per second for DAC (range 1 to 100k)");
sampleRate = readParameter();
if (sampleRate < 1 || sampleRate > 100000) sampleRate = 10000;
Serial.print("Sample rate set to ");
Serial.println(sampleRate);
Serial.println("Generating sine wave........");
Serial.end();
}

//waits for serial data and reads it in. This function reads in the parameters
// that are entered into the serial terminal
int readParameter() {
while(!Serial.available());
return Serial.parseInt(); //get int that was entered on Serial monitor
}

// Configures the TC to generate output events at the sample frequency.
//Configures the TC in Frequency Generation mode, with an event output once
//each time the audio sample frequency period expires.
 void tcConfigure(int sampleRate)
{
// Enable GCLK for TCC2 and TC5 (timer counter input clock)
GCLK->CLKCTRL.reg = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID(GCM_TC4_TC5)) ;
while (GCLK->STATUS.bit.SYNCBUSY);

tcReset(); //reset TC5

// Set Timer counter Mode to 16 bits
TC5->COUNT16.CTRLA.reg |= TC_CTRLA_MODE_COUNT16;
// Set TC5 mode as match frequency
TC5->COUNT16.CTRLA.reg |= TC_CTRLA_WAVEGEN_MFRQ;
//set prescaler and enable TC5
TC5->COUNT16.CTRLA.reg |= TC_CTRLA_PRESCALER_DIV1 | TC_CTRLA_ENABLE;
//set TC5 timer counter based off of the system clock and the user defined sample rate or waveform
TC5->COUNT16.CC[0].reg = (uint16_t) (SystemCoreClock / sampleRate - 1);
while (tcIsSyncing());

// Configure interrupt request
NVIC_DisableIRQ(TC5_IRQn);
NVIC_ClearPendingIRQ(TC5_IRQn);
NVIC_SetPriority(TC5_IRQn, 0);
NVIC_EnableIRQ(TC5_IRQn);

// Enable the TC5 interrupt request
TC5->COUNT16.INTENSET.bit.MC0 = 1;
while (tcIsSyncing()); //wait until TC5 is done syncing 
}

//Function that is used to check if TC5 is done syncing
//returns true when it is done syncing
bool tcIsSyncing()
{
  return TC5->COUNT16.STATUS.reg & TC_STATUS_SYNCBUSY;
}

//This function enables TC5 and waits for it to be ready
void tcStartCounter()
{
  TC5->COUNT16.CTRLA.reg |= TC_CTRLA_ENABLE; //set the CTRLA register
  while (tcIsSyncing()); //wait until snyc'd
}

//Reset TC5 
void tcReset()
{
  TC5->COUNT16.CTRLA.reg = TC_CTRLA_SWRST;
  while (tcIsSyncing());
  while (TC5->COUNT16.CTRLA.bit.SWRST);
}

//disable TC5
void tcDisable()
{
  TC5->COUNT16.CTRLA.reg &= ~TC_CTRLA_ENABLE;
  while (tcIsSyncing());
}

void TC5_Handler (void)
{
  analogWrite(A0, wavSamples[sIndex]);
  sIndex++;
  TC5->COUNT16.INTFLAG.bit.MC0 = 1;
}

Tuesday, November 18, 2014

Reducing Arduino’s Power Consumption Part 1

In part 1 of this 3 part series we look at how to use Arduino's built-in sleep modes to drastically cut down on power consumption to help you build projects with longer battery life. In part 2 of this series we will look at how to use power reduction registers and in part three we will look at some other ways to wake an Arduino up from sleep mode.


<avr/sleep.h> library: http://www.nongnu.org/avr-libc/user-manual/group__avr__sleep.html

//**********************Arduino Code***********************************
/*
Example program for using sleep modes in Arduino. This example code was used in a sleep mode tutorial video on the ForceTronics YouTube Channel.
This code is open for anybody to use at their own risk

The 5 different modes are:
     *     SLEEP_MODE_IDLE         -the least power savings
     *     SLEEP_MODE_ADC
     *     SLEEP_MODE_PWR_SAVE
     *     SLEEP_MODE_STANDBY
     *     SLEEP_MODE_PWR_DOWN
These are the arguments used to set the sleep mode in the function set_sleep_mode()
     */
     
#include <avr/sleep.h>

int led = 13; //variable for pin that the LED is on
int count = 0; //variable to control how many times LED blinks before sleep

void setup() {
   sleep_enable(); //enable the sleep capability
   set_sleep_mode(SLEEP_MODE_PWR_DOWN); //set the type of sleep mode. Default is Idle
   pinMode(led, OUTPUT); //set up the LED pin to output
}

void loop() {
  
  if(count < 4) { //For first four loops blink the LED
    digitalWrite(led, HIGH);   // turn the LED on (HIGH is the voltage level)
    delay(900);               // wait 
    digitalWrite(led, LOW);    // turn the LED off by making the voltage LOW
    delay(900);       // wait 
    count++; //increment count
  }
  else { //after blinking LED setup interrupt and then go to sleep. Note that sleep will only happen once since it is disabled in ISR
    attachInterrupt(0, interruptFunction, HIGH); 
    sleep_cpu(); //enter sleep mode. Next code that will be executed is the ISR when interrupt wakes Arduino from sleep
    count = 0; //Set the count back to zero 
  }
   
}

//This is the function called when the interrupt occurs (pin 2 goes high)
//this is often referred to as the interrupt service routine or ISR
//This cannot take any input arguments or return anything
void interruptFunction() {
 detachInterrupt(0); //this function call will turn the interrupt off
 sleep_disable(); //Disable the sleep mode so even if call to sleep is executed again it will be ignored
}

Tuesday, November 11, 2014

How to Interrupt Your Arduino


In this video tutorial we will look at how to use interupts on the Arduino Uno, Arduino Pro Mini, and any Arduino using the Atmega 328. Interrupts allow you to react immediately to an external event versus trying to constantly check for it in the main loop. 



Arduino Code****************************************************************
/*
  Interrupt Example
  Demonstrates how to use interrupts. When a high level is detected at pin 2 interrupt occurs and blinks LED at pin 13

  This example code is in the public domain.
 */

// Pin 13 has an LED connected on most Arduino boards.
// give it a name:
int led = 13;
//create a variable for ISR, has to be volatile if used in interrupt
volatile int bLED = 0;

void setup() {
  //Create interrupt: 0 for pin 2 or 1 for pin 3, the name of the interrupt function or ISR, and condition to trigger interrupt 
  attachInterrupt(0, interruptFunction, HIGH); 
  pinMode(led, OUTPUT); //set up the LED pin to output
}

// the loop routine runs over and over again forever:
void loop() {
  
  if(bLED) { //if this statement becomes true then the interrupt occurred
    digitalWrite(led, HIGH);   // turn the LED on (HIGH is the voltage level)
    delay(300);               // wait 
    digitalWrite(led, LOW);    // turn the LED off by making the voltage LOW
    delay(300);               // wait 
  }
  /*
  noInterrupts(); //disables interrupts
  // critical, time-sensitive code here
  interrupts();//enables interrupts
  */
}

//This is the function called when the interrupt occurs (pin 2 goes high)
//this is often referred to as the interrupt service routine or ISR
//This cannot take any input arguments or return anything
void interruptFunction() {
 bLED = 1; //with this variable set to 1 the LED will blink
 //detachInterrupt(0); this function call will turn the interrupt off
}