Shop OBEX P1 Docs P2 Docs Learn Events
Reading RC receiver pulses (using SX/B) — Parallax Forums

Reading RC receiver pulses (using SX/B)

RockyBRockyB Posts: 11
edited 2008-08-01 07:21 in General Discussion
I know this sounds so simple but try as I might (2 days now), I have been unable to read receiver channels. I am left feeling like the monkey at the monolith in 2001! If I could get a byte variable representing channel position 1-2ms I would be pleased as punch. Anyone know the proper pulsin parameters and maybe pin configuration to do this?

Thanks in advance,
Rocky

Comments

  • ZootZoot Posts: 2,227
    edited 2008-07-30 22:31
    
    RCchan1 PIN RA.0 INPUT
    
    RCx VAR Byte ' for storing input
    WATCH RCx  
    
    Get_RC_Pulse:
       IF RCchan1 = 1 THEN Get_RC_Pulse ' wait for pin to go low
       PULSIN RCchan1, 1, RCx ' measure 10 - 2550 us pulse, 0 = 0ms, 100 = 1ms, 200 = 2ms, 150 = 1.5ms (neutral)
       ' now RCX approx 100-200 (if there are actual pulses). You could adjust it so 128 = neutral...., etc
       GOTO Get_RC_Pulse
    
    



    Personally, I would poll RC receiver pin(s) in an interrupt, so that the last pulsewidth is always available "in the background", but the above should work. You might want to perform so error checking on the received value in case the pulsewidth is too long or too short (which could indicate that the transmitter is not on, or there is noise, etc).

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    When the going gets weird, the weird turn pro. -- HST

    1uffakind.com/robots/povBitMapBuilder.php
    1uffakind.com/robots/resistorLadder.php
  • RockyBRockyB Posts: 11
    edited 2008-07-30 23:12
    blush.gif Thanks Zoot, that was exactly the code I was trying, not knowing if I was in left field or not. Loaded it again and added another var that I load with RCx if it's non-zero before looping back for another poll. Bang, I am now getting values instead of what seemed to be nothing but zeros as before.
    I really was loosing hair over this one. My son's "drive by wire" (with daddy override and ultra sonic collision prevention for and aft) electric car can now be completed.

    Ty thanks you too
  • RockyBRockyB Posts: 11
    edited 2008-07-31 15:39
    I am still not quite where I want to be with this all. I "cookbook" grabbed a 4 channel PWM interrupt from another post, Jon Williams's handywork I think. Following your advice Zoot, I have made a feeble attempt at rolling the radio channel polling into the ISR. With this code I scaled the pulsin to happen once every 1000 cycles of the ISR. The radio is responsive enough here but with a scope on the PWM out to the motor h-bridges there is glitching and some jerking in the motors. Here is the wayward ISR, I of course am open and begging for any suggestions...

    =========================================================================
    INTERRUPT NOPRESERVE 20_000_000
    ' =========================================================================

    inc PWM_Count
    IF MotorOut(0) > PWM_Count THEN
    Out1 = 1
    ELSE
    Out1 = 0
    ENDIF

    IF MotorOut(1) > PWM_Count THEN
    Out2 = 1
    ELSE
    Out2 = 0
    ENDIF

    IF MotorOut(2) > PWM_Count THEN
    Out3 = 1
    ELSE
    Out3 = 0
    ENDIF

    IF MotorOut(3) > PWM_Count THEN
    Out4 = 1
    ELSE
    Out4 = 0
    ENDIF
    inc rcrefresh
    if rcrefresh <> 1000 then
    goto over2
    endif
    rcrefresh = 0
    PULSIN Speedchan, 1, RCSpeed1
    IF RCSpeed1 < 1 THEN
    goto over1
    endif
    RCSpeed = RCSpeed1
    over1:
    PULSIN SteerChan, 1, RCSteer1
    IF RCSteer1 < 1 THEN
    goto over2
    endif
    RCSteer = RCSteer1
    over2:

    RETURNINT
  • JonnyMacJonnyMac Posts: 9,215
    edited 2008-07-31 17:01
    You really shouldn't use PULSIN (or other "high level" functions) in the interrupt; it's very likely that the function is taking so long that you're missing interrupts (an interrupt can't be interrupted). If you'll define your goal I'll see what I can do to come up with an ISR that works for you.

    [noparse][[/noparse]Edit] As an educational exercise for myself I wrote the attached program which can measure a high pulse in 10 microsecond increments using the interrupt. You'll see that it's pretty easy and with a bit of copy/paste/edit you can add more channels. Note that it takes a simple approach: you can enable pulse measurement on a channel by clearing a flag; that flag will be set when the measurement is done, and further measurements will be ignored until that flag is cleared. Due to the simplicity of the code you can actually start the measurement in middle of a pulse. To get around that you could add a line to prevent it:

    \ JB    Ch1In, $                ' wait while input high
    \ CLRB  ch1Ready                ' allow measurement
    

    Post Edited (JonnyMac) : 7/31/2008 5:45:57 PM GMT
  • ZootZoot Posts: 2,227
    edited 2008-07-31 17:46
    [noparse][[/noparse]edit -- JonnyMac edited his post right when I hit submit on mine, he set flags to indicate a pulse ready (as I knew he would), which I alluded to in my words below.]

    What JonnyMac said smile.gif

    What I meant, was you *could* "bit-bang" the pulsin in your ISR (which seems way too fast, btw, for what you doing; hardly any time for ISR work and it'll run on every other instruction from the main program, that along w/the pulsin in the ISR and I would expect jitter -- remember that pulsin is gonna site there and *wait* for the pulse for eons -- relatively). I would check out the Nuts N Volts column JonnyMac posted for motor control -- it has ready unroll PWM outs and the ISR ready serial code if this project needs to communicate w/the outside world. My little snippet below is all SX/B, not assembly, but the PWM is similar to JonnyMac's (older) posted methods.

    e.g.

    INTERRUPT 100_000  ' 10us per isr "tick"
    
    IF RCx = 1 THEN  ' if pulse is high, presume we're counting at whatever the ISR "tick" rate is, in this case 10us units
       INC RCxPwidth
    ENDIF
    
    IF RCy = 1 THEN  ' if pulse is high, presume we're counting at whatever the ISR "tick" rate is, in this case 10us units
       INC RCyPwidth
    ENDIF
    
    INC PwidModCtr1  ' when this rolls over to zero, pwm pin goes high
    IF PwidModCtr1 = 0 THEN
       PwidModOut1 = 1
       ' set the speed
       PwidModCtr1 = PwidSpeed1
    ELSE
       PwidModOut1 = 0
    ENDIF
    ' more channels if you need
    
    RETURNINT
    
    
    ' now the mainline is responsible for checking the values out of the interrupt and resetting and parsing them...
    
    Going_Going:
    
       PwidSpeed1 = 255 
    
       ' read the RC pulses
       IF RCxPwidth > 0 THEN ' must have received highgoing pulse edge or this would be 0
          IF RCx = 0 THEN ' but it went low! yer done w/this pulse
            RCxVal = RCxPwidth ' save the pulsewidth
            RCxPwidth = 0  ' reset pulsewidth counter
          ENDIF
       ENDIF
    
       IF RCyPwidth > 0 THEN ' must have received highgoing pulse edge or this would be 0
          IF RCy = 0 THEN ' but it went low! yer done w/this pulse
            RCyVal = RCyPwidth ' save the pulsewidth
            RCyPwidth = 0  ' reset pulsewidth counter
          ENDIF
       ENDIF
    
       GOTO Going_Going
    
    



    This is not an ideal way to do it, but maybe it makes things clear -- the interrupt is very short, just setting the PWM pins high when the "speed" from 0-255 rolls over. On rollover the desired PWM width is reloaded into the counter(s). The Recvr counters just measure the width of the high going pulse. A weakness here is that this isr presumes the mainline will check the pulsecounters often enough that the next pulse won't have started, but again I was going for clarity.

    The main line just blazes through checking for the falling edge of an RC pulse and setting the speed.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    When the going gets weird, the weird turn pro. -- HST

    1uffakind.com/robots/povBitMapBuilder.php
    1uffakind.com/robots/resistorLadder.php
  • JonnyMacJonnyMac Posts: 9,215
    edited 2008-07-31 19:17
    Here's another program to look at; it uses the ISR-based pulse measurement I showed earlier with Guenther Daubach's PWM code (page 291 of his book). When the pulse input is 100 to 150 the motor runs in reverse (100 is the fastest), when 150 to 200 it runs forward. Note that the SET_MOTOR1 subroutine takes care of setting the speed and direction controls for the output. As the span between stopped (150) and either end is only 50 units, the raw speed is multiplied by 5 (without using multiplication) to allow the PWM code to give you the fastest speed at the ends (100 and 200) -- note, though, that you still only have 50 forward speeds and 50 reverse speeds.
  • RockyBRockyB Posts: 11
    edited 2008-07-31 21:35
    Thanks again JonnyMac and Zoot,
    This last code looks the part. I being both an asm AND an SX/B moron, will of course need a little time to digest it's operation but at a glance it looks pretty slick.

    I will need to modify the PWM to pulse two different outputs for each of my two motors (one for each direction). Originally I was going to run 8 bit PWM in locked anti-phase by placing an inverter between the forward and reverse H-bridge inputs. This would have made a PWM output of 128 neutral, but I feared reduced battery life from current flowing at zero speed. My H bridges are in an ancient Vantec 80's vintage T03 bipolar based dual motor unit. I am using one of course to drive this little car forward and backwards and another to drive the steered wheels back and forth. I plan to close the loop on the steering gearhead with a 5K pot. (So here I go with more I need to learn) Also I am mounting a 10K joystick pot for the kid to drive with until he runs into danger where I will take control with the radio unit. I have an SRF08 I think is the model sonar I will want to at least place on the front so as to have the car stop before any collisions.

    I am off to the garage as I have tie-rods to mount and a chair to craft etc...

    Much thanks, with you guys "helping" (read "doing my hard work for me") I am going to be able to give little Ty a lot nicer surprise than I could do on my own.
    Is 15mons too young for a 1st car?
  • ZootZoot Posts: 2,227
    edited 2008-07-31 21:42
    Somebody said...
    Is 15mons too young for a 1st car?

    Yes smile.gif

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    When the going gets weird, the weird turn pro. -- HST

    1uffakind.com/robots/povBitMapBuilder.php
    1uffakind.com/robots/resistorLadder.php
  • RockyBRockyB Posts: 11
    edited 2008-07-31 21:53
    It is to have "daddy override" and a seat belt. (though It will be some time before the second 12v gel cell goes on, pushing the 24V gearheads to full speed)

    He already has one of those red Vee Duhb push cars and will run and jump into it if you walk anywhere near it! And this thing with a joystick he need only push it in the direction he wants to go, I think he will love it.

    Can never be too young, skinny or rich.
    :P
  • RockyBRockyB Posts: 11
    edited 2008-08-01 00:43
    Okay, I have modified (as best I can understand) the Pulse to PWM program by Jon Williams. I have botched something as when I watch my RC chan pulse values they are kinda just random. The PWM section seems to be ok with the three added outputs. I think I just messed up the pulse capture stuff. If someone gets just really bored could you please critique my modifications?

    ' =========================================================================
    '
    ' File...... Pulse_To_PWM.SXB
    ' Purpose...
    ' Author.... Jon Williams, EFX-TEK
    ' Copyright (c) 2008 EFX-TEK
    ' Some Rights Reserved
    ' -- see http://creativecommons.org/licenses/by/3.0/
    ' E-mail.... jwilliams@efx-tek.com
    ' Started...
    ' Updated...
    '
    '
    '
    ' Device Settings
    '

    DEVICE SX28, OSCXT2, TURBO, STACKX, OPTIONX, BOR42
    FREQ 20_000_000
    ID "PulsePWM"




    '
    ' I/O Pins
    '

    Mtr1Pwm PIN RB.0 OUTPUT ' Drive motor forward speed
    Mtr2Pwm PIN RB.1 OUTPUT ' Drive motor reverse speed
    Mtr3Pwm PIN RB.2 OUTPUT ' Steering motor right speed
    Mtr4Pwm PIN RB.3 OUTPUT ' Steering motor left speed

    Ch1In PIN RA.0 INPUT ' Drive motor RC chan
    Ch2In PIN RA.1 INPUT ' Steering motor RC chan

    '
    ' Constants
    '

    Yes CON 1
    No CON 0


    '
    ' Variables
    '

    flags VAR Byte
    isrFlag VAR flags.0
    ch1Active VAR flags.1 ' 1 = measuring now
    ch1Ready VAR flags.2 ' 1 = measurement done
    ch2Active VAR flags.3 ' 1 = measuring now
    ch2Ready VAR flags.4 ' 1 = measurement done

    chan1 VAR Byte ' pulse input width
    chan2 VAR Byte ' pulse input width
    m1Speed VAR Byte ' motor speed
    m1Acc VAR Byte ' acc for PWM
    m2Speed VAR Byte ' motor speed
    m2Acc VAR Byte ' acc for PWM
    m3Speed VAR Byte ' motor speed
    m3Acc VAR Byte ' acc for PWM
    m4Speed VAR Byte ' motor speed
    m4Acc VAR Byte ' acc for PWM

    tmpW1 VAR Word ' for subs/funcs
    tmpB1 VAR Byte

    watch chan1
    watch chan2
    watch m1speed
    watch m2speed
    watch m3speed
    watch m4speed

    ' =========================================================================
    INTERRUPT NOPRESERVE 100_000 ' 10 microseconds
    ' =========================================================================

    Mark_ISR:
    ASM
    BANK flags
    SETB isrFlag
    ENDASM


    ' Measure high-going pulse in 10 us increments
    ' -- clear ch1Ready bit to start measurement

    Ch1_Check:
    ASM
    JB ch1Ready, Ch1_Exit ' exit if already measured
    JB ch1Active, Ch1_Measure ' update if active
    JNB Ch1In, Ch1_Exit ' exit if no pulse

    Ch1_Init:
    SETB ch1Active ' we're measureing
    MOV chan1, #1 ' initialize width
    JMP Ch1_Exit

    Ch1_Measure:
    ADDB chan1, Ch1In ' update pulse timing
    MOVB ch1Active, Ch1In ' update status
    SB Ch1In ' exit if still measuring
    SETB ch1Ready ' mark measurement ready

    Ch1_Exit:
    ENDASM

    Ch2_Check:
    ASM
    JB ch2Ready, Ch2_Exit ' exit if already measured
    JB ch2Active, Ch2_Measure ' update if active
    JNB Ch2In, Ch2_Exit ' exit if no pulse

    Ch2_Init:
    SETB ch2Active ' we're measureing
    MOV chan2, #1 ' initialize width
    JMP Ch2_Exit

    Ch2_Measure:
    ADDB chan2, Ch2In ' update pulse timing
    MOVB ch1Active, Ch2In ' update status
    SB Ch2In ' exit if still measuring
    SETB ch2Ready ' mark measurement ready

    Ch2_Exit:
    ENDASM

    ' fixed period PWM by Guenther Daubach
    ' -- PWM frequency is 390 Hz @ 10 uS interrupt

    M1_PWM:
    ASM
    SETB Mtr1Pwm ' preset output
    CSB m1Acc, m1Speed ' if pwm level reached
    CLRB Mtr1Pwm ' clear output
    INC m1Acc ' increment accumulator
    MOV W, ++m1Acc ' check for max
    SNZ ' end reached?
    CLR m1Acc ' yes, reset
    ENDASM

    M2_PWM:
    ASM
    SETB Mtr2Pwm ' preset output
    CSB m2Acc, m2Speed ' if pwm level reached
    CLRB Mtr2Pwm ' clear output
    INC m2Acc ' increment accumulator
    MOV W, ++m2Acc ' check for max
    SNZ ' end reached?
    CLR m2Acc ' yes, reset
    ENDASM

    M3_PWM:
    ASM
    SETB Mtr3Pwm ' preset output
    CSB m3Acc, m3Speed ' if pwm level reached
    CLRB Mtr3Pwm ' clear output
    INC m3Acc ' increment accumulator
    MOV W, ++m3Acc ' check for max
    SNZ ' end reached?
    CLR m3Acc ' yes, reset
    ENDASM

    M4_PWM:
    ASM
    SETB Mtr4Pwm ' preset output
    CSB m4Acc, m4Speed ' if pwm level reached
    CLRB Mtr4Pwm ' clear output
    INC m4Acc ' increment accumulator
    MOV W, ++m4Acc ' check for max
    SNZ ' end reached?
    CLR m4Acc ' yes, reset
    ENDASM

    RETURNINT


    ' =========================================================================
    PROGRAM Start
    ' =========================================================================


    '
    ' Subroutine / Function Declarations
    '

    DELAY_MS SUB 1, 2 ' replaces PAUSE

    SET_Drive_Motor SUB 1 ' set Drive PWM control
    SET_Steering_Motor SUB 1 ' set Steering PWM control

    '
    ' Program Code
    '

    Start:
    PLP_A = %00000011 ' enable select pull-ups
    PLP_B = %00001111
    PLP_C = %00000000


    Main:
    \ JB Ch1In, $ ' wait until no pulse
    \ CLRB ch1Ready ' allow new measurement
    SET_Drive_Motor chan1 ' update motor speed/dir
    \ JB Ch2In, $
    \ CLRB ch2Ready ' allow new measurement
    SET_Steering_Motor chan2

    GOTO Main


    '
    ' Subroutine / Function Code
    '

    ' Use: DELAY_MS duration
    ' -- replaces PAUSE
    ' -- uses interrupt timing

    SUB DELAY_MS
    IF __PARAMCNT = 1 THEN ' if byte parameter
    __WPARAM12_MSB = 0 ' clear MSB
    ENDIF
    DO WHILE __WPARAM12 > 0 ' while running
    __PARAM3 = 100 ' load 1ms timer
    DO WHILE __PARAM3 > 0 ' while 1ms timer running
    ASM
    CLRB isrFlag ' clear isr flag
    JNB isrFlag, $ ' wait for next
    DEC __PARAM3 ' update 1ms timer
    ENDASM
    LOOP
    DEC __WPARAM12 ' update duration timer
    LOOP
    ENDSUB

    '

    ' Use: SET_Drive_Motor
    ' -- speeds is 100 to 200, 150 = stopped
    ' -- speed < 150 = reverse
    ' -- has small deadband

    SUB SET_Drive_Motor
    tmpB1 = __PARAM1 MAX 200 ' limit range
    tmpB1 = tmpB1 MIN 100

    IF tmpB1 < 148 THEN ' set to reverse
    m1Speed = 150 - tmpB1 ' set reverse speed
    m2Speed = 0
    ELSEIF tmpB1 > 152 THEN ' set to forward
    m1Speed = 0
    m2Speed = tmpB1 - 150 ' set forward speed
    ELSE
    m1Speed = 0
    m2Speed = 0
    ENDIF

    ' multiply speed by 5 to fit into byte

    tmpB1 = m1Speed << 2 ' x4
    m1Speed = tmpB1 + m1Speed ' +1 = x5
    tmpB1 = m2Speed << 2 ' x4
    m2Speed = tmpB1 + m2Speed ' +1 = x5

    ENDSUB

    '

    ' Use: SET_Steering_Motor
    ' -- speeds is 100 to 200, 150 = straight
    ' -- speed < 150 = Left
    ' -- has small deadband

    SUB SET_Steering_Motor
    tmpB1 = __PARAM1 MAX 200 ' limit range
    tmpB1 = tmpB1 MIN 100

    IF tmpB1 < 148 THEN ' set to reverse
    m3Speed = 150 - tmpB1 ' set reverse speed
    m4Speed = 0
    ELSEIF tmpB1 > 152 THEN ' set to forward
    m3Speed = 0
    m4Speed = tmpB1 - 150 ' set forward speed
    ELSE
    m3Speed = 0
    m4Speed = 0
    ENDIF

    ' multiply speed by 5 to fit into byte

    tmpB1 = m3Speed << 2 ' x4
    m3Speed = tmpB1 + m3Speed ' +1 = x5
    tmpB1 = m4Speed << 2 ' x4
    m4Speed = tmpB1 + m4Speed ' +1 = x5

    ENDSUB
    '
    ' User Data
    '


    I don't know how to just attach a file here. This program posting sure does fill screens fast

    Thanks
    Rocky
  • JonnyMacJonnyMac Posts: 9,215
    edited 2008-08-01 02:42
    You missed an edit -- you have this line under Ch2_Measure:

    MOVB ch1Active, Ch2In ' update status
    


    You're updating the status bit from channel 1.
  • RockyBRockyB Posts: 11
    edited 2008-08-01 03:34
    Thanks JonnyMac,

    Another ID10T error Oh boy!

    Now on to learnin how to read pots.
    Oh and I2C for the sonar.
  • JonnyMacJonnyMac Posts: 9,215
    edited 2008-08-01 07:21
    If you're going to add an I2C buss that you should use that to read your pots as well; the interrupt means that RCTIME and ANALOGIN will not function properly.
Sign In or Register to comment.