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; } }