I am making a persistence of vision display using an ATTiny85, programmed with Arduino using the arduino-tiny core.
It consists of a stick with 16 LEDs on it which is spun round quickly to ‘draw’ a picture in the air. The display buffer is represented by an array, the next index of which is output every time the timer fires. It uses a hall sensor wired to INT0 to sense top dead centre, where it zeroes the array index.
It is an 8 bit processor and I have 16 LEDs connected to LED drivers, so I actually use two arrays for the display.
So the weird thing is that when I initialised the display with a cross pattern, it displayed a bunch of broken lines; so I zeroed it first in case memory had some random stuff in it. Now it doesn’t display anything (even though I write the cross pattern to it straight after zeroing it). I have no idea what is happening, any ideas?
I had it previously outputting just the index value, and the picture it traces appears to look like counting binary, so I think the hardware is working.
Note that I’m not using digitalWrite because it disables interrupts which might throw the timing off.
Here is the code: (sorry it’s quite a lot)
#define COLUMNCOUNT 180
const int datapin = 4;
const int clockpin = 0;
const int latchpin = 1;
const int rxpin = 3;
const int hallpin = 2;
volatile int columns0[COLUMNCOUNT];
volatile int columns1[COLUMNCOUNT];
volatile int counter = 0;
void setup()
{
pinMode(datapin, OUTPUT);
pinMode(clockpin, OUTPUT);
pinMode(latchpin, OUTPUT);
pinMode(rxpin, INPUT);
pinMode(hallpin, INPUT);
//make sure the arrays are all zeroed
for (int i = 0; i < COLUMNCOUNT; i++)
{
columns0[i] = 0;
columns1[i] = 0;
}
//make a cross pattern
columns0[0] = 255;
columns1[0] = 255;
columns0[45] = 255;
columns1[45] = 255;
columns0[90] = 255;
columns1[90] = 255;
columns0[135] = 255;
columns1[135] = 255;
//turn on the timer (prescale CK/16)
OCR1A = 255;
OCR1C = 255;
TCNT1 = 0;
TIMSK = _BV(OCIE1A);
TCCR1 = _BV(CTC1) | _BV(CS12) | _BV(CS10);
GIMSK = _BV(INT0);
sei();
}
void loop()
{
//nothing to do here
}
ISR(TIMER1_COMPA_vect)
{
if (counter < COLUMNCOUNT)
counter++;
outputWord(columns0[counter], columns1[counter]);
}
ISR(INT0_vect)
{
counter = 0;
}
void outputByte(int b)
{
int currentBit;
for (int i = 0; i < 8; i++)
{
currentBit = (b & 128) == 128;
PORTB = _BV(clockpin) | (currentBit ? _BV(datapin) : 0);
PORTB = PORTB ^ _BV(clockpin);
b <<= 1;
}
}
void outputWord(int hi, int lo)
{
outputByte(hi);
outputByte(lo);
PORTB = _BV(latchpin);
PORTB = 0;
}
Per C standard, global variables are initialised to 0. As such,
is not necessary. That action is taken care of automatically when the
__do_clear_bsssection is executed.Also, as per C standard, the int type must be at least 16 bits wide. In AVR, the minimum is used. If you are using the free software toolchain, it contains
inttypes.hwhich provides the functionality offered bystdint.hand some extra stuff. This was mentioned in another answer.The statement:
can be rewritten as:
which compiles to only 1 instruction, as per Atmel’s datasheets.
Macros provided by
pgmspace.hcan read from flash. Notice that you can not change the contents of flash as your program runs in most AVR chips.Beware local/global variables and stack/heap collisions when handling this much data.