Hard Drive clock- can I use Millis() with two other timers?

I’m trying to make a HDD clock for a school project, based upon this instructable: http://www.instructables.com/id/Hard-Drive-Persistence-of-Vision-HDPOV/?ALLSTEPS. The problem is that the hard drive automatically shuts down after 50 secs which is not really ideal for a clock. Originally, I thought I could just briefly interrupt power to the hdd’s control board to power cycle it, but now that i have looked at the code for the project and i see that it already uses uses both timers. For some reason i am under the vague impression that millis() uses one of the timers. Am i correct? Would adding a ‘blink without delay’ -type function causes problems in the following code?

#ifndef F_CPU
#define F_CPU           16000000UL  // 16 MHz
#endif

#include                <stdlib.h>
#include                <avr/io.h>
#include                <avr/interrupt.h>
#include                <util/delay.h>

// arduino redefines int, which bugs out stdio.h (needed for sscanf)
// see: http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1207028702/3
#undef int
#include                <stdio.h>

// The number of "pie slices" that make up a single image around the platter
#define DIVISIONS       0xFF

// Red pin, on PORTD
#define RED             3

// Green Pin, on PORTD
#define GRN             4

// Blue pin, on PORTD
#define BLU             5
#define POWER            12

// Macro used to communicate serial status
#define OK              1

// Number of pages in frame buffer
#define PAGES           2

// Helper macro to build LED values for PORTD
#define RGB(R,G,B)      (R << RED | G << GRN | B << BLU)

// The timers are configured with the prescaler set to 8, which means every
// 8 clock cycles equals on tick on the counter.  This is a constant to help
// convert timer cycles back to real time.
#define FREQ_DIV_8      (F_CPU / 8)

// Defines how many ticks a millisecond equals on our clock
#define MILLITICK       (FREQ_DIV_8 * .001)


#define SPURIOUS_INT    (2 * MILLITICK)

// Helper macros for frobbing bits
#define bitset(var,bitno) ((var) |= (1 << (bitno)))
#define bitclr(var,bitno) ((var) &= ~(1 << (bitno)))
#define bittst(var,bitno) (var& (1 << (bitno)))


const unsigned char divisions = DIVISIONS;

// The current slice being drawn
volatile unsigned char current_slice;

// The period of the platter in timer ticks
int period;

// The value of the hidden page
volatile int page_hidden;

// The value of the visible page
volatile int page_visible;

// A flag representing the need for the pages to be flipped
volatile unsigned char page_flip_flag;

// The double buffered frame buffer
unsigned char FrameBuffer[PAGES][DIVISIONS];


// sit and wait for the page to be flipped
void __inline__ wait_for_page_flip(void)
{
    while (page_flip_flag) {}
}


void __inline__ flip_page(void)
{
    page_flip_flag = 1;
    wait_for_page_flip();
}

// copy the visible page to the hidden page
void __inline__ copy_page(void)
{
    int x;
    for(x = 0; x < divisions; x++)
    {
        FrameBuffer[page_hidden][x] = FrameBuffer[page_visible][x];
    }
}

// write a value to the hidden page
void __inline__ write_page(unsigned char slice, unsigned char val)
{
    FrameBuffer[page_hidden][slice] = val;
}

// clear the hidden page
void clear_page(void)
{
    int x;
    for(x = 0; x < divisions; x += 1)
    {
        FrameBuffer[page_hidden][x] = 0;
    }
}

// called by the interrupt to flip to the next page
void __inline__ flip_to_next_page(void)
{
    if (page_flip_flag)
    {
        page_visible = page_hidden;
        page_hidden = !page_hidden;
        page_flip_flag = 0;
    }
}

void InitTestPattern1(void)
{
  int x;
  for(x = 0; x < divisions; x++)
  {
      switch (x / (divisions / 8))
      {
        case 0:
          write_page(x, RGB(0,0,0));
          break;  
        case 1:
          write_page(x, RGB(1,0,0));
          break;
        case 2:
          write_page(x, RGB(0,1,0));
          break;
        case 3:
          write_page(x, RGB(0,0,1));
          break;
        case 4:
          write_page(x, RGB(1,1,0));
          break;  
        case 5:
          write_page(x, RGB(1,0,1));
          break;  
        case 6:
          write_page(x, RGB(0,1,1));
          break;  
        case 7:
        default:
          write_page(x, RGB(1,1,1));
          break;
      }
    }
}

void InitTestPattern2(void)
{
  int x;
  for(x = 0; x < divisions; x++)
  {
      switch (x % 8)
      {
        case 0:
          write_page(x, RGB(0,0,0));
          break;  
        case 1:
          write_page(x, RGB(1,0,0));
          break;
        case 2:
          write_page(x, RGB(0,1,0));
          break;
        case 3:
          write_page(x, RGB(0,0,1));
          break;
        case 4:
          write_page(x, RGB(1,1,0));
          break;  
        case 5:
          write_page(x, RGB(1,0,1));
          break;  
        case 6:
          write_page(x, RGB(0,1,1));
          break;  
        case 7:
          write_page(x, RGB(1,1,1));
          break;
      }
    }
}

// Go through all pages of the frame buffer, and set them to 0
void init_pages(void)
{
    int page;
    int x;
    for(page = 0; page < PAGES; page += 1)
    {
        for(x = 0; x < divisions; x += 1)
        {
            FrameBuffer[page][x] = 0;
        }
    }
    page_hidden = 0;
    page_flip_flag = 0;
}

void SetupHardware(void)
{
  // setup serial
  Serial.begin(115200);

  Serial.print("[");
  pinMode(RED, OUTPUT);
  pinMode(GRN, OUTPUT);
  pinMode(BLU, OUTPUT);
  pinMode(POWER, OUTPUT);
  Serial.print("L");

  
  cli();

 
  TCCR0A = 0;
  TCCR0B = 0;  
  
  bitset(TCCR0A, WGM01);

  bitset(TCCR0B, CS01);
 
  bitset(TIMSK0, OCIE0A);
  Serial.print("0");
  
  
  TCCR1B = 0;
  TCCR1A = 0;
  
  bitset(TCCR1B, CS11);
 
  TCNT1 = 0;
 
  bitset(TIMSK1, TOIE1);
  Serial.print("1");

  
  EICRA = _BV(ISC01);
 
  EIMSK |= _BV(INT0);
  Serial.print("G");

  
  period = 0;
  
  // init pages
  init_pages();

 
  sei();

  Serial.println("]");
  Serial.flush();
}



}

code continued below

/**
 ** Serial Output
 **/

void report_status_to_serial(void)
{
  unsigned int rpm = 0;
  if (period)
  {
      rpm = (int)(((float)FREQ_DIV_8) / ((float)period) * 60.0);
  }
  Serial.print("Revolutions / Minute: ");
  Serial.println(rpm, DEC);
  Serial.print("Ticks / Revolution: ");
  Serial.println(period, DEC);
  Serial.print("OCR0A: ");
  Serial.println(OCR0A, DEC);
  Serial.print("Divisons: ");
  Serial.println(divisions, DEC);
  Serial.print("Current Page: ");
  Serial.println((!(page_hidden)), DEC);
  Serial.print("Red: 0x");
  Serial.println(_BV(RED), HEX);
  Serial.print("Green: 0x");
  Serial.println(_BV(GRN), HEX);
  Serial.print("Blue: 0x");
  Serial.println(_BV(BLU), HEX);
}

// Read the entire visible page to the serial port, printing
// each byte as a hexadecimal character
void read_page_to_serial(int page)
{
    int slice;

    for(slice = 0; slice < divisions; slice++)
    {
        Serial.print("0x");
        Serial.println(FrameBuffer[page][slice], HEX);
        Serial.flush();
    }
}

/**
 ** Serial Input
 **/


void __inline__ wait_for_serial_input()
{
    while (Serial.available() < 1) {}
}


int read_byte(void)
{
    int ret;
    char rstr[2];

    wait_for_serial_input();
    rstr[0] = Serial.read();
    wait_for_serial_input();
    rstr[1] = Serial.read();
    sscanf(rstr, "%x", &ret);
    return ret;
}


int write_page_from_serial(void)
{
    int val;
    int slice;
    int retval = OK;

    for(slice = 0; slice < divisions; slice++)
    {
        val = read_byte();
        write_page(slice, val);
    }

    return retval;
}



void RunStartupDisplay(void)
{
    int color;
    int slice;

    clear_page();
    flip_page();

    for (color = 0; color < 4; color++)
    {
        for (slice = 0; slice < divisions; slice += 2)
        {
            switch(color)
            {
                case 0:
                    /* red */
                    write_page(slice, _BV(RED));
                    write_page(slice+1, _BV(RED));
                    break;
                case 1:
                    /* green */
                    write_page(slice, _BV(GRN));
                    write_page(slice+1, _BV(GRN));
                    break;
                case 2:
                    /* blue */
                    write_page(slice, _BV(BLU));
                    write_page(slice+1, _BV(BLU));
                    break;
                case 3:
                    /* black */
                    write_page(slice, 0);
                    write_page(slice+1, 0);
                    break;
            }
            flip_page();
            copy_page();
        }
    }

    clear_page();
    flip_page();
}

// Top level setup, called by the Arduino core
void setup(void)
{
    SetupHardware();
    RunStartupDisplay();
}


void loop(void)
{
    int cmd;
    int slice, value;
    int okval = OK;

    Serial.print("~ ");
    wait_for_serial_input();

    cmd = Serial.read();
    Serial.println("");
    switch(cmd)
    {
        /* report status */
        case 'r':
            report_status_to_serial();
            break;
        /* flip to the next page */
        case 'f':
            flip_page();
            break;
       
        case 's':
            slice = read_byte();
            value = read_byte();
            write_page(slice, value);
            break;
    
        case 'h':
            okval = write_page_from_serial();
            break;
    
        case 'v':
            read_page_to_serial(!page_hidden);
            break;
       
        case '1':
            InitTestPattern1();
            break;
       
        case '2':
            InitTestPattern2();
            break;
        
        case 'c':
            clear_page();
            break;
        default:
            Serial.print("Unknown command: ");
            Serial.println(cmd, BYTE);
            break;
    }
    Serial.print("> ");
    Serial.println(okval, DEC);
    Serial.flush();
}

ISR(INT0_vect)
{
  
  period = TCNT1;
 
  if(period < SPURIOUS_INT)
  {
    return;
  }
 
  TCNT1 = 0;
 
  TCNT0 = 0;
 
  flip_to_next_page();
  
  PORTD = FrameBuffer[page_visible][0];
  // Divide the time of single platter rotation by the number of drawable
  // divisions
  OCR0A = (period / divisions);
 
  current_slice = 1;
}

ISR(TIMER0_COMPA_vect) {
  
  PORTD = FrameBuffer[page_visible][current_slice];
  
  current_slice = ((current_slice + 1) % divisions);
}

ISR(TIMER1_OVF_vect) {

I don't understand how your description of the problem (the hard disk stopping) relates to your Title question?

if you need an occasional pulse to tell the HDD to stay alive that should be easily managed with millis() and without the need for any other timer.

...R

sorry if i wasn't clear... the original sketch (not Mine) is already utilizing timer0 and timer1 to track the position of the hard drive platter and flash the LEDs at the precisely the right time. the person who attempted this project previously was lucky enough to find a hdd that spins indefinitely. unfortunately, mine does not, therefore i need to modify his code. so the question is: does millis() use either timer0 or timer1, and if it does, would it cause problems for the other sections of code already using the timers? I have never used timers or interrupts only millis(), so I am not really sure what to expect. does that make sense?

millis() uses Timer 0. As does delay() and micros().

Thank you! that was part of what I was trying to get at. Instead of relying on time, do you think it might be better to try to implement a counter as there is already a lot of time sensitive things going on?

You haven't explained what you need to do to keep the HDD from going to sleep. I can't help feeling that you are imagining a complex solution to a very simple problem.

...R

You haven't explained what you need to do to keep the HDD from going to sleep

I could just briefly interrupt power to the hdd's control board to power cycle it

if that wasn't explanation enough... here is my best guess as to why it powers down: Because i have hacked the drive, the read heads no longer provide the appropriate data to the controller. After 50 seconds, the controller stops trying to read non-existent data off a non-existent platter from previously damaged heads and powers down the spindle. I see two ways to deal with this: a) provide the appropriate data to the controller, or b) periodically reset the controller. I don't design or program HDDs so 'a' is definitely out. 'b' is simple enough- all I need is access to a reset pin or the main supply line (which I have). Through testing, I have determined that a 35 microsecond "LOW" pulse on the 5v pin will consistently reset the controller without a significant loss in spindle speed. Is this what you wanted to know?

I can't help feeling that you are imagining a complex solution to a very simple problem.

How so?

My question is: will calling millis() interfere with code that already utilizes both hardware timers ? Can Timer 0 be shared? Or am i trying to foresee a problem where there simply isn't one?

This seems like a very obvious case for suck-it-and-see - what harm can it do?

I would be very surprised if someone developed code for an Arduino project that interferes with millis() without telling everyone. Millis() is a very fundamental part of the Arduino system.

...R