Chip driver writing style guide

CircuitPython on hardware including Adafruit's boards, and CircuitPython libraries using Blinka on host computers.

Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.
Post Reply
User avatar
William8
 
Posts: 28
Joined: Thu Jul 14, 2022 4:00 pm

Chip driver writing style guide

Post by William8 »

Appreciate pointers to articles explaining the "SOFTWARE writing style and FORMAT" of driver. Assume the writer is hardware engineer and fully capable of reading the data sheet, register bit, etc. Many thanks

User avatar
William8
 
Posts: 28
Joined: Thu Jul 14, 2022 4:00 pm

Re: Chip driver writing style guide

Post by William8 »

Specifically, how to port to Circuit python, from
(a) the Arduino lib, by Ladyada and also
(b) another Micropython lib as shown below

Specifically, I am interested in SETTING gain, set ENABLE of the multiple photo diode sensor (big, small, visible and Infra) and the 4 coeff to ENABLE the chip to mathematically calculate UV index based on vis and ir readings

ARDUINO LIB, https://github.com/adafruit/Adafruit_SI1145_Library

Code: Select all

/***************************************************
  This is a library for the Si1145 UV/IR/Visible Light Sensor

  Designed specifically to work with the Si1145 sensor in the
  adafruit shop
  ----> https://www.adafruit.com/products/1777

  These sensors use I2C to communicate, 2 pins are required to
  interface
  Adafruit invests time and resources providing this open source code,
  please support Adafruit and open-source hardware by purchasing
  products from Adafruit!

  Written by Limor Fried/Ladyada for Adafruit Industries.
  BSD license, all text above must be included in any redistribution
 ****************************************************/

#include "Adafruit_SI1145.h"

/**
 * @brief Destructor
 *
 */
Adafruit_SI1145::~Adafruit_SI1145() {
  if (i2c_dev)
    delete i2c_dev;
}
/**
 * @brief Construct a new Adafruit_SI1145::Adafruit_SI1145 object
 *
 */
Adafruit_SI1145::Adafruit_SI1145() {}
/**
 * @brief Initize the driver, specifying the `TwoWire` bus to use
 *
 * @param pBus Pointer to the `TwoWire` I2C bus to use
 * @return boolean true: success false: failure to initialize the sensor
 */
boolean Adafruit_SI1145::begin(TwoWire *pBus) {
  return begin(SI1145_ADDR, pBus);
}
/**
 * @brief Initize the driver, supplying both a `TwoWire` bus and I2C address
 *
 * @param addr The I2C address of the Sensor
 * @param pBus Pointer to the `TwoWire` instance to use
 * @return boolean true: success false: failure to initize the sensor
 */
boolean Adafruit_SI1145::begin(uint8_t addr, TwoWire *pBus) {
  if (i2c_dev)
    delete i2c_dev;
  i2c_dev = new Adafruit_I2CDevice(addr, pBus);
  if (!i2c_dev->begin()) {
    return false;
  }

  uint8_t id = read8(SI1145_REG_PARTID);
  if (id != 0x45)
    return false; // look for SI1145

  reset();

  /***********************************/
  // enable UVindex measurement coefficients!
  write8(SI1145_REG_UCOEFF0, 0x29);
  write8(SI1145_REG_UCOEFF1, 0x89);
  write8(SI1145_REG_UCOEFF2, 0x02);
  write8(SI1145_REG_UCOEFF3, 0x00);

  // enable UV sensor
  writeParam(SI1145_PARAM_CHLIST,
             SI1145_PARAM_CHLIST_ENUV | SI1145_PARAM_CHLIST_ENALSIR |
                 SI1145_PARAM_CHLIST_ENALSVIS | SI1145_PARAM_CHLIST_ENPS1);
  // enable interrupt on every sample
  write8(SI1145_REG_INTCFG, SI1145_REG_INTCFG_INTOE);
  write8(SI1145_REG_IRQEN, SI1145_REG_IRQEN_ALSEVERYSAMPLE);

  /****************************** Prox Sense 1 */

  // program LED current
  write8(SI1145_REG_PSLED21, 0x03); // 20mA for LED 1 only
  writeParam(SI1145_PARAM_PS1ADCMUX, SI1145_PARAM_ADCMUX_LARGEIR);
  // prox sensor #1 uses LED #1
  writeParam(SI1145_PARAM_PSLED12SEL, SI1145_PARAM_PSLED12SEL_PS1LED1);
  // fastest clocks, clock div 1
  writeParam(SI1145_PARAM_PSADCGAIN, 0);
  // take 511 clocks to measure
  writeParam(SI1145_PARAM_PSADCOUNTER, SI1145_PARAM_ADCCOUNTER_511CLK);
  // in prox mode, high range
  writeParam(SI1145_PARAM_PSADCMISC,
             SI1145_PARAM_PSADCMISC_RANGE | SI1145_PARAM_PSADCMISC_PSMODE);

  writeParam(SI1145_PARAM_ALSIRADCMUX, SI1145_PARAM_ADCMUX_SMALLIR);
  // fastest clocks, clock div 1
  writeParam(SI1145_PARAM_ALSIRADCGAIN, 0);
  // take 511 clocks to measure
  writeParam(SI1145_PARAM_ALSIRADCOUNTER, SI1145_PARAM_ADCCOUNTER_511CLK);
  // in high range mode
  writeParam(SI1145_PARAM_ALSIRADCMISC, SI1145_PARAM_ALSIRADCMISC_RANGE);

  // fastest clocks, clock div 1
  writeParam(SI1145_PARAM_ALSVISADCGAIN, 0);
  // take 511 clocks to measure
  writeParam(SI1145_PARAM_ALSVISADCOUNTER, SI1145_PARAM_ADCCOUNTER_511CLK);
  // in high range mode (not normal signal)
  writeParam(SI1145_PARAM_ALSVISADCMISC, SI1145_PARAM_ALSVISADCMISC_VISRANGE);

  /************************/

  // measurement rate for auto
  write8(SI1145_REG_MEASRATE0, 0xFF); // 255 * 31.25uS = 8ms

  // auto run
  write8(SI1145_REG_COMMAND, SI1145_PSALS_AUTO);

  return true;
}
/**
 * @brief Reset the sensor's registers to an initial state
 *
 */
void Adafruit_SI1145::reset() {
  write8(SI1145_REG_MEASRATE0, 0);
  write8(SI1145_REG_MEASRATE1, 0);
  write8(SI1145_REG_IRQEN, 0);
  write8(SI1145_REG_IRQMODE1, 0);
  write8(SI1145_REG_IRQMODE2, 0);
  write8(SI1145_REG_INTCFG, 0);
  write8(SI1145_REG_IRQSTAT, 0xFF);

  write8(SI1145_REG_COMMAND, SI1145_RESET);
  delay(10);
  write8(SI1145_REG_HWKEY, 0x17);

  delay(10);
}

//////////////////////////////////////////////////////

/**
 * @brief Get the current UV reading
 *
 * @return uint16_t The the UV index * 100 (divide by 100 to get the index)
 */
uint16_t Adafruit_SI1145::readUV(void) { return read16(0x2C); }

/**
 * @brief Get the Visible & IR light levels
 *
 * @return uint16_t The Visible & IR light levels
 */
uint16_t Adafruit_SI1145::readVisible(void) { return read16(0x22); }

// returns IR light levels
/**
 * @brief Get the Infrared light level
 *
 * @return uint16_t The Infrared light level
 */
uint16_t Adafruit_SI1145::readIR(void) { return read16(0x24); }

// returns "Proximity" - assumes an IR LED is attached to LED
/**
 * @brief Gets the Proximity measurement - **Requires an attached IR LED**
 *
 * @return uint16_t The proximity measurement
 */
uint16_t Adafruit_SI1145::readProx(void) { return read16(0x26); }

/*********************************************************************/

uint8_t Adafruit_SI1145::writeParam(uint8_t p, uint8_t v) {
  // Serial.print("Param 0x"); Serial.print(p, HEX);
  // Serial.print(" = 0x"); Serial.println(v, HEX);

  write8(SI1145_REG_PARAMWR, v);
  write8(SI1145_REG_COMMAND, p | SI1145_PARAM_SET);
  return read8(SI1145_REG_PARAMRD);
}

uint8_t Adafruit_SI1145::readParam(uint8_t p) {
  write8(SI1145_REG_COMMAND, p | SI1145_PARAM_QUERY);
  return read8(SI1145_REG_PARAMRD);
}

/*********************************************************************/

uint8_t Adafruit_SI1145::read8(uint8_t reg) {
  uint8_t buffer[1] = {reg};
  i2c_dev->write_then_read(buffer, 1, buffer, 1);
  return buffer[0];
}

uint16_t Adafruit_SI1145::read16(uint8_t a) {
  uint8_t buffer[2] = {a, 0};
  i2c_dev->write_then_read(buffer, 1, buffer, 2);
  return ((uint16_t)buffer[0]) | ((uint16_t)buffer[1] << 8);
}

void Adafruit_SI1145::write8(uint8_t reg, uint8_t val) {
  uint8_t buffer[2] = {reg, val};
  i2c_dev->write(buffer, 2);
}




==================================================================================
=============================== Micropython Lib ==================================== 
"""
 MicroPython driver for SI1145 light I2C sensor, developer version :
 Version: 0.3.0 @ 2018/03/20
 Author: Nelio Goncalves Godoi 2017 < neliogodoi@yahoo.com.br >
 Stylized According to PEP8: 18/01/2018
 Based on the work by author Joe Gutting (2014)
    (https://github.com/THP-JOE/Python_SI1145)
"""

import time
from ustruct import unpack

# COMMANDS
SI1145_PARAM_QUERY = 0x80
SI1145_PARAM_SET = 0xA0
SI1145_NOP = 0x0
SI1145_RESET = 0x01
SI1145_BUSADDR = 0x02
SI1145_PS_FORCE = 0x05
SI1145_ALS_FORCE = 0x06
SI1145_PSALS_FORCE = 0x07
SI1145_PS_PAUSE = 0x09
SI1145_ALS_PAUSE = 0x0A
SI1145_PSALS_PAUSE = 0xB
SI1145_PS_AUTO = 0x0D
SI1145_ALS_AUTO = 0x0E
SI1145_PSALS_AUTO = 0x0F
SI1145_GET_CAL = 0x12
# Parameters
SI1145_PARAM_I2CADDR = 0x00
SI1145_PARAM_CHLIST = 0x01
SI1145_PARAM_CHLIST_ENUV = 0x80
SI1145_PARAM_CHLIST_ENAUX = 0x40
SI1145_PARAM_CHLIST_ENALSIR = 0x20
SI1145_PARAM_CHLIST_ENALSVIS = 0x10
SI1145_PARAM_CHLIST_ENPS1 = 0x01
SI1145_PARAM_CHLIST_ENPS2 = 0x02
SI1145_PARAM_CHLIST_ENPS3 = 0x04
SI1145_PARAM_PSLED12SEL = 0x02
SI1145_PARAM_PSLED12SEL_PS2NONE = 0x00
SI1145_PARAM_PSLED12SEL_PS2LED1 = 0x10
SI1145_PARAM_PSLED12SEL_PS2LED2 = 0x20
SI1145_PARAM_PSLED12SEL_PS2LED3 = 0x40
SI1145_PARAM_PSLED12SEL_PS1NONE = 0x00
SI1145_PARAM_PSLED12SEL_PS1LED1 = 0x01
SI1145_PARAM_PSLED12SEL_PS1LED2 = 0x02
SI1145_PARAM_PSLED12SEL_PS1LED3 = 0x04
SI1145_PARAM_PSLED3SEL = 0x03
SI1145_PARAM_PSENCODE = 0x05
SI1145_PARAM_ALSENCODE = 0x06
SI1145_PARAM_PS1ADCMUX = 0x07
SI1145_PARAM_PS2ADCMUX = 0x08
SI1145_PARAM_PS3ADCMUX = 0x09
SI1145_PARAM_PSADCOUNTER = 0x0A
SI1145_PARAM_PSADCGAIN = 0x0B
SI1145_PARAM_PSADCMISC = 0x0C
SI1145_PARAM_PSADCMISC_RANGE = 0x20
SI1145_PARAM_PSADCMISC_PSMODE = 0x04
SI1145_PARAM_ALSIRADCMUX = 0x0E
SI1145_PARAM_AUXADCMUX = 0x0F
SI1145_PARAM_ALSVISADCOUNTER = 0x10
SI1145_PARAM_ALSVISADCGAIN = 0x11
SI1145_PARAM_ALSVISADCMISC = 0x12
SI1145_PARAM_ALSVISADCMISC_VISRANGE = 0x20
SI1145_PARAM_ALSIRADCOUNTER = 0x1D
SI1145_PARAM_ALSIRADCGAIN = 0x1E
SI1145_PARAM_ALSIRADCMISC = 0x1F
SI1145_PARAM_ALSIRADCMISC_RANGE = 0x20
SI1145_PARAM_ADCCOUNTER_511CLK = 0x70
SI1145_PARAM_ADCMUX_SMALLIR = 0x00
SI1145_PARAM_ADCMUX_LARGEIR = 0x03
# REGISTERS
SI1145_REG_PARTID = 0x00
SI1145_REG_REVID = 0x01
SI1145_REG_SEQID = 0x02
SI1145_REG_INTCFG = 0x03
SI1145_REG_INTCFG_INTOE = 0x01
SI1145_REG_INTCFG_INTMODE = 0x02
SI1145_REG_IRQEN = 0x04
SI1145_REG_IRQEN_ALSEVERYSAMPLE = 0x01
SI1145_REG_IRQEN_PS1EVERYSAMPLE = 0x04
SI1145_REG_IRQEN_PS2EVERYSAMPLE = 0x08
SI1145_REG_IRQEN_PS3EVERYSAMPLE = 0x10
SI1145_REG_IRQMODE1 = 0x05
SI1145_REG_IRQMODE2 = 0x06
SI1145_REG_HWKEY = 0x07
SI1145_REG_MEASRATE0 = 0x08
SI1145_REG_MEASRATE1 = 0x09
SI1145_REG_PSRATE = 0x0A
SI1145_REG_PSLED21 = 0x0F
SI1145_REG_PSLED3 = 0x10
SI1145_REG_UCOEFF0 = 0x13
SI1145_REG_UCOEFF1 = 0x14
SI1145_REG_UCOEFF2 = 0x15
SI1145_REG_UCOEFF3 = 0x16
SI1145_REG_PARAMWR = 0x17
SI1145_REG_COMMAND = 0x18
SI1145_REG_RESPONSE = 0x20
SI1145_REG_IRQSTAT = 0x21
SI1145_REG_IRQSTAT_ALS = 0x01
SI1145_REG_ALSVISDATA0 = 0x22
SI1145_REG_ALSVISDATA1 = 0x23
SI1145_REG_ALSIRDATA0 = 0x24
SI1145_REG_ALSIRDATA1 = 0x25
SI1145_REG_PS1DATA0 = 0x26
SI1145_REG_PS1DATA1 = 0x27
SI1145_REG_PS2DATA0 = 0x28
SI1145_REG_PS2DATA1 = 0x29
SI1145_REG_PS3DATA0 = 0x2A
SI1145_REG_PS3DATA1 = 0x2B
SI1145_REG_UVINDEX0 = 0x2C
SI1145_REG_UVINDEX1 = 0x2D
SI1145_REG_PARAMRD = 0x2E
SI1145_REG_CHIPSTAT = 0x30
SI1145_ADDR = 0x60  # I2C Address


class SI1145(object):
    """Driver for SI1145 sensor"""

    def __init__(self, i2c=None, addr=SI1145_ADDR):
        if i2c is None:
            raise ValueError('An I2C object is required.')
        self._i2c = i2c
        self._addr = addr
        self._reset()  # reset device
        self._load_calibration()  # Load calibration values

    def _read8(self, register):
        result = unpack(
            'B',
            self._i2c.readfrom_mem(
                self._addr, register, 1)
        )[0] & 0xFF
        return result

    def _read16(self, register, little_endian=True):
        result = unpack('BB', self._i2c.readfrom_mem(self._addr, register, 2))
        result = ((result[1] << 8) | (result[0] & 0xFF))
        if not little_endian:
            result = ((result << 8) & 0xFF00) + (result >> 8)
        return result

    def _write8(self, register, value):
        value = value & 0xFF
        self._i2c.writeto_mem(self._addr, register, bytes([value]))

    def _reset(self):
        """Device reset"""
        self._write8(SI1145_REG_MEASRATE0, 0x00)
        self._write8(SI1145_REG_MEASRATE1, 0x00)
        self._write8(SI1145_REG_IRQEN, 0x00)
        self._write8(SI1145_REG_IRQMODE1, 0x00)
        self._write8(SI1145_REG_IRQMODE2, 0x00)
        self._write8(SI1145_REG_INTCFG, 0x00)
        self._write8(SI1145_REG_IRQSTAT, 0xFF)
        self._write8(SI1145_REG_COMMAND, SI1145_RESET)
        time.sleep(.01)
        self._write8(SI1145_REG_HWKEY, 0x17)
        time.sleep(.01)

    # write Param
    def _write_param(self, parameter, value):
        self._write8(SI1145_REG_PARAMWR, value)
        self._write8(SI1145_REG_COMMAND, parameter | SI1145_PARAM_SET)
        param_val = self._read8(SI1145_REG_PARAMRD)
        return param_val

    # load calibration to sensor
    def _load_calibration(self):
        # Enable UVindex measurement coefficients!
        self._write8(SI1145_REG_UCOEFF0, 0x7B)
        self._write8(SI1145_REG_UCOEFF1, 0x6B)
        self._write8(SI1145_REG_UCOEFF2, 0x01)
        self._write8(SI1145_REG_UCOEFF3, 0x00)

        # Enable UV sensor
        self._write_param(
            SI1145_PARAM_CHLIST,
            SI1145_PARAM_CHLIST_ENUV |
            SI1145_PARAM_CHLIST_ENAUX |
            SI1145_PARAM_CHLIST_ENALSIR |
            SI1145_PARAM_CHLIST_ENALSVIS |
            SI1145_PARAM_CHLIST_ENPS1)
        # Enable interrupt on every sample
        self._write8(SI1145_REG_INTCFG, SI1145_REG_INTCFG_INTOE)
        self._write8(SI1145_REG_IRQEN, SI1145_REG_IRQEN_ALSEVERYSAMPLE)

        # /***** Prox Sense 1 */
        # Program LED current
        # 20mA for LED 1 only
        self._i2c.writeto_mem(SI1145_ADDR, SI1145_REG_PSLED21, b'0x03')
        self._write_param(
            SI1145_PARAM_PS1ADCMUX,
            SI1145_PARAM_ADCMUX_LARGEIR)

        # Prox sensor #1 uses LED #1
        self._write_param(
            SI1145_PARAM_PSLED12SEL,
            SI1145_PARAM_PSLED12SEL_PS1LED1)

        # Fastest clocks, clock div 1
        self._write_param(SI1145_PARAM_PSADCGAIN, 0)

        # Take 511 clocks to measure
        self._write_param(
            SI1145_PARAM_PSADCOUNTER,
            SI1145_PARAM_ADCCOUNTER_511CLK)

        # in prox mode, high range
        self._write_param(
            SI1145_PARAM_PSADCMISC,
            SI1145_PARAM_PSADCMISC_RANGE |
            SI1145_PARAM_PSADCMISC_PSMODE)

        self._write_param(
            SI1145_PARAM_ALSIRADCMUX,
            SI1145_PARAM_ADCMUX_SMALLIR)

        # Fastest clocks, clock div 1
        self._write_param(SI1145_PARAM_ALSIRADCGAIN, 0)

        # Take 511 clocks to measure
        self._write_param(
            SI1145_PARAM_ALSIRADCOUNTER,
            SI1145_PARAM_ADCCOUNTER_511CLK)

        # in high range mode
        self._write_param(
            SI1145_PARAM_ALSIRADCMISC,
            SI1145_PARAM_ALSIRADCMISC_RANGE)

        # fastest clocks, clock div 1
        self._write_param(SI1145_PARAM_ALSVISADCGAIN, 0)

        # Take 511 clocks to measure
        self._write_param(
            SI1145_PARAM_ALSVISADCOUNTER,
            SI1145_PARAM_ADCCOUNTER_511CLK)

        # in high range mode (not normal signal)
        self._write_param(
            SI1145_PARAM_ALSVISADCMISC,
            SI1145_PARAM_ALSVISADCMISC_VISRANGE)

        # measurement rate for auto
        self._write8(SI1145_REG_MEASRATE0, 0xFF)  # 255 * 31.25uS = 8ms
        self._write8(SI1145_REG_COMMAND, SI1145_PSALS_AUTO)  # auto run

    @property
    def read_uv(self):
        """Returns the UV index"""
        return self._read16(0x2C, little_endian=True) / 100

    @property
    def read_visible(self):
        """Returns visible + IR light levels"""
        return self._read16(0x22, little_endian=True)

    @property
    def read_ir(self):
        """Returns IR light levels"""
        return self._read16(0x24, little_endian=True)

    @property
    def read_prox(self):
        """Returns "Proximity" - assumes an IR LED is attached to LED"""
        return self._read16(0x26, little_endian=True)
Last edited by adafruit_support_carter on Sun Aug 07, 2022 1:12 pm, edited 1 time in total.
Reason: added [code] tags

User avatar
adafruit_support_carter
 
Posts: 24414
Joined: Tue Nov 29, 2016 2:45 pm

Re: Chip driver writing style guide

Post by adafruit_support_carter »

FWIW, there's a CircuitPython driver for the SI1145 here:
https://github.com/adafruit/Adafruit_Ci ... hon_SI1145

It's fairly new, so is still having functionality added to it.

Post Reply
Please be positive and constructive with your questions and comments.

Return to “Adafruit CircuitPython”