<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://hades.mech.northwestern.edu//index.php?action=history&amp;feed=atom&amp;title=AP_KS_GM-pov_code.c</id>
	<title>AP KS GM-pov code.c - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://hades.mech.northwestern.edu//index.php?action=history&amp;feed=atom&amp;title=AP_KS_GM-pov_code.c"/>
	<link rel="alternate" type="text/html" href="https://hades.mech.northwestern.edu//index.php?title=AP_KS_GM-pov_code.c&amp;action=history"/>
	<updated>2026-04-10T16:40:31Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.35.9</generator>
	<entry>
		<id>https://hades.mech.northwestern.edu//index.php?title=AP_KS_GM-pov_code.c&amp;diff=11759&amp;oldid=prev</id>
		<title>Kwang Sim at 06:14, 19 March 2009</title>
		<link rel="alternate" type="text/html" href="https://hades.mech.northwestern.edu//index.php?title=AP_KS_GM-pov_code.c&amp;diff=11759&amp;oldid=prev"/>
		<updated>2009-03-19T06:14:08Z</updated>

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