Exemple #1
0
def adc_read(port, pin, raw=False):
    """
    Reads a raw or converted ADC value.

    Args:
        port: The port of the GPIO pin to read from.
        pin: The pin number of the GPIO pin to read from.
        raw: If raw is True, a raw read should be performed; otherwise a converted read.
            Defaults to converted.

    Returns:
        The ADC reading as a 16-bit integer.

    Raises:
        ValueError: if the range of the input args is invalid.
        Exception: if we receive a nonzero status code.
    """

    # If port is entered as a str, convert to int
    if isinstance(port, str):
        port = getattr(GpioPort, port.capitalize())

    # Check the ranges of input args
    if port < 0 or port >= GpioPort.NUM_GPIO_PORTS:
        raise ValueError("ERROR: invalid GPIO port")
    if pin < 0 or pin >= NUM_PINS_PER_PORT:
        raise ValueError("ERROR: invalid GPIO pin number")
    if raw not in (True, False):
        raise ValueError("ERROR: raw must be a bool value")

    # Send CAN message
    data = [
        (port, 1),
        (pin, 1),
        (raw, 1),
    ]
    data = can_util.can_pack(data)
    can_util.send_message(babydriver_id=BabydriverMessageId.ADC_READ_COMMAND, data=data)

    # Wait to receive the first CAN message with data
    data_msg = can_util.next_message(babydriver_id=BabydriverMessageId.ADC_READ_DATA)

    # Extract the low and high bytes of the ADC conversion
    result_low = data_msg.data[1]
    result_high = data_msg.data[2]

    # Wait to receive the second CAN message with status
    status_msg = can_util.next_message(babydriver_id=BabydriverMessageId.STATUS)
    status = status_msg.data[1]

    # Check if status is not ok
    if status != OK_STATUS:
        raise Exception("ERROR: received a nonzero STATUS_CODE: {}".format(status))

    return (result_high << 8) | result_low
Exemple #2
0
def i2c_read(port, address, rx_len, reg=None):
    """
    Reads a number of bytes over I2C
    Args:
        port: I2C port (1 or 2) to read from
        address: I2C address to read from
        rx_len: expected response length
        reg: register to read from, or None if it is a normal read (non-register read)
    Raises:
        ValueError: if parameters passed into i2c_read are invalid
        Exception: if a non-zero status code was received
    """
    is_reg = 0
    if reg is None:
        reg = 0
    else:
        is_reg = 1
    if port not in (1, 2):
        raise ValueError("Expected port of 1 or 2 ")
    if address < 0 or address > 255:
        raise ValueError("Expected an uint8 address between 0 and 255")
    if rx_len < 0 or rx_len > 255:
        raise ValueError(
            "Expected an uint8 response length value between 0 and 255")
    if reg < 0 or reg > 255:
        raise ValueError("Expected an uint8 register between 0 and 255")

    # shift port to 0 or 1, and send can message
    data_pack = can_util.can_pack([(port - 1, 1), (address, 1), (rx_len, 1),
                                   (is_reg, 1), (reg, 1)])
    can_util.send_message(babydriver_id=BabydriverMessageId.I2C_READ_COMMAND,
                          data=data_pack)

    read_data = []
    num_msgs = ceil(rx_len / 7)
    # Loop to receive the ceil(rx_len/7) CAN messages, with only the
    # last 7 bytes of each msg being data and storing only rx_len bytes
    # of data to the read_data list
    for message in range(num_msgs):
        read_msg = can_util.next_message(
            babydriver_id=BabydriverMessageId.I2C_READ_DATA)
        if message == (num_msgs - 1) and (rx_len % 7) != 0:
            read_data.extend(read_msg.data[1:rx_len % 7 + 1])
        else:
            read_data.extend(read_msg.data[1:8])
    # Wait to receive the status CAN message
    status_msg = can_util.next_message(
        babydriver_id=BabydriverMessageId.STATUS)
    status = status_msg.data[1]
    # Check if status is not STATUS_CODE_OK (0)
    if status != 0:
        raise Exception("Received STATUS_CODE {}".format(status))

    return read_data
Exemple #3
0
def gpio_get(port, pin):
    '''
    Returns the state of the GPIO pin at the given port and pin number as a bool

    The port can be entered as either a string or int value (e.g. 'A' or 0).
    The pin is an int value
    '''

    if isinstance(port, str):
        port = getattr(GpioPort, port.capitalize())

    if port < 0 or port >= GpioPort.NUM_GPIO_PORTS:
        raise ValueError("ERROR: invalid GPIO port")
    if pin < 0 or pin >= GPIO_PINS_PER_PORT:
        raise ValueError("ERROR: invalid GPIO pin number")

    data = can_util.can_pack([(port, 1), (pin, 1)])

    can_util.send_message(
        babydriver_id=BabydriverMessageId.GPIO_GET_COMMAND,
        data=data,
    )

    gpio_data_msg = can_util.next_message(
        babydriver_id=BabydriverMessageId.GPIO_GET_DATA,
    )

    status_msg = can_util.next_message(
        babydriver_id=BabydriverMessageId.STATUS,
    )

    status = status_msg.data[1]
    if status != 0:
        raise Exception("ERROR: Non-OK status returned: {}".format(status))

    raw_state = gpio_data_msg.data[1]

    gpio_pin_state = bool(raw_state)

    return gpio_pin_state
Exemple #4
0
def i2c_write(port, address, tx_bytes, reg=None):
    """
    Writes over I2C to the given port/address
    Args:
        port: port of the I2C to write to (1 or 2)
        address: I2C address to write to (0-255)
        tx_bytes: list of bytes to write over I2C (a list of ints 0-255)
        reg: the register to write to, or None if no register is required
    Raises:
        Exception: if a non-zero status code was received
    """
    if port not in (1, 2):
        raise ValueError("Expected port of 1 (I2C_PORT_1) or 2 (I2C_PORT_2)")
    if address < 0 or address > 255:
        raise ValueError("Expected address between 0 and 255")
    if not all(0 <= byte < 256 for byte in tx_bytes):
        raise ValueError("Expected list of bytes between 0 and 255")

    is_reg = 0
    if reg is None:
        reg = 0
    else:
        is_reg = 1
    if reg < 0 or reg > 255:
        raise ValueError("Expected register to write to between 0 and 255")

    can_pack = can_util.can_pack([(port - 1, 1), (address, 1),
                                  (len(tx_bytes), 1), (is_reg, 1), (reg, 1)])

    can_util.send_message(
        babydriver_id=BabydriverMessageId.I2C_WRITE_COMMAND,
        data=can_pack,
    )

    # Sends CAN message
    for i in range(0, len(tx_bytes), 7):
        can_util.send_message(
            babydriver_id=BabydriverMessageId.I2C_WRITE_DATA,
            data=tx_bytes[i:i + 7],
        )
    status_msg = can_util.next_message(
        babydriver_id=BabydriverMessageId.STATUS)
    # Raises Exception if status is non-OK
    if status_msg.data[1] != 0:
        raise Exception("Received STATUS_CODE {}".format(status_msg.data[1]))
def unregister_gpio_interrupt(port, pin):
    """
    Unregisters a gpio interrupt on a pin

    Args:
        port: Port of the GPIO pin to register an interrupt in
        pin: Pin number of the GPIO pin to register an interrupt in

    Raises:
        Value error: if the parameters passed into register_gpio_interrupt are incorrect
        Attribute error: if the port parameter is invalid (refer gpio_port.py
                         for acceptable port parameters)
        Exception: if a non-zero status code is received when attempting to uregister an interrupt
        Key error: if no interrupt is registered in the given port and pin
    """

    if isinstance(port, str):
        port = getattr(GpioPort, port.capitalize())

    if port < 0 or port >= GpioPort.NUM_GPIO_PORTS:
        raise ValueError("invalid GPIO port (Range: 'A' - 'F')")

    if pin < 0 or pin >= NUM_PINS_PER_PORT:
        raise ValueError("Invalid GPIO pin number (Range: 0 - 15)")

    msg_data_unregister_gpio_interrupt = [(port, 1), (pin, 1)]

    can_util.send_message(
        babydriver_id=message_defs.BabydriverMessageId.GPIO_IT_UNREGISTER_COMMAND,
        data=can_util.can_pack(msg_data_unregister_gpio_interrupt)
    )

    status_message = can_util.next_message(babydriver_id=message_defs.BabydriverMessageId.STATUS)
    received_status = status_message.data[1]

    # Check if status is invalid (0 refers to STATUS_CODE_OK)
    if received_status != 0:
        raise Exception("Received a nonzero STATUS_CODE: {}".format(received_status))

    # Clearing callback related to the interrupt that was unregistered
    if (port, pin) not in callback_dict:
        raise KeyError("No interrupt registered on given port and pin")

    del callback_dict[(port, pin)]
Exemple #6
0
def gpio_set(port, pin, state):
    """
    Sets a GPIO pin
    Args:
        port: port of the GPIO pin to set (can be entered as either a string or integer)
        pin: pin number of the GPIO pin to set
        state: 0 if setting low, 1 if setting high
    Raises:
        AttributeError: if port parameter is entered as a string and is invalid
        ValueError: if parameters passed into gpio_set are invalid
        Exception: if a non-zero status code was received
    """

    # If given port as a string, it is converted to a string
    if isinstance(port, str):
        port = getattr(GpioPort, port.capitalize())

    # Checks whether valid parameters were passed into gpio_set
    if port < 0 or port >= GpioPort.NUM_GPIO_PORTS:
        raise ValueError("Expected port between A and {}".format(
            chr(GpioPort.NUM_GPIO_PORTS + ord('A') - 1)
        ))
    if pin < 0 or pin >= NUM_PINS_PER_PORT:
        raise ValueError("Expected pin between 0 and {}".format(NUM_PINS_PER_PORT - 1))
    if state not in (0, 1):
        raise ValueError("Expected state of 0 (low) or 1 (high)")

    can_pack_args = [(port, 1), (pin, 1), (state, 1)]

    can_util.send_message(
        babydriver_id=BabydriverMessageId.GPIO_SET,
        data=can_util.can_pack(can_pack_args)
    )

    gpio_set_status = can_util.next_message(babydriver_id=BabydriverMessageId.STATUS)

    # Checks for valid status code
    if gpio_set_status.data[1] != 0:
        raise Exception("Received STATUS_CODE {}".format(gpio_set_status.data[1]))
def register_gpio_interrupt(port, pin, edge=InterruptEdge.RISING_AND_FALLING, callback=None):
    """
    Registers a gpio interrupt on a pin

    Args:
        port: port of the GPIO pin to register an interrupt in
        pin: pin number of the GPIO pin to register an interrupt in
        edge: callback function is called when this interrupt edge occurs. Can be entered as a
              string or a number (RISING (0), FALLING (1) or RISING_AND_FALLING (2))
        callback: if callback is None, a default callback function will be called
                  that prints "Interrupt on P<port><pin>: <edge>" in this format.
                  The user-defined callback function should follow this format:
                  function_name(info) where the only parameter is info,
                  a named tuple which holds the port (info.port),pin (info.pin) and
                  edge (info.edge) of the GPIO interrupt that occured.

    Raises:
        Value error: if the parameters passed into register_gpio_interrupt are incorrect
        Attribute error: if the port parameter or interrupt edge is invalid (refer gpio_port.py
                         for acceptable port parameters and InterruptEdge for appropriate edge
                         parameters)
        Exception: if a non-zero status code is received when attempting to register an interrupt
        Type error: if the callback function (called when interrupt occurs) is of incorrect format

    Note: There is a hard STM32 limit that only one GPIO interrupt can be registered at a time
    per pin number. For example, PA2 and PB2 would share the same GPIO pin.
    """

    if isinstance(port, str):
        port = getattr(GpioPort, port.capitalize())

    if port < 0 or port >= GpioPort.NUM_GPIO_PORTS:
        raise ValueError("invalid GPIO port (Range: 'A' - 'F')")

    if pin < 0 or pin >= NUM_PINS_PER_PORT:
        raise ValueError("Invalid GPIO pin number (Range: 0 - 15)")

    if isinstance(edge, str):
        if edge.upper() not in InterruptEdge.__members__:
            raise AttributeError("Enter 'RISING', 'FALLING' or 'RISING_AND_FALLING' for "
                                 "interrupt edge")
        edge = InterruptEdge[edge.upper()]

    if edge < 0 or edge >= InterruptEdge.NUM_INTERRUPT_EDGES:
        raise ValueError("invalid interrupt edge (enter 0 (Rising), 1 (Falling) "
                         "or 2 (Rising_and_falling")

    if callback is not None and not callable(callback):
        raise ValueError("invalid callback function")

    msg_data_register_gpio_interrupt = [(port, 1), (pin, 1), (edge, 1)]

    can_util.send_message(
        babydriver_id=message_defs.BabydriverMessageId.GPIO_IT_REGISTER_COMMAND,
        data=can_util.can_pack(msg_data_register_gpio_interrupt)
    )

    status_message = can_util.next_message(babydriver_id=message_defs.BabydriverMessageId.STATUS)
    received_status = status_message.data[1]

    # Check if status is invalid (0 refers to STATUS_CODE_OK)
    if received_status != 0:
        raise Exception("received a nonzero STATUS_CODE: {}".format(received_status))

    # Adding callback to dictionary to be called upon when interrupt occurs
    if callback is None:
        callback_dict[(port, pin)] = default_callback
    else:
        callback_dict[(port, pin)] = callback
Exemple #8
0
def spi_exchange(tx_bytes,
                 rx_len,
                 spi_port=1,
                 spi_mode=0,
                 baudrate=6000000,
                 cs=None):
    """
    Sends data through SPI
        The tx_bytes is an int array
        The rx_len is a non-negative int.
        The spi_port is a non-negative int.
        The spi_mode is a non-negative int.
        The baudrate is a non-negative int.
        The CS pin, if not None, is a tuple (port, pin), where pin is an
            int in [0, NUM_PINS_PER_PORT) and port is either an int in [0, NUM_GPIO_PORTS),
            or a string 'a' through 'f', case insensitive
    Args:
        tx_bytes: The bytes to TX.
        rx_len: Number of bytes to RX
        spi_port: port of the pin to perform SPI exchange on (1 or 2)
        spi_mode: SPI mode to use (0, 1, 2 or 3)
        baudrate: baudrate to use (defaults to 6000000)
        cs: chip select port and pin to use (defaults to (1, 1))
    Raises:
        AttributeError: if cs_port parameter is entered as a string and is invalid
        ValueError: if spi_port, spi_mode, rx_len, cs_port, cs_pin don't meet their requirements
        Exception: if a non-zero status code was received.
    """

    if spi_port not in (1, 2):
        raise ValueError("ERROR: Expected SPI port 1 or 2")
    if spi_mode not in (0, 1, 2, 3):
        raise ValueError("ERROR: Expected mode between 0 and 3")
    # pylint: disable=superfluous-parens
    if not (0 <= len(tx_bytes) <= 255):
        raise ValueError(
            "ERROR: numbers of tx_bytes must be between 0 and 255")
    # pylint: disable=superfluous-parens
    if not (0 <= rx_len <= 255):
        raise ValueError(
            "ERROR: rx_len must be a non-negative integer between 0 and 255")
    if baudrate < 0:
        raise ValueError("ERROR: baudrate must be a non-negative integer")

    cs_port = 0 if cs is None else cs[0]
    cs_pin = 0 if cs is None else cs[1]
    use_cs = 0 if cs is None else 1
    if isinstance(cs_port, str):
        cs_port = getattr(GpioPort, cs_port.capitalize())

    if cs_port < 0 or cs_port >= GpioPort.NUM_GPIO_PORTS:
        raise ValueError("ERROR: Expected CS port between A and {}".format(
            chr(GpioPort.NUM_GPIO_PORTS + ord('A') - 1)))
    if cs_pin < 0 or cs_pin >= GPIO_PINS_PER_PORT:
        raise ValueError("ERROR: Expected CS pin between 0 and {}".format(
            GPIO_PINS_PER_PORT - 1))

    data1 = [
        (spi_port - 1, 1),  # 0 for SPI_PORT 1, 1 for SPI_PORT_2
        (spi_mode, 1),
        (len(tx_bytes), 1),  # tx_len
        (rx_len, 1),
        (cs_port, 1),
        (cs_pin, 1),
        (use_cs, 1),
    ]

    data2 = [(baudrate, 4)]

    can_util.send_message(
        babydriver_id=BabydriverMessageId.SPI_EXCHANGE_METADATA_1,
        data=can_util.can_pack(data1))

    can_util.send_message(
        babydriver_id=BabydriverMessageId.SPI_EXCHANGE_METADATA_2,
        data=can_util.can_pack(data2))

    # collect bytes into groups of 7
    chunks = [tx_bytes[x:x + 7] for x in range(0, len(tx_bytes), 7)]

    for data in chunks:
        # pad with 0s if length isn't 7
        if len(data) < 7:
            data += [0] * (7 - len(data))
        tmp = [(d, 1) for d in data]
        can_util.send_message(
            babydriver_id=BabydriverMessageId.SPI_EXCHANGE_TX_DATA,
            data=can_util.can_pack(tmp))

    # Receive bytes
    rx_bytes_left = rx_len
    rx_data = []
    while rx_bytes_left > 0:
        rx_msg = can_util.next_message(
            babydriver_id=BabydriverMessageId.SPI_EXCHANGE_RX_DATA)
        msg_data = rx_msg.data
        # list constructor to handle bytearrays
        rx_data += list(msg_data[1:min(rx_bytes_left + 1, 8)])
        rx_bytes_left -= 7

    status = can_util.next_message(babydriver_id=BabydriverMessageId.STATUS)

    if status.data[1] != 0:
        raise Exception("ERROR: Received non-zero status code: {}".format(
            status.data[1]))

    return rx_data