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:
// 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