Dangerous Prototypes

In development => Project logs => Topic started by: arhi on May 08, 2011, 08:26:10 pm

Title: rc servo scan with pic16F690
Post by: arhi on May 08, 2011, 08:26:10 pm
I'm preparing to start some large project and decided to use my 10ch futaba remote control for it. Now, since this is rather new (FASST 10CG) remote control there is no digital signal on the receiver that I can tap into. The receiver do have some "digital lines" and can work with futaba HUB but I failed to find any reliable data on it and when attached scope to all the outputs of the received I was unable to make it peep anything but a simple PWM signal.

What I measured is that periode length is 14ms, that min signal length (calibrated fully down pushed fully down) is 970us and that max signal lenght (calibrated fully up, pushed fully up) is 2120us, while center point is 1520us.

I looked trough my drawer to find some pics and I still have some 16F690's so I decided to use it... it has interrupt on change both on A and B port + it has internal oscillator and bunch of analog pins. I decided to use them all. Read data and send it over serial port.

There are few issues.
1. hw serial TX pin is on RB7 and this is also the on-change pin. PICC also allows me to use sw rs232 protocol but with it either you want to disable interrupts while sending over rs232 or you can have "doggy" data going out. If you disable interrupts you will miss some events on the pin changes - not a big deal but "next" read will probably be wrong as you will miss half of the event so you'd have to implement some max/min error checking. I did some tests and 90% of characters went over ok without disabling interrupt so if you want to implement some fail safe reading on the client it could be ok, but I think sacrificing this one interrupt pin would be ok to use hw usart. Anyhow there is define at the beginning of the code that allows you to use hw or sw usart

2. RA4 and RA5 are interrupt on change pins but also OSC1/OSC2 so if you want to use external oscillator - you have to forget about them as your "on change" ... measurements are pretty accurate (0.5us) with internal 8MHz oscillator if you believe in the oscillator itself being accurate. In my project I will start using internal oscillator as I think it will not make too many issues for me - but I will switch to external if it looks like internal is making problems.

3. DAC takes time ... since there are not enough on change pins I'm using 6 remaining analog inputs. One could use them for some external sensory but I will use them in a way that PWM output from RC receiver will go trough simple 10k/10uF RC filter and then into some op-amp to swing it 0-5V so I can read pwm output values as analog value. Precision here will be way lower then on digital inputs but since my remote has few buttons that are actually with only few positions (2 or 3 positions) that will be more then enough. Unfortunately reading DAC on pic takes time, especially when you change channel, so there is also define at beginning of the code where you can disable analog section of the program.

Finally, here's the code:
Code: [Select]
// File servoDecoder.c
//
// Measures the signal length and sends it via serial port
//
// Author Bogdan Kecman <bogdan.kecman at crsn dot rs>
// Licence: I don't care - aka do what ever you like with the code, just don't bother me about it but it would be cool if you credit my work if you use it
//

// comment out any of these defines if you do not want it
#define __USARTHW__
#define __HSOSC__
#define __ANALOG__

#include <16F690.h>
#device adc=10
#FUSES NOWDT, NOPROTECT, NOBROWNOUT, NOMCLR, NOCPD, NOPUT, NOIESO, NOFCMEN

#ifdef __HSOSC__
  #FUSES HS
  #use delay(clock=20000000)
  #define RESOLUTION 0.2
#else
  #FUSES INTRC_IO
  #use delay(clock=8000000)
  #define RESOLUTION 0.5
#endif

#ifdef __USARTHW__
  #use rs232(baud=14400,parity=N,bits=8,UART1)
#else
  #use rs232(baud=14400,parity=N,xmit=PIN_C4,rcv=PIN_C5,bits=8)
#endif

#use fast_io(A)
#use fast_io(B)
#use fast_io(C)
#case

//volatile unsigned int16 brojac;


//servo channel values
volatile unsigned int16 S0; //A0
volatile unsigned int16 S1; //A1
volatile unsigned int16 S2; //A2
volatile unsigned int16 S3; //A3

#ifndef __HSOSC__
  volatile unsigned int16 S4; //A4
  volatile unsigned int16 S5; //A5
#endif

volatile unsigned int16 S6; //B4
volatile unsigned int16 S7; //B5
volatile unsigned int16 S8; //B6
#ifndef __USARTHW__ 
  volatile unsigned int16 S9; //B7
#endif

volatile unsigned int16 start0;
volatile unsigned int16 start1;
volatile unsigned int16 start2;
volatile unsigned int16 start3;

#ifndef __HSOSC__
  volatile unsigned int16 start4;
  volatile unsigned int16 start5;
#endif

volatile unsigned int16 start6;
volatile unsigned int16 start7;
volatile unsigned int16 start8;

#ifndef __USARTHW__
  volatile unsigned int16 start9;
#endif

volatile unsigned int8 stateA;
volatile unsigned int8 stateB;
volatile unsigned int8 overA;
volatile unsigned int8 overB;
volatile unsigned int16 T;

#ifdef __ANALOG__
  //analog pins
  volatile unsigned int16 A0; //C0 (AN4)
  volatile unsigned int16 A1; //C1 (AN5)
  volatile unsigned int16 A2; //C2 (AN6)
  volatile unsigned int16 A3; //C3 (AN7)
  volatile unsigned int16 A4; //C6 (AN8)
  volatile unsigned int16 A5; //C7 (AN9)
#endif



#int_RB
void  RB_isr(void) {
 
  T = get_timer1();
  overB = stateB ^ input_b();
  stateB = input_b();
 
  if (overB == 0) goto RA; //no changes on portB
 
#ifndef __USARTHW__ 
  if (bit_test(overB,7)){ //B7 S9
    if (bit_test(stateB,7)){
      start9 = T;
    }else{
      S9 = T - start9; //I can get here +1 if it wraps around zero but it can be ignored
    }
  }
#endif

  if (bit_test(overB,6)){ //B6 S8
    if (bit_test(stateB,6)){
      start8 = T;
    }else{
      S8 = T - start8;
    }
  }

  if (bit_test(overB,5)){ //B5 S7
      if (bit_test(stateB,5)){
      start7 = T;
    }else{
      S7 = T - start7;
    }
  }

  if (bit_test(overB,4)){ //B4 S6
    if (bit_test(stateB,4)){
      start6 = T;
    }else{
      S6 = T - start6;
    }
  }




// no INT_RA on 16F690 but changes on portA are treated like changes on portB
// (RABIF - single interrupt flag for both is used)
RA:


/* 
}

#int_RA
void  RA_isr(void) {
*/

 
  T = get_timer1();
  overA = stateA ^ input_a();
  stateA = input_a();
 
  if (overA == 0) return; //no changes on portA
 


  if (bit_test(overA,0)){ //A0 S0
    if (bit_test(stateA,0)){
      start0 = T;
    }else{
      S0 = T - start0;
    }
  }

  if (bit_test(overA,1)){ //A1 S1
    if (bit_test(stateA,1)){
      start1 = T;
    }else{
      S1 = T - start1;
    }
  }

  if (bit_test(overA,2)){ //A2 S2
    if (bit_test(stateA,2)){
      start2 = T;
    }else{
      S2 = T - start2;
    }
  }

  if (bit_test(overA,3)){ //A3 S3
    if (bit_test(stateA,3)){
      start3 = T;
    }else{
      S3 = T - start3;
    }
  }

#ifndef __HSOSC__

  if (bit_test(overA,4)){ //A4 S4
    if (bit_test(stateA,4)){
      start4 = T;
    }else{
      S4 = T - start4;
    }
  }

  if (bit_test(overA,5)){ //A5 S5
    if (bit_test(stateA,5)){
      start5 = T;
    }else{
      S5 = T - start5;
    }
  }
#endif

}



void main()
{
#ifdef __ANALOG__
  setup_adc_ports(sAN4|sAN5|sAN6|sAN7|sAN8|sAN9|VSS_VDD);
  #ifdef __HSOSC__
    setup_adc(ADC_CLOCK_DIV_32);
  #else
    setup_adc(ADC_CLOCK_INTERNAL);
  #endif
#else
  setup_adc_ports(NO_ANALOGS);
  setup_adc(ADC_OFF);
#endif

  setup_spi(SPI_SS_DISABLED);
  setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
  setup_timer_1(T1_INTERNAL|T1_DIV_BY_1);
  setup_timer_2(T2_DISABLED,0,1);
  setup_comparator(NC_NC_NC_NC);
#ifndef __HSOSC__
  setup_oscillator( OSC_8MHZ );
#endif
 
  start0 = 0;
  start1 = 0;
  start2 = 0;
  start3 = 0;
#ifndef __HSOSC__
  start4 = 0;
  start5 = 0;
#endif
  start6 = 0;
  start7 = 0;
  start8 = 0;
#ifndef __USARTHW__ 
  start9 = 0;
#endif

  stateA = 0;
  stateB = 0;
  //brojac = 0;

  set_tris_a(0xFF);
  *0x96 = 0xff;      //IOCA

#ifdef __USARTHW__
  set_tris_b(0xEF);  //B7 rs232 TX
  set_tris_c(0xFF);
  *0x116 = 0x7f;    //IOCB (B7 rs232 TX)
#else
  set_tris_b(0xFF);
  set_tris_c(0xEF);  //C4 - rs232 TX
  *0x116 = 0xff;    //IOCB
#endif

  enable_interrupts(INT_RB);
  //enable_interrupts(INT_RA);
  enable_interrupts(GLOBAL);
 
while1:

    //RESOLUTION is T1 resolution in microseconds   
    printf("%f, %f, %f, %f, ", S0 * RESOLUTION, S1 * RESOLUTION, S2 * RESOLUTION, S3 * RESOLUTION);
#ifndef __HSOSC__
    printf("%f, %f, ", S4 * RESOLUTION, S5 * RESOLUTION);
#endif
    printf("%f, %f, ", S6 * RESOLUTION, S7 * RESOLUTION);
    printf("%f", S8 * RESOLUTION);
#ifndef __USARTHW__           
    printf(", %f", S9 * RESOLUTION);
#endif

#ifdef __ANALOG__
    set_adc_channel(4);
    delay_ms(1);
    A0 = read_adc();
    printf(", %Lu", A0);
   
    set_adc_channel(5);
    delay_ms(1);
    A1 = read_adc();
    printf(", %Lu", A1);

    set_adc_channel(6);
    delay_ms(1);
    A2 = read_adc();
    printf(", %Lu", A2);

    set_adc_channel(7);
    delay_ms(1);
    A3 = read_adc();
    printf(", %Lu", A3);

    set_adc_channel(8);
    delay_ms(1);
    A4 = read_adc();
    printf(", %Lu", A4);

    set_adc_channel(9);
    delay_ms(1);
    A5 = read_adc();
    printf(", %Lu", A5);
#endif

    printf("nr");
    goto while1;
}

it uses CSC PICC and fills up only 50% of the pic .. so plenty more room for other stuff in .. (like checking if read data is in range etc..). There is no PCB/Schematic as you'd want to add this pic to a "project" where you need it to decode data from receiver and send data to your "brain" mcu via serial line ... I was thinking also to add i2c but I needed the lines for other stuff ...

Hope you can use it :D

( ! ) Fatal error: Uncaught exception 'Elk_Exception' with message 'Please try again. If you come back to this error screen, report the error to an administrator.' in /var/www/dangerousprototypes/forum/sources/database/Db-mysql.class.php on line 696
( ! ) Elk_Exception: Please try again. If you come back to this error screen, report the error to an administrator. in /var/www/dangerousprototypes/forum/sources/database/Db-mysql.class.php on line 696
Call Stack
#TimeMemoryFunctionLocation
10.00872049432session_write_close ( )...(null):0
20.00902181008ElkArte\sources\subs\SessionHandler\DatabaseHandler->write( )...(null):0
30.00902181784Database_MySQL->query( ).../DatabaseHandler.php:119
40.05442320504Database_MySQL->error( ).../Db-mysql.class.php:273