Showing posts with label Timer Interrupt. Show all posts
Showing posts with label Timer Interrupt. Show all posts

Tuesday, April 21, 2015

Arduino Nano: Capture analog input in Timer Interrupt

Last post show a "oscilloscope-like waveform on 0.96" 128X64 I2C OLED with Arduino Nano". But the analog input is captured inside loop(), it's not in fixed timing, and affected by the slow operation of displaying.

To capture analog input in accurate timing, we are going to implement TIMER1 ISR (Interrupt Service Routine) in this example, and call analogRead() to capture analog input inside ISR.


Connecttion between Arduino Nano and OLED, refer to last post.

To understand Arduino Timer and Interrupt, it have a good tutorial HERE.

I2C_OLED_scope.ino
// Display analog input to mini-OLED I2C,
// capture input inside TIMER1 ISR
// http://arduino-er.blogspot.com/

// OLED display: ref u8glib: https://code.google.com/p/u8glib/
// To install u8glib on Arduino IDE: http://goo.gl/j3olBA
#include "U8glib.h"

U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_NONE|U8G_I2C_OPT_DEV_0);

const int WIDTH=128;
const int HEIGHT=64;
const int LENGTH=WIDTH;

const int LED = 13;
boolean LEDst = false;

//true: request capture analog input in ISR
//false: stop capture, draw waveform in loop
boolean capture = false;

const int analogInPin = A0;
int analogInValue = 0;

int x;
int y[LENGTH];

/*
    reference: Arduino Timer and Interrupt Tutorial
    http://blog.oscarliang.net/arduino-timer-and-interrupt-tutorial/
    
    For our TIMER1 Interrupt:
    Clock Freq = 16MHz
    no prescale, 1
    16MHz - 0.0625us/cycle
   
    To calculator preload value to generate 1ms(1KHz) 
    (65536 - t) x 0.0625us = 1000us
    t = 65536 - 1000/0.0625 = 49536
    
    To calculator preload value to generate 0.5ms(2KHz)
    (65536 - t) x 0.0625us = 500us
    t = 65536 - 500/0.0625 = 57536

 */
const int TCNT1_PRELOAD = 57536;

void clearY(){
  for(int i=0; i<LENGTH; i++){
    y[i] = -1;
  }
}

void drawY(){
  u8g.drawPixel(0, y[0]);
  for(int i=1; i<LENGTH; i++){
    u8g.drawLine(i-1, y[i-1], i, y[i]);
  }
}

void setup(void) {
  
  pinMode(LED, OUTPUT);
  
  // initialize Timer1
  noInterrupts(); // disable all interrupts
  TCCR1A = 0;
  TCCR1B = 0;
  TCNT1 = TCNT1_PRELOAD;
  TCCR1B |= (1 << CS10);   // no prescaler
  TIMSK1 |= (1 << TOIE1); // enable timer overflow interrupt
    
  x = 0;
  clearY();
  capture = true;

  interrupts(); // enable all interrupts  
}

void loop(void) {
  if(!capture){
    
    u8g.firstPage();  
    do {
      drawY();
    } while( u8g.nextPage() );
    
    //start capture another frame
    x = 0;
    clearY();
    capture = true;
  }

  delay(100);

}

ISR(TIMER1_OVF_vect){
  TCNT1 = TCNT1_PRELOAD; // preload timer
  
  if(capture){
    //toggle LED
    digitalWrite(LED, LEDst=!LEDst);
  
    analogInValue = analogRead(analogInPin);
    y[x] = map(analogInValue, 0, 1023, HEIGHT-1, 0);
    
    x++;
    if(x >= WIDTH){
      capture = false;
    }
  }
}

Tuesday, September 9, 2014

Arduino Uno: scan LED Matrix in Timer Interrupt

The original tutorial "Row-columm Scanning to control an 8x8 LED Matrix" call refreshScreen() to draw the screen repeatly in main loop(). If anything in loop() have to do something for times, will block the program, and make the duty cycle (and the brightness) will become unstable. This example show how to scan LED Matrix in Timer Interrupt ISR, such that it will not be affect by the jobs in loop(). But the timing of Timer Interrupt will affect the performance. This video show how:



// 2-dimensional array of row pin numbers:
const int row[8] = {
  2, 7, 19, 5, 13, 18, 12, 16
};

// 2-dimensional array of column pin numbers:
const int col[8] = {
  6, 11, 10, 3, 17, 4, 8, 9
};

// 2-dimensional array of pixels:
int pixels[8][8];

int posX = 7;
int posY = 7;
int count = 30;
bool bg = false;

unsigned int timer1_counter;
int scanningRow = 0;

void setup() {
  Serial.begin(9600);
  
  // initialize the I/O pins as outputs
  // iterate over the pins:
  for (int thisPin = 0; thisPin < 8; thisPin++) {
    // initialize the output pins:
    pinMode(col[thisPin], OUTPUT);
    pinMode(row[thisPin], OUTPUT);
    // take the col pins (i.e. the cathodes) high to ensure that
    // the LEDS are off:
    digitalWrite(col[thisPin], HIGH);
  }

  // initialize timer1 
  noInterrupts();           // disable all interrupts
  TCCR1A = 0;
  TCCR1B = 0;

  //timer1_counter for 1 sec, with prescaler=256
  //65536 - 16000000/256
  //timer1_counter = 3036;
  //timer1_counter for 0.5 sec, with prescaler=256
  //65536 - 16000000/(256*2)
  //timer1_counter = 34286;
  //timer1_counter for 0.1 sec, with prescaler=256
  //65536 - 16000000/(256*10)
  //timer1_counter = 59286;
  //timer1_counter for 0.001 sec, with prescaler=256
  //65536 - 16000000/(256*100)
  //timer1_counter = 64911;
  //timer1_counter for 0.0002 sec, with prescaler=256
  //65536 - 16000000/(256*500)
  timer1_counter = 65411;
  
  TCNT1 = timer1_counter;   // preload timer
  TCCR1B |= (1 << CS12);    // 256 prescaler 
  TIMSK1 |= (1 << TOIE1);   // enable timer overflow interrupt
  interrupts();             // enable all interrupts
  
  //init pixels
  for (int x = 0; x < 4; x++) {
    for (int y = 0; y < 8; y++) {
      pixels[x][y] = HIGH;
    }
  }
  for (int x = 4; x < 8; x++) {
    for (int y = 0; y < 8; y++) {
      pixels[x][y] = LOW;
    }
  }
  
  pixels[1][3] = LOW;

}

ISR(TIMER1_OVF_vect)        // interrupt service routine 
{
  TCNT1 = timer1_counter;   // preload timer

  
  //scan LED Matrix in ISR
  digitalWrite(row[scanningRow], LOW);

  if(++scanningRow>=8){
    scanningRow = 0;
  }

  for (int thisCol = 0; thisCol < 8; thisCol++) {
    int thisPixel = pixels[scanningRow][thisCol];
    digitalWrite(col[thisCol], thisPixel);
  }
  digitalWrite(row[scanningRow], HIGH);
  
}

void loop() {
  //refreshScreen();
  
  //Do something, block the loop
  Serial.println("Hello!");
  //Serial.println("Hello! again");

}

void refreshScreen() {
  // iterate over the rows (anodes):
  for (int thisRow = 0; thisRow < 8; thisRow++) {
    // take the row pin (anode) high:
    digitalWrite(row[thisRow], HIGH);
    // iterate over the cols (cathodes):
    for (int thisCol = 0; thisCol < 8; thisCol++) {
      // get the state of the current pixel;
      int thisPixel = pixels[thisRow][thisCol];
      // when the row is HIGH and the col is LOW,
      // the LED where they meet turns on:
      digitalWrite(col[thisCol], thisPixel);
      // turn the pixel off:
      if (thisPixel == LOW) {
        digitalWrite(col[thisCol], HIGH);
      }
    }
    // take the row pin low to turn off the whole row:
    digitalWrite(row[thisRow], LOW);
  }
}

Monday, September 8, 2014

Using Timer Interrupt of Arduino Uno

Example to use Timer Interrupt of Arduino Uno.


WalkingLedMatrix.ino
// 2-dimensional array of row pin numbers:
const int row[8] = {
  2, 7, 19, 5, 13, 18, 12, 16
};

// 2-dimensional array of column pin numbers:
const int col[8] = {
  6, 11, 10, 3, 17, 4, 8, 9
};

// 2-dimensional array of pixels:
int pixels[8][8];

int posX = 7;
int posY = 7;
int count = 30;
bool bg = false;

volatile unsigned long lasttime;
volatile unsigned long now;

int timer1_counter;

void setup() {
  Serial.begin(9600);
  
  // initialize the I/O pins as outputs
  // iterate over the pins:
  for (int thisPin = 0; thisPin < 8; thisPin++) {
    // initialize the output pins:
    pinMode(col[thisPin], OUTPUT);
    pinMode(row[thisPin], OUTPUT);
    // take the col pins (i.e. the cathodes) high to ensure that
    // the LEDS are off:
    digitalWrite(col[thisPin], HIGH);
  }

  setupScreen();
  
  // initialize timer1 
  noInterrupts();           // disable all interrupts
  TCCR1A = 0;
  TCCR1B = 0;

  //timer1_counter for 1 sec, with prescaler=256
  //65536 - 16000000/256
  //timer1_counter = 3036;
  //timer1_counter for 0.5 sec, with prescaler=256
  //65536 - 16000000/(256*2)
  //timer1_counter = 34286;
  //timer1_counter for 0.1 sec, with prescaler=256
  //65536 - 16000000/(256*10)
  timer1_counter = 59286;
  
  
  TCNT1 = timer1_counter;   // preload timer
  TCCR1B |= (1 << CS12);    // 256 prescaler 
  TIMSK1 |= (1 << TOIE1);   // enable timer overflow interrupt
  interrupts();             // enable all interrupts
  
  lasttime = millis();

}

ISR(TIMER1_OVF_vect)        // interrupt service routine 
{
  TCNT1 = timer1_counter;   // preload timer

  if(posX--==0){
    posX = 7;
    if(posY--==0){
      posY = 7;
      bg = !bg;
    }
  }
  setupScreen();
  
  now = millis();
  Serial.println(now - lasttime);
  lasttime = now;
}

void loop() {
  refreshScreen();
}

void setupScreen(){
  if(bg){
    //ON all others
    for (int x = 0; x < 8; x++) {
      for (int y = 0; y < 8; y++) {
        pixels[x][y] = LOW;
      }
    }
    
    //OFF current pos
    pixels[posX][posY] = HIGH;
  }else{
    //OFF all others
    for (int x = 0; x < 8; x++) {
      for (int y = 0; y < 8; y++) {
        pixels[x][y] = HIGH;
      }
    }
    
    //ON current pos
    pixels[posX][posY] = LOW;
  }
}

void refreshScreen() {
  // iterate over the rows (anodes):
  for (int thisRow = 0; thisRow < 8; thisRow++) {
    // take the row pin (anode) high:
    digitalWrite(row[thisRow], HIGH);
    // iterate over the cols (cathodes):
    for (int thisCol = 0; thisCol < 8; thisCol++) {
      // get the state of the current pixel;
      int thisPixel = pixels[thisRow][thisCol];
      // when the row is HIGH and the col is LOW,
      // the LED where they meet turns on:
      digitalWrite(col[thisCol], thisPixel);
      // turn the pixel off:
      if (thisPixel == LOW) {
        digitalWrite(col[thisCol], HIGH);
      }
    }
    // take the row pin low to turn off the whole row:
    digitalWrite(row[thisRow], LOW);
  }
}