/* ORPP Motion Controller Source (C) 2005 Jason Hunt nulluser@gmail.com 16f871.h has been modified as follows: // original // #pragma chip PIC16F871, core 14, code 2048, ram 32 : 0xBF /0 /3 #pragma chip PIC16F871, core 14, code 0x1F00, ram 32 : 0x1FF #pragma cdata[0] = 0x0180 + 0x0A // CLRF PCLATH, prepare for code page change #pragma cdata[1] = 0 // NOP #pragma cdata[2] = 0 // NOP #pragma resetVector 3 // start address for user program This reserves space for the serial bootloader. */ #define start_string "ORPP-1. (C) 2005 Jason Hunt" #include #pragma chip PIC16f871 #pragma config |= 0x3FF2 // for ceramic resinator /* Pin Assignments */ #pragma bit led0 @ PORTB.6 #pragma bit led1 @ PORTB.7 #pragma bit digital_in_0 @ PORTB.0 #pragma bit digital_in_1 @ PORTA.4 #pragma bit digital_in_2 @ PORTE.0 #pragma bit digital_in_3 @ PORTE.1 #pragma bit digital_in_4 @ PORTE.2 #pragma bit radio_in_0 @ PORTD.6 #pragma bit radio_in_1 @ PORTD.7 #pragma bit open_collector_0 @ PORTB.4 #pragma bit open_collector_1 @ PORTB.5 #pragma bit pwm_out_0 @ PORTB.1 #pragma bit pwm_out_1 @ PORTB.2 #define num_servo 6 // number of servos connected to the board unsigned char servo_pos[num_servo]; // positions of the 8 servos unsigned char pwm_value_0; unsigned char pwm_value_1; /******************* ** Interrupt Code ** *******************/ #pragma interruptSaveCheck n #pragma origin 4 unsigned char int_mode; unsigned char cur_servo; unsigned char pwm_cycle; /* the ISR to update the servos */ interrupt ISR(void) { int_save_registers // check for timer zero interrupt if (T0IF) { if (int_mode == 0) // setupup for base pos wait { int_mode = 1; // next trigger we will setup for base delay OPTION = 4; // reset back into 1:16 prescaled mode OPTION &= 0xf8; // clear prescaler bits OPTION |= 0x04; // set to 1:32 prescaler TMR0 = 0xa0; // set base time delay unsigned char s = 1 << cur_servo; // create mask PORTC |= s; // turn this one on } else if (int_mode == 1) // setup for position hold { int_mode = 2; // next mode will turn off and check for long delay TMR0 = servo_pos[cur_servo]; // reset the timer to the pos } else if (int_mode == 2) // setupfor next servo { int_mode = 0; // reset back to base mode unsigned char s = 1 << cur_servo; // create mask s = 255 - s; PORTC &= s; // turn the current servo off cur_servo++; // next servo // Setup longer delay to fill the 20 ms window, // we need to burn 11ms on average // 5Mhz clock, 0.0512 MS per increment at 1:256 prescaler // 11ms / 0.0512 ms = 215 cycles to burn, start time at 40 if (cur_servo == num_servo) { cur_servo = 0; // reset back the first servo OPTION &= 0xf8; // clear prescaler bits OPTION |= 0x07; // set to 1:256 prescaler TMR0 = 40; // set the value for the longer delay } } T0IF = 0; // clear timer zero interrupt } else // check for timer one interrupt /* if (TMR1IF) { // TMR1L = 0x00; // reset timer // TMR1H = 0x80; TMR1IF = 0; // clear flag } else*/ if (TMR2IF) { pwm_cycle++; if (pwm_cycle == 0 && pwm_value_0) pwm_out_0 = 1; // cycle start, make sure pwm is nonzero if (pwm_cycle == 0 && pwm_value_1) pwm_out_1 = 1; // turn outputs off after their duty has passed if (pwm_cycle > pwm_value_0) pwm_out_0 = 0; if (pwm_cycle > pwm_value_1) pwm_out_1 = 0; TMR2 = 0xe0; // reset timer this is about 500 hz TMR2IF = 0; // clear flag } int_restore_registers } /* end of interrupt service routine */ /************************** ** End of interrupt Code ** **************************/ /*********************** ** Serial functions ** ***********************/ /* Set the serial port to 9600 8N1 */ void setup_serial(void) { // SPBRG = 129; // divisor for 9600 baud SPBRG = 64; // divisor for 19200 baud TXSTA = 0b.0010.0100; // transmitt enable, high speed RCSTA = 0b.1001.0000; // serial enable char ch = RCREG; // flush buffer ch = RCREG; ch = RCREG; } /* end of setup serial */ /* sends a byte to the serial port */ void send_serial(unsigned char byte) { open_collector_0 = 1; while (!TRMT); // wait until buffer is empty TXREG = byte; // send the byte open_collector_0 = 0; } /* end of send byte */ /* sends a byte to the serial port */ unsigned char get_serial(void) { while (!RCIF); return(RCREG); } /* end of send byte */ /* sends 4 bits in hex format */ void send_nibble( unsigned char n ) { if (n > 9) send_serial(n - 10 + 'A'); else send_serial(n + '0'); } /* end of send_nibble */ /* sends a byte as hex */ void send_hex(unsigned char d) { send_nibble(d >> 4); send_nibble(d & 0xf); } /* end of send_hex */ /* sends a byte as hex */ void linefeed( void ) { send_serial(0x0d); send_serial(0x0a); } /* end of send_hex */ /* gets a hex digit from the serial port */ unsigned char get_hex_nibble( void ) { unsigned char c = get_serial(); // force upper case if (c > '9') { c &= 0xdf; return(c - 'A' + 10); } else return(c - '0'); } /* end of get_hex_nib */ /* gets a byte in hex from the serial port */ unsigned char get_hex(void) { unsigned char val; val = get_hex_nibble() << 4; return(val + get_hex_nibble()); } /* end of get_hex */ /* sends a string to the serial port */ void send_string(const char *s, unsigned char line_feed) { unsigned char i=0; while (s[i]) { send_serial(s[i]); i++; } if (line_feed) linefeed(); } /* end send_string */ /***************************** ** End of serial functions ** *****************************/ /******************* ** Initialization ** *******************/ /* system startup */ void startup( void ) { PORTA = 0; // clear ports PORTB = 0; PORTC = 0; PORTD = 0; PORTE = 0; TRISA = 0b.1111.1111; // all inputs TRISB = 0b.0000.0001; // bit 0 is digital in 2, bit 3 is led1 TRISC = 0b.1000.0000; // bit 7 is rx TRISD = 0b.1100.0000; // D6, D7 radio in, all others digital out TRISE = 0b.0000.0111; // E0-E2 digital in // setup a/d ADCON1 = 0b.1000.0010; // RE0, 1, 2 : digital RA0,1,2,3,5 : analog ADCS1 = 1; // Fosc/32 clocl ADCS0 = 0; ADON = 1; // turn on converter // ADCON0 = 0b.1000.0001 | mask; // clear core values before interrupts are turned on int_mode = 0; // finite state machine for servo control cur_servo = 0; // current servo number pwm_cycle = 0; // state of pwm system pwm_value_0 = 0; pwm_value_1 = 0; GIE = 1; // interrupts allowed PEIE = 1; // enable periphial interrupts // setup timer 0 OPTION = 4; // prescaler divide by 16 T0CS = 0; // internel clock TMR0 = 0; // clear the timer T0IE = 1; // enable interrupt for timer 0 // setup timer 1 TMR1L = 0; // reset timer TMR1H = 0; TMR1ON = 0; // shut off timer to modify values T1CKPS1 = 1; // setup prescaler T1CKPS0 = 0; T1OSCEN = 0; // shut off oscillator TMR1CS = 0; // internal clock // TMR1IE = 1; // enable interrupt TMR1ON = 1; // turn timer on TMR1IF = 0; // clear interrupt flag TMR2 = 0xe0; TMR2ON = 1; // enable timer 2 TMR2IE = 1; servo_pos[0] = 0x80; // setup inital servo positions servo_pos[1] = 0x80; servo_pos[2] = 0x80; servo_pos[3] = 0x80; servo_pos[4] = 0x80; servo_pos[5] = 0x80; } /* end of startup */ /************************** ** End of Initialization ** **************************/ /*********************** ** Utility functions ** ***********************/ /* return the state of the radio line passed as a parameter */ unsigned char get_radio_state(unsigned char n) { if (n == 0) return(radio_in_0); else if (n == 1) return(radio_in_1); else return(0); } /* end of get_radio_state */ /* return the position of a radio ipout */ // // The Radio may not be turned on or connected // A timeout system is in place to prevent locking // unsigned char read_radio( unsigned char n) { TMR1H = 0; TMR1L = 0; while (get_radio_state(n) && TMR1H < 0x80); // wait for end of current pulse if (TMR1H >= 0x80) return(0); TMR1H = 0; TMR1L = 0; while (!get_radio_state(n)&& TMR1H < 0x80); // wait for start of pulse if (TMR1H >= 0x80) return(0); // read time TMR1H = 0; TMR1L = 0; while (get_radio_state(n) && TMR1H < 0x80); // wait for end of pulse if (TMR1H >= 0x80) return(0); unsigned char tl = TMR1L; unsigned char th = TMR1H; unsigned char v = tl >> 4; v |= th << 4; return(v); } /* end of read_radio */ /* general use delay function */ void delay(unsigned char time) { TMR1H = 255 - time; while(TMR1H); } /* end of delay */ /****************************** ** End of Utility functions ** ******************************/ /**************************************** ** Serial command interface functions ** ****************************************/ /* deal with an led command */ void led_command( void ) { unsigned char n = get_hex_nibble(); // get led number unsigned char v = get_hex_nibble(); // get led number if (v > 1) return; // range check if (n == 0) led0 = v; else if (n == 1) led1 = v; } /* end of led_command */ /* deal with digital out command */ void digital_out_command ( void ) { unsigned char n = get_hex_nibble(); // get output number if (n > 5) return; // range check unsigned shift = 1 << n; // create bit mask unsigned char v = get_hex_nibble(); // get value (on or off) if( v == 0) { shift = 255 - shift; // and mask PORTD &= shift; } if (v == 1) PORTD |= shift; // or mask } /* end of digital_out_command */ /* deal with set servo command */ void servo_pos_command ( void ) { unsigned char n = get_hex_nibble(); // get servo number if (n > 7) return; // range check unsigned char p = get_hex(); // compiler workaround servo_pos[n] = p; } /* end of servo_pos_command */ /* dead with set open collector command */ void open_collector_command ( void ) { unsigned char n = get_hex_nibble(); // get output number if (n > 1) return; // range check unsigned char v = get_hex_nibble(); // get state if (n == 0) open_collector_0 = v; else if (n == 1) open_collector_1 = v; } /* end of open_collector_command */ /* deal with set pwm command */ void set_pwm_command ( void ) { unsigned char n = get_hex_nibble(); // get the output number unsigned char v = get_hex(); // get duty if (n > 1) return; // range check if (n == 0) pwm_value_0 = v; else if (n == 1) pwm_value_1 = v; } /* end of set pwm command */ /* deal with read radio command */ void read_radio_command( void ) { unsigned char n = get_hex_nibble(); // get channel number if (n > 1) return; // range check if (n == 0) send_hex(read_radio(0)); else if (n == 1) send_hex(read_radio(1)); } /* end of read_radio_command */ /* return the valur of a digital input */ bit get_digital_value(unsigned char n) { if (n == 0) return(digital_in_0); else if (n == 1) return(digital_in_1); else if (n == 2) return(digital_in_2); else if (n == 3) return(digital_in_3); else if (n == 4) return(digital_in_4); return(0); } /* end of get_digital_value */ /* deal with read analog input command */ void read_analog_command ( unsigned char ch, unsigned pack ) // if pack is set, we send as normal // binary and pack D[n] into the msb { if (ch > 4) return; // range check unsigned int mask = ch << 3; // create mask for channel ADCON0 = (ADCON0 & 0b.1100.0111) | mask; // mask off channel bits, and set new channel delay(0x08); GO = 1; while(GO); // select the next channel so the next hold cap can charge during the serial delay unsigned char next_ch = ch + 1; if (next_ch > 4) next_ch = 0; mask = next_ch << 3; ADCON0 = ADCON0 & 0b.1100.0111 | mask; // mask off channel bits, and set new channel // send as normal binary with D[n] packed into MSB if (pack) { mask = get_digital_value(ch); mask <<= 7; send_serial(ADRESH | mask); send_serial(ADRESL); } else { // send analog values send_hex(ADRESH); send_hex(ADRESL); } } /* end of read analog inputs */ /* deal with read digital input command */ void digital_in_command ( void ) { unsigned char n = get_hex_nibble(); // get input number if (n > 4) return; if (get_digital_value(n)) send_serial('1'); else send_serial('0'); } /* end of digital_in_command */ void get_all_command() { int i; for (i = 0; i < 5; i++) read_analog_command (i, 1); } /* read all outputs at the same time */ void set_all_command() { // get servo positions servo_pos[0] = get_serial(); servo_pos[1] = get_serial(); servo_pos[2] = get_serial(); servo_pos[3] = get_serial(); servo_pos[4] = get_serial(); servo_pos[5] = get_serial(); // get pwm pwm_value_0 = get_serial(); pwm_value_1 = get_serial(); // get digital outputs unsigned char digital = get_serial(); PORTD = digital & 0x3f; // strip upper bits digital = get_serial(); // decode other if (digital & 0x01) led0 = 1; else led0 = 0; if (digital & 0x02) led1 = 1; else led1 = 0; if (digital & 0x04) open_collector_0 = 1; else open_collector_0 = 0; if (digital & 0x08) open_collector_1 = 1; else open_collector_1 = 0; } /* end of set_all_command */ /*********************************************** ** End of serial command interface functions ** ***********************************************/ /* main function */ void main ( void ) { startup(); delay(0xff); // wait for serial to stabilize setup_serial(); linefeed(); // make sure start message is on the same line // infinte loop while (1) { char c = get_serial(); // wait for serial command // parse command if (c == 'c') startup(); else // C Clear to boot values if (c == 'l') led_command(); else // L Set led stateLed if (c == 'd') digital_out_command(); else // D Set digital out state if (c == 's') servo_pos_command (); else // S Set servo pos if (c == 'o') open_collector_command(); else // O Set open collector output if (c == 'p') set_pwm_command (); else // P Set PWM duty if (c == 'r') read_radio_command(); else // R Get radio if (c == 'a') read_analog_command (get_hex_nibble(), 0); else // A Get analog input if (c == 'i') digital_in_command(); else // I Get digital input if (c == 'g') get_all_command(); else // I Get digital input if (c == 't') set_all_command(); else // I Get digital input if (c == '.') send_serial('.'); } } /* end of main */