Shop OBEX P1 Docs P2 Docs Learn Events
Another dumb Spin question? — Parallax Forums

Another dumb Spin question?

Hi all,
Been away from the Propellers for a while and trying to get my head straight.
Or properly twisted?

Multi-bit (range) I/O syntax

 outa [23..16] := $00              

IS there any clever way to take the magic numbers out of that statement?
OTHER than something like this???

Byte LED1, LED8
LED8 := 23
LED1 := 16
outa [LED8..LED1] := LED

«1

Comments

  • JonnyMacJonnyMac Posts: 8,912
    edited 2022-08-02 03:30

    I would suggest that you make your MSB and LSB values of the range constants, since those connections will not move during the operation of the program

    con
    
      LED8 = 23
      LED1 = 16
    

    To keep your code tidy you may want to write function like this:

    pub update_leds(value)
    
      outa[LED8..LED1] := value
      dira[LED8..LED1] := %11111111
    
  • I still like the ~~ notation:

    dira[LED8 .. LED1]~~
    

    Saves having to count 1's. :)

    -Phil

  • I've been trying to make that work as a constant for mumble something hours now.
    I kept getting an error message (expected ":")
    But since YOU said it, it now works.
    That's some heavy juju you got there, JonnyMac.

    I do not know what my problem was, but it works now, so back to work.
    Thank you, Jon.

    Hey Phil,
    Yeah, that too!

    Reading that old first-cut robot code from a few years ago...
    Makes me wonder -
    Who wrote this mess???
    Oh...
    (high squeeky voice) "Nevermind"

    Richard

    @JonnyMac said:
    I would suggest that you make your MSB and LSB values of the range constants, since those connections will not move during the operation of the program

    con
    
      LED8 = 23
      LED1 = 16
    

    To keep your code tidy you may want to write function like this:

    pub update_leds(value)
    
      outa[LED8..LED1] := value
      dira[LED8..LED1] := %11111111
    
  • I still like the ~~ notation:

    I prefer the raw bits because it's verbose and is faster than ~~. If I don't want to be bothered counting bit I use -1, but this is rarely the case.

  • @JonnyMac said:

    I still like the ~~ notation:

    I prefer the raw bits because it's verbose and is faster than ~~. If I don't want to be bothered counting bit I use -1, but this is rarely the case.

    Using true (which evals to -1) could be argued is better or more "idiomatic". Using -1 instead of the actual bit mask will also save 1 byte of code (-1 constant is single byte code, bitmask constant is 2 bytes).

    Also note that in flexspin, there's no codegen difference between a -1/0 assignment and a freestanding postset operator.

  • Using true (which evals to -1) could be argued is better or more "idiomatic".

    Indeed. Good point. Thank you.

  • cavelambcavelamb Posts: 720
    edited 2022-08-02 15:48

    all is -1.
    If Shakespeare said it, it must be -1.

    My ignorance is showing, but what is flexspin?

  • JonnyMacJonnyMac Posts: 8,912
    edited 2022-08-02 16:11

    Flexspin is a compiler than understands Spin1, Spin2, C, and BASIC. I don't use it much (except for testing), but it has become quite popular with many forum members.

    You can download from here:
    -- https://github.com/totalspectrum/flexprop/releases

  • @JonnyMac said:
    Flexspin is a compiler than understands Spin1, Spin2, C, and BASIC.

    And combinations thereof :+1:

    Craig

  • @Mickster said:

    @JonnyMac said:
    Flexspin is a compiler than understands Spin1, Spin2, C, and BASIC.

    And combinations thereof :+1:

    Craig

    GIMMEE!

  • cavelambcavelamb Posts: 720
    edited 2022-08-03 08:53

    Well, until then?

    My project is a small table-top robot that I'd like to NOT fall off the table.
    Robots don't bounce very well.

    This is a kind of cleaned-up version of where I left it a couple of years ago.
    It's baby-talk level Spin. I get that. I'm back to trying to learn...
    Lab .01 code to exercise the circuitry and machinery to see if anything works right.

    There are some fun things I'd like to try, but I don't know how to say it in Spin yet.
    Like PWM control of the drive motors.
    Like Bluetooth comms instead of the TV remote.
    Like Line-following, mapping, tracking, etc?
    But I gotta get Spun up first...

    In this episode the big thing I'm tripped up over...
    Printing text to the LCD display.
    Printing "literals" is obviously no problem.
    The Serial-LCD driver handles that nicely.

    But sending a string variable?
    I haven't quite got a handle on that one yet.
    Pointer to a variable array, but not sure how to stuff the array.
    Or get it shipped over to the LCD display.

    example:

    In the code...

        ir#chUp  :                                'Forward
             if Power == True
                 LCD.CLS
                 LCD.gotoxy(0, 0)   ' col, line
                 lcd.str(string("Forward        "))  <<== printing a literal
                 GoFwd
        other      
    
     (loop)   
    

    What I'd like to do...

        ir#chUp  :                                'Forward
            if Power == TRUE 
                'fill in Txt[]  array with "Forward"  <<== a string?
        other
    
        'and after the case terminates ...
    
        if the string is not blank?
           LCD.CLS
           LCD.gotoxy(0, 0)   ' col, line
           print the string  ' lcd.str(string("????")) 
    
    (loop)      
    

    Probably something like...

        repeat strsize(strAddr) 
          bytemove(byte[strAddr++])   ???
    

    All files are in the zip.
    I couldn't get the main program to format here...
    :(

  • cavelambcavelamb Posts: 720
    edited 2022-08-12 14:50

  • cavelambcavelamb Posts: 720
    edited 2022-08-02 21:18
      _CLKMODE = XTAL1 + PLL16X            ' 80 Mhz clock
      _XINFREQ = 5_000_000
      OneSec = _XINFREQ
      MSec   = _XINFREQ / 1_000     * 8
      USec   = _XINFREQ / 1_000_000
    '====================================
    ' Pin Definitions:
    '            MSB                             LSB  ' port addresses
    '            33222222_22222111_11111110_00000000  ' pin numbers are port +1
    '            10987654_32109876_54321098_76543210  
    '
    ' Quicksart touch pads
      PADpins = %00000000_00000000_00000000_11111111  '00..07
    
    ' Motor control pins for TB6612FNG dual motor controller
      MOTPins = %00000000_00000000_00001111_00000000  '08..11
      MotA1   =  8
      MotA2   =  9
      MotB1   = 10
      MotB2   = 11
    
      PWMpins = %00000000_00000000_00110000_00000000  '12..13
      PwmL    = 12
      PwmR    = 13
    
    ' 2 line liquid crystal display
      LCDpins = %00000000_00000000_01000000_00000000  '14
      LCDpin  = 14
    
    ' Infra Red Remote COntrol Decoder
      IRCpin  = 15
    
    ' Quickstart LED display used as RANGE [LED8..LED1]
      LEDpins = %00000000_11111111_00000000_00000000  '16 to 23
      LED8    = 23
      LED4    = 19
      LED1    = 16
    
    ' GP2Y0D810Z0F IR obsticle sensors input pins
      OBXpins = %00001111_00000000_00000000_00000000  '24 to 27
      ObxFL   = 24
      ObxFR   = 25
      ObxRL   = 26
      BoxRR   = 27
    
    ' Quickstart EPROM serial port
      EPCpins = %00010000_00000000_00000000_00000000  '28 EEPROM Clock
      EPDpins = %00100000_00000000_00000000_00000000  '29 EEPROM Data
    
    ' Quickstart USB serial port
      USBRX   = %01000000_00000000_00000000_00000000  '30
      USBTX   = %10000000_00000000_00000000_00000000  '31
    
      OUTpins = MOTPins | PWMpins |LCDpins| LEDpins |LCDpins
    '====================================
    ' Motor Direction Masks for TB6612FNG                   '
      Mot_Fwd = %0110 ' Go fwd
      Mot_Aft = %1001 ' Go Aft
      Mot_Rit = %0010 ' Turn Right
      Mot_Lft = %0100 ' Turn Left
      Mot_RoR = %1010 ' Rotate Right
      Mot_RoL = %0101 ' Rotate Left
      Mot_Off = %0000 ' All Stop
    '====================================
    ' Liquid Crystal Display - 2 line'
      LCDon1        = $16               ' LCD on; cursor off, blink off
      LCDline1      = $80               ' move to line 1, column 0
      LCDline2      = $94               ' move to line 2, column 0
      LCD_TimeOut   =  10               ' seconds
    '====================================
    OBJ
      ir      : "IRC"
      lcd     : "serial_lcd"
      num     : "simple_numbers"
    '====================================
    VAR
      long Stack1[6]
      long IRcog, Power
      Byte LCD_Time, IRC_ret, TurnType
      Byte TxtLin1[16], TxtLin2[16]
    '====================================
    PUB Init | freq, index, cog, IRcode    ' *COG*
    
    ' variable inits
      Power := TRUE
    
    ' pin directions
      outa [LED8..LED1] := $00             ' all LED pins off '
      dira [27..00] := OUTpins             ' make all output pins be outputs
      outa [PwmL..PwmL] := %11             ' PWMpins enable motors  1=ON
    '====================================
    ' init LCD
      if lcd.start(LCDpin, 9600, 2)        ' *COG*
    
         lcd.putc(lcd#Lcd_On1)             ' no cursor
         lcd.backlight(1)
         lcd.cls
    
         ProgramVersion
    
    ' Start LCD_Timeout                    ' *COG*
      LCD_Time := LCD_TimeOut              ' reset LCD_Time each time a key is pressed
      cognew (LCD_Timer, @stack1 )
    '====================================
    ' Init IR remote
      IRcog := ir.Start(IRCpin, @IRC_ret)  ' *COG*
    '====================================
    ' Top of IR Code input loop:
      if IRcog > 0
    
         repeat
    
            If LCD_Time >0 and power == TRUE
               LCD.backlight(1)            ' turn it on  (won't workwith TRUE/FALSE?)
            else                           ' timed out
               LCD.backlight(0)            ' turn if off
    
            If IRC_ret <> ir#NoNewCode     ' we have a key code
               IRcode := IRC_ret
               ir.Start(IRCpin, @IRC_ret)  ' set up for next code
    
               LCD_Time := LCD_Timeout     ' reset LCD_Timeout each time a key is pressed
                lcd.gotoxy(0,1)
    
               'Start of user input parser
    
                case IRcode                                 ' control keys
                  ir#power :
                      LCD.CLS
                    if Power == TRUE
                       Power := FALSE                           ' turn power off
                       lcd.str(string("Power OFF"))
                       GoStop                               ' shut down motors
                       waitMS(2000)
                       lcd.backlight(0)
                    else
                       Power := TRUE                           ' turn power on
                        lcd.str(string("Power ON"))
    
                  ir#chUp  :                                'Forward
                    if Power == True
                       LCD.CLS
                       LCD.gotoxy(0, 0)   ' col, line
                       lcd.str(string("Forward        "))
                       GoFwd
    
                  ir#chDn  :                                'backward
                    if Power == True
                       LCD.CLS
                       LCD.gotoxy(0, 0)   ' col, line
                       lcd.str(string("Back           "))
                       GoBack
    
                  ir#volDn :                                'left
                    if Power == True
                       LCD.CLS
                       lcd.str(string("Left           "))
                       GoLeft
    
                  ir#volUp:                                 'right
                    if Power == True
                       LCD.CLS
                       lcd.str(string("Right          "))
                       GoRight
    
                  ir#OK    :
                    if Power == True
                       LCD.CLS
                       lcd.str(string(LcdLine1, "OKAY"))     ' okay = enter
                       GoStop
    
                  ir#zero  :                                ' numeric keys
                    if Power == True
                       LCD.CLS
                       lcd.str(string("<0>"))
                  other:
                       LCD.CLS
    
               waitcnt((clkfreq / 1000) * 30 + cnt)
    
            waitcnt((clkfreq / 1000) * 30 + cnt)
    '====================================
    'PUB str(strAddr)
    '      repeat strsize(TXT)                              ' for each character in string
    '         (byte[strAddr++])                             '   write the character
    PUB GoFwd
          outa [Led4..Led1]   := Mot_Fwd   '
          outa [MotB2..MotA1] := Mot_Fwd   '
    '====================================
    PUB GoBack
          outa [Led4..Led1]   := MOT_Aft   '
          outa [MotB2..MotA1] := Mot_Aft   '
    '====================================
    PUB GoRight                            '
        If TurnType == 1                   ' turn
          outa [Led4..Led1]   := Mot_Rit   '
          outa [MotB2..MotA1] := Mot_Rit   '
        else                               ' pivot
          outa [Led4..Led1]   := Mot_RoR   '
          outa [MotB2..MotA1] := Mot_RoR   '
    '====================================
    PUB GoLeft
        If TurnType == 1                   ' turn
          outa [Led4..Led1]   := Mot_Lft   '
          outa [MotB2..MotA1] := Mot_Lft   '
        else                               ' pivot
          outa [Led4..Led1]   := Mot_RoL
          outa [MotB2..MotA1] := Mot_RoL
    '====================================
    PUB GoStop                             '
          outa [PwmL..PwmL]   := %00       ' hit brakes
          WaitMS(500)                      '
          outa [LED8..LED1]   := Mot_Off   '
          outa [MotB2..MotA1] := Mot_Off   '
          outa [PwmL..PwmL]   := %11       ' release brakes
    '====================================
    PUB Brakes(y)
          outa [PwmL..PwmL]   := %00      ' hit brakes
          WaitMS(y)                       ' for this long
          outa [LED8..LED1]   := Mot_Off  '
          outa [MotB2..MotA1] := Mot_Off  '
          outa [PwmL..PwmL]   := %11      ' re-enable motors
    '====================================
    PUB WaitMS(W)                         ' wait for W milliseconds
      W := W * MSec
      WaitCNT (W+cnt)
    '====================================
    PUB LCD_Timer
      Repeat                            ' loop forever
          waitcnt(clkfreq + cnt)        ' wait one second
          if  byte[@LCD_Time] =>   1    ' keep counting
              byte[@LCD_Time] --        ' down
    '====================================
    PUB ProgramVersion
        lcd.str(string(lcdline1, "QBot"))
        lcd.str(string(lcdline2, "06/02/2022"))
        return
    '====================================
    
  • cavelambcavelamb Posts: 720
    edited 2022-08-02 21:21

    Thank you for the Flexprop link, Jon.

  • cavelambcavelamb Posts: 720
    edited 2022-08-02 21:18

    had troubles formatting code!

  • JonnyMacJonnyMac Posts: 8,912
    edited 2022-08-02 22:37

    Like PWM control of the drive motors.

    I've attached a dual PWM driver that I've used in pan/tilt controllers for video cameras. You might need to make changes for your driver chip, but the code will show you how to run fixed-frequency. variable duty-cycle PWM in a secondary Spin cog. This code uses the cog counter to generate pulse outputs. It works well.

    Like Bluetooth comms instead of the TV remote.

    You can do this with a serial input. I've attached a big project (that has other goodies you may find useful); this was a mini "Stranger Things" wall that could accept commands and spell out other words using a string of WS2811 Christmas bulbs. Note that his project was coded for a P1-powered HC-8 controller from EFX-TEK (I designed it for them). I used an HC-06 Bluetooth (BT4) module.

    But sending a string variable?

    There are no such things as string variables in Spin. As in C, a string is an array of characters terminated with 0 (null). You can build a string inline with the string() function, but if that string will be used more than once or needs to be referenced in code, it's best to define it in a DAT section. The Stranger Things example. For methods that require strings, you'll pass a pointer to that string. @ provides the run-time address of an object; we use this with specified strings.

  • cavelambcavelamb Posts: 720
    edited 2022-08-03 00:01

    I've already been studying the dual_motor_ez code.
    Nice piece. Works well.
    But I had some problems with it too.
    Not with your code.
    With my little toy motors.

    I'm using the Tamiya double motor gearbox.
    They use a pair of cheapie permanent magnet motors.
    I tried characterizing a couple of them and found out that they don't run at the same speeds.
    And the difference is different at different speeds.
    Which makes going straight a problem!

    The robot only needs four speeds to run well.
    Stop, ahead 1/4, 1/2, 3/4, and flank (in naval terms)
    That translates pretty close to 1,2,3, and 4 inches per second.
    Table tops are not unlimited space!
    And I want to use masking tape to draw off mazes.

    Anyway,
    I characterized each motors to see what it takes in the way of PWM ticks to achieve a given RPM.
    Put those numbers in a dual array (left and right) and see if it can go straight.

    Turn Rates, I think, would be controlled by a different paradigm - mule train.
    Gee little bit, Haw little bit = increase outside motor speed or decrease inside motor speed.
    For line following or edge following.

    I know you guys have done a ton of work on small robot projects.
    I should probably sit down and read through it.

    Just opened Pandora's box of Stranger Things.
    It may take a while!

  • I gotta get better at shifting...
    That scan_ttl_ins for reading a 74165...
    Speed shifting a 16 speed gearbox!
    cool

  • @JonnyMac said:

    Like Bluetooth comms instead of the TV remote.

    You can do this with a serial input. I've attached a big project (that has other goodies you may find useful); this was a mini "Stranger Things" wall that could accept commands and spell out other words using a string of WS2811 Christmas bulbs. Note that his project was coded for a P1-powered HC-8 controller from EFX-TEK (I designed it for them). I used an HC-06 Bluetooth (BT4) module.

    What did you use to **send **Bluetooth.
    Not on the wall end, but the user end?
    Computer?
    Phone?
    Another Propeller?

    A phone would be ideal, but that would mean writing a phone app too?

  • Gee: Right
    Gee-yup(or hup): Hard right.
    Haw: Left.
    Haw-yup (or hup): Hard left.
    Geep (jipp): Onward/faster.
    Whoe: Slow/stop/stand
    Click-click: attention/listen/watch
    Hald: stand still, head down (if human near).
    On: pull fwd until there is no slack and stop.
    Givit: pick foot up (pick feet)
    Nawp: no/quit whatever you are doing/wrong/listen to me
    Hawz (or Hawz-up): Rest after backing up a step to unload team from pulling the plow/wagon pull (unloaded rest position)
    Whoops: Spilled my beer (ok, I made that one up)

    We draft horse owners call these the “12 Commandments” of draft horse management. :) Just sayin’

  • JonnyMacJonnyMac Posts: 8,912
    edited 2022-08-03 00:56

    That scan_ttl_ins for reading a 74165...

    That code is unrolled so that it can run as fast as possible (no loop overhead). It's running in a background cog that has a 1ms loop which is convenient for timing. I also do red-green color mixing for a 2-lead R/G LED which gives me off, red, green, and yellow, with solid and blinking states. Turns out you can do quite a lot of work in 1ms in an 80MHz P1.

    A phone would be ideal, but that would mean writing a phone app too?

    No, you can use a simple terminal app since the input to the program is just text (which makes it easy to test through the PST window, too). This code lets me select how the program is going to take input. Once the command parser is tested and running through PST, I switch to BT. Easy-peasy.

    For the trade show where that display was setup I wrote a little Android app for EFX-TEK using MIT App Inventor. It's kind of clunky (it's a "blocks" environment), but it's a tool that works and is free. My understanding is that it now supports iPhones so I may look at it again having moved from Android to iPhone late last year.

  • @JRoark said:
    Gee: Right
    Gee-yup(or hup): Hard right.
    Haw: Left.
    Haw-yup (or hup): Hard left.
    Geep (jipp): Onward/faster.
    Whoe: Slow/stop/stand
    Click-click: attention/listen/watch
    Hald: stand still, head down (if human near).
    On: pull fwd until there is no slack and stop.
    Givit: pick foot up (pick feet)
    Nawp: no/quit whatever you are doing/wrong/listen to me
    Hawz (or Hawz-up): Rest after backing up a step to unload team from pulling the plow/wagon pull (unloaded rest position)
    Whoops: Spilled my beer (ok, I made that one up)

    We draft horse owners call these the “12 Commandments” of draft horse management. :) Just sayin’

    That navigation module is going to be a LOT more interesting now.
    :)

  • @cavelamb

    The day that I first used the Prop (late 2011), I also had a Bluetooth module and I immediately wanted to communicate with my Android phone and so I tried this:
    https://rfobasic.miraheze.org/wiki/Main_Page

    One of the sample programs is for Bluetooth. I had the phone talking to the Prop in minutes :smile:

    I selected the Android platform as the HMI for my machine control systems. I now have them all over the place in some pretty grimy environments. This has proven to be a great way to go :+1:

    Some examples:



    On-board schematics:


    Craig

  • Hey Mick,

    Yeah. I've played with RFO BASIC a bit.
    Wrote a Retro Dialer.

    I'll have to go back and look a that again.

  • :lol: Sure enough, they exist on Google Play. Just installed one and it's cool as heck :+1:

    Craig

  • cavelambcavelamb Posts: 720
    edited 2022-08-12 14:33

    Okay, next dumb question please?

    In the following I have commented where the difficulty lives.

    Using IR remote control - the program needs to check for user input to turn the Power variable
    on or off.

    If Power is off, do not parse the remaining cases.

    So, how to structure that - without the ugly mess in the listing -
    i.e. having to test for Power on in each case?

    It seems interrupting the Case structure is not allowed?

  • I fixed that problem right after posting the above...

    Gilda Radner voice - "Nevermind"...

  • dgatelydgately Posts: 1,621
    edited 2022-08-11 17:24

    Just curious... Why are you restarting the ir code in a loop?

         repeat
            ...  ' other code
            If IRC_ret <> ir#NoNewCode     ' we have a key code
               IRcode := IRC_ret
               ir.Start(IRCpin, @IRC_ret)  ' set up for next code      ' <== seems dubious
    

    Shoudn't you just use something like:

        ir.getSonyCode(IRCpin, @IRC_ret)
    

    to get the most recent IR code? Seems odd to create a new cog in each loop of your main code. Am I missing something?

    dgately

  • Yep.
    You are missing the Sony IR driver that Tom Doyle wrote.
    It's written to be restarted that way.
    There is a "continuous" version somewhere.
    But I haven't used it.

    {{
    
          IR_Remote_NewCog.spin
          Tom Doyle
          2 March 2007
    
          The IR_Remote.spin object receives and decodes keycodes from a Sony TV remote control.
          The procedure to read and decode is run in a separate cog. This eliminates the big problem
          of the mainline program hanging while waiting for a key to be pressed. When a keycode is
          received it is written into main memory (IRcode) by the cog. See IR_Remote.spin for
          more information.
    
    }}
    
    
    CON
    
      _CLKMODE = XTAL1 + PLL16X        ' 80 Mhz clock
      _XINFREQ = 5_000_000
    
      IRdetPin  =    5                   ' IR Receiver - Propeller Pin
    
    
    OBJ
    
      ir    : "IR_Remote"
      pst   : "Parallax Serial Terminal"
      num   : "simple_numbers"
    
    VAR
    
      byte IRcode                                             ' keycode from IR Receiver
    
    PUB Init | freq, index, cog, lcode
    
      if pst.start(9600)
        pst.str(string("IR Remote"))
    
        cog := ir.Start(IRdetPin, @IRcode)  ' Propeller pin connected to the IR receiver, address of variable
        if cog > 0
          repeat
    
            If IRcode <> ir#NoNewCode
    
              lcode := IRcode
              ir.Start(IRdetPin, @IRcode)  ' set up for next code
    
              pst.str(string("Binary: "))
              pst.str(num.bin(lcode, 7))
              pst.str(string("  "))
              pst.str(string("Decimal: "))
              pst.str(num.dec(lcode))
              pst.str(string("  ",13))
    
            waitcnt((clkfreq / 1000) * 30 + cnt) 
    
    
    
    {{
          IR_Remote_NewCog.spin
          Tom Doyle
          2 March 2007
    
          Panasonic IR Receiver - Parallax #350-00014
    
          Receive and display codes sent from a Sony TV remote control.
          See "Infrared Decoding and Detection appnote" and "IR Remote for the Boe-Bot Book v1.1"
          on Parallax website for additional info on TV remotes.
    
          The procedure uses counter A to measure the pulse width of the signals received
          by the Panasonic IR Receiver. The procedure waits for a start pulse and then decodes the
          next 12 bits. The entire 12 bit result is returned. The lower 7 bits contain the actual
          key code. The upper 5 bits contain the device information (TV, VCR etc.) and are masked off
          for the display.
    
          Most TV Remotes send the code over and over again as long as the key is pressed.
          This allows auto repeat for TV operations like increasing volume. The volume continues to
          increase as long as you hold the 'volume up' key down. Even if the key is pressed for a
          very short time there is often more than one code sent. The best way to eliminate the
          auto key repeat is to look for an idle gap in the IR receiver output. There is a period of
          idle time (20-30 ms) between packets. The getSonyCode procedure will wait for an idle period
          controlled by the gapMin constant. This value can be adjusted to eliminate auto repeat
          while maintaining a fast response to a new keypress. If auto repeat is desired the indicated
          section of code at the start of the getSonyCode procedure can be commented out.
    
          The procedure sets a tolerance for the width of the start bit and the logic level 1 bit to
          allow for variation in the pulse widths sent out by different remotes. It is assumed that a
          bit is 0 if it is not a 1.
    
          The procedure to read the keycode ( getSonyCode ) is run in a separate cog. This allows
          the main program loop to continue without waiting for a key to be pressed. The getSonyCode
          procedure writes the NoNewCode value (255) into the keycode variable in main memory to
          indicate that no new keycode is available. When a keycode is received it writes the keycode
          into the main memory variable and terminates. With only 8 cogs available it seems to be a
          good idea to free up cogs rather than let them run forever. The main program can fire off
          the procedure if and when it is interested in a new keycode.
    
    }}
    
    
    CON
    
      _CLKMODE = XTAL1 + PLL16X        ' 80 Mhz clock
      _XINFREQ = 5_000_000
    
      NoNewCode    =  255               ' indicates no new keycode received
    
      gapMin       =   2000             ' minimum idle gap - adjust to eliminate auto repeat
      startBitMin  =   2000             ' minimum length of start bit in us (2400 us reference)
      startBitMax  =   2800             ' maximum length of start bit in us (2400 us reference)
      oneBitMin    =   1000             ' minimum length of 1 (1200 us reference)
      oneBitMax    =   1400             ' maximum length of 1 (1200 us reference)
    
    
    VAR
    
      byte  cog
      long  Stack[20]  
    
    
    PUB Start(Pin, addrMainCode) : result
    {{
       Pin - propeller pin connected to IR receiver
       addrMainCode - address of keycode variable in main memory
    }}
    
        stop
        byte[addrMainCode] := NoNewCode
        cog := cognew(getSonycode(Pin, addrMainCode), @Stack) + 1
        result := cog
    
    
    PUB Stop
    {{
       stop cog if in use
    }}
    
        if cog
          cogstop(cog~ -1)
    
    
    PUB getSonyCode(pin, addrMainCode) | irCode, index, pulseWidth, lockID
    
    {{
       Decode the Sony TV Remote key code from pulses received by the IR receiver
    }}
    
       ' wait for idle period (ir receiver output = 1 for gapMin)
       ' comment out "auto repeat" code if auto key repeat is desired
    
       ' start of "auto repeat" code section
       dira[pin]~
       index := 0
       repeat
         if ina[Pin] == 1
           index++
         else
           index := 0
       while index < gapMin
       ' end of "auto repeat" code section
    
       frqa := 1
       ctra := 0
       dira[pin]~
    
       ' wait for a start pulse ( width > startBitMin and < startBitMax  )
       repeat      
          ctra := (%10101 << 26 ) | (PIN)                      ' accumulate while A = 0  
          waitpne(0 << pin, |< Pin, 0)                         
          phsa:=0                                              ' zero width
          waitpeq(0 << pin, |< Pin, 0)                         ' start counting
          waitpne(0 << pin, |< Pin, 0)                         ' stop counting                                               
          pulseWidth := phsa  / (clkfreq / 1_000_000) + 1    
       while ((pulseWidth < startBitMin) OR (pulseWidth > startBitMax))
    
       ' read in next 12 bits
       index := 0
       irCode := 0
       repeat
          ctra := (%10101 << 26 ) | (PIN)                      ' accumulate while A = 0  
          waitpne(0 << pin, |< Pin, 0)                         
          phsa:=0                                              ' zero width
          waitpeq(0 << pin, |< Pin, 0)                         ' start counting
          waitpne(0 << pin, |< Pin, 0)                         ' stop counting                                               
          pulseWidth := phsa  / (clkfreq / 1_000_000) + 1
    
        if (pulseWidth > oneBitMin) AND (pulseWidth < oneBitMax)
           irCode := irCode + (1 << index)
        index++
       while index < 11
    
       irCode := irCode & $7f                                   ' mask off upper 5 bits
    
       byte[addrMainCode] := irCode
    
Sign In or Register to comment.