#!/usr/bin/python3 """ This is a clone of the 'gpio/WiringPi' project, written specifically for the OrangePi i96. Not all of gpio is re-implemented. The 'mode' command allows the user to select any pin a GPIO or its special-funtion (i2c, spi, uart, pcm, etc) Pat Beirne Implemented: gpio read => displays a value, returns 0 on success gpio write # set a pin high or low gpio mode [in | out | alt] # set the pin for in/out or special-function gpio readall # display all current pin settings # and the pin assignments on the 40 pin i96 gpio readallx # similar to above, but shows RDA pinout gpio exports # simply dump the state of all the exports # which exist gpio leds # like readall, but for LEDs, switches # and other on-board devices NOTE: pin numbers are always gpio numbers. use 'readall' to see the connector assignment By default, the 'mode' function will create/delete an export. By default, the 'read' and 'write' function will set the mode and create an export. To disable this default, use the '-d' option. Variable plan: functions which are "is_" or "has_" return boolean functions which involve 'in' 'out' 'alt' pass-&-return strings functions which involve values pass-&-return ints....-1 means invalid gpio is always a short int Attack plan: mode set: check iomux, then create-&-use export -d set: check iomux, then low-level set read: check iomux, then check export, then use export -d: check iomux, then low-level read write: check iomux, then check export then set mode=out then use export -d: check iomux, then set direction=out, tne low-level write NOTE: 'read' and 'write' will auto-set the GPIO mode and disable 'alt' NOTE: 'read' and 'write' will auto-set the GPIO mode and disable 'alt' Classes: GPIO to access a pin through the export-gpio methods GPIO_DIRECT to access only through low-level calls read/write/mode will use GPIO unless the -d flag is used exports will always use GPIO readall/readallx/leds will use GPIO_DIRECT """ from mmap import mmap from struct import pack, unpack import os, sys, argparse, pathlib, re, logging VERSION = "2.2" ############ board specific class Board: def __init__(self,full_name,short_name,config_file): self.name = full_name self.short_name = short_name self.config_file = config_file def __repr__(self): return self.name board_i96 = Board("OrangePi i96", "OrangePi i96", "/etc/OrangePi-i96") board_2g = Board("OrangePi 2G-IOT","OrgPi 2G-iot", "/etc/OrangePi-2g-iot") # pins, arranged according to i96 connector # i96 label, rda_port number, pio number, rda special function board_i96.PINS = ( ("GND", "", -1, "", ""), # 1 ("GND", "", -1, "", ""), ("UART2.CTS", "B8", 40, "CTS", "ttyS1.cts"), ("PWR_BTN_N", "", -1, "", ""), ("UART2.TX", "C8", 104, "TX", "ttyS1.tx"), ("RST_BTN_N", "", -1, "1.4v", ""), ("UART2.RX", "C7", 103, "RX", "ttyS1.rx"), ("SPI2.CLK", "A2", 2, "CLK","spi2.clk"), ("UART2.RTS", "B9", 41, "RTS","ttyS1.rts"), ("SPI2.DI", "A4", 4, "DI", "spi2.di"), # 10 ("UART1.TX", "A14", 14, "TX", "ttyS0.tx"), ("SPI2.CS", "A6", 6, "CS", "spi2.cs"), ("UART1.RX", "C6", 102, "RX", "ttyS0.rx"), ("SPI2.DO", "A3", 3, "DO", "spi2.do"), ("I2C2.SCL", "A0", 0, "SCL", "i2c-1.scl"), ("I2S.LRCK", "A10", 10, "LRCK", "pcm.fp"), ("I2C2.SDA", "A1", 1, "SDA", "i2c-1.sda"), ("I2S.BCK", "A9", 9, "BCK", "pcm.clk"), ("I2C3.SCL", "B6", 38, "SCL", "i2c-2.scl"), ("I2S.DO", "A13", 13, "DO", "pcm.do"), # 20 ("I2C3.SDA", "B7", 39, "SDA", "i2c-2.sda"), ("I2S.DI", "A11", 11, "DI", "pcm.di"), ("GPIO.A", "A15", 15, "CTS", "ttyS0.cts"), ("GPIO.B", "A20", 20, "LCD", "lcd"), ("GPIO.C", "B24", 56, "ROM", "n/a"), ("GPIO.D", "D2", 66, "CTS", "ttyS2.cts"), ("GPIO.E", "D3", 67, "RTS", "ttyS2.rts"), ("GPIO.F", "A22", 22, "LCD", "lcd"), ("GPIO.G", "A30", 30, "LCD", "lcd"), ("GPIO.H", "A29", 29, "LCD", "lcd"), # 30 ("GPIO.I", "A28", 28, "LCD", "lcd"), ("GPIO.J", "A27", 27, "LCD", "lcd"), ("GPIO.K", "A26", 26, "LCD", "lcd"), ("GPIO.L", "A25", 25, "LCD", "lcd"), ("V_PAD", "", -1, "1.8v", ""), ("SYS_DCIN", "", -1, "n/c", ""), ("VDD_IN", "", -1, "5V", ""), ("SYS_DCIN", "", -1, "n/c", ""), ("GND", "", -1, "", ""), ("GND", "", -1, "", "") # 40 ) deleted_board_2g_14_PINS = ( ("VCC", "", -1,""), ("I2C1.SDA", "B31", 63, "SDA"), ("I2C1.SCL", "B30", 62, "SCL"), ("GPIO.B24", "B24", 56, ""), ("GND", "", -1, ""), ("UART1.RX", "C6", 102, "RX"), ("UART1.TX", "A14", 14, "TX"), ("UART1.CT", "A15", 15,"CTS"), ("VCC", "", -1,""), ("SPI2.DI", "A4", 4, "DI"), #10 ("SPI2.DIO", "A3", 3, "DIO"), ("SPI2.CLK", "A2", 2, "CLK"), ("GND", "", -1, ""), ("I2C2.SDA", "A1", 1, ""), ("GPIO.C26", "C26", 122, "SIM"), ("GPIO.C27", "C27", 123, "SIM"), ("GPIO.C28", "C28", 124, "LCD"), ("GPIO.C29", "C29", 125, "LCD"), ("GPIO.C30", "C30", 126, "LCD"), ("GND", "", -1, ""), #20 ("I2C3.SDA", "B7", 39, "SDA"), ("I2C3.SCL", "B6", 38, "SCL"), ("UART2.CT", "B8", 40, "CTS"), ("GND", "", -1, ""), ("UART2.RT", "B9", 41, "RTS"), ("GND", "", -1, ""), ("I2C2.SCL", "A0", 0, "SCL"), ("SPI2.CS0", "A5", 5, "CS0"), ("SPI2.CS1", "A6", 6, "CS1"), ("UART1.RT", "A16", 16, "RTS"), #30 ("GND", "", -1, ""), ("GPIO.C25", "C25", 121, "SIM"), ("GPIO.C5", "C5", 101, ""), ("GND", "", -1, ""), ("GPIO.B5", "B5", 37, ""), ("UART2.RX", "C7", 103,""), ("UART2.TX", "C8", 104,""), ("GND", "", -1, ""), ("VCC", "", -1,""), ("VCC", "", -1,""), #40 ) board_2g.PINS = ( ("V_PAD", "", -1, "2.8v", ""), ("VDD_IN", "", -1, "5V", ""), ("I2C1.SDA", "B31", 63, "SDA","i2c-0.sda"), ("VDD_IN", "", -1, "5V", ""), ("I2C1.SCL", "B30", 62, "SCL", "i2c-0.scl"), ("GND", "", -1, "", ""), ("GPIO.B24", "B24", 56, "", ""), ("UART2.TX", "C8", 104,"", "ttyS1.tx"), ("GND", "", -1, "", ""), ("UART2.RX", "C7", 103,"", "ttyS1.rx"), #10 ("UART1.RX", "C6", 102, "RX", "ttyS0.rx"), ("GPIO.B5", "B5", 37, "", ""), ("UART1.TX", "A14", 14, "TX", "ttyS0.tx"), ("GND", "", -1, "", ""), ("UART1.CT", "A15", 15,"CTS", "ttyS0.cts"), ("GPIO.C5", "C5", 101, "", "",""), ("V_PAD", "", -1, "2.8v", ""), ("GPIO.C25", "C25", 121, "SIM", ""), ("SPI2.DI", "A4", 4, "DI", "spi-1.di"), ("GND", "", -1, "", ""), #20 ("SPI2.DIO", "A3", 3, "DIO", "spi-1.do"), ("UART1.RT", "A16", 16, "RTS", "ttyS0.rts"), ("SPI2.CLK", "A2", 2, "CLK", "spi-1.clk"), ("SPI2.CS0", "A5", 5, "CS0", "spi-1.cs"), ("GND", "", -1, "", ""), ("SPI2.CS1", "A6", 6, "CS1", "spi-1.cs1"), ("I2C2.SDA", "A1", 1, "", "i2c-1.sda"), ("I2C2.SCL", "A0", 0, "SCL", "i2c-1.scl"), ("GPIO.C26", "C26", 122, "SIM", ""), ("GND", "", -1, "", ""), #30 ("GPIO.C27", "C27", 123, "SIM", ""), ("UART2.RT", "B9", 41, "RTS", "ttyS1.rts"), ("GPIO.C28", "C28", 124, "LCD", "lcd"), ("GND", "", -1, "", ""), ("GPIO.C29", "C29", 125, "LCD", "lcd"), ("UART2.CT", "B8", 40, "CTS", "ttyS1.cts"), ("GPIO.C30", "C30", 126, "LCD", "lcd"), ("I2C3.SCL", "B6", 38, "SCL", "i2c2-scl"), ("GND", "", -1, "", ""), ("I2C3.SDA", "B7", 39, "SDA", "i2c2-sda"), #40 ) board_2g.other = ( ) board_i96.other = ( ("LED2", "C30", 126, ""), ("LED3", "C29", 125, ""), ("LED5", "C5", 101, ""), ("J2", "C2", 98, "boot sd"), ("OTGPWR","A17",17,""), # ("DBG_TX","D1",65,"TX"), # ("DBG_RX","D0",64,"RX"), ) ########################## cpu specific class Cpu: def __init__(self,name): self.name = name RDA_PORTC_IOMUX = 0x11a09008 RDA_PORTA_IOMUX = 0x11a0900c RDA_PORTB_IOMUX = 0x11a09010 RDA_PORTD_IOMUX = 0x11a09014 cpu_rda = Cpu("RDA8810") cpu_rda.IOMUX_ADDRESSES = (RDA_PORTA_IOMUX, RDA_PORTB_IOMUX, RDA_PORTD_IOMUX, RDA_PORTC_IOMUX) cpu = cpu_rda GPIO_PER_PORT = 32 PAGE_MASK = ~0xFFF PAGE_OFFSET = 0xFFF SYS_PATH = "/sys/class/gpio/" def pin_has_export(gpio): return pathlib.Path('/sys/class/gpio/gpio{}'.format(gpio)).exists() #################### memory access def mem_set(address, bitmask, value): try: with open("/dev/mem","w+b") as m: mem = mmap(m.fileno(), 32, offset = address & PAGE_MASK) address &= PAGE_OFFSET data = unpack("9s} {:3} {:<6} {}".format(f,m,v,iop)) def do_leds(): print("+------+-------+------+-----+") print("| gpio | func | mode |value|") print("+------+-------+------+-----+") for p in board.other: #print("led function:", p) g = GPIO_DIRECT(p[2]) m,v = g.get_mode_value() print("| {:3} | {:6}| {:4} | {:1} |".format( p[2],p[0],m,v)) print("+------+-------+------+-----+") def check_gpio_valid(): if args.gpio == None or args.gpio<0 or args.gpio>127: print("the {} command requires a gpio pin number".format(args.cmd)) sys.exit(1) def do_read(): logging.debug("do_read: %d",args.gpio) check_gpio_valid() gpio = args.gpio g = GPIO_factory(gpio,args.direct) print(g.get()) def do_write(): logging.debug("do_write: %d %s",args.gpio, args.extra) check_gpio_valid() gpio = args.gpio g = GPIO_factory(gpio, args.direct) if args.extra in ('1', 'on', 'ON'): v = 1 elif args.extra in ('0', 'off', 'OFF'): v = 0 else: print("the 'write' command requires a 2nd argument, either 0 or 1") sys.exit(1) g.set(v) def do_mode(): logging.info("do_mode %d %s", args.gpio, args.extra) logging.info("the -d flag is {}".format(args.direct)) check_gpio_valid() if args.extra == None: m,v = GPIO_DIRECT(args.gpio).get_mode_value() print(m) return if args.extra == "alt": args.direct = True gpio = GPIO_factory(args.gpio,args.direct) if args.extra in ('in','out'): logging.info("set mode: %s",args.extra) gpio.set_iomux(True) gpio.set_mode(args.extra) elif args.extra=='alt': logging.info("set alt mode") gpio.set_iomux(False) def do_readallx(): exports_dirty = False print(""" +-----+-----+----------+------+-+ {} +-+------+----------+-----+-----+ | gpio| RDA | alt name | Mode | V | Physical | V | Mode | alt name | RDA | gpio| +-----+-----+----------+------+---+----++----+---+------+----------+-----+-----+""".format( board.short_name)) for i in range(20): left_gpio = board.PINS[2*i][2] if left_gpio != -1: left_g = GPIO_DIRECT(left_gpio) left_mode, left_value = left_g.get_mode_value() left_gpio_str = str(left_gpio) if '*' in left_mode: exports_dirty = True else: left_gpio_str = left_mode = left_value = "" right_gpio = board.PINS[2*i+1][2] if right_gpio != -1: right_g = GPIO_DIRECT(right_gpio) right_mode, right_value = right_g.get_mode_value() right_gpio_str = str(right_gpio) if '*' in right_mode: exports_dirty = True else: right_gpio_str = right_mode = right_value = "" left_value,right_value = str(left_value),str(right_value) print("| {:3s} | {:4s}| {:9s}| {:4s} | {:1s} | {:2d} || {:2d} | {:1s} | {:4s} | {:9s}| {:4s}| {:3s} |".format( left_gpio_str,board.PINS[2*i][1],board.PINS[2*i][4], left_mode,left_value,2*i+1, 2*i+2,right_value,right_mode,board.PINS[2*i+1][4], board.PINS[2*i+1][1],right_gpio_str)) print("+-----+-----+----------+------+---+----++----+---+------+----------+-----+-----+") if exports_dirty: print("Note: *these pins are set to GPIO mode but do NOT have exports in /sys/class/gpio") print("Note: the alt names are based on the Linux naming scheme") args = None if __name__ == "__main__": #logging.basicConfig(stream=sys.stderr, level=logging.DEBUG) #logging.basicConfig(stream=sys.stderr, level=logging.INFO) parser = argparse.ArgumentParser(description = "Access GPIO on the OrangePi RDA boards") parser.add_argument('-v','--version', action='version', version="opio "+VERSION+" -- python rewrite of gpio; by Pat Beirne") parser.add_argument('cmd',help="one of: read/in, write/out, mode, readall/status, readallx/statusx, leds, exports") parser.add_argument('gpio',nargs='?',type=int,help="gpio number 0...126") parser.add_argument('extra',nargs='?') parser.add_argument('-d',"--direct",help="use low-level access",action="store_true") parser.add_argument('-2',"--op2giot",help="configure for OrangePi 2G-iot [disable auto-detect]",action="store_true") parser.add_argument('-9',"--i96",help="configure for OrangePi i96 [disable auto-detect]",action="store_true") args = parser.parse_args() board_auto_sense(args) switcher = {"readall":do_readall, "readallx":do_readallx, "status":do_readall, "statusx":do_readallx, "leds":do_leds, "mode":do_mode, "read":do_read, "write":do_write, "in":do_read, "out":do_write, "exports":do_exports} f = switcher.get(args.cmd) if not f: print("the available commands are:", ", ".join(sorted(list(switcher.keys())))) print("function",args.cmd,"not found") sys.exit(1) f()