Browse Source

README.md is beefed up; i2c demo scripts are included

Pat Beirne 2 years ago
parent
commit
c271c4cca8
6 changed files with 340 additions and 6 deletions
  1. 8 6
      README.md
  2. 11 0
      i2c/i2cscan.py
  3. 33 0
      i2c/i2cscan2.py
  4. 182 0
      i2c/lcd.py
  5. 21 0
      i2c/lcd_ip.py
  6. 85 0
      i2c/smbus.py

+ 8 - 6
README.md

@@ -1,10 +1,8 @@
 # i96
 
-Documentation and apps for the OrangePi-i96 board. Fixes the GPIO access, and includes notes to help make the board more useable.
+Documentation and apps for the OrangePi-i96 board. Fixes the GPIO access, helps the WiFi keep a persistent MAC address and the [wiki](http://alt.pbeirne.com:3000/patb/i96/wiki) includes notes to help make the board more useable.
 
-See the [wiki](http://alt.pbeirne.com:3000/patb/i96/wiki) for the documentation.
-
-Download the `gpio_fixup.py` file onto your OrangePi-i96. I usually install it into `/usr/local/bin'.
+To get full access to the GPIO pins, download the `gpio_fixup.py` file onto your OrangePi-i96. I usually install it into `/usr/local/bin'.
 
 ``` 
 sudo -i                  # change to root
@@ -19,8 +17,12 @@ That little script will run at boot time, and change the gpio pins on the 40 pin
 
 `gpio_fixup.sh` is the equivalent script written in bash, using `devmem2` to access the cpu registers.
 
-`devmem2.py` is a local version of the program to access the cpu registers. Use if you can't download `devmem2` from the Debian/Ubuntu repositories. `devmem2.py` uses Python3 and is very slow, and must be run by root or sudo.
+`devmem2.py` is a local version of the program to access the cpu registers. Use it if you can't download `devmem2` from the Debian/Ubuntu repositories. `devmem2.py` uses Python3 and is very slow, and must be run by root or sudo.
 
 `blink.py` is just an example to show how you might play with the GPIO lines.
 
-The `rdawfmac.ko` file included here contains the change that allows the WiFi MAC address to persist between reboots. This file (kernel module) can be copied into `/lib/modules/3.10.62-rel5.0.2+/kernel/drivers/net/wireless/rdaw80211/rdawlan/` to overwrite the existing file. If you decide to use this version of the module, be sure to create a folder off the root called `/data/misc/wifi/` so the module can store its randomized MAC address. **NOTE**: this module is only useable on the 3.10.62-rel5.0.2 kernel.** It works on the OrangePi-2Giot board as well.
+The `rdawfmac.ko` file included here contains the change that allows the WiFi MAC address to persist between reboots. This file (kernel module) can be copied into `/lib/modules/3.10.62-rel5.0.2+/kernel/drivers/net/wireless/rdaw80211/rdawlan/` to overwrite the existing file. If you decide to use this version of the module, be sure to create a folder off the root called `/data/misc/wifi/` so the module can store its randomized MAC address. **NOTE: this module is only useable on the 3.10.62-rel5.0.2 kernel.** It works on the OrangePi-2Giot board as well.
+
+The `i2c` folder contains test programs for the I2C bus on this board. The native Linux `i2cdetect` from the `i2c-tools` packages doesn't seem to work very well on this board, so I wrote something similar in Python, it's `i2c/i2cscan2.py` and requires that you install the 'python-smbus' package; that package doesn't seem to exist yet for Python3, so I have included a local version called `smbus.py`. I have also included a couple of LCD demo programs, using the I2C version of the 4x20 LCD modules that are easily available.  The `pip3` library system is helpful in finding libraries to handle various peripherals.
+
+**NOTE**: if you are going to use the I2C bus on the OrangePi-i96 board, you can access them through `/dev/i2c-1` and `/dev/i2c-2`; the other one `/dev/i2c-0` is used to communicate with the modem chip, and for the camera interface. If you are going to use I2C on the OrangePi-2Giot, avoid the `/dev/i2c-0` device (40 pin connector, pins 3/5) because it's used for communication between the CPU and the modem chips, and for the camera connector. Also on the 2Giot, the `/dev/i2c-2` bus is shared between the 40 pin connector and the LCD connector. Use `opio -2 statusx` to see the Linux driver name for each pin-group.

+ 11 - 0
i2c/i2cscan.py

@@ -0,0 +1,11 @@
+import smbus
+
+s=smbus.SMBus(1)
+for i in range(3,127):
+  try:
+    s.read_byte(i)
+    print("\nI2C detected: "+hex(i))
+  except:
+    print("skip: "+hex(i)+', ',end='',flush=True)
+
+

+ 33 - 0
i2c/i2cscan2.py

@@ -0,0 +1,33 @@
+#!/usr/bin/python3
+
+import smbus, sys
+
+bus = 1
+''' you can call this app with arguments: either a single small digit (0,1,2) or 
+    the device name /dev/i2c-1 '''
+if len(sys.argv)>1:
+  if sys.argv[1].isdigit():
+    bus = int(sys.argv[1])
+  if sys.argv[1].startswith("/dev/i2c"):
+    bus = int(sys.argv[1][-1])
+
+s=smbus.SMBus(bus)
+
+print("Scan I2C bus /dev/i2c-{} for devices".format(bus))
+print("     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f",end='')
+
+def check_i2c(n):
+  try:
+    s.read_byte(n)
+    return True
+  except:
+    return False
+
+for i in range(3,120):
+  if (i % 16 == 0) or (i == 3):
+    print("\n"+"{:02x}:".format(i),end='',flush=True)
+  if i == 3:
+    print("         ",end='')
+  print(" {:02x}".format(i) if check_i2c(i) else " --",end='',flush=True)
+print()
+

+ 182 - 0
i2c/lcd.py

@@ -0,0 +1,182 @@
+# -*- coding: utf-8 -*-
+# Original code found at:
+# https://gist.github.com/DenisFromHR/cc863375a6e19dce359d
+
+"""
+Compiled, mashed and generally mutilated 2014-2015 by Denis Pleic
+Made available under GNU GENERAL PUBLIC LICENSE
+
+# Modified Python I2C library for Raspberry Pi
+# as found on http://www.recantha.co.uk/blog/?p=4849
+# Joined existing 'i2c_lib.py' and 'lcddriver.py' into a single library
+# added bits and pieces from various sources
+# By DenisFromHR (Denis Pleic)
+# 2015-02-10, ver 0.1
+
+"""
+
+# i2c bus (0 -- original Pi, 1 -- Rev 2 Pi)
+I2CBUS = 0
+
+# LCD Address
+ADDRESS = 0x3f
+
+import smbus
+from time import sleep
+
+class i2c_device:
+   def __init__(self, addr, port=I2CBUS):
+      self.addr = addr
+      self.bus = smbus.SMBus(port)
+
+# Write a single command
+   def write_cmd(self, cmd):
+      self.bus.write_byte(self.addr, cmd)
+      sleep(0.0001)
+
+# Write a command and argument
+   def write_cmd_arg(self, cmd, data):
+      self.bus.write_byte_data(self.addr, cmd, data)
+      sleep(0.0001)
+
+# Write a block of data
+   def write_block_data(self, cmd, data):
+      self.bus.write_block_data(self.addr, cmd, data)
+      sleep(0.0001)
+
+# Read a single byte
+   def read(self):
+      return self.bus.read_byte(self.addr)
+
+# Read
+   def read_data(self, cmd):
+      return self.bus.read_byte_data(self.addr, cmd)
+
+# Read a block of data
+   def read_block_data(self, cmd):
+      return self.bus.read_block_data(self.addr, cmd)
+
+
+# commands
+LCD_CLEARDISPLAY = 0x01
+LCD_RETURNHOME = 0x02
+LCD_ENTRYMODESET = 0x04
+LCD_DISPLAYCONTROL = 0x08
+LCD_CURSORSHIFT = 0x10
+LCD_FUNCTIONSET = 0x20
+LCD_SETCGRAMADDR = 0x40
+LCD_SETDDRAMADDR = 0x80
+
+# flags for display entry mode
+LCD_ENTRYRIGHT = 0x00
+LCD_ENTRYLEFT = 0x02
+LCD_ENTRYSHIFTINCREMENT = 0x01
+LCD_ENTRYSHIFTDECREMENT = 0x00
+
+# flags for display on/off control
+LCD_DISPLAYON = 0x04
+LCD_DISPLAYOFF = 0x00
+LCD_CURSORON = 0x02
+LCD_CURSOROFF = 0x00
+LCD_BLINKON = 0x01
+LCD_BLINKOFF = 0x00
+
+# flags for display/cursor shift
+LCD_DISPLAYMOVE = 0x08
+LCD_CURSORMOVE = 0x00
+LCD_MOVERIGHT = 0x04
+LCD_MOVELEFT = 0x00
+
+# flags for function set
+LCD_8BITMODE = 0x10
+LCD_4BITMODE = 0x00
+LCD_2LINE = 0x08
+LCD_1LINE = 0x00
+LCD_5x10DOTS = 0x04
+LCD_5x8DOTS = 0x00
+
+# flags for backlight control
+LCD_BACKLIGHT = 0x08
+LCD_NOBACKLIGHT = 0x00
+
+En = 0b00000100 # Enable bit
+Rw = 0b00000010 # Read/Write bit
+Rs = 0b00000001 # Register select bit
+
+class lcd:
+   #initializes objects and lcd
+   def __init__(self):
+      self.lcd_device = i2c_device(ADDRESS)
+
+      self.lcd_write(0x03)
+      self.lcd_write(0x03)
+      self.lcd_write(0x03)
+      self.lcd_write(0x02)
+
+      self.lcd_write(LCD_FUNCTIONSET | LCD_2LINE | LCD_5x8DOTS | LCD_4BITMODE)
+      self.lcd_write(LCD_DISPLAYCONTROL | LCD_DISPLAYON)
+      self.lcd_write(LCD_CLEARDISPLAY)
+      self.lcd_write(LCD_ENTRYMODESET | LCD_ENTRYLEFT)
+      sleep(0.2)
+
+
+   # clocks EN to latch command
+   def lcd_strobe(self, data):
+      self.lcd_device.write_cmd(data | En | LCD_BACKLIGHT)
+      sleep(.0005)
+      self.lcd_device.write_cmd(((data & ~En) | LCD_BACKLIGHT))
+      sleep(.0001)
+
+   def lcd_write_four_bits(self, data):
+      self.lcd_device.write_cmd(data | LCD_BACKLIGHT)
+      self.lcd_strobe(data)
+
+   # write a command to lcd
+   def lcd_write(self, cmd, mode=0):
+      self.lcd_write_four_bits(mode | (cmd & 0xF0))
+      self.lcd_write_four_bits(mode | ((cmd << 4) & 0xF0))
+
+   # write a character to lcd (or character rom) 0x09: backlight | RS=DR<
+   # works!
+   def lcd_write_char(self, charvalue, mode=1):
+      self.lcd_write_four_bits(mode | (charvalue & 0xF0))
+      self.lcd_write_four_bits(mode | ((charvalue << 4) & 0xF0))
+  
+   # put string function with optional char positioning
+   def lcd_display_string(self, string, line=1, pos=0):
+    if line == 1:
+      pos_new = pos
+    elif line == 2:
+      pos_new = 0x40 + pos
+    elif line == 3:
+      pos_new = 0x14 + pos
+    elif line == 4:
+      pos_new = 0x54 + pos
+
+    self.lcd_write(0x80 + pos_new)
+
+    for char in string:
+      self.lcd_write(ord(char), Rs)
+
+   # clear lcd and set to home
+   def lcd_clear(self):
+      self.lcd_write(LCD_CLEARDISPLAY)
+      self.lcd_write(LCD_RETURNHOME)
+
+   # define backlight on/off (lcd.backlight(1); off= lcd.backlight(0)
+   def backlight(self, state): # for state, 1 = on, 0 = off
+      if state == 1:
+         self.lcd_device.write_cmd(LCD_BACKLIGHT)
+      elif state == 0:
+         self.lcd_device.write_cmd(LCD_NOBACKLIGHT)
+
+   # add custom characters (0 - 7)
+   def lcd_load_custom_chars(self, fontdata):
+      self.lcd_write(0x40);
+      for char in fontdata:
+         for line in char:
+            self.lcd_write_char(line)         
+
+l = lcd()
+l.lcd_display_string("hello world")
+

+ 21 - 0
i2c/lcd_ip.py

@@ -0,0 +1,21 @@
+#!/usr/bin/python3
+from signal import signal, SIGTERM, SIGHUP, pause
+from rpi_lcd import LCD
+import subprocess
+import time
+
+lcd = LCD(address=0x3f,bus=0)
+
+
+while True:
+  p = subprocess.run("ip -s link show dev wlan0 | tail -n 4",shell=True,stdout=subprocess.PIPE,universal_newlines=True)
+  r = p.stdout.splitlines()
+  r = [a.strip() for a in r]
+  lcd.text(r[0][:20],1)
+  lcd.text(r[1][:20],2)
+  lcd.text(r[2][:20],3)
+  lcd.text(r[3][:20],4)
+  time.sleep(3)
+
+lcd.clear()
+

+ 85 - 0
i2c/smbus.py

@@ -0,0 +1,85 @@
+"""pysmbus - A ure Python implementation of the I2C SMBus protocol."""
+# Copyright (C) 2015  Bjorn Tillenius <bjorn@tilenius.me>
+#
+# This library is free software; you can redistribute it and/or
+# # modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation;
+# version 2 of the License.
+# 
+# This library 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
+# Library General Public License for more details.
+
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA  02110-1301  USA
+
+
+import posix
+import struct
+from fcntl import ioctl
+from ctypes import c_int, c_uint8, POINTER, Structure
+
+
+I2C_SLAVE = 0x0703
+I2C_SMBUS = 0x0720
+I2C_SMBUS_WRITE = 0
+I2C_SMBUS_READ = 1
+I2C_SMBUS_BYTE_DATA = 2
+
+LP_c_uint8 = POINTER(c_uint8)
+
+
+class i2c_smbus_msg(Structure):
+
+    _fields_ = [
+        ('read_write', c_uint8),  # Should be c_char, but c_uint8 is the
+                                  # same size is makes it easier to
+                                  # support both Python 2.7 and 3.x.
+        ('command', c_uint8),
+        ('size', c_int),
+        ('data', LP_c_uint8)]
+
+    __slots__ = [name for name,type in _fields_]
+
+
+class SMBus(object):
+
+    def __init__(self, bus):
+        self.fd = posix.open("/dev/i2c-{}".format(bus), posix.O_RDWR)
+        self.addr = None
+
+    def _set_addr(self, addr):
+        if self.addr != addr:
+            ioctl(self.fd, I2C_SLAVE, addr);
+
+    def write_byte_data(self, i2c_addr, register, value):
+        """Write a single byte to a designated register."""
+        self._set_addr(i2c_addr)
+        byte_value = c_uint8(value)
+        data_pointer = LP_c_uint8(byte_value)
+        msg = i2c_smbus_msg(
+            read_write=I2C_SMBUS_WRITE, command=register,
+            size=I2C_SMBUS_BYTE_DATA, data=data_pointer)
+        ioctl(self.fd, I2C_SMBUS, msg)
+
+    def write_byte(self, i2c_addr, value):
+        self._set_addr(i2c_addr)
+        posix.write(self.fd, bytes([value]))
+
+    def read_byte_data(self, i2c_addr, register):
+        """Read a single byte from a designated register."""
+        self._set_addr(i2c_addr)
+        data_pointer = LP_c_uint8(c_uint8())
+        msg = i2c_smbus_msg(
+            read_write=I2C_SMBUS_READ, command=register,
+            size=I2C_SMBUS_BYTE_DATA, data=data_pointer)
+        ioctl(self.fd, I2C_SMBUS, msg)
+        [result] = struct.unpack("@b", data_pointer.contents)
+        return result
+
+    def read_byte(self, i2c_addr):
+        self._set_addr(i2c_addr)
+        return posix.read(self.fd, 1)