http://qs1969.pair.com?node_id=1148902

packetstormer has asked for the wisdom of the Perl Monks concerning the following question:

Hello monks, the following is a long-shot!

I have a small raspberry pi relay board that I am trying to use. The manufacturer has provided some sample Python code to open and close the relay. However, as the rest of the project is written in perl I'd like to get it working all in one module

Could anyone take a shot at explaining what the python lines below do and how they might translate to perl?

I can manage to open all ports (at the same time!), using Device::SMBus on the relay board in perl but I can't figure out what "&=" and "|&" means in the source script

class Relay(): global bus def __init__(self): self.DEVICE_ADDRESS = 0x20 #7 bit address (will be left shift +ed to add the read write bit) self.DEVICE_REG_MODE1 = 0x06 self.DEVICE_REG_DATA = 0xff bus.write_byte_data(self.DEVICE_ADDRESS, self.DEVICE_REG_MODE1, se +lf.DEVICE_REG_DATA) def ON_1(self): print 'ON_1...' self.DEVICE_REG_DATA &= ~(0x1<<0) bus.write_byte_data(self.DEVICE_ADDRESS, self.DEVICE_REG_MODE1 +, self.DEVICE_REG_DATA)

And my Perl Code

#!/usr/bin/perl use strict; use Device::SMBus; use Data::Dumper; my $dev = Device::SMBus->new( I2CBusDevicePath => '/dev/i2c-1', I2CDeviceAddress => 0x20, ); $dev->writeByteData(0x06,0x1<<0); print Dumper $dev

With the perl code all the relays open (or close). So it's really this line self.DEVICE_REG_DATA &= ~(0x1<<0) that's causing my trouble.

Like I said, I know this is a long shot but I'd appreciate some input

Replies are listed 'Best First'.
Re: I2C help (from python)
by toolic (Bishop) on Nov 30, 2015 at 15:49 UTC
    So it's really this line self.DEVICE_REG_DATA &= ~(0x1<<0) that's causing my trouble.
    This is just a wild guess since I don't know python, but I suspect it clears the least significant bit of the value stored in the DEVICE_REG_DATA variable.

    The &= looks like a read-modify-write register operation. My guess is that &= is the AND-EQUAL bit-wise operator and ~ is the bit-wise negation operator. (0x1 << 0) is simplified as 0x1, which is represented in binary format as 0000_0001 (8 bits). 0000_0001 negated is 1111_1110. So the line is probably equivalent to the pseudo-code:

    self.DEVICE_REG_DATA = self.DEVICE_REG_DATA & 1111_1110

    UPDATE: Added note to explicitly state that the code example I gave is pseudo.

      So the line is probably equivalent to: 01 self.DEVICE_REG_DATA = self.DEVICE_REG_DATA & 1111_1110

      That would have to be: self.DEVICE_REG_DATA = self.DEVICE_REG_DATA &0b1111_1110


      With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
      Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
      "Science is about questioning the status quo. Questioning authority". I knew I was on the right track :)
      In the absence of evidence, opinion is indistinguishable from prejudice.
Re: I2C help (from python)
by BrowserUk (Patriarch) on Nov 30, 2015 at 15:59 UTC
    So it's really this line self.DEVICE_REG_DATA &= ~(0x1<<0) that's causing my trouble.

    &= bitwise ANDs the value to the left with the value to the right and replaces the value to the left with the result. Ie. It is equivalent to $left = $left  & $right;. And the perl code would be exactly the same.

    The left value is initialised: self.DEVICE_REG_DATA = 0xff. The right value: ~(0x1<<0) is a stupidly complicated way of writing ~1:

    [0] Perl> printf "%b\n", ~(1<<0);; 1111111111111111111111111111111111111111111111111111111111111110 [0] Perl> printf "%b\n", ~(1);; 1111111111111111111111111111111111111111111111111111111111111110 [0] Perl> printf "%b\n", ~1;; 1111111111111111111111111111111111111111111111111111111111111110

    (The number of ones may vary depending upon the native size of integers in the version of Python being used.

    When ANDed with the initialisation value you get:

    printf "%b\n", 0xff & ~1;; 11111110

    In other words, the result of the &= statement is to clear the least significant bit of the byte, leaving all the other bits as they were.

    Does that help you solve your (unclear) question?

    BTW. Early in the post you mention &|, but that doesn't appear anywhere else in your post or code? And isn't a valid Python operator from my (rather awkward) quick search.


    With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority". I knew I was on the right track :)
    In the absence of evidence, opinion is indistinguishable from prejudice.

      Thanks for this

      Regarding the &|, you're correct, it should read |=, sorry about the confusion

Re: I2C help (from python)
by RonW (Parson) on Nov 30, 2015 at 18:24 UTC
    So it's really this line self.DEVICE_REG_DATA &= ~(0x1<<0) that's causing my trouble.

    Welcome to the world of "embedded systems programming".

    The others have provided good answers to your question. FWIW, I offer some additional information.

    That line looks like a too literal translation from C to Python. In C, the original was probably a macro:

    #define CLEAR_BIT(port, n) port.DEVICE_REG_DATA &= ~(0x1<<n)

    and would be used like:

    CLEAR_BIT(widget, 2);

    where widget is a structured variable mapped to a physical device port.

    So, the expression, port.DEVICE_REG_DATA &= ~(0x1<<n) (in C), will read the current value in the device port's data register, clear bit n, then write the new value back to the device port's data register.

    In Perl, if you needed real read-modify-write access to a physical register, you would need an XS module to give you a Perl callable API to low level functions that do the read-modify-write operation for your Perl code. This is because while Perl has read-modify-write operators, they were included partly for convenience and partly because expressions like $num_cookies = $num_cookies + $batch_size are more work to maintain than $num_cookies += $batch_size (because the former has more opportunities for making mistakes). Even if Perl internally uses read-modify-write operations, mapping the IV (or UV) field of a Perl scalar is not practical as the the physical registers (at least on the typical $0.50 processors used for embedded control systems) are not necessarily the same size as the IV/UV field, and other fields in the scalar might get in the way.

      In C, the original was probably a macro: #define CLEAR_BIT(port, n) port.DEVICE_REG_DATA &= ~(0x1<<n)

      Great observation! I withdraw my "stupidly complex" remark above.


      With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
      Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
      "Science is about questioning the status quo. Questioning authority". I knew I was on the right track :)
      In the absence of evidence, opinion is indistinguishable from prejudice.

        Thanks

Re: I2C help (from python) -- the laziest way
by Discipulus (Canon) on Dec 01, 2015 at 10:18 UTC
    Hello, better have a solution in Perl but if you are in a hurry or just too lazy... Inline::Python is here!

    L*
    There are no rules, there are no thumbs..
    Reinvent the wheel, then learn The Wheel; may be one day you reinvent one of THE WHEELS.