AP KS GM-pov code.c
From Mech
Jump to navigationJump to search/**********************************************
* pov.c
* Author:Greg McGlynn
* Persistence-of-Vision Display project
* Alexander Park, Kwang Xiong Sim, Grag McGlynn
* ME 333 Mechatronics - Winter 2008
* Northwestern University
*
* This code runs on the rotating display PIC. The main loop displays the
* pixels of the image one column at a time as the display rotations. It reads
* the LED states from the "pixels" array and turns on the appropriate LEDs,
* waits a few hundred microseconds, then moves on to the next column in the
* image and repeats the process. The image is 193 pixels wide by 14 tall.
*
* There are also interrupts happening that time the rotation of the POV,
* adjusting the column number and the delay between columns so that the image
* always appears in the same place no matter how fast the display is rotating.
* There is an interrupt that occurs every 250 microseconds and increments a
* counter. There is also an interrupt that occurs every time the hall sensor
* detects the stationary magnet. This interrupt reads and resets the 250us
* counter, thereby determining how long the last full rotation of the display
* took. It then adjusts the delay period between columns of pixels accordingly.
*
* Finally, the display is also listening for incoming RS232 commands. An
* incoming message triggers an interrupt to receive the command. All commands
* are four bytes long. There are two classes of commands. One gives a new state
* for a specified column of pixels in the image. This command is sent by the
* PC applet. This command has the syntax
*
* <column # (two bytes)> <new LED states (two bytes)>
*
* The second class of command has the syntax
*
* <0xff> <0xff> <command type> <command argument>
*
* Here command type can have four values:
* 0x00 - clear Clears the image (turns all pixels off). The
* argument is ignored.
* 0x01 - image angle Sets the image angle. The argument is a number from
* 0 to 192 that gives the new "home column."
* 0x02 - scroll speed Sets the scroll speed. The argument is a number from
* 0 to 255 that sets the scroll speed.
* 0x03 - builtin image Causes the display to show one of the builtin
* images. The argument is the image number (0-10)
**********************************************/
#include <18f4520.h>
#fuses EC,NOLVP,NOWDT,NOPROTECT
#use delay(clock=40000000)
#use rs232(baud=9600, xmit=PIN_B3, rcv=PIN_B1)
//the 14 pixels in one column are stored in two 8-bit integers: pixels[col][0]
//and pixels[col][1]. The wiring and storing is done in such a way that
//displaying the column can be accomplished by the two statements:
//output_c(pixels[column][1]);
//output_d(pixels[column][0]);
int pixels[193][2]; //the array storing the pixels of the image
int32 WIDTH = 193; //the total number of columns in the image
signed int16 home_column = 95; //the starting column. this column is the one
//shown as the display passes over the magnet
int16 col; //the offset of the current column from the home column
int32 delay = 800; //the wait between columns in microseconds
int32 us250 = 0; //the count of 250-us intervals in this rotation
//scrolling occurs by periodically adjusting the home column by one column
//either up or down:
int32 scroll_count = 0; //the count of 250-us intervals since we last scrolled
int32 us250_per_scroll = 100; //the number of 250-us intervals between scrolls
signed int16 scroll_dir = 1; //+1 is scrolling one way, -1 the other
//declarations of functions that show the builtin images by setting
//every element of the pixels array. These functions are hundreds of lines each.
void init0(); //"Hello World!!!"
void init1(); //"ME333 - Mechatronics"
void init2(); //"Gregory McGlynn"
void init3(); //"Alexander Park"
void init4(); //"Kwang Xiong Sim"
void init5(); //"Go Cats! N Go Cats! N" (with the Northwestern "N"!)
void init6(); //a Chinese message
void init7(); //some spirals
void init8(); //"R.I.P. XBEES #12&16"
void init9(); //A Mario level (our masterpiece)
void init10(); //"Goodbye World!!!" in inverted text (dark on bright)
//turn all the LEDs off:
void clear_image() {
int16 x;
for(x = 0; x < 193; x++) {
pixels[x][0] = 0;
pixels[x][1] = 0;
}
}
//this interrupt is called every 250 microseconds
#int_timer2
void inc_us250() {
us250++; //increment the count of 250-us intervals
scroll_count++;
if(scroll_count >= us250_per_scroll) {
scroll_count = 0;
//we can scroll by simply shifting the home column, since
//everything is relative to it:
home_column = (home_column + scroll_dir);
if(home_column < 0) home_column += 193;
if(home_column > 192) home_column -= 193;
}
}
//this is called whenever the hall switch is triggered
#int_ext
void hall_switch_triggered() {
col = 0; //go to the home column
//delay per column = (total rotation time) / (total # of columns)
delay = (int32)250*us250/(WIDTH+9);
us250 = 0; //restart our timer for the rotation
}
int ignore = true; //ignore the first interrupt on B1; it's a false alarm
//this is called when we start to receive a 4-byte message from the controller
#int_ext1
void incoming_rs232() {
unsigned int byte1, byte2, byte3, byte4;
int16 updateCol;
int16 timer_ticks_elapsed;
//this interupt fires on startup even though there's no message. so
//ignore this first message
if(ignore) {
ignore = false;
return;
}
//Servicing this interrupt takes several microseconds during which several
//columns should go by. In an effort to reduce display weirdness while
//we receive commands, we turn off all the LEDs while we read the RS232
//commands and time how long we're out for, then try to set col properly
//when we're done. This doesn't actually work very well. A better way to
//do it would be to have the LED updates done in a high-priority interrupt
//service routing that could interrupt this one, so that the LEDs would
//still get updated while we received commands.
set_timer3(0);
output_d(0);
output_c(0);
//get the four bytes
byte1 = getc();
byte2 = getc();
byte3 = getc();
byte4 = getc();
//special commands start with the bytes 0xff 0xff
if(byte1 == 0xff && byte2 == 0xff) {
switch(byte3) {
case 0x00: //clear
clear_image();
break;
case 0x01: //set angle
home_column = byte4;
break;
case 0x02: //set scroll speed
if(byte4 >= 98 && byte4 <= 158) { //deadband, don't scroll
us250_per_scroll = 32000;
scroll_dir = 0;
} else if (byte4 > 158) { //scroll forward
us250_per_scroll = 3000 / ((int16)byte4 - (int16)158);
scroll_dir = -1;
} else { //scroll backward
us250_per_scroll = 3000 / ((int16)98 - (int16)byte4);
scroll_dir = 1;
}
break;
case 0x03: //display builtin image
switch(byte4) {
case 0: init0(); break;
case 1: init1(); break;
case 2: init2(); break;
case 3: init3(); break;
case 4: init4(); break;
case 5: init5(); break;
case 6: init6(); break;
case 7: init7(); break;
case 8: init8(); break;
case 9: init9(); break;
case 10: init10(); break;
default: break;
}
break;
default:
break;
}
} else {
//if it's not a special command, it's a column update command
//extract the # of the column being updated:
updateCol = (((int16)byte1) << 8) | ((int16)byte2);
//store the new column state:
pixels[updateCol][0] = byte3;
pixels[updateCol][1] = byte4;
}
//try to compensate for the time this isr took:
timer_ticks_elapsed = get_timer3();
col = col + timer_ticks_elapsed / (delay * 5 / 4);
if(col > 192) col = col - 193;
}
void main() {
int16 disp_column;
//set up timers and interrupts interrupts:
setup_timer_3(T3_INTERNAL | T3_DIV_BY_8); //ticks every 0.8us
setup_timer_2(T2_DIV_BY_16, 51, 3); //interrupts every 250us
enable_interrupts(INT_TIMER2); //250us interrupt
enable_interrupts(INT_EXT); //hall sensor interrupt
ext_int_edge(0, H_TO_L);
enable_interrupts(INT_EXT1); //rs232 interrupt
ext_int_edge(1, H_TO_L);
enable_interrupts(GLOBAL);
init1(); //"ME333 - Mechatronics"
col = 0;
while(true) { //infinite loop
//everything is relative to the home column:
disp_column = (home_column + col);
if(disp_column > 192) disp_column = disp_column - 192;
//set the LED states:
output_c(pixels[disp_column][1]);
output_d(pixels[disp_column][0]);
//wait while we rotate a bit:
delay_us(delay);
//increment the column number:
col = col + 1;
if(col > 192) col = 0;
}
}