OS: Windows Vista (SP2)
Board: Arduino Uno (ATMega328p-pu)
I have 2 sketches that both work nicely on their own. One uses SPI to drive a pair of chained 74HC595 8-bit shift registers. The other sketch uses Timer2 to call a short ISR function about 8000 times a second. I have verified that both sketches (separately) do the correct things with the correct timings.
However, I want to make a hybrid of these that uses the ISR to call SPI.transmit(...) and do a bit of integer math to calculate what data to transmit. BUT when I try to combine the SPI sketch with the Timer2 sketch, it seems that the timing is badly disrupted and I'm getting incorrect output on the shift register (indicated by 16 leds), it simply looks like nothing is happening when I try to run the hybrid sketch.
On the Arduino, I'm using digital pins 10, 11 and 13 (and +5v and gnd) to drive the 595 chips, otherwise the circuit is identical to the one in the "ShiftOut()" tutorials on the main arduino site. The circuit is verified as correct.
I'll post my code here, but truncated quite a bit...
#include <avr/io.h>
#include <avr/interrupt.h>
#include <SPI.h>
#define SHIFT_REGISTER_LATCH_SS (10)
#define PHYSICAL_PANEL_WIDTH (24) //columns of LEDs
#define PHYSICAL_PANEL_HEIGHT (8) //rows of LEDs
#define BITS_PER_PIXEL (8) //1 bit in the memory image maps to a single LED on the panel
//starting values are the initial valid bit position for each bit field...
#define AMU_COLL_START (0x04)
#define AMU_BASE_START (0x10)
#define CMU_EMIT_START (0x01)
#define CMU_BASE_START (0x01)
//resets are one left shift further than we want for each bit field...
#define AMU_COLL_RESET (0x100)
#define AMU_BASE_RESET (0x100)
#define CMU_EMIT_RESET (0x10)
#define CMU_BASE_RESET (0x04)
volatile int amu_Coll = AMU_COLL_START;
volatile int amu_Base = AMU_BASE_START;
volatile int cmu_Emit = CMU_EMIT_START;
volatile int cmu_Base = CMU_BASE_START;
volatile byte hiByte = 0x00;
volatile byte loByte = 0x0f;
unsigned int tcnt2;
ISR(TIMER2_OVF_vect)
{
// if((*pixelPtr & pixelBit) == 0)
// UpdateDisplay(0, 0x0f);
// else
UpdateDisplay(hiByte, loByte);
amu_Coll <<= 1;
if(amu_Coll == AMU_COLL_RESET) //the 1 bit was shifted right out of the byte leaving all zeroes
{
amu_Coll = AMU_COLL_START;
amu_Base <<= 1;
if(amu_Base == AMU_BASE_RESET)
{
amu_Base = AMU_BASE_START;
cmu_Emit <<= 1;
if(cmu_Emit == CMU_EMIT_RESET)
{
cmu_Emit = CMU_EMIT_START;
cmu_Base <<= 1;
if(cmu_Base == CMU_BASE_RESET)
{
cmu_Base = CMU_BASE_START;
}
else
{
hiByte = amu_Coll | cmu_Base;
}
}
else
{
loByte = amu_Base | (cmu_Emit ^ 0x0f);
}
}
}
}
static void FailSafe()
{
amu_Coll = AMU_COLL_START;
amu_Base = AMU_BASE_START;
cmu_Emit = CMU_EMIT_START;
cmu_Base = CMU_BASE_START;
UpdateDisplay(0x00, 0x0f);
}
int UpdateDisplay(byte hiByte, byte loByte)
{
SPI.transfer(loByte);
SPI.transfer(hiByte);
digitalWrite(SHIFT_REGISTER_LATCH_SS, LOW);
digitalWrite(SHIFT_REGISTER_LATCH_SS, HIGH);
}
void InitScreenRefreshInterrupt()
{
/* First disable the timer overflow interrupt while we're configuring */
TIMSK2 &= ~(1<<TOIE2);
/* Configure timer2 in normal mode (pure counting, no PWM etc.) */
TCCR2A &= ~((1<<WGM21) | (1<<WGM20));
TCCR2B &= ~(1<<WGM22);
/* Select clock source: internal I/O clock */
ASSR &= ~(1<<AS2);
/* Disable Compare Match A interrupt enable (only want overflow) */
TIMSK2 &= ~(1<<OCIE2A);
/* Now configure the prescaler to CPU clock divided by 128 */
TCCR2B |= (1<<CS22) | (1<<CS20); // Set bits
TCCR2B &= ~(1<<CS21); // Clear bit
tcnt2 = 238;
/* Finally load end enable the timer */
TCNT2 = tcnt2;
TIMSK2 |= (1<<TOIE2);
}
void setup()
{
FailSafe();
InitScreenRefreshInterrupt();
//initialize SPI:
SPI.setBitOrder(LSBFIRST);
SPI.begin();
//Serial.begin(9600);
pinMode(SHIFT_REGISTER_LATCH_SS, OUTPUT);
}
void loop()
{
delay(1000);
noInterrupts();
}
Is Timer2 hardware interfering with the SPI stuff (or vice versa) or am I simply doing something stupid?