Merge branch 'rpi'
authorDobrica Pavlinusic <dpavlin@rot13.org>
Fri, 1 Dec 2017 18:58:54 +0000 (19:58 +0100)
committerDobrica Pavlinusic <dpavlin@rot13.org>
Fri, 1 Dec 2017 18:58:54 +0000 (19:58 +0100)
README.md
i2c-userspace/ina219.c [new file with mode: 0644]
i2c-userspace/tmp.c [new file with mode: 0644]
overlay-load.sh [new file with mode: 0755]

index 2f90f4c..dc2adcc 100644 (file)
--- a/README.md
+++ b/README.md
@@ -12,3 +12,8 @@ on your board to make wiring easier.
 
 For device tree information, best source right now is this presentation:
 https://elinux.org/images/d/dc/Elce_2017_dt_bof.pdf
+
+
+device-tree/ directory contains examples
+
+i2c-usersapce/ contains random i2c userspace device drivers
diff --git a/i2c-userspace/ina219.c b/i2c-userspace/ina219.c
new file mode 100644 (file)
index 0000000..2247c8b
--- /dev/null
@@ -0,0 +1,403 @@
+/*
+* INA219 util - part of PowerCape by AndiceLabs
+*
+* Copyright (C) 2014  AndiceLabs admin@andicelabs.com  http://andicelabs.com
+* Copyright (C) 2014  Zig Fisher flyrouter@gmail.com   http://zftlab.org
+*
+* This program is free software; you can redistribute it and/or
+* modify it under the terms of the GNU General Public License
+* as published by the Free Software Foundation; either version 2
+* of the License, or (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU General Public License for more details.
+*
+*
+* Example run and output:
+*
+* OpenWRT:~# ina219 -b 0 -i 60
+*
+* 22:49 12168mV  134.2mA
+* 22:50 12168mV  239.9mA
+* 22:51 12168mV  134.7mA
+*
+*/
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <endian.h>
+#include <string.h>
+#include <time.h>
+#include <getopt.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <linux/i2c-dev.h>
+
+#define CONFIG_REG          0
+#define SHUNT_REG           1
+#define BUS_REG             2
+#define POWER_REG           3
+#define CURRENT_REG         4
+#define CALIBRATION_REG     5
+
+#define AVR_ADDRESS         0x21
+#define INA_ADDRESS         0x40
+
+typedef enum {
+    OP_DUMP,
+    OP_VOLTAGE,
+    OP_CURRENT,
+    OP_MONITOR,
+    OP_NONE
+} op_type;
+
+op_type operation = OP_DUMP;
+
+int interval = 60;
+int i2c_bus = 0;
+int i2c_address = INA_ADDRESS;
+int handle;
+int whole_numbers = 0;
+
+
+void msleep( int msecs )
+{
+    usleep( msecs * 1000 );
+}
+
+
+int i2c_read( void *buf, int len )
+{
+    int rc = 0;
+
+    if ( read( handle, buf, len ) != len )
+    {
+        printf( "I2C read failed: %s\n", strerror( errno ) );
+        rc = -1;
+    }
+
+    return rc;
+}
+
+
+int i2c_write( void *buf, int len )
+{
+    int rc = 0;
+
+    if ( write( handle, buf, len ) != len ) 
+    {
+        printf( "I2C write failed: %s\n", strerror( errno ) );
+        rc = -1;
+    }
+
+    return rc;
+}
+
+
+int register_read( unsigned char reg, unsigned short *data )
+{
+    int rc = -1;
+    unsigned char bite[ 4 ];
+
+    bite[ 0 ] = reg;
+    if ( i2c_write( bite, 1 ) == 0 )
+    {
+        if ( i2c_read( bite, 2 ) == 0 )
+        {
+            *data = ( bite[ 0 ] << 8 ) | bite[ 1 ];
+            rc = 0;
+        }
+    }
+
+    return rc;
+}
+
+
+int register_write( unsigned char reg, unsigned short data )
+{
+    int rc = -1;
+    unsigned char bite[ 4 ];
+
+    bite[ 0 ] = reg;
+    bite[ 1 ] = ( data >> 8 ) & 0xFF;
+    bite[ 2 ] = ( data & 0xFF );
+
+    if ( i2c_write( bite, 3 ) == 0 )
+    {
+        rc = 0;
+    }
+
+    return rc;
+}
+
+
+void show_usage( char *progname )
+{
+    fprintf( stderr, "Usage: %s <mode> \n", progname );
+    fprintf( stderr, "   Mode (required):\n" );
+    fprintf( stderr, "      -h --help           Show usage.\n" );
+    fprintf( stderr, "      -i --interval       Set interval for monitor mode.\n" );
+    fprintf( stderr, "      -w --whole          Show whole numbers only. Useful for scripts.\n" );
+    fprintf( stderr, "      -v --voltage        Show battery voltage in mV.\n" );
+    fprintf( stderr, "      -c --current        Show battery current in mA.\n" );
+    fprintf( stderr, "      -a --address <addr> Override I2C address of INA219 from default of 0x%02X.\n", i2c_address );
+    fprintf( stderr, "      -b --bus <i2c bus>  Override I2C bus from default of %d.\n", i2c_bus );
+    exit( 1 );
+}
+
+
+void parse( int argc, char *argv[] )
+{
+    while( 1 )
+    {
+        static const struct option lopts[] =
+        {
+            { "address",    0, 0, 'a' },
+            { "bus",        0, 0, 'b' },
+            { "current",    0, 0, 'c' },
+            { "help",       0, 0, 'h' },
+            { "interval",   0, 0, 'i' },
+            { "voltage",    0, 0, 'v' },
+            { "whole",      0, 0, 'w' },
+            { NULL,         0, 0, 0 },
+        };
+        int c;
+
+        c = getopt_long( argc, argv, "a:b:chi:vw", lopts, NULL );
+
+        if( c == -1 )
+            break;
+
+        switch( c )
+        {
+            case 'a':
+            {
+                errno = 0;
+                i2c_address = (int)strtol( optarg, NULL, 0 );
+                if ( errno != 0 )
+                {
+                    fprintf( stderr, "Unknown address parameter %s.\n", optarg );
+                    exit( 1 );
+                }
+                break;
+            }
+
+            case 'b':
+            {
+                errno = 0;
+                i2c_bus = (int)strtol( optarg, NULL, 0 );
+                if ( errno != 0 )
+                {
+                    fprintf( stderr, "Unknown bus parameter %s.\n", optarg );
+                    exit( 1 );
+                }
+                break;
+            }
+
+            case 'c':
+            {
+                operation = OP_CURRENT;
+                break;
+            }
+
+            default:
+            case 'h':
+            {
+                operation = OP_NONE;
+                show_usage( argv[ 0 ] );
+                break;
+            }
+
+            case 'i':
+            {
+                operation = OP_MONITOR;
+                interval = atoi( optarg );
+                if ( ( interval == 0 ) && ( errno != 0 ) )
+                {
+                    fprintf( stderr, "Invalid interval value\n" );
+                    exit( 1 );
+                }
+                break;
+            }
+
+            case 'v':
+            {
+                operation = OP_VOLTAGE;
+                break;
+            }
+
+            case 'w':
+            {
+                whole_numbers = 1;
+                break;
+            }
+        }
+    }
+}
+
+
+int get_voltage( float *mv )
+{
+    short bus;
+
+    if ( register_read( BUS_REG, (unsigned short*)&bus ) != 0 )
+    {
+        return -1;
+    }
+
+    *mv = ( float )( ( bus & 0xFFF8 ) >> 1 );
+    return 0;
+}
+
+
+int get_current( float *ma )
+{
+    short shunt;
+
+    if ( register_read( SHUNT_REG, &shunt ) != 0 )
+    {
+        return -1;
+    }
+
+    *ma = (float)shunt / 10;
+    return 0;
+}
+
+
+void show_current( void )
+{
+    float ma;
+
+    if ( get_current( &ma ) )
+    {
+        fprintf( stderr, "Error reading current\n" );
+        return;
+    }
+
+    if ( whole_numbers )
+    {
+        printf( "%4.0f\n", ma );
+    }
+    else
+    {
+        printf( "%04.1f\n", ma );
+    }
+}
+
+
+void show_voltage( void )
+{
+    float mv;
+
+    if ( get_voltage( &mv ) )
+    {
+        fprintf( stderr, "Error reading voltage\n" );
+        return;
+    }
+    printf( "%4.0f\n", mv );
+}
+
+
+void show_voltage_current( void )
+{
+    float mv, ma;
+
+    if ( get_current( &ma ) || get_voltage( &mv ) )
+    {
+        fprintf( stderr, "Error reading voltage/current\n" );
+        return;
+    }
+
+    if ( whole_numbers )
+    {
+        printf( "%04.0fmV  %4.0fmA\n", mv, ma );
+    }
+    else
+    {
+        printf( "%04.0fmV  %04.1fmA\n", mv, ma );
+    }
+    fflush(stdout);
+}
+
+
+void monitor( void )
+{
+    struct tm *tmptr;
+    time_t seconds;
+
+    while ( 1 )
+    {
+        seconds = time( NULL );
+        tmptr = localtime( &seconds );
+        printf( "%04d-%02d-%02dT%02d:%02d:%02d ",
+               tmptr->tm_year + 1900, tmptr->tm_mon +1, tmptr->tm_mday,
+               tmptr->tm_hour, tmptr->tm_min, tmptr->tm_sec );
+        show_voltage_current();
+        sleep( interval );
+    }
+}
+
+
+int main( int argc, char *argv[] )
+{
+    char filename[ 20 ];
+
+    parse( argc, argv );
+
+    snprintf( filename, 19, "/dev/i2c-%d", i2c_bus );
+    handle = open( filename, O_RDWR );
+    if ( handle < 0 ) 
+    {
+        fprintf( stderr, "Error opening bus %d: %s\n", i2c_bus, strerror( errno ) );
+        exit( 1 );
+    }
+
+    if ( ioctl( handle, I2C_SLAVE, i2c_address ) < 0 ) 
+    {
+        fprintf( stderr, "Error setting address %02X: %s\n", i2c_address, strerror( errno ) );
+        exit( 1 );
+    }
+
+    switch ( operation )
+    {
+        case OP_DUMP:
+        {
+            show_voltage_current();
+            break;
+        }
+
+        case OP_VOLTAGE:
+        {
+            show_voltage();
+            break;
+        }
+
+        case OP_CURRENT:
+        {
+            show_current();
+            break;
+        }
+
+        case OP_MONITOR:
+        {
+            monitor();
+            break;
+        }
+
+        default:
+        case OP_NONE:
+        {
+            break;
+        }
+    }
+
+    close( handle );
+    return 0;
+}
+
diff --git a/i2c-userspace/tmp.c b/i2c-userspace/tmp.c
new file mode 100644 (file)
index 0000000..0e04795
--- /dev/null
@@ -0,0 +1,237 @@
+// https://github.com/ManuelSchneid3r/RaspberryPi/raw/master/sensors/src/tmp.c
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <linux/i2c-dev.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <getopt.h>
+#include <math.h>
+
+void print_usage(char * appname) {
+    printf("Usage: %s  [--resolution|-r<0-3>][--faultqueue|-f<0-3>][--oneshot|--nooneshot][--activehigh|--activelow][--interruptmode|--comparatormode][--shutdownmode|--noshutdownmode][--query] <i2c char device> <hex address>\n", appname);
+}
+
+void print_help(char * appname) {
+  print_usage(appname);
+  printf(
+      "\n"
+      "\t--shutdownmode, --noshutdownmode\n"
+      "\t\tThe Shutdown Mode allows the user to save maximum power by shutting down all device circuitry other than the serial interface, which reduces current consumption to typically less than 0.1 μA.\n\n"
+      "\t--oneshot, --nooneshot\n"
+      "\t\tThe TMP175 and TMP75 feature a One-Shot Temperature Measurement Mode. When the device is in Shutdown Mode, writing 1 to the OS bit will start a single temperature conversion. The device will return to the shutdown state at the completion of the single conversion. This is useful to reduce power consumption in the TMP175 and TMP75 when continuous temperature monitoring is not required.\n\n"
+      "\t--interruptmode, --comparatormode\n"
+      "\t\tThe Thermostat Mode bit of the TMP175 and TMP75 indicates to the device whether to operate in Comparator Mode or Interrupt Mode (Alert pin). In Comparator mode, the ALERT pin is activated when the temperature equals or exceeds the value in the T(HIGH) register and it remains active until the temperature falls below the value in the T(LOW)register. In Interrupt mode, the ALERT pin is activated when the temperature exceeds T(HIGH) or goes below T(LOW) registers. The ALERT pin is cleared when the host controller reads the temperature register. For more information see the sensors datasheet.\n\n"
+      "\t--activehigh, --activelow\n"
+      "\t\tThe Polarity Bit of the TMP175 lets the user adjust the polarity of the ALERT pin output. If the POL bit is set to 0 (default), the ALERT pin becomes active low. When POL bit is set to 1, the ALERT pin becomes active high and the state of the ALERT pin is inverted.\n\n"
+      "\t--faultqueue, -f=N\n"
+      "\t\tA fault condition is defined as when the measured temperature exceeds the user-defined limits set in the THIGH and TLOW Registers. Additionally, the number of fault conditions required to generate an alert may be programmed using the Fault Queue. The Fault Queue is provided to prevent a false alert as a result of environmental noise. The Fault Queue requires consecutive fault measurements in order to trigger the alert function.\n\n"
+      "\t--resolution, -r={0, 1, 2, 3}\n"
+      "\t\tThe Converter Resolution Bits control the resolution of the internal analog-to-digital (ADC) converter. This control allows the user to maximize efficiency by programming for higher resolution or faster conversion time. \n\n"
+      "\tR\tRESOLUTION\t(TYPICAL) CONVERSION TIME \n"
+      "\t0\t0.5°C\t\t27.5 ms\n"
+      "\t1\t0.25°C\t\t55 ms\n"
+      "\t2\t0.125°C\t\t110 ms\n"
+      "\t3\t0.0625°C\t220 ms\n"
+      "\t--query, -q\n"
+      "\t\tQueries the current cofiguration.\n\n"
+      "\t--help\n"
+      "\t\tPrint this help\n\n"
+      "Author: Manuel Schneider manuelschneid3r@googles mail server\n"
+      );
+}
+
+void err_write(int fd, const void *buf, size_t count) {
+  if ((write(fd, buf, count)) != count) {
+    printf("Error writing to i2c slave.\n");
+    exit(1);
+  }
+}
+
+void err_read(int fd, void *buf, size_t count) {
+  if (read(fd, buf, count) != count) {
+    printf("Unable to read from slave.\n");
+    exit(1);
+  }
+}
+
+struct config_t {
+  int OS;   // Oneshot mode bool
+  int RES;  // Resolution 0-3
+  int FQ;   // Faultqueue 0-3
+  int POL;  // Polarity bool
+  int TM;   // Termostat xor
+  int SD;   // Shutdownmode bool
+};
+
+int main(int argc, char **argv)
+{
+  static struct config_t config = {-1,-1,-1,-1,-1,-1};
+  static int query = 0;
+  static int c;
+  static int help = 0;
+
+  static struct option long_options[] =
+  {
+    {"oneshot",         no_argument,       &config.OS,  1},
+    {"nooneshot",       no_argument,       &config.OS,  0},
+    {"resolution",      required_argument, 0,           'r'},
+    {"faultqueue",      required_argument, 0,           'f'},
+    {"activehigh",      no_argument,       &config.POL, 1},
+    {"activelow",       no_argument,       &config.POL, 0},
+    {"interruptmode",   no_argument,       &config.TM,  1},
+    {"comparatormode",  no_argument,       &config.TM,  0},
+    {"shutdownmode",    no_argument,       &config.SD,  1},
+    {"noshutdownmode",  no_argument,       &config.SD,  0},
+    {"query",           no_argument,       0,           'q'},
+    {"help",            no_argument,       &help,       1},
+    {0, 0, 0, 0}
+  };
+
+  /*
+   * Get the parameters and set the relevant flags for operation.
+   */
+  int option_index = 0;
+  while ((c = getopt_long (argc, argv, "qr:f:",
+                           long_options, &option_index)) != -1)
+  {
+    switch (c)
+    {
+    case 0:
+      // getopt_long set flag to val
+      break;
+    case 'q':
+      query = 1;
+      break;
+    case 'f':
+      config.FQ = atoi(optarg);
+      if (config.FQ < 0 || config.FQ > 3){
+        printf("Invalid fault queue parameter\n");
+        exit(1);
+      }
+      break;
+    case 'r':
+      config.RES = atoi(optarg);
+      if (config.RES < 0 || config.RES > 3){
+        printf("Invalid resoltion parameter\n");
+        exit(1);
+      }
+      break;
+    case '?':
+      /* getopt_long already printed an error message. */
+      return 1;
+    default:
+      abort ();
+    }
+  }
+
+  // Print help if requested
+  if (help==1) {
+    print_help(argv[0]);
+    exit(0);
+  }
+
+  // Print usage mesage in case of a wrong amount of params
+  if ((argc-optind) != 2){
+    print_usage(argv[0]);
+    exit(1);
+  }
+
+  /*
+   * Now start with the initializaton of the communication
+   */
+  // Open port for reading and writing
+  int fd;
+  if ((fd = open(argv[optind], O_RDWR)) < 0) {
+    printf("Failed to open i2c port. Root?\n");
+    exit(1);
+  }
+
+  // Set the port options and the address of the device we wish to speak to
+  int  address = strtol(argv[optind+1], NULL, 0);
+  if (ioctl(fd, I2C_SLAVE, address) < 0) {
+    printf("Unable to get bus access to talk to slave.\n");
+    exit(1);
+  }
+
+
+  /*
+   * If the program was called with any of the configuration parameters get the
+   * config resister, modifiy it and write it bac to the IC.
+   */
+  if (config.OS  != -1 || config.RES != -1 || config.FQ  != -1 ||
+      config.POL != -1 || config.TM  != -1 || config.SD  != -1) {
+    // Set register pointer on IC to config register
+    unsigned char buf[2] = {1, 0};
+    err_write(fd, buf, 1);
+
+    // Read configuration register
+    err_read(fd, buf+1, 1);
+
+    // Set or unset the bits
+    if (config.OS != -1)
+      config.OS ? buf[1]|(1<<7) : buf[1]&~(1<<7);
+    if (config.RES != -1){
+      buf[1]&=~(3<<5); // Unset the resolution
+      buf[1]|=config.RES<<5; //Set the resolution
+    }
+    if (config.FQ != -1){
+      buf[1]&=~(3<<3); // Unset the FQ
+      buf[1]|=config.FQ<<3; //Set the FQ
+    }
+    if (config.POL != -1)
+      config.POL ? buf[1]|(1<<2) : buf[1]&~(1<<2) ;
+    if (config.TM != -1)
+      config.TM ? buf[1]|(1<<1) : buf[1]&~(1<<1) ;
+    if (config.SD != -1)
+      config.SD ? buf[1]|(1<<0) : buf[1]&~(1<<0) ;
+
+    // Write the register back to the IC
+    err_write(fd, buf, 2);
+
+    // Query an pretty print the config
+    query = 1;
+  }
+
+
+  /*
+   * If this is a query prettyprint the config register to stdout
+   * */
+  if (query) {
+    // Set register pointer on IC to config register
+    unsigned char buf[2] = {1, 0};
+    err_write(fd, buf, 1);
+
+    // Read configuration register
+    err_read(fd, buf+1, 1);
+
+    // Pretty print config register
+    int resolution = ((buf[1] & (3<<5)) >> 5);
+    int fault_queue = ((buf[1] & (3<<3)) >> 3);
+    printf("Oneshot:         %s\n", (buf[1] & (1<<7)) ? "Enabled" : "Disabled" );
+    printf("Resolution:      %d (%.4f)\n", resolution, 1/pow(2, 1 + resolution));
+    printf("Fault queue:     %d\n", fault_queue);
+    printf("Alert pin:       Active %s\n", (buf[1] & (1<<2)) ? "high" : "low" );
+    printf("Thermostat mode: %s mode\n", (buf[1] & (1<<1)) ? "Interrupt" : "Comparator" );
+    printf("Shutdown mode:   %s\n", (buf[1] & (1<<0)) ? "Enabled" : "Disabled" );
+    return 0;
+  }
+
+  /*
+   * If this was not a config query get the temperature and quit
+   */
+  // Send register to read from. "0"/temperature register
+  unsigned char buf[2]={0,0};
+  err_write(fd, buf, 1);
+
+  // Read back data into buf[]
+  err_read(fd, buf, 2);
+
+  // Compute the result
+  signed short data = (buf[0]<<8)|buf[1];
+  printf("%.1f\n", (float)data/256);
+
+  return 0;
+}
+
diff --git a/overlay-load.sh b/overlay-load.sh
new file mode 100755 (executable)
index 0000000..21c873e
--- /dev/null
@@ -0,0 +1,11 @@
+#!/bin/sh -xe
+
+dtb=$1
+test -f "$dtb" || ( echo "Usage: $0 overlay.dtb" ; exit 1 )
+config=`mount -t configfs | awk '{ print $3 }'`
+name=`basename $1`
+dir=$config/device-tree/overlays/$name
+test -d $dir && rmdir $dir
+mkdir $dir
+cat $dtb > $dir/dtbo
+cat $dir/status