These things are fun to put together and interesting how a single line of LEDs moving fast enough, can trick our eyes into seeing more. This is the effect of Persistence Of Vision.
Ir emitter pictured, attached to frame. Ir emitter is powered by the fan dc power source, with a current limiting resistor that will be suitable depending on the voltage of the fans supply. Emitter and receiver need to have a meeting point, a point where the Ir emitter will be detected by the photo transistor.
Some balancing was needed. A piece of proto board with some washers taped to it worked. There was still some wobble to it, but much less than without a counter balance.
The software consists of a few components, interrupts for handling the start position and timer, in-line delay for creating the delay between degrees of rotation, and functions for displaying text/numbers on the spinning display.
System clock is 20 MHz, one instruction takes 4 clock cycles, instruction clock is:
\[20MHz/4 = 200ns\]
With prescaler off, one interrupt with this 8-bit timer will take:
\[256*200ns = 51.2us\]
50us would be easier to work with so:
\[50us/200ns = 250\]
Timer reload value should be:
\[256-250 = 6\]
Then to get a 1ms counter, count up to 20 timer 0 interrupts.
void interrupt isr(){
if(T0IF&&T0IE){
t0_tpms++;
if(t0_tpms >= 20){
ms_counter++;
t0_tpms = 0;
}
TMR0 += 6;
T0IF = 0;
}
}
Knowing there’s 60,000 milliseconds in a minute we can get the expected number of rotations in a minute. And thus the spinning device can determine its own RPM. For my system, I’m expecting the voltage or RPM to vary, and so I have the microprocessor determine how long the delay should be between setting LED’s.
rpm = 60000/time of one rotation in ms
We need to know how long one rotation takes, if we know the RPM then:
Time of one rotation in ms = 60000/RPM
Or if using a millisecond counter we can know how long one rotation takes, then its just a matter of determining how long one degree is. In terms of micro seconds:
Time of one degree = (1000us/ms) * (time of one rotation in ms) / (360 degree)
if RPM is 2700:
\[ 1000*(60000/2700) / 360 = 61.7 us \]
Knowing how long one degree is, we can decide how long of a delay to create between setting LED’s. One instruction is 200ns, and an in-line delay loop will take several instructions. one loop of a delay will take:
\[7*200ns = 1.4us\]
So to create a 61us delay:
\[61/1.4 = 43\]
void delay(){
uint8_t adj_delay = 43;
while(adj_delay--);
}
I combine the terms as much as I can, avoiding decimals, and using the “u” suffix on literals to let the compiler know to treat them as unsigned int, this also reduces code size as working with negative numbers takes more instructions. The calculation for delay per degree can be written as:
//(1000us*ms_counter/360 degree)(1/us per instruction clock)
//1000/360 -> 2.77 * 100/100 -> 277/100
//time of one instruction is 1/(20/4) = 200ns
//one cycle of a delay loop will take several instructions 7*200ns = 1.4us
//and there's additional instructions with setting LEDs so I pad
//the delay to 2us
delay_per_degree = (277u*ms_counter)/200u;
Two interrupts are used, timer 0 and external interrupt pin.
The timer is used to count milliseconds which in turn can give seconds, minutes hours, and also used in determine RPM and delay between displaying to LED’s
The int pin is used for finding the start point in rotation. When the Ir Emitter and photo transistor cross paths, the photo transistor will react to the Ir and pull the signal line low, causing the interrupt on edge to trigger. Display The two 373 Latches make it possible to control 16 LED’s with an 8 pins. The display function takes value for writing to port pins, then sends a signal to one or both latches to to tell them to accept the value.
//Low enabled LEDs
// 0 -> outer 8 LEDs
// 1 -> inner 8 LEDs
// 2+ -> set both LEDs with same value
void set_leds(uint8_t leds,uint8_t section){
PORTA = (leds) & 0x0F;
PORTB = (leds) & 0xF0;
if(section== 0){
RB1 = 1;
RB1 = 0;
}
else if(section== 1){
RB2 = 1;
RB2 = 0;
}
else{
PORTB |= 0x06;
PORTB &= 0xF0;
}
}