Continuing from previous part 1 (rx) , part 2 (tx) , part 3 (different protocol) and part 4 (other sensor) .
Now with sources! This was written for 32bit STM32 MCU, but it should be relatively easily portable to any system (even 16-bit with fixing type definitions). Timing uses RTC's SSR (sub-second counter), but any timer should be usable instead as well.
This obviously isn't drop-in solution, but should be fairly easily to tweak to suit your needs.
// PWM define struct rf433_pwm_def { unsigned int rf_ssr0; unsigned int rxValue; unsigned char rxState; unsigned char rxLen; unsigned char minHeader; // minimum "header" length, 11 unsigned char minLen0, maxLen0; // minimum and maximum period of 0 bit unsigned char minLen1, maxLen1; // minimum and maximum period of 1 bit unsigned char minPos,maxPos; // min/max positive pulse length }; struct rf433_ppm_def { unsigned int rf_ssr0; unsigned long rxValue; unsigned char rxState; unsigned char rxLen; unsigned char rxExpectLength; unsigned char minHi,maxHi; // min/max 3..5 unsigned char minLen0, maxLen0; // minimum and maximum period of 0 bit; 13..17 unsigned char minLen1, maxLen1; // minimum and maximum period of 1 bit; 29..33 }; struct rf433_pwm_def rf_pwmstate; struct rf433_ppm_def rf_ppmstate1; // 1900 / 3800us low pulses with 560us high pulse in between struct rf433_ppm_def rf_ppmstate2; // 940 / 1900us low pulses with 520us high pulse in between ...
// Default values for receiver states // pwm receiver, with 380 and 1140us bit pulse widths rf_pwmstate.rxState = 0; rf_pwmstate.minHeader = 11; rf_pwmstate.minLen0 = 1; rf_pwmstate.maxLen0 = 5; rf_pwmstate.minLen1 = 8; rf_pwmstate.maxLen1 = 13; rf_pwmstate.minPos = 10; rf_pwmstate.maxPos = 15; // ppm receiver 1; high pulse 520us ; low pulses 1900 and 3800us rf_ppmstate1.rxState = 0; rf_ppmstate1.minHi = 3; rf_ppmstate1.maxHi = 5; rf_ppmstate1.rxExpectLength = 32; rf_ppmstate1.minLen0 = 13; rf_ppmstate1.maxLen0 = 17; rf_ppmstate1.minLen1 = 29; rf_ppmstate1.maxLen1 = 33; // ppm receiver 2; high pulse 520us; low pulses 950 and 1900 us rf_ppmstate2.rxState = 0; rf_ppmstate2.minHi = 3; rf_ppmstate2.maxHi = 5; rf_ppmstate2.rxExpectLength = 36; rf_ppmstate2.minLen0 = 5; rf_ppmstate2.maxLen0 = 9; rf_ppmstate2.minLen1 = 13; rf_ppmstate2.maxLen1 = 17; ...Some helper functions:
/* -------------------------------------------- * Actual implementation left to reader. * * Calculate difference between two timer values, taking care of single overflow. */ signed int rtcSsrDiff(signed int first, signed int second) { signed int d = second-first; if (d < 0) d += SSR_MAX; // counter wrap-around in case of overflow return d; } /* ---------------------------------------------- * Read timer value. */ signed int rtcReadSsr() { // implement with your customer timer. Preferably direct hardware counter, software counter in timer interrupt handler might cause some issues. // Counter period should be at least twice as long than the maximum period you expect to measure (i.e. single low/high pulse length in PWM/PPM), // If possible at least a decace or longer (that is, if you expect to measure 6ms pulse, counter should have wrap-around period of at least 60+ms) return 0; }PWM handler.
/* ----------------------------------------------------- * PWM receive handler, using state/definitions in 'def' * ssr : current timer value * iostate : state of io pin * def : pointer to receiver definitions */ void rf433_PWM_handler(unsigned int ssr, unsigned char ioState, struct rf433_pwm_def *def) { switch (def->rxState) { case 0 : // use low signal with minimum of 5000us (40-ish ticks as start condition) (as per pwm 1 definitions) if (ioState == 0) // falling edge { def->rf_ssr0 = ssr; def->rxState = 1; } break; case 1 : { // rising edge, after short quiet period signed int d = rtcSsrDiff(ssr, def->rf_ssr0); // Calculate low period time. This handles also overflows (e.g. timer going from 8190 to 2) if (d < def->minHeader) // too short "header", ignore and return to idle { def->rxState = 0; break; } def->rf_ssr0 = ssr; // first bit start time } // fall-through to start parse case 2 : { def->rf_ssr0 = ssr; def->rxState = 3; def->rxValue = 0; def->rxLen = 0; break; } case 3 : { signed int d = rtcSsrDiff(ssr, def->rf_ssr0); if (ioState == 0) // falling edge - Determine received pulse width and parse as bit { // 2..5 (380us nominal) or 8..12 (1140us nominal) (pwm1) if ((d >= def->minLen0) && (d <= def->maxLen0)) { def->rxValue <<= 1; // 0-bit def->rxLen++; } else if ((d >= def->minLen0) && (d <= def->maxLen0)) { def->rxValue = (def->rxValue << 1) | 1; // 1-bit def->rxLen++; } else { // bad positive pulse length, drop to idle def->rxState = 0; } if (def->rxLen > 31) // too long data, drop to idle (24-bit values expected) def->rxState = 0; } else { // next positive pulse should be at 1500us (12 nominal; 10 .. 15) (as per pwm 1 definitions) // in case of bad signal drop to idle if ((d < def->minPos) || (d > def->maxPos)) { def->rxState = 0; } def->rf_ssr0 = ssr; } if ((def->rxState == 0) || (def->rxLen == 24)) // stopped (error) or received expected data; parse { // unsigned int i,w; if (def->rxLen >= 20) // more than 20 bits of received data; parse it { debugWriteInt("rcv pwm len=", def->rxLen); debugWriteHex("rcv pwm val : ", def->rxValue); // TBD: handle received value : def->rxValue ) } def->rxState = 0; // drop to idle } break; } default: def->rxState = 0; } }PPM handler.
/* ----------------------------------------------------- * PPM receive handler, using state/definitions in 'def' * ssr : timer value * iostate : state of io pin * def : pointer to receiver definitions */ void rf433_PPM_handler(unsigned int ssr, unsigned char ioState, struct rf433_ppm_def *def) { /* Temperature sensors appear to use PPM modulation: (ppm 1 definitions) * -signal drops * -low period of: * 1,9ms=0 - 3,8ms=1 (or other way around?) repeat 32 times; very short low period to end * 15,5 ticks (accept 14,15,16) 31 ticks (accept 30,31,32) * -high pulse of 560us, low period repeats * 4,5 ticks; accept 3,4,5 */ switch (def->rxState) { case 0 : if (ioState == 0) { // signal dropped. mark time def->rf_ssr0 = ssr; def->rxState = 1; def->rxLen = 0; def->rxValue = 0; } break; case 1 : if (ioState == 1) // pulse risen { signed int d = rtcSsrDiff(ssr, def->rf_ssr0); // Low period time in ticks def->rf_ssr0 = ssr; if ((d >= def->minLen0) && (d <= def->maxLen0)) // 1900us approx (ppm 1) { // treat as zero bit; shift value up def->rxValue <<= 1; def->rxLen++; def->rxState = 2; } else if ((d >= def->minLen1) && (d <= def->maxLen1)) // 3800us approx (ppm 1) { def->rxValue = (def->rxValue << 1) | 1; def->rxState = 2; def->rxLen++; } else { // bad pulse period, skip to parse def->rxState = 3; } if (def->rxLen >= def->rxExpectLength) { def->rxState = 3; // enough bits, parse data break; } } case 2 : if (ioState == 0) // signal dropped { signed int d = rtcSsrDiff(ssr, def->rf_ssr0); // Low period time in ticks if ((d >= def->minHi) && (d <= def->maxHi)) // high period of ~530us (ppm 1) { def->rxState = 1; // next bit def->rf_ssr0 = ssr; // start new time count from this point } else { def->rxState = 3; // bad length; move to end of transmit } } break; } // End of receive. Done outside switch so we can process this immediately and start waiting for next word. if (def->rxState == 3) { if (def->rxLen >= 20) { debugWriteHex64("rval ppm=", def->rxValue); // received value in rxValue. Handling TBD. } def->rxState = 0; // reset receiver for next value } }
Main I/O interrupt handler.
/* -------------------------------------------------------- * RX interrupt handler. This is set to interrupt on both rising and falling edge of receive pin - F14 in this case. */ void rf433_rx_interrupt_handler() { unsigned int ioState = ioGetInput(PORTF | 14); // Read RF data line. "Idle" low, expecting high pulses. // RTC SSR (sub-second counter) is (in my setup) set to run at 1/8192s resolution, i.e. 122us per tick. // Other hardware timers can also be used for timing, as long as they're free-running (to specific value) // and can be read reliably. // For this system to run reliably, timing resolution should be at least 200us (5000 Hz), preferably better. signed int ssr; rtcReadSsr(&ssr); rf433_PWM_handler(ssr,ioState, &rf_pwmstate); rf433_PPM_handler(ssr,ioState, &rf_ppmstate1); rf433_PPM_handler(ssr,ioState, &rf_ppmstate2); // This is attempt to catch valid input data from noisy imput signals if one does not have access to transmitter // (for example outside temperature sensor I have that is too well sealed to safely open and close again). // This continuously measures period of low signals (high periods ignored), storing those in FIFO and looks for // low periods of almost same length in recent data. If enough same-ish low periods are found, this triggers // output signal that can be used to trigger scope, with other scope channel probing receiver output data. // From there data can be analyzed so above receivers can be adapted to received data. #if 0 if (ioState == 0) { rf_ssr0 = ssr; // start of low pulse ioPin(PORTF | 13, 0); } else { // rising signal signed int d = rtcSsrDiff(ssr, rf_ssr0); // Low period time in ticks const int FIFO_LEN = 12; if (rf_len < FIFO_LEN) { rf_rxlen[rf_len++] = d; // first, fill fifo with data } else { // Fifo full. Check if recent data has matches with this pulse. unsigned int i; if (d > 4) // ignore "too short" (less than 600-ish us here) pulses completely. { unsigned int j = 0; for (i = 0; i < FIFO_LEN; ++i) { if (abs( ((signed)rf_rxlen[i])-d ) < 2) ++j; } if (j > 6) { debugWriteInt("possible signal, reps=", j); debugWriteInt(" pulse len=", d); ioPin(PORTF | 13, 1); rf_len = 0; // clear fifo and start over again } } if (rf_len) // no trigger; roll fifo { for (i = 0; i < FIFO_LEN-1; ++i) rf_rxlen[i] = rf_rxlen[i+1]; rf_rxlen[FIFO_LEN-1] = d; } } } #endif }
Have fun!
Ei kommentteja:
Lähetä kommentti