├── PREREQS.md ├── README.md ├── setup.py ├── smbpi ├── __init__.py ├── ads1015.py ├── ads1115.py ├── c_gpio.c ├── c_gpio.h ├── dpmem.py ├── dpmem_common.py ├── dpmem_direct.py ├── dpmem_direct_ext.c ├── dpmem_wiringpi.py ├── ds1820.py ├── encoder.py ├── fan4pin.py ├── fdc.py ├── gps.py ├── i2c-utils.h ├── i2c_with_crc.py ├── ioexpand.py ├── max6921.py ├── mcp42100.py ├── mcp4821.py ├── micros.c ├── micros.h ├── motor.py ├── motorpot.py ├── nixie_shift.py ├── pigencoder.py ├── pigpio_off.py ├── realtime_ext.c ├── supervisor_direct_ext.c ├── supervisor_h8_ext.c ├── tlc59116.py ├── ups.py ├── version.py ├── vfd.py ├── vfdcontrol.py └── wd37c65_direct_ext.c └── up.sh /PREREQS.md: -------------------------------------------------------------------------------- 1 | sudo apt-get install -y wiringpi python-setuptools 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Raspberry Pi Library Code 2 | Scott Baker, http://www.smbaker.com/ 3 | 4 | This repository contains common library functions that I use in my various 5 | Raspberry Pi projects. I put them in a common place to make reuse easier. 6 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | from setuptools import setup, Extension 5 | 6 | from smbpi.version import __version__ 7 | 8 | if sys.version_info < (3,0): 9 | # python 2.x 10 | dpmem_direct_ext = Extension('smbpi.dpmem_direct_ext', 11 | sources = ['smbpi/dpmem_direct_ext.c'], 12 | libraries = ['wiringPi']) 13 | 14 | wd37c65_direct_ext = Extension('smbpi.wd37c65_direct_ext', 15 | sources = ['smbpi/wd37c65_direct_ext.c', 'smbpi/micros.c'], 16 | libraries = ['wiringPi']) 17 | 18 | realtime_ext = Extension('smbpi.realtime_ext', 19 | sources = ['smbpi/realtime_ext.c'], 20 | libraries = []) 21 | setup_result = setup(name='smbpi', 22 | version=__version__, 23 | description="Scott Baker's Raspberry Pi Library", 24 | packages=['smbpi'], 25 | zip_safe=False, 26 | ext_modules=[dpmem_direct_ext, wd37c65_direct_ext, realtime_ext] 27 | ) 28 | else: 29 | supervisor_direct_ext = Extension('smbpi.supervisor_direct_ext', 30 | sources = ['smbpi/supervisor_direct_ext.c']) 31 | #libraries = ['wiringPi']) 32 | 33 | supervisor_direct_v2_ext = Extension('smbpi.supervisor_direct_v2_ext', 34 | sources = ['smbpi/supervisor_direct_v2_ext.c'], 35 | library_dirs = ['/usr/local/lib'], 36 | libraries = ['pigpio']) 37 | 38 | supervisor_h8_ext = Extension('smbpi.supervisor_h8_ext', 39 | sources = ['smbpi/supervisor_h8_ext.c'], 40 | library_dirs = ['/usr/local/lib'], 41 | libraries = ['pigpio']) 42 | 43 | 44 | 45 | # python 3.x 46 | # wiringpi is not supported 47 | setup_result = setup(name='smbpi', 48 | version=__version__, 49 | description="Scott Baker's Raspberry Pi Library", 50 | packages=['smbpi'], 51 | zip_safe=False, 52 | ext_modules=[supervisor_direct_ext, supervisor_direct_v2_ext, supervisor_h8_ext] 53 | ) 54 | 55 | -------------------------------------------------------------------------------- /smbpi/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /smbpi/ads1015.py: -------------------------------------------------------------------------------- 1 | """ 2 | ADS1015 driver 3 | Scott Baker, http://www.smbaker.com/ 4 | 5 | Interface with ADS1015 chip. 6 | """ 7 | 8 | from __future__ import print_function 9 | import time 10 | 11 | REG_CONV=0 12 | REG_CONFIG=1 13 | REG_LO_THRESH=2 14 | REG_HI_THRESH=3 15 | 16 | OS = 1 << 15 17 | 18 | MUX_AIN0_AIN1 = 0b000 << 12 19 | MUX_AIN0_AIN3 = 0b001 << 12 20 | MUX_AIN1_AIN3 = 0b010 << 12 21 | MUX_AIN2_AIN3 = 0b011 << 12 22 | MUX_AIN0 = 0b100 << 12 23 | MUX_AIN1 = 0b101 << 12 24 | MUX_AIN2 = 0b110 << 12 25 | MUX_AIN3 = 0b111 << 12 26 | 27 | PGA_6V = 0b000 << 9 28 | PGA_4V = 0b001 << 9 29 | PGA_2V = 0b010 << 9 30 | PGA_1V = 0b011 << 9 31 | PGA_05V = 0b100 << 9 32 | PGA_02V = 0b101 << 9 33 | 34 | MODE_CONT = 0 35 | MODE_SINGLE = 1 << 8 36 | 37 | DATA_128 = 0b000 << 5 38 | DATA_250 = 0b001 << 5 39 | DATA_490 = 0b010 << 5 40 | DATA_920 = 0b011 << 5 41 | DATA_1600 = 0b100 << 5 42 | DATA_2400 = 0b101 << 5 43 | DATA_3300 = 0b110 << 5 44 | 45 | COMP_MODE_TRAD = 0 46 | COMP_MODE_WINDOW = 1 << 4 47 | 48 | COMP_POL_LOW = 0 49 | COMP_POL_HIGH = 1 << 3 50 | 51 | COMP_NON_LAT = 0 52 | COMP_LAT = 1 << 2 53 | 54 | COMP_QUE_ONE = 0 55 | COMP_QUE_TWO = 1 56 | COMP_QUE_THREE = 2 57 | COMP_QUE_DISABLE = 3 58 | 59 | # default = MUX_AIN0_AIN1 | PGA_2V | MODE_SINGLE | DATA_1600 | COMP_MODE_TRAD | COMP_POL_LOW | COMP_NON_LAT | COMP_QUE_DISBALE 60 | 61 | class ADS1015: 62 | def __init__(self, bus, addr): 63 | self.addr = addr 64 | self.bus = bus 65 | self.lastConfig = None 66 | 67 | def write_config(self, bits): 68 | self.lastConfig = bits 69 | bytes = [(bits >> 8) & 0xFF, bits & 0xFF] 70 | self.bus.write_i2c_block_data(self.addr, REG_CONFIG, bytes) 71 | 72 | def get_data_rate(self): 73 | dr = self.lastConfig & (0b110 << 5) 74 | if (dr == DATA_128): 75 | return 128 76 | elif (dr == DATA_250): 77 | return 250 78 | elif (dr == DATA_490): 79 | return 490 80 | elif (dr == DATA_920): 81 | return 920 82 | elif (dr == DATA_1600): 83 | return 1600 84 | elif (dr == DATA_2400): 85 | return 2400 86 | else: 87 | return 3300 88 | 89 | def wait_samp(self): 90 | # NOTE: Might not be long enough 91 | time.sleep(1.0/self.get_data_rate()+0.0001) 92 | 93 | def read_conversion(self): 94 | result = self.bus.read_i2c_block_data(self.addr, REG_CONV, 2) 95 | val = (result[0] << 8) | (result[1] & 0xFF) 96 | 97 | # if the result >= 0x8000 then it's a negative number 98 | # UNTESTED on ADS1015; taken from my ADS1115 module 99 | if (val >= 0x8000): 100 | val = -((~val & 0xFFFF) + 1) 101 | 102 | # 12-bit ADC, so shift off the lower bits 103 | val = val >> 4 104 | 105 | return val 106 | 107 | def main(): 108 | import smbus 109 | import time 110 | bus = smbus.SMBus(1) 111 | adc = ADS1015(bus, 0x48) 112 | adc.write_config(MUX_AIN0 | PGA_4V | MODE_CONT | DATA_1600 | COMP_MODE_TRAD | COMP_POL_LOW | COMP_NON_LAT | COMP_QUE_DISABLE) 113 | adc.wait_samp() 114 | while True: 115 | print("%d \r" % adc.read_conversion()) 116 | time.sleep(0.1) 117 | 118 | if __name__ == "__main__": 119 | main() 120 | -------------------------------------------------------------------------------- /smbpi/ads1115.py: -------------------------------------------------------------------------------- 1 | """ 2 | ADS1115 driver 3 | Scott Baker, http://www.smbaker.com/ 4 | 5 | Interface with ADS1115 chip. 6 | """ 7 | 8 | from __future__ import print_function 9 | import time 10 | 11 | REG_CONV = 0 12 | REG_CONFIG = 1 13 | REG_LO_THRESH = 2 14 | REG_HI_THRESH = 3 15 | 16 | OS = 1 << 15 17 | 18 | MUX_AIN0_AIN1 = 0b000 << 12 19 | MUX_AIN0_AIN3 = 0b001 << 12 20 | MUX_AIN1_AIN3 = 0b010 << 12 21 | MUX_AIN2_AIN3 = 0b011 << 12 22 | MUX_AIN0 = 0b100 << 12 23 | MUX_AIN1 = 0b101 << 12 24 | MUX_AIN2 = 0b110 << 12 25 | MUX_AIN3 = 0b111 << 12 26 | 27 | PGA_6V = 0b000 << 9 28 | PGA_4V = 0b001 << 9 29 | PGA_2V = 0b010 << 9 30 | PGA_1V = 0b011 << 9 31 | PGA_05V = 0b100 << 9 32 | PGA_02V = 0b101 << 9 33 | 34 | MODE_CONT = 0 35 | MODE_SINGLE = 1 << 8 36 | 37 | DATA_8 = 0b000 << 5 38 | DATA_16 = 0b001 << 5 39 | DATA_32 = 0b010 << 5 40 | DATA_64 = 0b011 << 5 41 | DATA_128 = 0b100 << 5 42 | DATA_250 = 0b101 << 5 43 | DATA_475 = 0b110 << 5 44 | DATA_860 = 0b111 << 5 45 | 46 | COMP_MODE_TRAD = 0 47 | COMP_MODE_WINDOW = 1 << 4 48 | 49 | COMP_POL_LOW = 0 50 | COMP_POL_HIGH = 1 << 3 51 | 52 | COMP_NON_LAT = 0 53 | COMP_LAT = 1 << 2 54 | 55 | COMP_QUE_ONE = 0 56 | COMP_QUE_TWO = 1 57 | COMP_QUE_THREE = 2 58 | COMP_QUE_DISABLE = 3 59 | 60 | # default = MUX_AIN0_AIN1 | PGA_2V | MODE_SINGLE | DATA_128 | COMP_MODE_TRAD | COMP_POL_LOW | COMP_NON_LAT | COMP_QUE_DISBALE 61 | 62 | 63 | class ADS1115: 64 | def __init__(self, bus, addr): 65 | self.addr = addr 66 | self.bus = bus 67 | self.lastConfig = None 68 | 69 | def write_config(self, bits): 70 | self.lastConfig = bits 71 | bytes = [(bits >> 8) & 0xFF, bits & 0xFF] 72 | self.bus.write_i2c_block_data(self.addr, REG_CONFIG, bytes) 73 | 74 | def get_data_rate(self): 75 | dr = self.lastConfig & (0b110 << 5) 76 | if (dr == DATA_8): 77 | return 8 78 | elif (dr == DATA_16): 79 | return 16 80 | elif (dr == DATA_32): 81 | return 32 82 | elif (dr == DATA_64): 83 | return 64 84 | elif (dr == DATA_128): 85 | return 128 86 | elif (dr == DATA_250): 87 | return 250 88 | elif (dr == DATA_475): 89 | return 475 90 | else: 91 | return 860 92 | 93 | def wait_samp(self): 94 | # note that this isn't enough -- I had to wait an additional 8ms at 95 | # DATA_128 and an addition 32ms at DATA_32. This was while using 96 | # MUX_AIN0_AIN3 and MUX_AIN1_AIN3, so maybe it takes twice as long 97 | # to do a relative conversion. 98 | # TODO: investigate 99 | time.sleep(1.0/self.get_data_rate()+0.0001) 100 | 101 | def read_conversion(self): 102 | result = self.bus.read_i2c_block_data(self.addr, REG_CONV, 2) 103 | val = (result[0] << 8) | (result[1] & 0xFF) 104 | 105 | # if the result >= 0x8000 then it's a negative number 106 | if (val >= 0x8000): 107 | val = -((~val & 0xFFFF) + 1) 108 | 109 | return val 110 | 111 | 112 | def main(): 113 | import smbus 114 | import time 115 | bus = smbus.SMBus(1) 116 | adc = ADS1115(bus, 0x48) 117 | adc.write_config(MUX_AIN0 | PGA_4V | MODE_CONT | DATA_128 | COMP_MODE_TRAD | COMP_POL_LOW | COMP_NON_LAT | COMP_QUE_DISABLE) 118 | adc.wait_samp() 119 | while True: 120 | print("%d \r" % adc.read_conversion()) 121 | time.sleep(0.1) 122 | 123 | 124 | if __name__ == "__main__": 125 | main() 126 | -------------------------------------------------------------------------------- /smbpi/c_gpio.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "c_gpio.h" 8 | 9 | #define GPIO_IN(g) *(gpio+((g)/10)) &= ~(7<<(((g)%10)*3)) 10 | #define GPIO_OUT(g) *(gpio+((g)/10)) |= (1<<(((g)%10)*3)) 11 | #define GPIO_ALT(g,a) *(gpio+(((g)/10))) |= (((a)<=3?(a)+4:(a)==4?3:2)<<(((g)%10)*3)) 12 | 13 | #define GPIO_SET(g) *(gpio+7) = 1<<(g) /* sets bit which are 1, ignores bit which are 0 */ 14 | #define GPIO_CLR(g) *(gpio+10) = 1<<(g) /* clears bit which are 1, ignores bit which are 0 */ 15 | #define GPIO_LEV(g) (*(gpio+13) >> (g)) & 0x00000001 16 | 17 | // ----------------------------------------------------------- 18 | // GPIO stuff, when I flipped out that time because wiringpi didn't work 19 | // note to self: it needed an upgrade, because the peri base changed 20 | // ----------------------------------------------------------- 21 | 22 | /* GPIO registers address */ 23 | #define BCM2708_PERI_BASE 0xFE000000 // pi4 24 | //0x20000000 some other pi 25 | #define GPIO_BASE (BCM2708_PERI_BASE + 0x200000) /* GPIO controller */ 26 | #define BLOCK_SIZE (256) 27 | 28 | int mem_fd; 29 | void *gpio_map; 30 | volatile uint32_t *gpio; 31 | 32 | void myGpioInit(void) 33 | { 34 | /* open /dev/mem */ 35 | mem_fd = open("/dev/mem", O_RDWR|O_SYNC); 36 | if (mem_fd == -1) { 37 | perror("Cannot open /dev/mem"); 38 | exit(1); 39 | } 40 | 41 | /* mmap GPIO */ 42 | gpio_map = mmap(NULL, BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, mem_fd, GPIO_BASE); 43 | if (gpio_map == MAP_FAILED) { 44 | perror("mmap() failed"); 45 | exit(1); 46 | } 47 | /* Always use volatile pointer! */ 48 | gpio = (volatile uint32_t *)gpio_map; 49 | } 50 | 51 | unsigned int myDigitalRead(unsigned int pin) 52 | { 53 | return GPIO_LEV(pin); 54 | } 55 | 56 | void myDigitalWrite(unsigned int pin, unsigned int val) 57 | { 58 | if (val) { 59 | fprintf(stdout, "set %d\n", pin); 60 | GPIO_SET(pin); 61 | } else { 62 | fprintf(stdout, "clr %d\n", pin); 63 | GPIO_CLR(pin); 64 | } 65 | } 66 | 67 | void myPinModeInput(unsigned int pin) 68 | { 69 | fprintf(stdout, "input %d\n", pin); 70 | GPIO_IN(pin); 71 | } 72 | 73 | void myPinModeOutput(unsigned int pin) 74 | { 75 | fprintf(stdout, "output %d\n", pin); 76 | GPIO_IN(pin); 77 | GPIO_OUT(pin); 78 | } 79 | 80 | -------------------------------------------------------------------------------- /smbpi/c_gpio.h: -------------------------------------------------------------------------------- 1 | void myGpioInit(void); 2 | unsigned int myDigitalRead(unsigned int pin); 3 | void myDigitalWrite(unsigned int pin, unsigned int val); 4 | void myPinModeInput(unsigned int pin); 5 | void myPinModeOutput(unsigned int pin); 6 | -------------------------------------------------------------------------------- /smbpi/dpmem.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | import string 3 | import sys 4 | import time 5 | import RPi.GPIO as IO 6 | 7 | from dpmem_common import * 8 | 9 | class DualPortMemoryGPIO: 10 | def __init__(self): 11 | IO.setmode(IO.BCM) 12 | for addrpin in DP_ADDRPINS: 13 | IO.setup(addrpin, IO.OUT) 14 | 15 | for datapin in DP_DATAPINS: 16 | IO.setup(datapin, IO.IN) 17 | 18 | for controlpin in DP_CONTROLPINS: 19 | IO.setup(controlpin, IO.OUT) 20 | 21 | IO.setup(DP_INTR, IO.IN, pull_up_down=IO.PUD_UP) 22 | 23 | IO.output(DP_W, 1) 24 | IO.output(DP_R, 1) 25 | IO.output(DP_CE, 1) 26 | 27 | def read(self, addr): 28 | try: 29 | for pin in DP_DATAPINS: 30 | IO.setup(pin, IO.IN) 31 | 32 | for pin in DP_ADDRPINS: 33 | IO.output(pin, addr & 1) 34 | addr = addr >> 1 35 | 36 | IO.output(DP_CE, 0) 37 | IO.output(DP_R, 0) 38 | 39 | val=0 40 | for pin in reversed(DP_DATAPINS): 41 | val=val<<1 42 | val = val | IO.input(pin) 43 | finally: 44 | IO.output(DP_R, 1) 45 | IO.output(DP_CE, 1) 46 | 47 | return val 48 | 49 | def write(self, addr, val): 50 | try: 51 | for pin in DP_DATAPINS: 52 | IO.setup(pin, IO.OUT) 53 | 54 | for pin in DP_ADDRPINS: 55 | IO.output(pin, addr & 1) 56 | addr = addr >> 1 57 | 58 | IO.output(DP_CE, 0) 59 | IO.output(DP_W, 0) 60 | 61 | for pin in DP_DATAPINS: 62 | IO.output(pin, val & 1) 63 | val = val >> 1 64 | finally: 65 | IO.output(DP_W, 1) 66 | IO.output(DP_CE, 1) 67 | 68 | return val 69 | 70 | def write_blcok(self, addr, data, count): 71 | for i in range(0, count): 72 | self.mem.write(addr+i, ord(data[i])) 73 | 74 | def read_block(self, addr, count): 75 | bytes=[] 76 | for i in range(0, count): 77 | bytes = bytes + chr(self.mem.read(addr+i)) 78 | return bytes 79 | 80 | def get_interrupt(self): 81 | return IO.input(DP_INTR) == 0 82 | 83 | def clear_interrupt(self): 84 | self.read(0x3FF) 85 | 86 | def str_to_int(val): 87 | if "x" in val: 88 | val = string.atoi(val, 16) 89 | else: 90 | val = string.atoi(val) 91 | return val 92 | 93 | def help(): 94 | print("read ") 95 | print("write ") 96 | print("waitint") 97 | 98 | def main(): 99 | mem = DualPortMemory() 100 | 101 | if sys.argv[1] == "read": 102 | addr = str_to_int(sys.argv[2]) 103 | print("addr %04x = %02X" % (addr, mem.read(addr))) 104 | 105 | elif sys.argv[1] == "write": 106 | addr = str_to_int(sys.argv[2]) 107 | val = str_to_int(sys.argv[3]) 108 | mem.write(addr, val) 109 | 110 | elif sys.argv[1] == "waitint": 111 | while not mem.get_interrupt(): 112 | time.sleep(0.0001) 113 | mem.clear_interrupt() 114 | 115 | else: 116 | help() 117 | 118 | 119 | 120 | if __name__ == "__main__": 121 | main() 122 | -------------------------------------------------------------------------------- /smbpi/dpmem_common.py: -------------------------------------------------------------------------------- 1 | import string 2 | import sys 3 | import time 4 | 5 | DP_ADDRPINS=[13, 19, 26, 21, 20, 16, 12, 7, 8, 18] 6 | DP_DATAPINS=[24, 25, 4, 17, 27, 22, 10, 9] 7 | DP_INTR=23 8 | DP_W=5 9 | DP_R=6 10 | DP_CE=11 11 | DP_CONTROLPINS=[DP_W, DP_R, DP_CE] 12 | 13 | ISA_RESET = 2 #15 14 | ISA_POWER = 14 15 | -------------------------------------------------------------------------------- /smbpi/dpmem_direct.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | import string 3 | import sys 4 | import time 5 | import wiringpi 6 | 7 | import dpmem_direct_ext 8 | from dpmem_common import * 9 | 10 | WPI_IN = 0 11 | WPI_OUT = 1 12 | 13 | class DualPortMemory(): 14 | def __init__(self, n_address_bits=10, enable_reset=True, support_read=True): 15 | self.n_address_bits = n_address_bits 16 | self.support_read = support_read 17 | 18 | wiringpi.wiringPiSetupGpio() 19 | for i in range(0, n_address_bits): 20 | wiringpi.pinMode(DP_ADDRPINS[i], WPI_OUT) 21 | 22 | for datapin in DP_DATAPINS: 23 | wiringpi.pinMode(datapin, WPI_IN) 24 | 25 | wiringpi.pinMode(DP_W, WPI_OUT) 26 | wiringpi.pinMode(DP_CE, WPI_OUT) 27 | 28 | wiringpi.digitalWrite(DP_W, 1) 29 | wiringpi.digitalWrite(DP_CE, 1) 30 | 31 | if self.support_read: 32 | wiringpi.pinMode(DP_R, WPI_OUT) 33 | wiringpi.digitalWrite(DP_R, 1) 34 | 35 | wiringpi.pinMode(DP_INTR, WPI_IN) 36 | wiringpi.pullUpDnControl(DP_INTR, 2) 37 | 38 | if (enable_reset): 39 | wiringpi.pinMode(ISA_RESET, WPI_OUT) 40 | wiringpi.digitalWrite(ISA_RESET, 0) 41 | 42 | def read(self, addr): 43 | return dpmem_direct_ext.read_byte(addr, self.n_address_bits) 44 | 45 | def read_block(self, addr, count): 46 | return dpmem_direct_ext.read_block(addr, self.n_address_bits, count) 47 | 48 | def write(self, addr, val): 49 | dpmem_direct_ext.write_byte(addr, self.n_address_bits, val) 50 | 51 | def write_block(self, addr, data, count): 52 | dpmem_direct_ext.write_block(addr, self.n_address_bits, data, count) 53 | 54 | def read_old(self, addr): 55 | try: 56 | dpmem_direct_ext.config_input() 57 | dpmem_direct_ext.set_addr(addr) 58 | 59 | wiringpi.digitalWrite(DP_CE, 0) 60 | wiringpi.digitalWrite(DP_R, 0) 61 | 62 | val=dpmem_direct_ext.get_data() 63 | finally: 64 | wiringpi.digitalWrite(DP_R, 1) 65 | wiringpi.digitalWrite(DP_CE, 1) 66 | 67 | return val 68 | 69 | def write_old(self, addr, val): 70 | try: 71 | dpmem_direct_ext.config_output() 72 | dpmem_direct_ext.set_addr(addr) 73 | 74 | wiringpi.digitalWrite(DP_CE, 0) 75 | wiringpi.digitalWrite(DP_W, 0) 76 | 77 | dpmem_direct_ext.set_data(val) 78 | finally: 79 | wiringpi.digitalWrite(DP_W, 1) 80 | wiringpi.digitalWrite(DP_CE, 1) 81 | 82 | return val 83 | 84 | def get_interrupt(self): 85 | return wiringpi.digitalRead(DP_INTR) == 0 86 | 87 | def clear_interrupt(self): 88 | self.read(0x3FF) 89 | 90 | def toggle_reset(self): 91 | wiringpi.digitalWrite(ISA_RESET, 1) 92 | time.sleep(0.1) 93 | wiringpi.digitalWrite(ISA_RESET, 0) 94 | 95 | 96 | def str_to_int(val): 97 | if "x" in val: 98 | val = string.atoi(val, 16) 99 | else: 100 | val = string.atoi(val) 101 | return val 102 | 103 | def help(): 104 | print("read ") 105 | print("write ") 106 | print("waitint") 107 | 108 | def main(): 109 | mem = DualPortMemory() 110 | 111 | if sys.argv[1] == "read": 112 | addr = str_to_int(sys.argv[2]) 113 | print("addr %04x = %02X" % (addr, mem.read(addr))) 114 | 115 | elif sys.argv[1] == "write": 116 | addr = str_to_int(sys.argv[2]) 117 | val = str_to_int(sys.argv[3]) 118 | mem.write(addr, val) 119 | 120 | elif sys.argv[1] == "waitint": 121 | while not mem.get_interrupt(): 122 | time.sleep(0.0001) 123 | mem.clear_interrupt() 124 | 125 | elif sys.argv[1] == "readblock": 126 | addr = str_to_int(sys.argv[2]) 127 | count = str_to_int(sys.argv[3]) 128 | data = mem.read_block(addr, count) 129 | for b in data: 130 | print("%02X" % ord(b), end="") 131 | print() 132 | 133 | elif sys.argv[1] == "writeblock": 134 | addr = str_to_int(sys.argv[2]) 135 | count = str_to_int(sys.argv[3]) 136 | data = "" 137 | for arg in sys.argv[4:]: 138 | data = data + chr(str_to_int(arg)) 139 | mem.write_block(addr, data, count) 140 | 141 | elif sys.argv[1] == "benchread": 142 | t=time.time() 143 | for i in range(0,100): 144 | mem.read_block(0,512) 145 | elapsed = time.time()-t 146 | print("elapsed =", elapsed, "ops/s = ", 100.0/elapsed, "KB/s =", 100.0/elapsed*512/1024) 147 | return 148 | 149 | elif sys.argv[1] == "benchwrite": 150 | controlblock = "" 151 | for i in range(0, 512): 152 | controlblock = controlblock + chr(i & 0xFF) 153 | t=time.time() 154 | for i in range(0,100): 155 | mem.write_block(0,controlblock,512) 156 | elapsed = time.time()-t 157 | print("elapsed =", elapsed, "ops/s = ", 100.0/elapsed, "KB/s =", 100.0/elapsed*512/1024) 158 | return 159 | 160 | elif sys.argv[1] == "testread": 161 | last_block = mem.read_block(0,512) 162 | while True: 163 | block = mem.read_block(0, 512) 164 | errors=0; 165 | for i in range(0,512): 166 | if block[i]!=last_block[i]: 167 | errors+=1 168 | if errors>0: 169 | print("errors", errors) 170 | last_block = block 171 | 172 | elif sys.argv[1] == "testreadwrite": 173 | passnum = 0 174 | while True: 175 | controlblock = "" 176 | for i in range(0, 512): 177 | controlblock = controlblock + chr((i+passnum) & 0xFF) 178 | 179 | mem.write_block(0,controlblock,512) 180 | 181 | block = mem.read_block(0, 512) 182 | errors=0; 183 | for i in range(0,512): 184 | if block[i]!=controlblock[i]: 185 | errors+=1 186 | if errors>0: 187 | print("errors", errors) 188 | 189 | passnum+=1 190 | 191 | else: 192 | help() 193 | 194 | 195 | 196 | if __name__ == "__main__": 197 | main() 198 | -------------------------------------------------------------------------------- /smbpi/dpmem_direct_ext.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define DP_W 5 6 | #define DP_R 6 7 | #define DP_CE 11 8 | 9 | const int DP_ADDRPINS[] = { 13, 19, 26, 21, 20, 16, 12, 7, 8, 18 }; 10 | const int DP_DATAPINS[] = { 24, 25, 04, 17, 27, 22, 10, 9 }; 11 | const int DP_DATAPINS_REVERSED[] = { 9, 10, 22, 27, 17, 04, 25, 24 }; 12 | 13 | void dpmem_config_input(void) 14 | { 15 | int i; 16 | for (i=0; i<8; i++) { 17 | pinMode(DP_DATAPINS[i], INPUT); 18 | } 19 | } 20 | 21 | void dpmem_config_output(void) 22 | { 23 | int i; 24 | for (i=0; i<8; i++) { 25 | pinMode(DP_DATAPINS[i], OUTPUT); 26 | } 27 | } 28 | 29 | void dpmem_set_addr_nbits(unsigned int addr, unsigned int nbits) 30 | { 31 | int i; 32 | for (i=0; i> 1; 35 | } 36 | } 37 | 38 | void dpmem_set_addr(unsigned int addr) 39 | { 40 | dpmem_set_addr_nbits(addr, 10); 41 | } 42 | 43 | void dpmem_set_data(unsigned int data) 44 | { 45 | int i; 46 | for (i=0; i<8; i++) { 47 | digitalWrite(DP_DATAPINS[i], data & 0x01); 48 | data = data >> 1; 49 | } 50 | } 51 | 52 | unsigned int dpmem_get_data(void) 53 | { 54 | int i; 55 | int data = 0; 56 | for (i=0; i<8; i++) { 57 | data = data << 1; 58 | data = data | digitalRead(DP_DATAPINS_REVERSED[i]); 59 | } 60 | return data; 61 | } 62 | 63 | void short_delay(void) 64 | { 65 | // Just do nothing for a while. This is to allow the RAM some time to do it's work. 66 | // 67 | int j; 68 | 69 | for (j=0; j<1; j++) { 70 | asm("nop"); 71 | } 72 | } 73 | 74 | static PyObject *dpmem_direct_set_addr(PyObject *self, PyObject *args) 75 | { 76 | int i; 77 | if (!PyArg_ParseTuple(args, "i", &i)) { 78 | return NULL; 79 | } 80 | dpmem_set_addr(i); 81 | return Py_BuildValue(""); 82 | } 83 | 84 | static PyObject *dpmem_direct_set_addr_nbits(PyObject *self, PyObject *args) 85 | { 86 | int addr, nbits; 87 | if (!PyArg_ParseTuple(args, "ii", &addr, &nbits)) { 88 | return NULL; 89 | } 90 | dpmem_set_addr_nbits(addr, nbits); 91 | return Py_BuildValue(""); 92 | } 93 | 94 | static PyObject *dpmem_direct_set_data(PyObject *self, PyObject *args) 95 | { 96 | int i; 97 | if (!PyArg_ParseTuple(args, "i", &i)) { 98 | return NULL; 99 | } 100 | dpmem_set_data(i); 101 | return Py_BuildValue(""); 102 | } 103 | 104 | static PyObject *dpmem_direct_get_data(PyObject *self, PyObject *args) 105 | { 106 | if (!PyArg_ParseTuple(args, "")) { 107 | return NULL; 108 | } 109 | return Py_BuildValue("i", dpmem_get_data()); 110 | } 111 | 112 | static PyObject *dpmem_direct_config_input(PyObject *self, PyObject *args) 113 | { 114 | if (!PyArg_ParseTuple(args, "")) { 115 | return NULL; 116 | } 117 | dpmem_config_input(); 118 | return Py_BuildValue(""); 119 | } 120 | 121 | static PyObject *dpmem_direct_config_output(PyObject *self, PyObject *args) 122 | { 123 | if (!PyArg_ParseTuple(args, "")) { 124 | return NULL; 125 | } 126 | dpmem_config_output(); 127 | return Py_BuildValue(""); 128 | } 129 | 130 | static PyObject *dpmem_direct_read_byte(PyObject *self, PyObject *args) 131 | { 132 | unsigned int addr, nbits; 133 | unsigned int data; 134 | if (!PyArg_ParseTuple(args, "ii", &addr, &nbits)) { 135 | return NULL; 136 | } 137 | dpmem_config_input(); 138 | dpmem_set_addr_nbits(addr, nbits); 139 | digitalWrite(DP_CE,0); 140 | digitalWrite(DP_R,0); 141 | // see comment in dpmem_direct_read_block. Just to be safe... 142 | digitalRead(DP_DATAPINS_REVERSED[0]); 143 | data = dpmem_get_data(); 144 | digitalWrite(DP_R,1); 145 | digitalWrite(DP_CE,1); 146 | return Py_BuildValue("i", data); 147 | } 148 | 149 | static PyObject *dpmem_direct_write_byte(PyObject *self, PyObject *args) 150 | { 151 | unsigned int addr, nbits, data; 152 | if (!PyArg_ParseTuple(args, "iii", &addr, &nbits, &data)) { 153 | return NULL; 154 | } 155 | dpmem_config_output(); 156 | dpmem_set_addr_nbits(addr, nbits); 157 | dpmem_set_data(data); 158 | digitalWrite(DP_CE,0); 159 | digitalWrite(DP_W,0); 160 | digitalWrite(DP_W,1); 161 | digitalWrite(DP_CE,1); 162 | return Py_BuildValue(""); 163 | } 164 | 165 | static PyObject *dpmem_direct_read_block(PyObject *self, PyObject *args) 166 | { 167 | unsigned int addr, nbits, count; 168 | char buf[1024]; 169 | int i; 170 | 171 | if (!PyArg_ParseTuple(args, "iii", &addr, &nbits, &count)) { 172 | return NULL; 173 | } 174 | 175 | if (count > 1024) { 176 | // throw exception? 177 | return NULL; 178 | } 179 | 180 | dpmem_config_input(); 181 | 182 | for (i=0; i> 1 39 | 40 | wiringpi.digitalWrite(DP_CE, 0) 41 | wiringpi.digitalWrite(CP_R, 0) 42 | 43 | val=0 44 | for pin in reversed(DP_DATAPINS): 45 | val=val<<1 46 | val = val | wiringpi.digitalRead(pin) 47 | finally: 48 | wiringpi.digitalWrite(DP_R, 1) 49 | wiringpi.digitalWrite(DP_CE, 1) 50 | 51 | return val 52 | 53 | def write(self, addr, val): 54 | try: 55 | for pin in DP_DATAPINS: 56 | wiringpi.pinMode(pin, WPI_OUT) 57 | 58 | for pin in DP_ADDRPINS: 59 | wiringpi.digitalWrite(pin, addr & 1) 60 | addr = addr >> 1 61 | 62 | wiringpi.digitalWrite(DP_CE, 0) 63 | wiringpi.digitalWrite(DP_W, 0) 64 | 65 | for pin in DP_DATAPINS: 66 | wiringpi.digitalWrite(pin, val & 1) 67 | val = val >> 1 68 | finally: 69 | wiringpi.digitalWrite(DP_W, 1) 70 | wiringpi.digitalWrite(DP_CE, 1) 71 | 72 | return val 73 | 74 | def write_block(self, addr, data, count): 75 | for i in range(0, count): 76 | self.mem.write(addr+i, ord(data[i])) 77 | 78 | def read_block(self, addr, count): 79 | bytes=[] 80 | for i in range(0, count): 81 | bytes = bytes + chr(self.mem.read(addr+i)) 82 | return bytes 83 | 84 | def get_interrupt(self): 85 | return wiringpi.digitalRead(DP_INTR) == 0 86 | 87 | def clear_interrupt(self): 88 | self.read(0x3FF) 89 | 90 | def str_to_int(val): 91 | if "x" in val: 92 | val = string.atoi(val, 16) 93 | else: 94 | val = string.atoi(val) 95 | return val 96 | 97 | def help(): 98 | print("read ") 99 | print("write ") 100 | print("waitint") 101 | 102 | def main(): 103 | mem = DualPortMemory() 104 | 105 | if sys.argv[1] == "read": 106 | addr = str_to_int(sys.argv[2]) 107 | print("addr %04x = %02X" % (addr, mem.read(addr))) 108 | 109 | elif sys.argv[1] == "write": 110 | addr = str_to_int(sys.argv[2]) 111 | val = str_to_int(sys.argv[3]) 112 | mem.write(addr, val) 113 | 114 | elif sys.argv[1] == "waitint": 115 | while not mem.get_interrupt(): 116 | time.sleep(0.0001) 117 | mem.clear_interrupt() 118 | 119 | else: 120 | help() 121 | 122 | 123 | 124 | if __name__ == "__main__": 125 | main() 126 | -------------------------------------------------------------------------------- /smbpi/ds1820.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | import os 3 | import sys 4 | import traceback 5 | 6 | DEVICE_DIR = "/sys/bus/w1/devices" 7 | 8 | 9 | class DS1820: 10 | def __init__(self): 11 | self.find_devices() 12 | 13 | def find_devices(self): 14 | self.devices = [] 15 | for fn in os.listdir(DEVICE_DIR): 16 | # literature says 28-, but mine are 10- 17 | if fn.startswith("10-") or fn.startswith("28-"): 18 | self.devices.append(fn) 19 | 20 | def measure_device(self, name): 21 | try: 22 | fn = os.path.join(DEVICE_DIR, name, "w1_slave") 23 | if not os.path.exists(fn): 24 | return None 25 | f = open(fn) 26 | firstLine = f.readline().strip() 27 | if not firstLine.endswith("YES"): 28 | return None 29 | secondLine = f.readline().strip() 30 | parts = secondLine.split("t=") 31 | if len(parts)!=2: 32 | return None 33 | tempC = float(parts[1])/1000.0 34 | return tempC 35 | except Exception: 36 | traceback.print_exc() 37 | 38 | def measure_first_device(self): 39 | if not self.devices: 40 | return None 41 | return self.measure_device(self.devices[0]) 42 | 43 | def device_count(self): 44 | return len(self.devices) 45 | 46 | 47 | def main(): 48 | ds = DS1820() 49 | if ds.device_count() == 0: 50 | print("No devices found", file=sys.stderr) 51 | sys.exit(-1) 52 | 53 | print("%d devices found" % ds.device_count()) 54 | 55 | tempC = ds.measure_first_device() 56 | if tempC == None: 57 | print("Failed to read temperature", file=sys.stderr) 58 | sys.exit(-1) 59 | 60 | print("Temperature %0.2f" % tempC) 61 | 62 | 63 | if __name__ == "__main__": 64 | main() -------------------------------------------------------------------------------- /smbpi/encoder.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | import math 3 | import threading 4 | import time 5 | import traceback 6 | import RPi.GPIO as GPIO 7 | 8 | # pin numbers from scott's e-paper project 9 | 10 | PIN_ENC_A = 20 11 | PIN_ENC_B = 21 12 | 13 | PIN_ENC_A2 = 12 14 | PIN_ENC_B2 = 16 15 | 16 | class EncoderHandler(object): 17 | 18 | def __init__(self, pin_a=PIN_ENC_A, pin_b=PIN_ENC_B, pin_button=None, pud=GPIO.PUD_OFF, button_pud=None, min_pos=None, max_pos=None, invert=False, num=0, thread=None): 19 | self.num = num 20 | self.thread = thread 21 | self.pin_enc_a = pin_a 22 | self.pin_enc_b = pin_b 23 | self.pin_button = pin_button 24 | self.min_position = min_pos 25 | self.max_position = max_pos 26 | self.invert = invert 27 | GPIO.setup(self.pin_enc_a, GPIO.IN, pull_up_down=pud) 28 | GPIO.setup(self.pin_enc_b, GPIO.IN, pull_up_down=pud) 29 | 30 | if not button_pud: 31 | button_pud = pud 32 | 33 | if self.pin_button: 34 | GPIO.setup(self.pin_button, GPIO.IN, pull_up_down=button_pud) 35 | 36 | self.a_state = 0 37 | self.b_state = 0 38 | self.button_state = 1 39 | 40 | self.position = 0 41 | self.delta = 0 42 | 43 | # button history 44 | self.last_button_poll_state = self.button_state 45 | self.button_up_event = False 46 | self.button_down_event = False 47 | 48 | # encoder init 49 | self.poll_input() 50 | self.last_delta = 0 51 | self.r_seq = self.rotation_sequence() 52 | self.steps_per_cycle = 4 # 4 steps between detents 53 | self.remainder = 0 54 | 55 | def poll_input(self): 56 | self.a_state = GPIO.input(self.pin_enc_a) 57 | self.b_state = GPIO.input(self.pin_enc_b) 58 | if self.pin_button: 59 | self.button_state = GPIO.input(self.pin_button) 60 | 61 | def rotation_sequence(self): 62 | r_seq = (self.a_state ^ self.b_state) | self.b_state << 1 63 | return r_seq 64 | 65 | # Returns offset values of -2,-1,0,1,2 66 | def get_delta(self): 67 | delta = 0 68 | r_seq = self.rotation_sequence() 69 | if r_seq != self.r_seq: 70 | delta = (r_seq - self.r_seq) % 4 71 | if delta==3: 72 | delta = -1 73 | elif delta==2: 74 | delta = int(math.copysign(delta, self.last_delta)) # same direction as previous, 2 steps 75 | 76 | self.last_delta = delta 77 | self.r_seq = r_seq 78 | 79 | return delta 80 | 81 | def get_cycles(self): 82 | self.remainder += self.get_delta() 83 | cycles = self.remainder // self.steps_per_cycle 84 | self.remainder %= self.steps_per_cycle # remainder always remains positive 85 | if self.invert: 86 | cycles = -cycles 87 | return cycles 88 | 89 | def update(self): 90 | delta = self.get_cycles() 91 | self.delta += delta 92 | self.position += delta 93 | if (self.min_position is not None) and (self.position < self.min_position): 94 | self.position = self.min_position 95 | if (self.max_position is not None) and (self.position > self.max_position): 96 | self.position = self.max_position 97 | return delta 98 | 99 | def update_button(self): 100 | something_happened = False 101 | if self.button_state != self.last_button_poll_state: 102 | self.last_button_poll_state = self.button_state 103 | if self.button_state: 104 | if not self.button_up_event: 105 | self.button_up_event = True 106 | something_happened = True 107 | else: 108 | if not self.button_down_event: 109 | self.button_down_event = True 110 | something_happened = True 111 | return something_happened 112 | 113 | class EncoderThread(threading.Thread): 114 | def __init__(self, encoders, store_points=False): 115 | super(EncoderThread,self).__init__() 116 | self.delay = 0.0001 117 | self.daemon = True 118 | self.store_points = store_points 119 | self.points = [] 120 | self.lock = threading.Lock() 121 | self.handlers=[] 122 | for encoder in encoders: 123 | handler = EncoderHandler(num=len(self.handlers), thread=self, **encoder) 124 | self.handlers.append(handler) 125 | 126 | def updated(self, handler): 127 | # override me 128 | pass 129 | 130 | def run(self): 131 | while True: 132 | need_notify = [] 133 | something_changed = False 134 | for handler in self.handlers: 135 | handler.poll_input() 136 | with self.lock: 137 | delta = handler.update() 138 | if delta != 0: 139 | something_changed = True 140 | need_notify.append(handler) 141 | 142 | if handler.update_button(): 143 | if handler not in need_notify: 144 | need_notify.append(handler) 145 | 146 | # this stores a vector of all encoders, so they can be read at once 147 | if (self.store_points) and (something_changed): 148 | point = [] 149 | with self.lock: 150 | for handler in self.handlers: 151 | point.append(handler.position) 152 | self.points.append(point) 153 | 154 | # call these outside of self.lock 155 | for handler in need_notify: 156 | self.updated(handler) 157 | 158 | time.sleep(self.delay) 159 | 160 | def get_delta(self, num): 161 | with self.lock: 162 | delta = self.handlers[num].delta 163 | self.handlers[num].delta = 0 164 | return delta 165 | 166 | def get_button_down_event(self, num): 167 | with self.lock: 168 | event = self.handlers[num].button_down_event 169 | self.handlers[num].button_down_event = False 170 | return event 171 | 172 | def get_button_up_event(self, num): 173 | with self.lock: 174 | event = self.handlers[num].button_up_event 175 | self.handlers[num].button_up_event = False 176 | return event 177 | 178 | def get_position(self, num): 179 | return self.handlers[num].position 180 | 181 | def get_points(self): 182 | with self.lock: 183 | points = self.points[:] 184 | self.points = [] 185 | return points 186 | """ 187 | main: 188 | """ 189 | 190 | def main(): 191 | GPIO.setmode(GPIO.BCM) 192 | 193 | encoder = EncoderThread(encoders 194 | =[{"pin_a": PIN_ENC_A, "pin_b": PIN_ENC_B}, 195 | {"pin_a": PIN_ENC_A2, "pin_b": PIN_ENC_B2}], 196 | store_points = True) 197 | #encoder = EncoderThread(encoders 198 | # =[{"pin_a": 18, "pin_b": 23, "pud": GPIO.PUD_UP}, 199 | # ], 200 | # store_points = True) 201 | encoder.start() 202 | 203 | last_delta = 0 204 | last_position = 0 205 | last_delta2 = 0 206 | last_position2 = 0 207 | while True: 208 | points = encoder.get_points() 209 | if points: 210 | print(points) 211 | """ 212 | delta = encoder.get_delta(0) 213 | position = encoder.get_position(0) 214 | delta2 = encoder.get_delta(1) 215 | position2 = encoder.get_position(1) 216 | if (delta!=last_delta) or (position!=last_position) or (delta2!=last_delta2) or (position!=last_position2): 217 | print(position, delta, position2, delta2) 218 | last_delta = delta 219 | last_position = position 220 | last_delta2 = delta2 221 | last_position2 = position2 222 | """ 223 | time.sleep(0.01) 224 | 225 | 226 | if __name__ == "__main__": 227 | main() -------------------------------------------------------------------------------- /smbpi/fan4pin.py: -------------------------------------------------------------------------------- 1 | # fan4pin.py 2 | # Scott Baker, http://www.smbaker.com/ 3 | # 4 | # 4pin fan driver using pigpiod. 5 | # 6 | # Uses a hardware pwm pin (GPIO18) to smoothly run fan 7 | # Optionally supports RPM readback 8 | # 9 | # Tested using a delta ASB0305HP-00CP4 10 | # 11 | # Requires pigpiod installed 12 | 13 | from __future__ import print_function 14 | import pigpio 15 | import sys 16 | 17 | FAN_PWM_PIN = 18 18 | FAN_RPM_PIN = 23 19 | 20 | 21 | class Fan4Pin(object): 22 | def __init__(self, pi, pin=FAN_PWM_PIN, pin_rpm=FAN_RPM_PIN, weighting=0.1, initial_pwm=255): 23 | self.pi = pi 24 | self.pin = pin 25 | self.pin_rpm = pin_rpm 26 | self.pwm = 0 27 | self._freq = 25000 28 | 29 | # Since we're using hardware PWM, limit to a pin that supports it 30 | if (self.pin not in [12, 13, 18, 19]): 31 | print("Only support PWM on 12, 13, 18, or 19", file=sys.stderr) 32 | sys.exit(-1) 33 | 34 | if (self.pin_rpm): 35 | self.pulses_per_rev = 2 36 | self._new = 1.0 - weighting 37 | self._old = weighting 38 | self._high_tick = None 39 | self._period = None 40 | self._watchdog = 200 41 | self._rpm_callback_enabled = False 42 | self.pi.set_mode(self.pin_rpm, pigpio.INPUT) 43 | self.pi.set_pull_up_down(self.pin_rpm, pigpio.PUD_UP) 44 | self.enable_rpm() 45 | 46 | if initial_pwm is not None: 47 | self.set_pwm(initial_pwm) 48 | 49 | def _set_pwm(self, v): 50 | # v is a value between 0 and 255 51 | print("XXX", v, self.pin, self._freq, v*1000/255) 52 | self.pi.hardware_PWM(self.pin, self._freq, v*1000000/255) 53 | 54 | def set_pwm(self, v): 55 | self._set_pwm(v) 56 | self.pwm = v 57 | 58 | def _rpm_callback_handler(self, pin, level, tick): 59 | if not self._rpm_callback_enabled: 60 | return 61 | 62 | if level == 1: # Rising edge. 63 | if self._high_tick is not None: 64 | t = pigpio.tickDiff(self._high_tick, tick) 65 | 66 | if t > 0: 67 | # compute an rpm for bounds checking 68 | t_rpm = 60000000.0 / (t * self.pulses_per_rev) 69 | else: 70 | t_rpm = 0 71 | 72 | if (t_rpm > 12500) or (t_rpm < 100): 73 | # abnormal reading, discard 74 | pass 75 | else: 76 | if self._period is not None: 77 | self._period = (self._old * self._period) + (self._new * t) 78 | else: 79 | self._period = t 80 | 81 | self._high_tick = tick 82 | 83 | elif level == 2: # Watchdog timeout. 84 | if self._period is not None: 85 | if self._period < 2000000000: 86 | self._period += (self._watchdog * 1000) 87 | 88 | def enable_rpm(self): 89 | self._high_tick = None 90 | self._rpm_callback = self.pi.callback(self.pin_rpm, pigpio.RISING_EDGE, self._rpm_callback_handler) 91 | self._rpm_callback_enabled = True 92 | 93 | def disable_rpm(self): 94 | if self._rpm_callback: 95 | self._rpm_callback_enabled = False 96 | self._rpm_callback.cancel() 97 | self._rpm_callback = None 98 | 99 | def get_fan_rpm(self): 100 | if self._period is not None: 101 | return 60000000.0 / (self._period * self.pulses_per_rev) 102 | else: 103 | return 0 104 | 105 | def get_rpm(self): 106 | return self.get_fan_rpm() 107 | 108 | def report_rpm(self, rpm): 109 | pass 110 | 111 | 112 | def main(): 113 | import time 114 | 115 | pi = pigpio.pi() 116 | 117 | fan = Fan4Pin(pi) 118 | 119 | if len(sys.argv) > 1: 120 | fan.set_pwm(int(sys.argv[1])) 121 | 122 | while True: 123 | print("rpm", int(fan.get_rpm())) 124 | time.sleep(1) 125 | 126 | 127 | if __name__ == "__main__": 128 | main() 129 | -------------------------------------------------------------------------------- /smbpi/fdc.py: -------------------------------------------------------------------------------- 1 | # Raspberry Pi WD37C65 driver 2 | # Scott Baker, https://www.smbaker.com/ 3 | # 4 | # Borrowed liberally from the RomWBW floppy driver 5 | 6 | from __future__ import print_function 7 | import sys 8 | import time 9 | 10 | # MSR is CS=FDC, A0=0 11 | # DATA is CS=FDC, A0=1 12 | # DOR is CS=DOR 13 | # DCR is CS=DCR 14 | 15 | import wd37c65_direct_ext 16 | 17 | FDM720 = 0 18 | FDM144 = 1 19 | FDM360 = 2 20 | FDM120 = 3 21 | FDM111 = 4 22 | 23 | FRC_OK = 0 24 | FRC_NOTIMPL = 1 25 | FRC_CMDERR = 2 26 | FRC_ERROR = 3 27 | FRC_ABORT = 4 28 | FRC_BUFMAX = 5 29 | FRC_ABTERM = 8 30 | FRC_INVCMD = 9 31 | FRC_DSKCHG = 0x0A 32 | FRC_ENDCYL = 0x0B 33 | FRC_DATAERR = 0x0C 34 | FRC_OVERRUN = 0x0D 35 | FRC_NODATA = 0x0E 36 | FRC_NOTWRIT = 0x0F 37 | FRC_MISADR = 0x10 38 | FRC_TOFDRRDY = 0x11 39 | FRC_TOSNDCMD = 0x12 40 | FRC_TOGETRES = 0x13 41 | FRC_TOEXEC = 0x14 42 | FRC_TOSEEKWT = 0x15 43 | FRC_OVER_DRAIN = 0x16 44 | FRC_OVER_CMDRES = 0x17 45 | FRC_TO_READRES = 0x18 46 | FRC_READ_ERROR = 0x19 47 | FRC_WRITE_ERROR = 0x20 48 | FRC_SHORT = 0x21 49 | FRC_LONG = 0x22 50 | FRC_INPROGRESS = 0x23 51 | 52 | CFD_READ = 0B00000110 # CMD,HDS/DS,C,H,R,N,EOT,GPL,DTL --> ST0,ST1,ST2,C,H,R,N 53 | CFD_READDEL = 0B00001100 # CMD,HDS/DS,C,H,R,N,EOT,GPL,DTL --> ST0,ST1,ST2,C,H,R,N 54 | CFD_WRITE = 0B00000101 # CMD,HDS/DS,C,H,R,N,EOT,GPL,DTL --> ST0,ST1,ST2,C,H,R,N 55 | CFD_WRITEDEL = 0B00001001 # CMD,HDS/DS,C,H,R,N,EOT,GPL,DTL --> ST0,ST1,ST2,C,H,R,N 56 | CFD_READTRK = 0B00000010 # CMD,HDS/DS,C,H,R,N,EOT,GPL,DTL --> ST0,ST1,ST2,C,H,R,N 57 | CFD_READID = 0B00001010 # CMD,HDS/DS --> ST0,ST1,ST2,C,H,R,N 58 | CFD_FMTTRK = 0B00001101 # CMD,HDS/DS,N,SC,GPL,D --> ST0,ST1,ST2,C,H,R,N 59 | CFD_SCANEQ = 0B00010001 # CMD,HDS/DS,C,H,R,N,EOT,GPL,STP --> ST0,ST1,ST2,C,H,R,N 60 | CFD_SCANLOEQ = 0B00011001 # CMD,HDS/DS,C,H,R,N,EOT,GPL,STP --> ST0,ST1,ST2,C,H,R,N 61 | CFD_SCANHIEQ = 0B00011101 # CMD,HDS/DS,C,H,R,N,EOT,GPL,STP --> ST0,ST1,ST2,C,H,R,N 62 | CFD_RECAL = 0B00000111 # CMD,DS --> 63 | CFD_SENSEINT = 0B00001000 # CMD --> ST0,PCN 64 | CFD_SPECIFY = 0B00000011 # CMD,SRT/HUT,HLT/ND --> 65 | CFD_DRVSTAT = 0B00000100 # CMD,HDS/DS --> ST3 66 | CFD_SEEK = 0B00001111 # CMD,HDS/DS --> 67 | CFD_VERSION = 0B00010000 # CMD --> ST0 68 | 69 | CFD_NAME = {CFD_READ: "READ", 70 | CFD_READDEL: "READDEL", 71 | CFD_WRITE: "WRITE", 72 | CFD_WRITEDEL: "WRITEDEL", 73 | CFD_READTRK: "READTRK", 74 | CFD_READID: "READID", 75 | CFD_FMTTRK: "FMTTRK", 76 | CFD_SCANEQ: "SCANEQ", 77 | CFD_SCANLOEQ: "SCANLOEQ", 78 | CFD_SCANHIEQ: "SCANHIEQ", 79 | CFD_RECAL: "RECAL", 80 | CFD_SENSEINT: "SENSEINT", 81 | CFD_SPECIFY: "SPECIFY", 82 | CFD_DRVSTAT: "DRVSTAT", 83 | CFD_SEEK: "SEEK", 84 | CFD_VERSION: "VERSION"} 85 | 86 | class FDCException(Exception): 87 | def __init__(self, fstRC, msg=None): 88 | if not msg: 89 | msg="FDC Exception %2X" % fstRC 90 | Exception.__init__(self, msg) 91 | self.fstRC = fstRC 92 | 93 | class FDC: 94 | def __init__(self, media = "144", verbose=True): 95 | self.verbose = verbose 96 | 97 | self.DOR_INIT = 0B00001100 98 | self.DOR_BR250 = self.DOR_INIT 99 | self.DOR_BR500 = self.DOR_INIT 100 | self.DCR_BR250 = 1 101 | self.DCR_BR500 = 0 102 | 103 | # dynamic 104 | self.ds = 0 105 | self.cyl = 0 106 | self.head = 0 107 | self.record = 0 108 | self.fillByte = 0xE5 109 | self.dop = 0 110 | self.idleCount = 0 111 | self.to = 0 112 | self.fdcReady = False 113 | self.motoSwap = False 114 | 115 | # unit data 116 | self.track = 0xFF 117 | 118 | self.dor = 0 119 | 120 | self.setMedia(media) 121 | 122 | def setMedia(self, what): 123 | if (what == "144") or (what == "pc144") or (what == "14.4") or (what == "1440") or (what == "pc1440"): 124 | self.set144() 125 | elif (what == "720") or (what == "pc720"): 126 | self.set720() 127 | elif (what == "360") or (what == "pc360"): 128 | self.set360() 129 | elif (what == "120") or (what == "pc120"): 130 | self.set120() 131 | elif (what == "111") or (what == "pc111"): 132 | self.set111() 133 | else: 134 | raise Exception("Unknown media %s"% what) 135 | 136 | def set360(self): 137 | self.numCyl = 0x28 138 | self.numHead = 2 139 | #self.numSec = 9 # redundant with self.secCount ?? 140 | self.sot = 1 141 | self.secCount = 9 142 | self.eot = self.secCount 143 | self.secSize = 0x200 144 | self.N = 2 # 2 = 512 bytes/sector 145 | self.gapLengthRW = 0x2A 146 | self.gapLengthFormat = 0x50 147 | self.stepRate = (13 << 4) | 0 # srtHut 148 | self.headLoadTimeNonDma = (4 << 1) | 1 # hltNd 149 | self.DOR = self.DOR_BR250 150 | self.DCR = self.DCR_BR250 151 | self.media = FDM360 152 | 153 | def set9836(self): 154 | # HP 9836 155 | self.numCyl = 0x23 156 | self.numHead = 2 157 | #self.numSec = 0x0F # redundant with self.secCount ?? 158 | self.sot = 1 159 | self.secCount = 0x0F 160 | self.eot = self.secCount 161 | self.secSize = 0x100 162 | self.N = 1 # 1 = 256 bytes/sector 163 | self.gapLengthRW = 0x2A 164 | self.gapLengthFormat = 0x50 165 | self.stepRate = (13 << 4) | 0 # srtHut 166 | self.headLoadTimeNonDma = (4 << 1) | 1 # hltNd 167 | self.DOR = self.DOR_BR250 168 | self.DCR = self.DCR_BR250 169 | self.media = FDM360 170 | 171 | def set720(self): 172 | self.numCyl = 0x50 173 | self.numHead = 2 174 | #self.numSec = 0x09 175 | self.sot = 1 176 | self.secCount = 0x09 177 | self.eot = self.secCount 178 | self.secSize = 0x200 179 | self.N = 2 # 2 = 512 bytes/sector 180 | self.gapLengthRW = 0x2A 181 | self.gapLengthFormat = 0x50 182 | self.stepRate = (13 << 4) | 0 # srtHut 183 | self.headLoadTimeNonDma = (4 << 1) | 1 # hltNd 184 | self.DOR = self.DOR_BR250 185 | self.DCR = self.DCR_BR250 186 | self.media = FDM720 187 | 188 | def set144(self): 189 | self.numCyl = 0x50 190 | self.numHead = 2 191 | #self.numSec = 0x12 192 | self.sot = 1 193 | self.secCount = 0x12 194 | self.eot = self.secCount 195 | self.secSize = 0x200 196 | self.N = 2 # 2 = 512 bytes/sector 197 | self.gapLengthRW = 0x1B 198 | self.gapLengthFormat = 0x6C 199 | self.stepRate = (13 << 4) | 0 # srtHut 200 | self.headLoadTimeNonDma = (8 << 1) | 1 # hltNd 201 | self.DOR = self.DOR_BR500 202 | self.DCR = self.DCR_BR500 203 | self.media = FDM144 204 | 205 | def set120(self): 206 | self.numCyl = 0x50 207 | self.numHead = 2 208 | #self.numSec = 0x0F 209 | self.sot = 1 210 | self.secCount = 0x0F 211 | self.eot = self.secCount 212 | self.secSize = 0x200 213 | self.N = 2 # 2 = 512 bytes/sector 214 | self.gapLengthRW = 0x1B 215 | self.gapLengthFormat = 0x54 216 | self.stepRate = (10 << 4) | 0 # srtHut 217 | self.headLoadTimeNonDma = (8 << 1) | 1 # hltNd 218 | self.DOR = self.DOR_BR500 219 | self.DCR = self.DCR_BR500 220 | self.media = FDM120 221 | 222 | def set111(self): 223 | self.numCyl = 0x28 224 | self.numHead = 2 225 | #self.numSec = 0x0F 226 | self.sot = 1 227 | self.secCount = 0x0F 228 | self.eot = self.secCount 229 | self.secSize = 0x200 230 | self.N = 2 # 2 = 512 bytes/sector 231 | self.gapLengthRW = 0x1B 232 | self.gapLengthFormat = 0x54 233 | self.stepRate = (13 << 4) | 0 # srtHut 234 | self.headLoadTimeNonDma = (25 << 1) | 1 # hltNd 235 | self.DOR = self.DOR_BR500 236 | self.DCR = self.DCR_BR500 237 | self.media = FDM111 238 | 239 | def log(self, x, newline=True): 240 | if not self.verbose: 241 | return 242 | if newline: 243 | print(x, file=sys.stderr) 244 | else: 245 | print(x, end='', file=sys.stderr) 246 | 247 | # ------------ chip funcs ----------------- 248 | 249 | def readDataBlock(self, count): 250 | status, blk = wd37c65_direct_ext.read_block(count) 251 | if status not in [FRC_OK, FRC_READ_ERROR]: 252 | raise FDCException(status) 253 | 254 | self.dskBuf = blk 255 | 256 | def writeDataBlock(self, count, autoTerminate=True): 257 | status = wd37c65_direct_ext.write_block(self.dskBuf, count, autoTerminate) 258 | if status not in [FRC_OK, FRC_WRITE_ERROR]: 259 | raise FDCException(status) 260 | 261 | def writeData(self, d): 262 | wd37c65_direct_ext.write_data(d) 263 | 264 | def drain(self): 265 | status = wd37c65_direct_ext.drain() 266 | if status != 0: 267 | raise FDCException(status) 268 | 269 | def wait_msr(self, mask, val): 270 | return wd37c65_direct_ext.wait_msr(mask, val) 271 | 272 | def get_msr(self): 273 | return wd37c65_direct_ext.get_msr() 274 | 275 | def readResult(self): 276 | status, blk = wd37c65_direct_ext.read_result() 277 | if status != 0: 278 | raise FDCException(status) 279 | 280 | self.frb = blk 281 | self.frbLen = len(self.frb) 282 | 283 | def resetFDC(self): 284 | wd37c65_direct_ext.reset(self.dor) 285 | 286 | def initFDC(self): 287 | wd37c65_direct_ext.init() 288 | 289 | def writeDOR(self, dor): 290 | wd37c65_direct_ext.write_dor(dor) 291 | self.dor = dor 292 | 293 | def writeDCR(self, dcr): 294 | wd37c65_direct_ext.write_dcr(dcr) 295 | self.dcr = dcr 296 | 297 | # ----------------------------------------- 298 | 299 | def init(self): 300 | self.fcpBuf = [0, 0, 0, 0, 0, 0, 0, 0, 0] 301 | self.idleCount = 0 302 | self.dor = self.DOR_INIT 303 | self.initFDC() 304 | self.resetFDC() 305 | self._clearDiskChange() 306 | self.fdcReady = True 307 | self.motorBoth = False 308 | 309 | def done(self): 310 | self._motorOff() 311 | 312 | def _reset(self): 313 | self.resetFDC() 314 | self._clearDiskChange() 315 | self.track = 0xFF # mark needing recal 316 | self.fdcReady = True 317 | 318 | def read(self, cyl=None, head=None, record=None, retries=0): 319 | if cyl is not None: 320 | self.cyl = cyl 321 | if head is not None: 322 | self.head = head 323 | if record is not None: 324 | self.record = record 325 | 326 | while (retries >= 0): 327 | retries -= 1 328 | self._start() 329 | if (self.fstRC != FRC_OK): 330 | print("*** read _start retry %02X" % self.fstRC, file=sys.stderr) 331 | continue 332 | 333 | self._setupIO(CFD_READ | 0B11100000) 334 | self._fop() 335 | 336 | if self.fstRC == FRC_OK: 337 | return self.fstRC 338 | 339 | print("*** read retry %02X" % self.fstRC, file=sys.stderr) 340 | 341 | # retries exhausted 342 | return self.fstRC 343 | 344 | def write(self, cyl=None, head=None, record=None, retries=0): 345 | if cyl is not None: 346 | self.cyl = cyl 347 | if head is not None: 348 | self.head = head 349 | if record is not None: 350 | self.record = record 351 | 352 | while (retries >= 0): 353 | retries -= 1 354 | self._start() 355 | if (self.fstRC != FRC_OK): 356 | print("*** write _start retry %02X" % self.fstRC, file=sys.stderr) 357 | continue 358 | 359 | self._setupIO(CFD_WRITE | 0B11000000) 360 | self._fop() 361 | 362 | if self.fstRC == FRC_OK: 363 | return self.fstRC 364 | 365 | print("*** write retry %02X" % self.fstRC, file=sys.stderr) 366 | 367 | # retries exhausted 368 | return self.fstRC 369 | 370 | def format(self, cyl=None, head=None, retries=0): 371 | if cyl is not None: 372 | self.cyl = cyl 373 | if head is not None: 374 | self.head = head 375 | 376 | self.dskBuf = "" 377 | for i in range(0, self.secCount): 378 | self.dskBuf += chr(self.cyl) 379 | self.dskBuf += chr(self.head) 380 | self.dskBuf += chr(i+1) # secNum 381 | self.dskBuf += chr(self.N) # 2 = 512 bytes per sector 382 | 383 | while (retries >= 0): 384 | retries -= 1 385 | self._start() 386 | if (self.fstRC != FRC_OK): 387 | print("*** format _start retry %02X" % self.fstRC, file=sys.stderr) 388 | continue 389 | 390 | self._setupFormat(CFD_FMTTRK | 0B01000000) 391 | self._fop() 392 | 393 | if self.fstRC == FRC_OK: 394 | return self.fstRC 395 | 396 | print("*** format retry %02X" % self.fstRC, file=sys.stderr) 397 | 398 | # retries exhausted 399 | return self.fstRC 400 | 401 | def readID(self): 402 | self._setupCommand(CFD_READID | 0B01000000) 403 | return self._fop() 404 | 405 | def _recal(self): 406 | self._setupCommand(CFD_RECAL) 407 | return self._fop() 408 | 409 | def _senseInt(self): 410 | self._setupCommand(CFD_SENSEINT) 411 | self.fcpLen = 1 412 | return self._fop() 413 | 414 | def _specify(self, stepRate=None, headLoadTimeNonDma=None): 415 | if stepRate: 416 | self.stepRate = stepRate 417 | if headLoadTimeNonDma: 418 | self.headLoadTimeNonDma = headLoadTimeNonDma 419 | self._setupSpecify() 420 | return self._fop() 421 | 422 | def _seek(self): 423 | self._setupSeek() 424 | return self._fop() 425 | 426 | def _start(self): 427 | if not self.fdcReady: 428 | self.log(">>> start:reset") 429 | self._reset() 430 | if self.fstRC != FRC_OK: 431 | return self.fstRC 432 | 433 | self._motorOn() 434 | 435 | if self.track == 0xFF: 436 | self.log(">>> start:driveReset") 437 | self._driveReset() 438 | if self.fstRC != FRC_OK: 439 | return self.fstRC 440 | 441 | if self.track != self.cyl: 442 | self.log(">>> start:seek (%d,%d)" % (self.track, self.cyl)) 443 | self._seek() 444 | if self.fstRC != FRC_OK: 445 | return self.fstRC 446 | self._waitSeek() 447 | if self.fstRC != FRC_OK: 448 | return self.fstRC 449 | self.track = self.cyl 450 | 451 | self.fstRC = FRC_OK 452 | return self.fstRC 453 | 454 | def _driveReset(self): 455 | self.log(">>> driveReset:specify") 456 | self._specify() 457 | if self.fstRC != FRC_OK: 458 | return self.fstRC 459 | 460 | self.log(">>> driveReset:recal") 461 | self._recal() 462 | if self.fstRC != FRC_OK: 463 | return self.fstRC 464 | 465 | self.log(">>> driveReset:waitseek1") 466 | self._waitSeek() 467 | if (self.fstRC == FRC_OK): 468 | # succeeded! 469 | return self.fstRC 470 | 471 | # try once more 472 | self.log(">>> driveReset:waitseek2") 473 | self._waitSeek() 474 | return self.fstRC 475 | 476 | def _motorOn(self): 477 | # DOR bit 0 is DS, either 0 or 1 478 | # DOR bit 4 is motor enable for ds0 479 | # DOR bit 5 is motor enable for ds1 480 | if self.motorBoth: 481 | motorMask = (3 << 4) 482 | else: 483 | motorMask = (1 << (self.ds+4)) 484 | self.writeDOR(self.dor & 0B11111100 | self.ds | motorMask) 485 | self.writeDCR(self.DCR) 486 | 487 | if (self.dor & motorMask)==0: 488 | self.log(">>> motor delay") 489 | # motor delay 490 | time.sleep(1) 491 | 492 | def _motorOff(self): 493 | self.dor = self.DOR_INIT 494 | self.writeDOR(self.dor) 495 | 496 | def _clearDiskChange(self): 497 | for i in range(0, 5): 498 | self._senseInt() 499 | if (self.fstRC & FRC_DSKCHG) == 0: 500 | return 501 | # I think we can just ignore the remaining ones 502 | 503 | def _setupCommand(self, cmd): 504 | self.fcpBuf[0] = cmd & 0x5F 505 | self.fcpCmd = (cmd & 0x5F) & 0B00011111 506 | self.fcpBuf[1] = ((self.head & 0x1)<<2) | (self.ds & 0x3) 507 | self.fcpLen = 2 508 | 509 | def _setupSeek(self): 510 | self._setupCommand(CFD_SEEK) 511 | self.fcpBuf[2] = self.cyl 512 | self.fcpLen = 3 513 | 514 | def _setupSpecify(self): 515 | self._setupCommand(CFD_SPECIFY) 516 | self.fcpBuf[1] = self.stepRate 517 | self.fcpBuf[2] = self.headLoadTimeNonDma 518 | self.fcpLen = 3 519 | 520 | def _setupIO(self, cmd): 521 | self._setupCommand(cmd) 522 | self.fcpBuf[2] = self.cyl 523 | self.fcpBuf[3] = self.head 524 | self.fcpBuf[4] = self.record 525 | self.fcpBuf[5] = self.N # sector size, 2 = 512 bytes 526 | self.fcpBuf[6] = self.eot 527 | self.fcpBuf[7] = self.gapLengthRW 528 | self.fcpBuf[8] = self.gapLengthFormat 529 | self.fcpLen = 9 530 | 531 | def _setupFormat(self, cmd): 532 | self._setupCommand(cmd) 533 | self.fcpBuf[2] = self.N # sector size, 2 = 512 bytes 534 | self.fcpBuf[3] = self.secCount 535 | self.fcpBuf[4] = self.gapLengthFormat 536 | self.fcpBuf[5] = self.fillByte 537 | self.fcpLen = 6 538 | 539 | def _cfdName(self, x): 540 | x = x & 0B1111 541 | return CFD_NAME.get(x, "UNKNOWN") 542 | 543 | def _fop(self): 544 | try: 545 | fcpBytes = [] 546 | for i in range(0, self.fcpLen): 547 | fcpBytes.append("%02X" % self.fcpBuf[i]) 548 | 549 | self.log("FOP <%s> %s ->" % (self._cfdName(self.fcpBuf[0]), (" ".join(fcpBytes))), newline=False) 550 | 551 | result = self._fop_internal() 552 | 553 | frbBytes = [] 554 | for i in range(0, self.frbLen): 555 | frbBytes.append("%02X" % ord(self.frb[i])) 556 | self.log("-> [result %02X] %s" % (result, (" ".join(frbBytes)))) 557 | return result 558 | except FDCException as e: 559 | print("Exception in _Fop %s, code %2X" % (e, e.fstRC), file=sys.stderr) 560 | self.fstRC = e.fstRC 561 | return self.fstRC 562 | 563 | def _fop_internal(self): 564 | self.frbLen = 0 565 | self.fstRC = FRC_OK 566 | 567 | self.drain() 568 | 569 | time.sleep(0.001) 570 | 571 | if (self.get_msr() & 0x90) == 0x90: 572 | # Idiot-Check, this should never happen. 573 | print("Holy Corrupted Floppy Drivers, Batman! We're in the middle of a read or write already!\n", file=sys.stderr) 574 | self.fdcReady = False; 575 | self.fstRC = FRC_INPROGRESS 576 | return self.fstRC 577 | 578 | for i in range(0, self.fcpLen): 579 | # DIO=0 and RQM=1 indicate byte is ready to write 580 | self.fstRC = self.wait_msr(0xC0, 0x80) 581 | if (self.fstRC!=0): 582 | self.log("Sendcommand error in wait_msr %02X" % self.fstRC) 583 | return self.fstRC 584 | self.writeData(self.fcpBuf[i]) 585 | 586 | #fcpStr = "" 587 | #for i in range(0, self.fcpLen): 588 | # fcpStr = fcpStr + chr(self.fcpBuf[i]) 589 | #wd37c65_direct_ext.write_command(fcpStr, self.fcpLen) 590 | 591 | # execution phase 592 | 593 | if (self.fcpCmd == CFD_READ): 594 | self.readDataBlock(self.secSize) 595 | elif (self.fcpCmd == CFD_WRITE): 596 | self.writeDataBlock(self.secSize) 597 | elif (self.fcpCmd == CFD_FMTTRK): 598 | self.writeDataBlock(4 * self.secCount) 599 | elif (self.fcpCmd == CFD_READID): 600 | self.waitMSR(0xE0, 0xC0) 601 | else: 602 | pass # null 603 | 604 | self.frb = "" 605 | self.readResult() 606 | 607 | if (self.fcpCmd == CFD_DRVSTAT): 608 | # driveState has nothing to evaluate 609 | return self.fstRC 610 | elif (self.frbLen == 0): 611 | # if there's no st0, then nothing to evaluate 612 | return self.fstRC 613 | 614 | st0 = ord(self.frb[0]) 615 | if (st0 & 0B11000000) == 0B01000000: 616 | # ABTERM 617 | if (self.fcpCmd == CFD_SENSEINT) or (self.frbLen == 1): 618 | # Senseint doesn't use ST1 619 | self.fstRC = FRC_ABTERM 620 | return self.fstRC 621 | 622 | # evalst1 623 | st1 = ord(self.frb[1]) 624 | if (st1 & 80) == 0x80: 625 | self.fstRC = FRC_ENDCYL 626 | elif (st1 & 0x20) == 0x20: 627 | self.fstRC = FRC_DATAERR 628 | elif (st1 & 0x10) == 0x10: 629 | self.fstRC = FRC_OVERRUN 630 | elif (st1 & 0x04) == 0x08: 631 | self.fstRC = FRC_NODATA 632 | elif (st1 & 0x02) == 0x04: 633 | self.fstRC = FRC_NOWRIT 634 | elif (st1 & 0x01) == 0x01: 635 | self.fstRC = FRC_MISADR 636 | 637 | return self.fstRC 638 | elif (st0 & 0B11000000) == 0B10000000: 639 | # INVCMD 640 | self.fstRC = FRC_INVCMD 641 | return self.fstRC 642 | elif (st0 & 0B11000000) == 0B11000000: 643 | # DSKCHG 644 | self.fstRC = FRC_DSKCHG 645 | return self.fstRC 646 | 647 | # no error bits are set 648 | 649 | return self.fstRC 650 | 651 | def _waitSeek(self): 652 | loopCount = 0x1000 653 | while (loopCount>0): 654 | self._senseInt() 655 | if self.fstRC == FRC_ABTERM: 656 | # seek error 657 | return self.fstRC 658 | elif self.fstRC == FRC_OK: 659 | return self.fstRC 660 | loopCount -= 1 661 | 662 | self.fstRC = FRC_TOSEEKWT 663 | return self.fstRC 664 | 665 | -------------------------------------------------------------------------------- /smbpi/gps.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | import datetime 3 | import pigpio 4 | import sys 5 | import threading 6 | import time 7 | import traceback 8 | 9 | DEFAULT_PIN_TX = 23 10 | DEFAULT_PIN_RX = 24 11 | 12 | class GPS(threading.Thread): 13 | def __init__(self, pi, tx=DEFAULT_PIN_TX, rx=DEFAULT_PIN_RX): 14 | threading.Thread.__init__(self) 15 | 16 | self.pi = pi 17 | self.tx = tx 18 | self.rx = rx 19 | 20 | self.hour = 0 21 | self.minute = 0 22 | self.second = 0 23 | self.hundredths = 0 24 | self.day = 0 25 | self.year = 0 26 | self.month = 0 27 | 28 | self.daemon = True 29 | 30 | self.pi.set_mode(self.tx, pigpio.INPUT) 31 | 32 | try: 33 | self.pi.bb_serial_read_close(self.tx) 34 | except: 35 | pass 36 | 37 | self.pi.bb_serial_read_open(self.tx, 9600, 8) 38 | 39 | # $GPRMC,041841.00,A,4401.29549,N,12308.03025,W,0.415,,280620,,,A*6A 40 | 41 | def getDateTime(self): 42 | return datetime.datetime(self.year, self.month, self.day, self.hour, self.minute, self.second, self.hundredths*10000) 43 | 44 | def eventGPRMC(self): 45 | print("%02d:%02d:%02d.%02d %02d-%02d-%02d" % \ 46 | (self.hour, self.minute, self.second, self.hundredths, 47 | self.month, self.day, self.year)) 48 | 49 | def parseGPRMC(self, line): 50 | line = line[7:] 51 | if len(line)<9: 52 | return 53 | 54 | parts = line.split(",") 55 | 56 | timeStr = parts[0] 57 | if len(timeStr) < 6: 58 | return 59 | 60 | self.hour = int(timeStr[0:2]) 61 | self.minute = int(timeStr[2:4]) 62 | self.second = int(timeStr[4:6]) 63 | 64 | if timeStr[6] == ".": 65 | self.hundredths = int(timeStr[7:9]) 66 | else: 67 | self.hundredths = 0 68 | 69 | # parts[8] is the date 70 | if len(parts) >= 9: 71 | dateStr = parts[8] 72 | self.day = int(dateStr[0:2]) 73 | self.month = int(dateStr[2:4]) 74 | self.year = 2000 + int(dateStr[4:6]) 75 | 76 | self.eventGPRMC() 77 | 78 | def parseLine(self, line): 79 | if line.startswith("$GPRMC"): 80 | self.parseGPRMC(line) 81 | 82 | def readInput(self): 83 | buf="" 84 | while True: 85 | (count, data) = self.pi.bb_serial_read(self.tx) 86 | if count>0: 87 | for d in data: 88 | d = chr(d) 89 | if d=="\r": 90 | pass 91 | elif d=="\n": 92 | self.parseLine(buf) 93 | buf="" 94 | else: 95 | buf = buf + d 96 | 97 | def streamToConsole(self): 98 | while True: 99 | (count, data) = self.pi.bb_serial_read(self.tx) 100 | if count>0: 101 | for d in data: 102 | sys.stdout.write(chr(d)) 103 | 104 | def run(self): 105 | while True: 106 | try: 107 | self.readInput() 108 | except: 109 | traceback.print_exc() 110 | time.sleep(10) 111 | 112 | 113 | def main(): 114 | pi = pigpio.pi() 115 | g = GPS(pi) 116 | g.start() 117 | 118 | while True: 119 | time.sleep(1) 120 | 121 | 122 | if __name__ == "__main__": 123 | main() 124 | 125 | 126 | -------------------------------------------------------------------------------- /smbpi/i2c-utils.h: -------------------------------------------------------------------------------- 1 | /* 2 | i2c-utils.h - I2C & SMBUS functions extracted from linux/i2c-dev-user.h now 3 | that i2c-utils isn't part of the yocto image built for the WP 4 | modules. 5 | 6 | Copyright (C) 1995-97 Simon G. Vogl 7 | Copyright (C) 1998-99 Frodo Looijaard 8 | 9 | This program is free software; you can redistribute it and/or modify 10 | it under the terms of the GNU General Public License as published by 11 | the Free Software Foundation; either version 2 of the License, or 12 | (at your option) any later version. 13 | 14 | This program is distributed in the hope that it will be useful, 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | GNU General Public License for more details. 18 | 19 | You should have received a copy of the GNU General Public License 20 | along with this program; if not, write to the Free Software 21 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 22 | MA 02110-1301 USA. 23 | */ 24 | 25 | #ifndef I2C_UTILS_H 26 | #define I2C_UTILS_H 27 | 28 | #include 29 | #include 30 | #include 31 | 32 | 33 | /* -- i2c.h -- */ 34 | 35 | 36 | /* 37 | * I2C Message - used for pure i2c transaction, also from /dev interface 38 | */ 39 | struct i2c_msg { 40 | __u16 addr; /* slave address */ 41 | unsigned short flags; 42 | #define I2C_M_TEN 0x10 /* we have a ten bit chip address */ 43 | #define I2C_M_RD 0x01 44 | #define I2C_M_NOSTART 0x4000 45 | #define I2C_M_REV_DIR_ADDR 0x2000 46 | #define I2C_M_IGNORE_NAK 0x1000 47 | #define I2C_M_NO_RD_ACK 0x0800 48 | short len; /* msg length */ 49 | char *buf; /* pointer to msg data */ 50 | }; 51 | 52 | /* To determine what functionality is present */ 53 | 54 | #define I2C_FUNC_I2C 0x00000001 55 | #define I2C_FUNC_10BIT_ADDR 0x00000002 56 | #define I2C_FUNC_PROTOCOL_MANGLING 0x00000004 /* I2C_M_{REV_DIR_ADDR,NOSTART,..} */ 57 | #define I2C_FUNC_SMBUS_PEC 0x00000008 58 | #define I2C_FUNC_SMBUS_BLOCK_PROC_CALL 0x00008000 /* SMBus 2.0 */ 59 | #define I2C_FUNC_SMBUS_QUICK 0x00010000 60 | #define I2C_FUNC_SMBUS_READ_BYTE 0x00020000 61 | #define I2C_FUNC_SMBUS_WRITE_BYTE 0x00040000 62 | #define I2C_FUNC_SMBUS_READ_BYTE_DATA 0x00080000 63 | #define I2C_FUNC_SMBUS_WRITE_BYTE_DATA 0x00100000 64 | #define I2C_FUNC_SMBUS_READ_WORD_DATA 0x00200000 65 | #define I2C_FUNC_SMBUS_WRITE_WORD_DATA 0x00400000 66 | #define I2C_FUNC_SMBUS_PROC_CALL 0x00800000 67 | #define I2C_FUNC_SMBUS_READ_BLOCK_DATA 0x01000000 68 | #define I2C_FUNC_SMBUS_WRITE_BLOCK_DATA 0x02000000 69 | #define I2C_FUNC_SMBUS_READ_I2C_BLOCK 0x04000000 /* I2C-like block xfer */ 70 | #define I2C_FUNC_SMBUS_WRITE_I2C_BLOCK 0x08000000 /* w/ 1-byte reg. addr. */ 71 | 72 | #define I2C_FUNC_SMBUS_BYTE (I2C_FUNC_SMBUS_READ_BYTE | \ 73 | I2C_FUNC_SMBUS_WRITE_BYTE) 74 | #define I2C_FUNC_SMBUS_BYTE_DATA (I2C_FUNC_SMBUS_READ_BYTE_DATA | \ 75 | I2C_FUNC_SMBUS_WRITE_BYTE_DATA) 76 | #define I2C_FUNC_SMBUS_WORD_DATA (I2C_FUNC_SMBUS_READ_WORD_DATA | \ 77 | I2C_FUNC_SMBUS_WRITE_WORD_DATA) 78 | #define I2C_FUNC_SMBUS_BLOCK_DATA (I2C_FUNC_SMBUS_READ_BLOCK_DATA | \ 79 | I2C_FUNC_SMBUS_WRITE_BLOCK_DATA) 80 | #define I2C_FUNC_SMBUS_I2C_BLOCK (I2C_FUNC_SMBUS_READ_I2C_BLOCK | \ 81 | I2C_FUNC_SMBUS_WRITE_I2C_BLOCK) 82 | 83 | /* Old name, for compatibility */ 84 | #define I2C_FUNC_SMBUS_HWPEC_CALC I2C_FUNC_SMBUS_PEC 85 | 86 | /* 87 | * Data for SMBus Messages 88 | */ 89 | #define I2C_SMBUS_BLOCK_MAX 32 /* As specified in SMBus standard */ 90 | #define I2C_SMBUS_I2C_BLOCK_MAX 32 /* Not specified but we use same structure */ 91 | union i2c_smbus_data { 92 | __u8 byte; 93 | __u16 word; 94 | __u8 block[I2C_SMBUS_BLOCK_MAX + 2]; /* block[0] is used for length */ 95 | /* and one more for PEC */ 96 | }; 97 | 98 | /* smbus_access read or write markers */ 99 | #define I2C_SMBUS_READ 1 100 | #define I2C_SMBUS_WRITE 0 101 | 102 | /* SMBus transaction types (size parameter in the above functions) 103 | Note: these no longer correspond to the (arbitrary) PIIX4 internal codes! */ 104 | #define I2C_SMBUS_QUICK 0 105 | #define I2C_SMBUS_BYTE 1 106 | #define I2C_SMBUS_BYTE_DATA 2 107 | #define I2C_SMBUS_WORD_DATA 3 108 | #define I2C_SMBUS_PROC_CALL 4 109 | #define I2C_SMBUS_BLOCK_DATA 5 110 | #define I2C_SMBUS_I2C_BLOCK_BROKEN 6 111 | #define I2C_SMBUS_BLOCK_PROC_CALL 7 /* SMBus 2.0 */ 112 | #define I2C_SMBUS_I2C_BLOCK_DATA 8 113 | 114 | static inline __s32 i2c_smbus_access(int file, char read_write, __u8 command, 115 | int size, union i2c_smbus_data *data) 116 | { 117 | struct i2c_smbus_ioctl_data args; 118 | 119 | args.read_write = read_write; 120 | args.command = command; 121 | args.size = size; 122 | args.data = data; 123 | return ioctl(file,I2C_SMBUS,&args); 124 | } 125 | 126 | 127 | static inline __s32 i2c_smbus_write_quick(int file, __u8 value) 128 | { 129 | return i2c_smbus_access(file,value,0,I2C_SMBUS_QUICK,NULL); 130 | } 131 | 132 | static inline __s32 i2c_smbus_read_byte(int file) 133 | { 134 | union i2c_smbus_data data; 135 | if (i2c_smbus_access(file,I2C_SMBUS_READ,0,I2C_SMBUS_BYTE,&data)) 136 | return -1; 137 | else 138 | return 0x0FF & data.byte; 139 | } 140 | 141 | static inline __s32 i2c_smbus_write_byte(int file, __u8 value) 142 | { 143 | return i2c_smbus_access(file,I2C_SMBUS_WRITE,value, 144 | I2C_SMBUS_BYTE,NULL); 145 | } 146 | 147 | static inline __s32 i2c_smbus_read_byte_data(int file, __u8 command) 148 | { 149 | union i2c_smbus_data data; 150 | if (i2c_smbus_access(file,I2C_SMBUS_READ,command, 151 | I2C_SMBUS_BYTE_DATA,&data)) 152 | return -1; 153 | else 154 | return 0x0FF & data.byte; 155 | } 156 | 157 | static inline __s32 i2c_smbus_write_byte_data(int file, __u8 command, 158 | __u8 value) 159 | { 160 | union i2c_smbus_data data; 161 | data.byte = value; 162 | return i2c_smbus_access(file,I2C_SMBUS_WRITE,command, 163 | I2C_SMBUS_BYTE_DATA, &data); 164 | } 165 | 166 | static inline __s32 i2c_smbus_read_word_data(int file, __u8 command) 167 | { 168 | union i2c_smbus_data data; 169 | if (i2c_smbus_access(file,I2C_SMBUS_READ,command, 170 | I2C_SMBUS_WORD_DATA,&data)) 171 | return -1; 172 | else 173 | return 0x0FFFF & data.word; 174 | } 175 | 176 | static inline __s32 i2c_smbus_write_word_data(int file, __u8 command, 177 | __u16 value) 178 | { 179 | union i2c_smbus_data data; 180 | data.word = value; 181 | return i2c_smbus_access(file,I2C_SMBUS_WRITE,command, 182 | I2C_SMBUS_WORD_DATA, &data); 183 | } 184 | 185 | static inline __s32 i2c_smbus_process_call(int file, __u8 command, __u16 value) 186 | { 187 | union i2c_smbus_data data; 188 | data.word = value; 189 | if (i2c_smbus_access(file,I2C_SMBUS_WRITE,command, 190 | I2C_SMBUS_PROC_CALL,&data)) 191 | return -1; 192 | else 193 | return 0x0FFFF & data.word; 194 | } 195 | 196 | 197 | /* Returns the number of read bytes */ 198 | static inline __s32 i2c_smbus_read_block_data(int file, __u8 command, 199 | __u8 *values) 200 | { 201 | union i2c_smbus_data data; 202 | int i; 203 | if (i2c_smbus_access(file,I2C_SMBUS_READ,command, 204 | I2C_SMBUS_BLOCK_DATA,&data)) 205 | return -1; 206 | else { 207 | for (i = 1; i <= data.block[0]; i++) 208 | values[i-1] = data.block[i]; 209 | return data.block[0]; 210 | } 211 | } 212 | 213 | static inline __s32 i2c_smbus_write_block_data(int file, __u8 command, 214 | __u8 length, const __u8 *values) 215 | { 216 | union i2c_smbus_data data; 217 | int i; 218 | if (length > 32) 219 | length = 32; 220 | for (i = 1; i <= length; i++) 221 | data.block[i] = values[i-1]; 222 | data.block[0] = length; 223 | return i2c_smbus_access(file,I2C_SMBUS_WRITE,command, 224 | I2C_SMBUS_BLOCK_DATA, &data); 225 | } 226 | 227 | /* Returns the number of read bytes */ 228 | /* Until kernel 2.6.22, the length is hardcoded to 32 bytes. If you 229 | ask for less than 32 bytes, your code will only work with kernels 230 | 2.6.23 and later. */ 231 | static inline __s32 i2c_smbus_read_i2c_block_data(int file, __u8 command, 232 | __u8 length, __u8 *values) 233 | { 234 | union i2c_smbus_data data; 235 | int i; 236 | 237 | if (length > 32) 238 | length = 32; 239 | data.block[0] = length; 240 | if (i2c_smbus_access(file,I2C_SMBUS_READ,command, 241 | length == 32 ? I2C_SMBUS_I2C_BLOCK_BROKEN : 242 | I2C_SMBUS_I2C_BLOCK_DATA,&data)) 243 | return -1; 244 | else { 245 | for (i = 1; i <= data.block[0]; i++) 246 | values[i-1] = data.block[i]; 247 | return data.block[0]; 248 | } 249 | } 250 | 251 | static inline __s32 i2c_smbus_write_i2c_block_data(int file, __u8 command, 252 | __u8 length, 253 | const __u8 *values) 254 | { 255 | union i2c_smbus_data data; 256 | int i; 257 | if (length > 32) 258 | length = 32; 259 | for (i = 1; i <= length; i++) 260 | data.block[i] = values[i-1]; 261 | data.block[0] = length; 262 | return i2c_smbus_access(file,I2C_SMBUS_WRITE,command, 263 | I2C_SMBUS_I2C_BLOCK_BROKEN, &data); 264 | } 265 | 266 | /* Returns the number of read bytes */ 267 | static inline __s32 i2c_smbus_block_process_call(int file, __u8 command, 268 | __u8 length, __u8 *values) 269 | { 270 | union i2c_smbus_data data; 271 | int i; 272 | if (length > 32) 273 | length = 32; 274 | for (i = 1; i <= length; i++) 275 | data.block[i] = values[i-1]; 276 | data.block[0] = length; 277 | if (i2c_smbus_access(file,I2C_SMBUS_WRITE,command, 278 | I2C_SMBUS_BLOCK_PROC_CALL,&data)) 279 | return -1; 280 | else { 281 | for (i = 1; i <= data.block[0]; i++) 282 | values[i-1] = data.block[i]; 283 | return data.block[0]; 284 | } 285 | } 286 | 287 | #endif // I2C_UTILS_H 288 | -------------------------------------------------------------------------------- /smbpi/i2c_with_crc.py: -------------------------------------------------------------------------------- 1 | import crc8 2 | import time 3 | import traceback 4 | 5 | class I2CError(Exception): 6 | pass 7 | 8 | class ReceiveCRCError(Exception): 9 | pass 10 | 11 | class ReceiveSizeError(Exception): 12 | pass 13 | 14 | class ReceiveUninitializedError(Exception): 15 | pass 16 | 17 | class CRCError(Exception): 18 | pass 19 | 20 | class NoMoreRetriesError(Exception): 21 | pass 22 | 23 | class I2CWithCrc: 24 | def __init__(self, bus=None, addr=0x4, pi=None, sdaPin=2, sclPin=3): 25 | self.bus = bus 26 | self.addr = addr 27 | self.pi = pi 28 | self.sdaPin = sdaPin 29 | self.sclPin = sclPin 30 | 31 | self.errorIO = 0 32 | self.errorCRC = 0 33 | self.errorReceiveCRC = 0 34 | self.errorReceiveUninitialized = 0 35 | self.errorReceiveSize = 0 36 | self.errorSuccess = 0 37 | 38 | if (self.pi): 39 | try: 40 | self.pi.bb_i2c_close(self.sdaPin) 41 | except: 42 | pass 43 | # pigpiod 44 | self.pi.bb_i2c_open(self.sdaPin, self.sclPin, 100000) 45 | 46 | @property 47 | def errorCount(self): 48 | return self.errorIO + self.errorReceiveCRC + self.errorReceiveUninitialized + self.errorReceiveSize + self.errorCRC 49 | 50 | def readreg_once(self, reg): 51 | if self.bus: 52 | hash = crc8.crc8() 53 | hash.update(chr(reg)) 54 | crc = hash.digest() 55 | self.bus.write_i2c_block_data(self.addr, reg, [ord(crc)]) 56 | 57 | data = self.bus.read_byte(self.addr) 58 | crc = self.bus.read_byte(self.addr) 59 | else: 60 | hash = crc8.crc8() 61 | hash.update(chr(reg)) 62 | crc = hash.digest() 63 | (count, i2cdata) = self.pi.bb_i2c_zip(self.sdaPin, 64 | (4, self.addr, # set addr to self.addr 65 | 2, 7, 2, reg, crc, # start, write two byte (reg, crc) 66 | 2, 6, 1, # (re)start, read one byte 67 | 2, 6, 1, # (re)start, read one byte 68 | 3, # stop 69 | 0)) # end 70 | if count<0: 71 | raise I2CError("i2c error") 72 | if count!=2: 73 | raise I2CError("i2c wrong byte count") 74 | 75 | data = i2cdata[0] 76 | crc = i2cdata[1] 77 | 78 | if (data == 0xFF) and (crc == 0xFF): 79 | raise ReceiveCRCError("receive crc error") 80 | if (data == 0xFF) and (crc == 0xFE): 81 | raise ReceiveSizeError("receive size error") 82 | if (data == 0xFF) and (crc == 0xFD): 83 | raise ReceiveUninitializedError("receive uninitialized error") 84 | 85 | hash = crc8.crc8() 86 | hash.update(chr(data)) 87 | if crc != ord(hash.digest()): 88 | raise CRCError("crc error, data=%2X, crc=%2X, localCrc=%X" % (data, crc, ord(hash.digest()))) 89 | 90 | return data 91 | 92 | def writereg_once(self, reg, v): 93 | if self.bus: 94 | hash = crc8.crc8() 95 | hash.update(chr(reg)) 96 | hash.update(chr(v)) 97 | crc = hash.digest() 98 | self.bus.write_i2c_block_data(self.addr, reg, [v, ord(crc)]) 99 | 100 | readBack = self.bus.read_byte(self.addr) 101 | crc = self.bus.read_byte(self.addr) 102 | else: 103 | hash = crc8.crc8() 104 | hash.update(chr(reg)) 105 | hash.update(chr(v)) 106 | crc = hash.digest() 107 | (count, i2cdata) = self.pi.bb_i2c_zip(self.sdaPin, 108 | (4, self.addr, # set addr to self.addr 109 | 2, 7, 3, reg, v, crc, # start, write three bytes (reg, v, crc) 110 | 2, 6, 1, # (re)start, read one byte 111 | 2, 6, 1, # (re)start, read one byte 112 | 3, # stop 113 | 0)) # end 114 | if count<0: 115 | raise I2CError("i2c error") 116 | if count!=2: 117 | raise I2CError("i2c wrong byte count") 118 | 119 | readBack = i2cdata[0] 120 | crc = i2cdata[1] 121 | 122 | # note that readBack will actually return the next register. But, 123 | # that's alright, we don't care -- we just want to check to make 124 | # sure we didn't get an error back. 125 | 126 | if (readBack == 0xFF) and (crc == 0xFF): 127 | raise ReceiveCRCError("receive crc error") 128 | if (readBack == 0xFF) and (crc == 0xFE): 129 | raise ReceiveSizeError("receive size error") 130 | if (readBack == 0xFF) and (crc == 0xFD): 131 | raise ReceiveUninitializedError("receive uninitialized error") 132 | 133 | hash = crc8.crc8() 134 | hash.update(chr(readBack)) 135 | if crc != ord(hash.digest()): 136 | raise CRCError("crc error, readBack=%2X, crc=%2X, localCrc=%X" % (readBack, crc, ord(hash.digest()))) 137 | 138 | def readreg(self, reg): 139 | for i in range(0, 10): 140 | try: 141 | v = self.readreg_once(reg) 142 | self.errorSuccess += 1 143 | return v 144 | except I2CError: 145 | self.errorIO += 1 146 | except IOError: 147 | self.errorIO += 1 148 | except ReceiveCRCError: 149 | self.errorReceiveCRC += 1 150 | except ReceiveSizeError: 151 | self.errorReceiveSize += 1 152 | except ReceiveUninitializedError: 153 | self.errorReceivedUninitialized += 1 154 | except CRCError: 155 | self.errorCRC += 1 156 | raise NoMoreRetriesError() 157 | 158 | def writereg(self, reg, v): 159 | for i in range(0, 10): 160 | try: 161 | self.writereg_once(reg, v) 162 | self.errorSuccess += 1 163 | return 164 | except I2CError: 165 | self.errorIO += 1 166 | except IOError: 167 | self.errorIO += 1 168 | except ReceiveCRCError: 169 | self.errorReceiveCRC += 1 170 | except ReceiveSizeError: 171 | self.errorReceiveSize += 1 172 | except ReceiveUninitializedError: 173 | self.errorReceivedUninitialized += 1 174 | except CRCError: 175 | self.errorCRC += 1 176 | except TypeError: 177 | pass 178 | raise NoMoreRetriesError() 179 | 180 | # different capitalization 181 | def readReg(self, reg): 182 | return self.readreg(reg) 183 | 184 | # different capitalization 185 | def writeReg(self, reg, v): 186 | self.writereg(reg, v) -------------------------------------------------------------------------------- /smbpi/ioexpand.py: -------------------------------------------------------------------------------- 1 | """ 2 | IO Expanders 3 | Scott Baker, http://www.smbaker.com/ 4 | 5 | Various I2C IO Expanders 6 | """ 7 | 8 | IODIR = 0x00 9 | IPOL = 0x02 10 | GPINTEN = 0x04 11 | DEFVAL = 0x06 12 | INTCON = 0x08 13 | IOCON = 0x0A 14 | GPPU = 0x0C 15 | INTF = 0x0E 16 | MCP23017_GPIO = 0x12 17 | OLAT = 0x14 18 | 19 | IOCON_BANK = 0x80 20 | IOCON_MIRROR = 0x40 21 | IOCON_SEQOP = 0x20 22 | IOCON_DISSLW = 0x10 23 | IOCON_HAEN = 0x80 24 | IOCON_ODR = 0x40 25 | IOCON_INTPOL = 0x20 26 | 27 | class MCP23017: 28 | def __init__(self, bus, addr): 29 | self.addr = addr 30 | self.bus = bus 31 | self.banks = 2 32 | self.gpio_bits = [0, 0] 33 | self.dir_bits = [0, 0] 34 | 35 | def writereg(self, reg, bits): 36 | self.bus.write_byte_data(self.addr, reg, bits) 37 | 38 | def readreg(self, reg): 39 | return self.bus.read_byte_data(self.addr, reg) 40 | 41 | def set_iodir(self, bank, bits): 42 | self.writereg(bank + IODIR, bits) 43 | self.dir_bits[bank] = bits 44 | 45 | def set_polarity(self, bank, bits): 46 | self.writereg(bank + IPOL, bits) 47 | 48 | def set_interrupt(self, bank, bits): 49 | self.writereg(bank + GPINTEN, bits) 50 | 51 | def set_intdef(self, bank, bits): 52 | self.writereg(bank + DEFVAL, bits) 53 | 54 | def set_intcon(self, bank, bits): 55 | self.writereg(bank + INTCON, bits) 56 | 57 | def set_config(self, bits): 58 | self.writereg(IOCON, bits) 59 | 60 | def set_pullup(self, bank, bits): 61 | self.writereg(bank + GPPU, bits) 62 | 63 | def set_gpio(self, bank, bits): 64 | self.writereg(bank + MCP23017_GPIO, bits) 65 | self.gpio_bits[bank] = bits 66 | 67 | def set_latch(self, bank, bits): 68 | self.writereg(bank + OLAT, bits) 69 | 70 | def get_gpio(self, bank): 71 | return self.readreg(bank + MCP23017_GPIO) 72 | 73 | def get_intf(self, bank): 74 | return self.readreg(bank + INTF) 75 | 76 | def configure_as_keypad(self): 77 | self.set_pullup(0, 0xFF) 78 | self.set_pullup(1, 0xFF) 79 | 80 | def configure_as_display(self): 81 | self.set_iodir(0, 0x00) 82 | self.set_iodir(1, 0x00) 83 | 84 | def configure_as_led_keypad(self): 85 | # bank 0 is inputs 86 | # bank 1 is outputs 87 | self.set_pullup(0, 0xFF) 88 | self.set_iodir(1, 0x00) 89 | 90 | def set_gpio_mask(self, bank, mask, bits): 91 | mask = ((~mask) & 0xFF) # invert the mask 92 | #print("XXX %02X %02X %02X" % (mask, bits, ((self.gpio_bits[bank] & mask) | bits))) 93 | self.set_gpio(bank, (self.gpio_bits[bank] & mask) | bits) 94 | 95 | 96 | def or_gpio(self, bank, bits): 97 | self.set_gpio(bank, self.gpio_bits[bank] | bits) 98 | 99 | def not_gpio(self, bank, bits): 100 | bits = ((~bits) & 0xFF) 101 | self.set_gpio(bank, self.gpio_bits[bank] & bits) 102 | 103 | def or_dir(self, bank, bits): 104 | self.set_iodir(bank, self.dir_bits[bank] | bits) 105 | 106 | def not_dir(self, bank, bits): 107 | bits = ((~bits) & 0xFF) 108 | self.set_iodir(bank, self.dir_bits[bank] & bits) 109 | 110 | 111 | class PCF8574: 112 | def __init__(self, bus, addr): 113 | self.addr = addr 114 | self.bus = bus 115 | self.banks = 1 116 | self.gpio_bits = 0xFF 117 | 118 | def get_gpio(self, bank): 119 | return self.bus.read_byte(self.addr) 120 | 121 | def set_gpio(self, bank, value): 122 | self.bus.write_byte(self.addr, value) 123 | self.gpio_bits = value 124 | 125 | def configure_as_keypad(self): 126 | self.bus.write_byte(self.addr,0xFF) 127 | 128 | def or_gpio(self, bank, bits): 129 | self.set_gpio(bank, self.gpio_bits | bits) 130 | 131 | def not_gpio(self, bank, bits): 132 | bits = ((~bits) & 0xFF) 133 | self.set_gpio(bank, self.gpio_bits & bits) 134 | 135 | -------------------------------------------------------------------------------- /smbpi/max6921.py: -------------------------------------------------------------------------------- 1 | """ 2 | MAX6921 Driver, for use with IV-28B Russian VFD Tube 3 | Scott M Baker, 2020 4 | http://www.smbaker.com/ 5 | 6 | Raspberry pi driver for MAX6921 VFD Controller. 7 | 8 | This implementation is specific to use with an IV-28 display. 9 | The BIT_ constants will explain which of the MAXX6921 outputs 10 | are connected to which anodes and grids on the IV-28. If you're 11 | using a different display or you've wired yours differently, 12 | then adjusting the BIT_ constants will be necessary. 13 | 14 | Requires pigpio in order to smoothly multiplex the display. 15 | 16 | Pigpio Installation notes: 17 | rm -f pigpio.zip 18 | sudo rm -rf pigpio-master 19 | wget https://github.com/joan2937/pigpio/archive/master.zip -O pigpio.zip 20 | unzip pigpio.zip 21 | cd pigpio-master 22 | make 23 | sudo make install 24 | 25 | Pigpio run notes: 26 | sudo pigpiod -s 2 27 | """ 28 | 29 | from __future__ import print_function 30 | import time 31 | import pigpio 32 | 33 | PIN_VFD_LOAD = 17 34 | PIN_VFD_CLK = 22 35 | PIN_VFD_DATA = 27 36 | PIN_VFD_BLANK = 16 37 | 38 | BIT_A = 1 39 | BIT_B = 2 40 | BIT_C = 4 41 | BIT_D = 8 42 | BIT_E = 0x10 43 | BIT_F = 0x20 44 | BIT_G = 0x40 45 | BIT_DP = 0x80 46 | BIT_G1 = 0x100 47 | BIT_G2 = 0x200 48 | BIT_G3 = 0x400 49 | BIT_G4 = 0x800 50 | BIT_G5 = 0x1000 51 | BIT_G6 = 0x2000 52 | BIT_G7 = 0x4000 53 | BIT_G8 = 0x8000 54 | BIT_G9 = 0x10000 55 | 56 | BITI_A = BIT_D 57 | BITI_B = BIT_C 58 | BITI_C = BIT_B 59 | BITI_D = BIT_A 60 | BITI_E = BIT_F 61 | BITI_F = BIT_E 62 | BITI_G = BIT_G 63 | BITI_DP = BIT_DP 64 | BITI_G1 = BIT_G1 65 | BITI_G2 = BIT_G9 66 | BITI_G3 = BIT_G8 67 | BITI_G4 = BIT_G7 68 | BITI_G5 = BIT_G6 69 | BITI_G6 = BIT_G5 70 | BITI_G7 = BIT_G4 71 | BITI_G8 = BIT_G3 72 | BITI_G9 = BIT_G2 73 | 74 | ZERO = BIT_A | BIT_B | BIT_C | BIT_D | BIT_E | BIT_F 75 | ONE = BIT_B | BIT_C 76 | TWO = BIT_A | BIT_B | BIT_D | BIT_E | BIT_G 77 | THREE = BIT_A | BIT_B | BIT_C | BIT_D | BIT_G 78 | FOUR = BIT_B | BIT_C | BIT_F | BIT_G 79 | FIVE = BIT_A | BIT_C | BIT_D | BIT_F | BIT_G 80 | SIX = BIT_A | BIT_C | BIT_D | BIT_E | BIT_F | BIT_G 81 | SEVEN = BIT_A | BIT_B | BIT_C 82 | EIGHT = BIT_A | BIT_B | BIT_C | BIT_D | BIT_E | BIT_F | BIT_G 83 | NINE = BIT_A | BIT_B | BIT_C | BIT_F | BIT_G 84 | 85 | ZEROI = BITI_A | BITI_B | BIT_C | BIT_D | BIT_E | BIT_F 86 | ONEI = BITI_B | BITI_C 87 | TWOI = BITI_A | BITI_B | BITI_D | BITI_E | BITI_G 88 | THREEI = BITI_A | BITI_B | BITI_C | BITI_D | BITI_G 89 | FOURI = BITI_B | BITI_C | BITI_F | BITI_G 90 | FIVEI = BITI_A | BITI_C | BITI_D | BITI_F | BITI_G 91 | SIXI = BITI_A | BITI_C | BITI_D | BITI_E | BITI_F | BITI_G 92 | SEVENI = BITI_A | BITI_B | BITI_C 93 | EIGHTI = BITI_A | BITI_B | BITI_C | BITI_D | BITI_E | BITI_F | BITI_G 94 | NINEI = BITI_A | BITI_B | BITI_C | BITI_F | BITI_G 95 | 96 | DIGITS = [ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE] 97 | GATES = [BIT_G1, BIT_G2, BIT_G3, BIT_G4, BIT_G5, BIT_G6, BIT_G7, BIT_G8, BIT_G9] 98 | 99 | DIGITSI = [ZEROI, ONEI, TWOI, THREEI, FOURI, FIVEI, SIXI, SEVENI, EIGHTI, NINEI] 100 | GATESI = [BITI_G1, BITI_G2, BITI_G3, BITI_G4, BITI_G5, BITI_G6, BITI_G7, BITI_G8, BITI_G9] 101 | 102 | def TO_BIT(x): 103 | return 1<low transitions 140 | self.pi.write(self.data, 0) # might as well default it to something 141 | 142 | def shiftIn(self, data, postDelay=10): 143 | pulses = [] 144 | for i in range(0, 20): 145 | if (data & 0x80000) != 0: 146 | pulses.append(pigpio.pulse(TO_BIT(self.data), 0, 1)) 147 | else: 148 | pulses.append(pigpio.pulse(0, TO_BIT(self.data), 1)) 149 | pulses.append(pigpio.pulse(0, TO_BIT(self.clk), 1)) 150 | pulses.append(pigpio.pulse(TO_BIT(self.clk), 0, 1)) 151 | data = data << 1 152 | 153 | pulses.append(pigpio.pulse(TO_BIT(self.load), 0, 1)) 154 | pulses.append(pigpio.pulse(0, TO_BIT(self.load), postDelay)) 155 | 156 | return pulses 157 | 158 | def setDP(self, number, value, exclusive = False): 159 | if exclusive: 160 | self.dps = [False, False, False, False, False, False, False, False, False] 161 | self.dps[number] = value 162 | 163 | def setDPList(self, l): 164 | self.setDP(0, False, True) 165 | for item in l: 166 | self.setDP(item, True, False) 167 | 168 | def setLeader(self, top=False, mid=False, bot=False): 169 | # The "leader" is the special symbols before the leftmost digit of the 170 | # display. "top" and "bot" are both dots. "mid" is a dash and might be 171 | # useful as a minus sign 172 | self.leaderTop = top 173 | self.leaderMid = mid 174 | self.leaderBot = bot 175 | 176 | def generateDigit(self, digit, value, dp=False): 177 | if self.flipped_ud: 178 | segs = DIGITSI[value] 179 | else: 180 | segs = DIGITS[value] 181 | 182 | if self.flipped_lr: 183 | gate = GATESI[digit+1] 184 | else: 185 | gate = GATES[digit+1] 186 | 187 | if dp: 188 | segs = segs | BIT_DP 189 | 190 | return self.shiftIn(segs | gate) 191 | 192 | def generateLeader(self): 193 | segs = 0 194 | if self.leaderTop: 195 | segs = segs + BIT_F 196 | if self.leaderMid: 197 | segs = segs + BIT_G 198 | if self.leaderBot: 199 | segs = segs + BIT_DP 200 | 201 | if segs != 0: 202 | return self.shiftIn(segs | GATES[0]) + self.shiftIn(0, postDelay=0) 203 | else: 204 | return [] 205 | 206 | def generateNumber(self, value, leadingZero=False): 207 | pulses = [] 208 | for i in range(0, 8): 209 | pulses = pulses + self.generateDigit(i, value % 10, self.dps[i]) 210 | value = value/10 211 | # blank the outputs to prevent ghosting between digits 212 | pulses = pulses + self.shiftIn(0, postDelay=0) 213 | 214 | if (not leadingZero) and (value == 0): 215 | return pulses 216 | return pulses 217 | 218 | def generateString(self, s): 219 | # gnerateString supports using spaces to leave digits blank 220 | pulses = [] 221 | i = 7 222 | for c in s: 223 | if c.isdigit(): 224 | pulses = pulses + self.generateDigit(i, ord(c)-ord("1")+1, self.dps[i]) 225 | # blank the outputs to prevent ghosting between digits 226 | pulses = pulses + self.shiftIn(0, postDelay=0) 227 | i = i - 1 228 | pulses = pulses + self.generateLeader() 229 | self.shiftIn(DIGITS[8] + GATES[0] + BIT_DP) # XXX is this leftover, can be deleted? 230 | return pulses 231 | 232 | def displayWaveOrig(self, pulses): 233 | # This was my original wave display function that stopped all waves 234 | # and lceared all resources, then created and displayed the new 235 | # wave. It worked, but was a little flickery. 236 | if self.pilock: 237 | self.pilock.acquire() 238 | try: 239 | self.pi.wave_clear() 240 | 241 | self.pi.wave_add_generic(pulses) 242 | self.waveDisplay = self.pi.wave_create() 243 | self.pi.wave_send_repeat(self.waveDisplay) 244 | finally: 245 | if self.pilock: 246 | self.pilock.release() 247 | 248 | def displayWaveNewThenDeleteOld(self, pulses): 249 | # This was my first attempt at reducing the flicker. Here I start 250 | # displaying the new wave before I delete the old. Flicker is 251 | # reduced but there are occasional inconsistencies/pauses. 252 | if self.pilock: 253 | self.pilock.acquire() 254 | try: 255 | self.pi.wave_add_generic(pulses) 256 | newWaveDisplay = self.pi.wave_create() 257 | 258 | # display the new one 259 | self.pi.wave_send_repeat(newWaveDisplay) 260 | 261 | # stop the old one 262 | # it might fail if someone else called clear_wave 263 | if (self.waveDisplay is not None) and \ 264 | (self.waveDisplay != newWaveDisplay): 265 | try: 266 | self.pi.wave_delete(self.waveDisplay) 267 | except: 268 | # it might fail if someone else called clear_wave 269 | pass 270 | 271 | self.waveDisplay = newWaveDisplay 272 | finally: 273 | if self.pilock: 274 | self.pilock.release() 275 | 276 | def displayWave(self, pulses): 277 | # This is my current attempt at reducing the flicker. I create the 278 | # new wave, then stop and delete the old one, then start the new 279 | # one. 280 | if self.pilock: 281 | self.pilock.acquire() 282 | try: 283 | if not pulses: 284 | # no pulses, so generate a blank display 285 | pulses = self.shiftIn(0) 286 | 287 | self.pi.wave_add_generic(pulses) 288 | newWaveDisplay = self.pi.wave_create() 289 | 290 | # stop the old one 291 | # it might fail if someone else called clear_wave 292 | if (self.waveDisplay is not None) and \ 293 | (self.waveDisplay != newWaveDisplay): 294 | try: 295 | self.pi.wave_delete(self.waveDisplay) 296 | except: 297 | pass 298 | 299 | # display the new one 300 | self.pi.wave_send_repeat(newWaveDisplay) 301 | 302 | self.waveDisplay = newWaveDisplay 303 | finally: 304 | if self.pilock: 305 | self.pilock.release() 306 | 307 | def displayNumber(self, n, leadingZero=False): 308 | self.displayWave(self.generateNumber(n, leadingZero=leadingZero)) 309 | 310 | def displayString(self, s): 311 | self.displayWave(self.generateString(s)) 312 | 313 | 314 | 315 | def main(): 316 | # Self-test demo, starts by displaying 12345678 317 | # then increments ten times per second. 318 | 319 | pi = pigpio.pi() 320 | 321 | pi.wave_clear() 322 | pi.wave_tx_stop() 323 | 324 | vfd = Max6921(pi = pi) 325 | 326 | n = 12345678 327 | 328 | while True: 329 | #vfd.displayNumber(n) 330 | vfd.setLeader(mid=((n%10)<=5)) 331 | vfd.setDP(n % 9, 1, True) 332 | vfd.displayString("%d" % n) 333 | 334 | time.sleep(0.1) 335 | 336 | n = n + 1 337 | 338 | 339 | if __name__ == "__main__": 340 | main() -------------------------------------------------------------------------------- /smbpi/mcp42100.py: -------------------------------------------------------------------------------- 1 | """ 2 | MCP42100 driver 3 | Scott Baker, http://www.smbaker.com/ 4 | 5 | Interface with MCP42100 chip. 6 | """ 7 | 8 | 9 | DEFAULT_CS = 0 10 | 11 | 12 | class MCP42100: 13 | def __init__(self, spi, bus=0, cs=DEFAULT_CS): 14 | self.spi = spi 15 | self.bus = bus 16 | self.cs = cs 17 | self.spi.open(self.bus, self.cs) 18 | 19 | def SetValue(self, potNum, val): 20 | self.spi.xfer2([0x11+potNum, val]) 21 | 22 | 23 | def main(): 24 | import spidev 25 | import sys 26 | if len(sys.argv)<=2: 27 | print >> sys.stderr, "Please specify pot and value as command-line arg" 28 | sys.exit(-1) 29 | 30 | p = int(sys.argv[1]) 31 | v = int(sys.argv[2]) 32 | 33 | spi = spidev.SpiDev() 34 | try: 35 | pot = MCP42100(spi) 36 | 37 | pot.SetValue(p, v) 38 | finally: 39 | spi.close() 40 | 41 | 42 | if __name__ == "__main__": 43 | main() 44 | -------------------------------------------------------------------------------- /smbpi/mcp4821.py: -------------------------------------------------------------------------------- 1 | """ 2 | MCP4821 Driver 3 | Scott M Baker, 2020 4 | http://www.smbaker.com/ 5 | 6 | Raspberry pi driver for MCP4821 DAC. Also ought to work 7 | for MCP4921. Written and tested on 12-bit DAC. I put code 8 | in to support the 8-bit and 10-bit variants, but it's 9 | untested. Just spend a few cents and buy the 12-bit one. 10 | """ 11 | 12 | from __future__ import print_function 13 | import spidev 14 | import RPi.GPIO as GPIO 15 | import sys 16 | 17 | DEFAULT_CE=0 18 | DEFAULT_GAIN=1 19 | DEFAULT_LDAQ=22 20 | 21 | class MCP4821: 22 | def __init__(self, spi, ce=DEFAULT_CE, ldaq=DEFAULT_LDAQ, gain=DEFAULT_GAIN): 23 | self.spi = spi 24 | self.ce = ce 25 | self.ldaq = ldaq 26 | self.vRef = 2048 27 | self.bits = 12 28 | self.resolution = 2**self.bits 29 | self.gain = gain 30 | 31 | spi.open(0, self.ce) 32 | spi.max_speed_hz = 4 * 1000000 33 | 34 | if self.ldaq is not None: 35 | GPIO.setmode(GPIO.BOARD) 36 | GPIO.setup(self.ldaq, GPIO.OUT) 37 | GPIO.output(self.ldaq, GPIO.LOW) 38 | 39 | def SetValue(self, val): 40 | if self.gain == 2: 41 | gainBit = 0 42 | else: 43 | gainBit = 1 44 | 45 | if self.bits == 12: 46 | # lower 8 bits of data 47 | lowByte = val & 0xFF 48 | 49 | # highbyte has 0, 0, Gain, Shdn, D11, D10, D9, D8 50 | highByte = 0b0 << 7 | 0b0 << 6 | gainBit << 5 | 0b1 << 4 | ((val >> 8) & 0xff) 51 | elif self.bits == 10: 52 | # lower 8 bits of data 53 | lowByte = (val << 2) & 0xFF 54 | 55 | # highbyte has 0, 0, Gain, Shdn, D11, D10, D9, D8 56 | highByte = 0b0 << 7 | 0b0 << 6 | gainBit << 5 | 0b1 << 4 | ((val >> 6) & 0xff) 57 | elif self.bits == 8: 58 | # lower 8 bits of data 59 | lowByte = (val << 4) & 0xFF 60 | 61 | # highbyte has 0, 0, Gain, Shdn, D11, D10, D9, D8 62 | highByte = 0b0 << 7 | 0b0 << 6 | gainBit << 5 | 0b1 << 4 | ((val >> 4) & 0xff) 63 | 64 | self.last_highByte = highByte 65 | self.last_lowByte = lowByte 66 | self.last_value = val 67 | 68 | self.spi.xfer2([highByte, lowByte]) 69 | 70 | def SetVoltage(self, volts): 71 | self.SetValue(self.voltageToValue(volts)) 72 | 73 | def valueToVoltage(self, val): 74 | return float(val) * self.gain * self.vRef / self.resolution / 1000.0 75 | 76 | def voltageToValue(self, volts): 77 | return int(float(volts)*1000.0*float(self.resolution)/float(self.vRef)/float(self.gain)) 78 | 79 | 80 | def main(): 81 | if len(sys.argv)<=1: 82 | print("Please specify value as command-line arg", file=sys.stderr) 83 | sys.exit(-1) 84 | 85 | v = int(sys.argv[1]) 86 | 87 | spi = spidev.SpiDev() 88 | 89 | try: 90 | dac = MCP4821(spi) 91 | 92 | dac.SetValue(v) 93 | 94 | print("Voltage: %0.4f" % dac.valueToVoltage(v)) 95 | print("Binary value : {0:12b} (12 bit)".format(v)) 96 | print("Highbyte = {0:8b}".format(dac.last_highByte)) 97 | print("Lowbyte = {0:8b}".format(dac.last_lowByte)) 98 | finally: 99 | spi.close() 100 | 101 | 102 | if __name__ == '__main__': 103 | main() 104 | -------------------------------------------------------------------------------- /smbpi/micros.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | /* Borrowed from https://www.raspberrypi.org/forums/viewtopic.php?t=228727 */ 8 | 9 | #include 10 | #include 11 | #ifndef _GNU_SOURCE 12 | #define _GNU_SOURCE 13 | #endif 14 | #include 15 | 16 | #include "micros.h" 17 | 18 | static volatile uint32_t *systReg; 19 | static int fdMem; 20 | static uint32_t phys; 21 | 22 | void initMicros(void) 23 | { 24 | // based on pigpio source; simplified and re-arranged 25 | fdMem = open("/dev/mem",O_RDWR|O_SYNC); 26 | if(fdMem < 0) { 27 | fprintf(stderr,"Cannot map memory (need sudo?)\n"); 28 | exit(1); 29 | } 30 | // figure out the address 31 | FILE *f = fopen("/proc/cpuinfo","r"); 32 | char buf[1024]; 33 | if (fgets(buf,sizeof(buf),f)!=0) { // skip first line 34 | fprintf(stderr,"failed to skip line1\n"); 35 | exit(1); 36 | } 37 | if (fgets(buf,sizeof(buf),f) !=0) { // model name 38 | fprintf(stderr,"failed to skip line1\n"); 39 | exit(1); 40 | } 41 | // would be a better way to check: 42 | // https://www.raspberrypi-spy.co.uk/2012/09/checking-your-raspberry-pi-board-version/ 43 | if(strstr(buf,"ARMv6")) { 44 | phys = 0x20000000; 45 | } else if(strstr(buf,"ARMv7")) { 46 | //phys = 0x3F000000; 47 | phys = 0xFE000000; // address for pi4 48 | } else if(strstr(buf,"ARMv8")) { 49 | phys = 0x3F000000; 50 | } else { 51 | fprintf(stderr,"Unknown CPU type\n"); 52 | exit(1); 53 | } 54 | fclose(f); 55 | systReg = (uint32_t *)mmap(0,0x1000,PROT_READ|PROT_WRITE, 56 | MAP_SHARED|MAP_LOCKED,fdMem,phys+0x3000); 57 | 58 | } 59 | 60 | void delayMicros(int us) 61 | { 62 | // The final microsecond can be short; don't let the delay be short. 63 | ++us; 64 | 65 | // usleep() on its own gives latencies 20-40 us; this combination 66 | // gives < 25 us. 67 | uint32_t start = micros(); 68 | if(us >= 100) 69 | usleep(us - 50); 70 | while(micros()-start < us) 71 | ; 72 | } 73 | 74 | uint32_t micros(void) { 75 | return systReg[1]; 76 | } 77 | -------------------------------------------------------------------------------- /smbpi/micros.h: -------------------------------------------------------------------------------- 1 | #ifndef __MICROS__ 2 | #define __MICROS__ 3 | 4 | void initMicros(void); 5 | uint32_t micros(void); 6 | void delayMicros(int us); 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /smbpi/motor.py: -------------------------------------------------------------------------------- 1 | """ 2 | L293 Motor Driver 3 | Scott Baker, http://www.smbaker.com/ 4 | 5 | Use L293 chip to control a motor. 6 | """ 7 | 8 | from __future__ import print_function 9 | import RPi.GPIO as IO 10 | 11 | L293_1 = 24 # 22 for the original prototype 12 | L293_2 = 23 # 23 for the original prototype 13 | L293_ENABLE = 18 14 | 15 | # current prototype using these 16 | L293_3 = 27 17 | L293_4 = 22 18 | L293_ENABLE2 = 17 19 | 20 | class Motor: 21 | def __init__(self, pin1=L293_1, pin2=L293_2, enable=L293_ENABLE): 22 | self.pin1 = pin1 23 | self.pin2 = pin2 24 | self.enable = enable 25 | 26 | print("pin1=",pin1, "pin2=",pin2, "enable=", enable) 27 | 28 | IO.setmode(IO.BCM) 29 | IO.setup(self.enable, IO.OUT) 30 | IO.setup(self.pin1, IO.OUT) 31 | IO.setup(self.pin2, IO.OUT) 32 | 33 | IO.output(self.pin1, True) 34 | IO.output(self.pin2, False) 35 | IO.output(self.enable, False) 36 | 37 | self.pwm = IO.PWM(self.enable,100) 38 | self.pwm.start(0) 39 | 40 | def set_dir(self, dir): 41 | if (dir > 0): 42 | IO.output(self.pin1, True) 43 | IO.output(self.pin2, False) 44 | else: 45 | IO.output(self.pin1, False) 46 | IO.output(self.pin2, True) 47 | 48 | def set_speed(self, speed): 49 | self.pwm.ChangeDutyCycle(100*speed/100) 50 | 51 | -------------------------------------------------------------------------------- /smbpi/motorpot.py: -------------------------------------------------------------------------------- 1 | """ 2 | MotorPot Driver 3 | Scott Baker, http://www.smbaker.com/ 4 | 5 | Interface with a motorized potentiomer using an L293 and ADS1015 6 | """ 7 | 8 | from __future__ import print_function 9 | import sys 10 | import time 11 | from threading import Thread 12 | from motor import Motor, L293_1, L293_2, L293_ENABLE, L293_3, L293_4, L293_ENABLE2 13 | from ads1015 import ADS1015, MUX_AIN0, PGA_4V, MODE_CONT, DATA_1600, COMP_MODE_TRAD, COMP_POL_LOW, COMP_NON_LAT, COMP_QUE_DISABLE 14 | 15 | # moved the L293 enable from gpio18 to gpio15 to prevent conflict with 16 | # digi+ io card 17 | STEREO_L293_ENABLE = 15 18 | 19 | class MotorPot(Thread): 20 | def __init__(self, bus, adc_addr=0x48, motor_pin1=L293_1, motor_pin2=L293_2, motor_enable = STEREO_L293_ENABLE, dirmult=1, verbose=False): 21 | Thread.__init__(self) 22 | 23 | self.motor = Motor(pin1=motor_pin1, pin2=motor_pin2, enable = motor_enable) 24 | self.motor.set_speed(0) 25 | 26 | self.adc = ADS1015(bus, adc_addr) 27 | 28 | self.adc.write_config(MUX_AIN0 | PGA_4V | MODE_CONT | DATA_1600 | COMP_MODE_TRAD | COMP_POL_LOW | COMP_NON_LAT | COMP_QUE_DISABLE) 29 | 30 | self.dirmult = dirmult 31 | 32 | self.setPoint = None 33 | self.newSetPoint = False 34 | self.moving = False 35 | 36 | self.daemon = True 37 | self.verbose = verbose 38 | 39 | self.lastStopTime = time.time() 40 | 41 | self.start() 42 | 43 | def set(self, value): 44 | self.setPoint = value 45 | self.newSetPoint = True 46 | 47 | def check_for_request(self): 48 | pass 49 | 50 | def handle_value(self): 51 | pass 52 | 53 | def run(self): 54 | lastStallValue = -1 55 | stallStack = [] 56 | setPoint = None 57 | while True: 58 | self.check_for_request() 59 | 60 | self.value = self.adc.read_conversion() 61 | 62 | self.handle_value() 63 | 64 | if self.newSetPoint: 65 | setPoint = self.setPoint 66 | self.newSetPoint=False 67 | settle = 0 68 | stallStack = [] 69 | 70 | if setPoint is not None: 71 | if (self.value < setPoint): 72 | dir = 1 73 | else: 74 | dir = -1 75 | 76 | # the 'P' part of a PID controller... 77 | error = abs(self.value - setPoint) 78 | if (error <= 1): 79 | speed = 0 80 | 81 | # are we done yet? 82 | settle=settle+1 83 | if (settle>32): 84 | setPoint = None 85 | else: 86 | settle = 0 87 | if (error < 10): 88 | speed = 50 89 | elif (error < 25): 90 | speed = 55 91 | elif (error < 50): 92 | speed = 65 93 | elif (error < 100): 94 | speed = 75 95 | else: 96 | speed = 100 97 | 98 | stallStack.insert(0, self.value) 99 | stallStack = stallStack[:250] 100 | minv = min(stallStack) 101 | maxv = max(stallStack) 102 | if (len(stallStack)>=250) and ((maxv-minv) < 25): 103 | print("stalled at", self.value, maxv, minv) 104 | speed = 0 105 | setPoint = None 106 | 107 | if self.verbose: 108 | print("moving", self.value, setPoint, dir, speed) 109 | 110 | self.motor.set_dir(dir * self.dirmult) 111 | self.motor.set_speed(speed) 112 | 113 | self.moving = True 114 | 115 | time.sleep(0.001) 116 | else: 117 | if self.moving: 118 | self.lastStopTime = time.time() 119 | 120 | self.moving = False 121 | 122 | if self.verbose: 123 | print("monitor", self.value) 124 | 125 | # course-grained if we're just reading it 126 | time.sleep(0.1) 127 | 128 | def main(): 129 | if len(sys.argv)<2: 130 | print("syntax: motorpot.py ") 131 | return 132 | 133 | print(sys.argv) 134 | 135 | import smbus 136 | bus = smbus.SMBus(1) 137 | 138 | #motorpot = MotorPot(bus, dirmult=-1, verbose=True) 139 | motorpot = MotorPot(bus, dirmult=1, verbose=True, motor_pin1=L293_3, motor_pin2=L293_4, motor_enable = L293_ENABLE2) 140 | 141 | if sys.argv[1]!="none": 142 | motorpot.set(int(sys.argv[1])) 143 | 144 | while True: 145 | time.sleep(1) 146 | 147 | 148 | if __name__== "__main__": 149 | main() 150 | 151 | -------------------------------------------------------------------------------- /smbpi/nixie_shift.py: -------------------------------------------------------------------------------- 1 | """ 2 | Raspberry Pi Nixie Tubes from Shift Registers 3 | Scott M Baker, 2013 4 | http://www.smbaker.com/ 5 | 6 | This was used in some of my early pi projects, notably a clock 7 | and a temperature/hudity display. The Nixie tubes are driven 8 | by 74HCT595 shift registers from three pins on the pi. 9 | """ 10 | 11 | import RPi.GPIO as GPIO 12 | import time 13 | import datetime 14 | import os, sys, termios, tty, fcntl 15 | 16 | PIN_DATA = 23 17 | PIN_LATCH = 25 #24 breadboard 18 | PIN_CLK = 24 #25 breadboard 19 | 20 | PIN_DP6 = 14 21 | PIN_DP7 = 15 22 | PIN_POWCTL = 18 23 | PIN_DP1 = 2 24 | PIN_DP2 = 3 25 | 26 | class NixieShift(object): 27 | def __init__(self, pin_data, pin_latch, pin_clk, digits, flipRows, blank=[], 28 | pin_dp6 = PIN_DP6, pin_dp7 = PIN_DP7, 29 | pin_dp1 = PIN_DP1, pin_dp2 = PIN_DP2, 30 | pin_powctl = PIN_POWCTL, 31 | enable_rev2 = False): 32 | self.pin_data = pin_data 33 | self.pin_latch = pin_latch 34 | self.pin_clk = pin_clk 35 | self.digits = digits 36 | self.flipRows = flipRows 37 | self.blank = blank 38 | 39 | self.pin_dp6 = pin_dp6 40 | self.pin_dp7 = pin_dp7 41 | self.pin_dp1 = pin_dp1 42 | self.pin_dp2 = pin_dp2 43 | self.pin_powctl = pin_powctl 44 | self.enable_rev2 = enable_rev2 45 | 46 | self.state_dp6= 0 47 | self.state_dp7 = 0 48 | self.state_dp1 = 0 49 | self.state_dp2 = 0 50 | self.state_powctl = 0 51 | 52 | GPIO.setmode(GPIO.BCM) 53 | 54 | # Setup the GPIO pins as outputs 55 | GPIO.setup(self.pin_data, GPIO.OUT) 56 | GPIO.setup(self.pin_latch, GPIO.OUT) 57 | GPIO.setup(self.pin_clk, GPIO.OUT) 58 | 59 | # Set the initial state of our GPIO pins to 0 60 | GPIO.output(self.pin_data, False) 61 | GPIO.output(self.pin_latch, False) 62 | GPIO.output(self.pin_clk, False) 63 | 64 | if (enable_rev2): 65 | GPIO.setup(self.pin_dp6, GPIO.OUT) 66 | GPIO.setup(self.pin_dp7, GPIO.OUT) 67 | GPIO.setup(self.pin_dp1, GPIO.OUT) 68 | GPIO.setup(self.pin_dp2, GPIO.OUT) 69 | GPIO.setup(self.pin_powctl, GPIO.OUT) 70 | 71 | self.set_dp(1, 0) 72 | self.set_dp(2, 0) 73 | self.set_dp(6, 0) 74 | self.set_dp(7, 0) 75 | self.set_powctl(0) 76 | 77 | def delay(self): 78 | # We'll use a 10ms delay for our clock 79 | time.sleep(0.001) 80 | 81 | def transfer_latch(self): 82 | # Trigger the latch pin from 0->1. This causes the value that we've 83 | # been shifting into the register to be copied to the output. 84 | GPIO.output(self.pin_latch, True) 85 | self.delay() 86 | GPIO.output(self.pin_latch, False) 87 | self.delay() 88 | 89 | def tick_clock(self): 90 | # Tick the clock pin. This will cause the register to shift its 91 | # internal value left one position and the copy the state of the DATA 92 | # pin into the lowest bit. 93 | GPIO.output(self.pin_clk, True) 94 | self.delay() 95 | GPIO.output(self.pin_clk, False) 96 | self.delay() 97 | 98 | def shift_bit(self, value): 99 | # Shift one bit into the register. 100 | GPIO.output(self.pin_data, value) 101 | self.tick_clock() 102 | 103 | def shift_digit(self, value): 104 | # Shift a 4-bit BCD-encoded value into the register, MSB-first. 105 | self.shift_bit(value&0x08) 106 | value = value << 1 107 | self.shift_bit(value&0x08) 108 | value = value << 1 109 | self.shift_bit(value&0x08) 110 | value = value << 1 111 | self.shift_bit(value&0x08) 112 | 113 | def set_blank(self, blank): 114 | # Update the blanking digits 115 | # This won't cause a display refresh -- be sure to also call set_value() 116 | self.blank = blank 117 | 118 | def set_value(self, value): 119 | # Shift a decimal value into the register 120 | 121 | str = "%0*d" % (self.digits, value) 122 | 123 | if self.flipRows: 124 | str = str[4:] + str[:4] 125 | 126 | index = 1 127 | for digit in str: 128 | if (index in self.blank): 129 | self.shift_digit(12) 130 | else: 131 | self.shift_digit(int(digit)) 132 | index = index + 1 133 | 134 | self.transfer_latch() 135 | 136 | def set_dp(self, n, v): 137 | if self.enable_rev2: 138 | vname = "pin_dp%d" % n 139 | GPIO.output(getattr(self,vname), v) 140 | 141 | vname = "state_dp%d" % n 142 | setattr(self,vname, v) 143 | 144 | def get_dp(self, n): 145 | vname = "state_dp%d" % n 146 | return getattr(self,vname) 147 | 148 | def set_powctl(self, v): 149 | if self.enable_rev2: 150 | GPIO.output(self.pin_powctl, v) 151 | 152 | self.state_powctl = v 153 | 154 | def get_powctl(self): 155 | return self.state_powctl 156 | 157 | 158 | TEST_FIXED = "f" 159 | TEST_DIGMOVE = "m" 160 | TEST_COUNT = "c" 161 | TEST_ALL = "a" 162 | KEY_POWCTL = "p" 163 | 164 | testmode = TEST_FIXED 165 | 166 | def getKey(): 167 | fd = sys.stdin.fileno() 168 | fl = fcntl.fcntl(fd, fcntl.F_GETFL) 169 | fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK) 170 | old = termios.tcgetattr(fd) 171 | new = termios.tcgetattr(fd) 172 | new[3] = new[3] & ~termios.ICANON & ~termios.ECHO 173 | new[6][termios.VMIN] = 1 174 | new[6][termios.VTIME] = 0 175 | termios.tcsetattr(fd, termios.TCSANOW, new) 176 | key = None 177 | try: 178 | key = os.read(fd, 3) 179 | except: 180 | return 0 181 | finally: 182 | termios.tcsetattr(fd, termios.TCSAFLUSH, old) 183 | return key 184 | 185 | def testpatterns_8dig(nixie): 186 | global testmode 187 | val1=0 188 | val2=0 189 | 190 | while True: 191 | key = getKey() 192 | if (key in [TEST_FIXED, TEST_DIGMOVE, TEST_COUNT,TEST_ALL]): 193 | testmode = key 194 | 195 | if (key in ["1", "2", "6", "7"]): 196 | cur = nixie.get_dp(int(key)) 197 | if cur == 0: 198 | cur = 1 199 | else: 200 | cur = 0 201 | nixie.set_dp(int(key), cur) 202 | 203 | if key == KEY_POWCTL: 204 | if nixie.get_powctl()!=0: 205 | nixie.set_powctl(0) 206 | else: 207 | nixie.set_powctl(1) 208 | 209 | if (testmode==TEST_FIXED): 210 | nixie.set_value(12345678) 211 | elif (testmode==TEST_DIGMOVE): 212 | val1 = val1 + 1 213 | if (val1>=8): 214 | val1 = 0 215 | val2 = val2 +1 216 | if (val2>=10) or (val2<=1): 217 | val2=1 218 | nixie.set_value(int( str(val2) + ("0" * val1))) 219 | time.sleep(0.01) 220 | elif (testmode==TEST_COUNT): 221 | val1=val1+1 222 | nixie.set_value(val1) 223 | time.sleep(0.001) 224 | elif (testmode==TEST_ALL): 225 | val1 = val1 + 1 226 | if (val1 >= 10): 227 | val1 = 0 228 | nixie.set_value(int( str(val1)*8 )) 229 | 230 | def main(): 231 | try: 232 | nixie = NixieShift(PIN_DATA, PIN_LATCH, PIN_CLK, 8, True, enable_rev2=True) 233 | 234 | # Uncomment for a simple test pattern 235 | # nixie.set_value(12345678) 236 | 237 | testpatterns_8dig(nixie) 238 | 239 | # Repeatedly get the current time of day and display it on the tubes. 240 | # (the time retrieved will be in UTC; you'll want to adjust for your 241 | # time zone) 242 | 243 | #while True: 244 | # dt = datetime.datetime.now() 245 | # nixie.set_value(dt.hour*100 + dt.minute) 246 | 247 | finally: 248 | # Cleanup GPIO on exit. Otherwise, you'll get a warning next time toy 249 | # configure the pins. 250 | GPIO.cleanup() 251 | 252 | if __name__ == "__main__": 253 | main() 254 | -------------------------------------------------------------------------------- /smbpi/pigencoder.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # based on pigpio2 rotary encoder example 4 | 5 | import pigpio 6 | import threading 7 | 8 | class PigEncoder: 9 | def __init__(self, pi, gpioA, gpioB): 10 | self.pi = pi 11 | self.gpioA = gpioA 12 | self.gpioB = gpioB 13 | 14 | self.levA = 0 15 | self.levB = 0 16 | 17 | self.lastGpio = None 18 | 19 | self.pi.set_mode(gpioA, pigpio.INPUT) 20 | self.pi.set_mode(gpioB, pigpio.INPUT) 21 | 22 | self.pi.set_pull_up_down(gpioA, pigpio.PUD_UP) 23 | self.pi.set_pull_up_down(gpioB, pigpio.PUD_UP) 24 | 25 | self.cbA = self.pi.callback(gpioA, pigpio.EITHER_EDGE, self._pulse) 26 | self.cbB = self.pi.callback(gpioB, pigpio.EITHER_EDGE, self._pulse) 27 | 28 | # We can easily fall behind if we callback every time a fast encoder changes 29 | # Instead, keep a running total of the delta, and the caller can poll us for 30 | # changes. 31 | self.lock = threading.Lock() 32 | self.delta = 0 33 | 34 | def _pulse(self, gpio, level, tick): 35 | 36 | """ 37 | Decode the rotary encoder pulse. 38 | 39 | +---------+ +---------+ 0 40 | | | | | 41 | A | | | | 42 | | | | | 43 | +---------+ +---------+ +----- 1 44 | 45 | +---------+ +---------+ 0 46 | | | | | 47 | B | | | | 48 | | | | | 49 | ----+ +---------+ +---------+ 1 50 | """ 51 | 52 | if gpio == self.gpioA: 53 | self.levA = level 54 | else: 55 | self.levB = level; 56 | 57 | if gpio != self.lastGpio: # debounce 58 | self.lastGpio = gpio 59 | 60 | if gpio == self.gpioA and level == 1: 61 | if self.levB == 1: 62 | with self.lock: 63 | self.delta += 1 64 | elif gpio == self.gpioB and level == 1: 65 | if self.levA == 1: 66 | with self.lock: 67 | self.delta -= 1 68 | 69 | def getAndResetDelta(self): 70 | with self.lock: 71 | delta = self.delta 72 | self.delta = 0 73 | return delta 74 | 75 | def cancel(self): 76 | self.cbA.cancel() 77 | self.cbB.cancel() 78 | -------------------------------------------------------------------------------- /smbpi/pigpio_off.py: -------------------------------------------------------------------------------- 1 | import pigpio 2 | pi = pigpio.pi() 3 | pi.bb_i2c_close(2) 4 | -------------------------------------------------------------------------------- /smbpi/realtime_ext.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #ifndef _GNU_SOURCE 9 | #define _GNU_SOURCE 10 | #endif 11 | #include 12 | 13 | /* Borrowed from https://www.raspberrypi.org/forums/viewtopic.php?t=228727 14 | * 15 | * tjrob's techniques for real time scheduling a process on a pi. 16 | * 17 | * works by isolating a cpu from Linux, then pinning the process to that cpu 18 | */ 19 | 20 | /* A different comment from another thread (https://www.raspberrypi.org/forums/viewtopic.php?t=200793), about using taskset: 21 | * Heater ... I have done everything you said, and it works just perfectly now: 22 | * - add this at kernel boot prompt, to reserve cores 2 and 3 (first core is #0): isolcpus=2,3 23 | * - start my bash script on core 2: /usr/bin/taskset -c 2 /root/ws2812-spi/ws2812.sh 24 | *- in this script, call the python API (the one that really performs the SPI calls) on core 3: taskset -c 3 nice -n -20 /root/ws2812-spi/ws2812.05.py 25 | * since I am now using taskset, nice is probably useless. And then, reboot. 26 | */ 27 | 28 | /* Another option may be partrt, uses cgroups 29 | */ 30 | 31 | /* some tests 32 | * 33 | * fdc with no special options, 4.4% failure 34 | * fdc with --realtime, 3% failure 35 | * a lot of these are 90 followed by D0 errors, and I wonder if something else is going on 36 | * fdc with --realtime and sleep before the op, 3% failure 37 | * fdc with --realtime --pincpu 3 and isolcpus=3 on cmdline, 3% failure 38 | * 39 | */ 40 | 41 | 42 | static PyObject *realtime_realTimeSched(PyObject *self, PyObject *args) 43 | { 44 | int prio; 45 | int rc; 46 | struct sched_param param; 47 | 48 | if (!PyArg_ParseTuple(args, "")) { 49 | return NULL; 50 | } 51 | 52 | prio = sched_get_priority_max(SCHED_FIFO); 53 | param.sched_priority = prio; 54 | 55 | sched_setscheduler(0,SCHED_FIFO,¶m); 56 | // This permits realtime processes to use 100% of a CPU, but on a 57 | // RPi that starves the kernel. Without this there are latencies 58 | // up to 50 MILLISECONDS. 59 | rc = system("echo -1 >/proc/sys/kernel/sched_rt_runtime_us"); 60 | return Py_BuildValue("i", rc); 61 | } 62 | 63 | static PyObject *realtime_pinCPU(PyObject *self, PyObject *args) 64 | { 65 | unsigned int cpu; 66 | 67 | if (!PyArg_ParseTuple(args, "i", &cpu)) { 68 | return NULL; 69 | } 70 | 71 | cpu_set_t cpuset; 72 | CPU_ZERO(&cpuset); 73 | CPU_SET(cpu,&cpuset); 74 | sched_setaffinity(0,sizeof(cpu_set_t),&cpuset); 75 | 76 | return Py_BuildValue("i", 0); 77 | } 78 | 79 | static PyObject *realtime_enableTurbo(PyObject *self, PyObject *args) 80 | { 81 | unsigned int turbo; 82 | int rc; 83 | 84 | if (!PyArg_ParseTuple(args, "i", &turbo)) { 85 | return NULL; 86 | } 87 | 88 | if (turbo) { 89 | rc = system("sudo cp /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq " 90 | "/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq"); 91 | } else { 92 | rc = system("sudo cp /sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq " 93 | "/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq"); 94 | } 95 | 96 | return Py_BuildValue("i", rc); 97 | } 98 | 99 | static PyMethodDef realtime_methods[] = { 100 | {"realTimeSched", realtime_realTimeSched, METH_VARARGS, "Enable real-time scheduling"}, 101 | {"pinCPU", realtime_pinCPU, METH_VARARGS, "Pin process to CPU"}, 102 | {"enableTurbo", realtime_enableTurbo, METH_VARARGS, "Enable or disble turbo mode"}, 103 | 104 | {NULL, NULL, 0, NULL} 105 | }; 106 | 107 | PyMODINIT_FUNC initrealtime_ext(void) 108 | { 109 | (void) Py_InitModule("realtime_ext", realtime_methods); 110 | } 111 | -------------------------------------------------------------------------------- /smbpi/supervisor_direct_ext.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "i2c-utils.h" 7 | 8 | #define IODIR 0x00 9 | #define IPOL 0x02 10 | #define GPINTEN 0x04 11 | #define DEFVAL 0x06 12 | #define INTCON 0x08 13 | #define IOCON 0x0A 14 | #define GPPU 0x0C 15 | #define INTF 0x0E 16 | #define MCP23017_GPIO 0x12 17 | #define OLAT 0x14 18 | 19 | #define A16 0x01 20 | #define A17 0x02 21 | #define A18 0x04 22 | #define A19 0x08 23 | #define RAML 0x10 24 | #define RAMH 0x20 25 | #define ROML 0x40 26 | #define ROMH 0x80 27 | 28 | #define MR 0x01 29 | #define MW 0x02 30 | #define IOR 0x04 31 | #define IOW 0x08 32 | #define RX 0x10 33 | #define RESET 0x20 34 | #define BUSREQ 0x40 35 | #define BUSACK 0x80 36 | 37 | #define LBUSREQ (0xFF & ~BUSREQ) 38 | #define LBUSREQ_LRESET ((0xFF & ~BUSREQ) & ~RESET) 39 | 40 | #define A16_BANK 0 41 | #define A17_BANK 0 42 | #define A18_BANK 0 43 | #define A19_BANK 0 44 | #define RAML_BANK 0 45 | #define RAMH_BANK 0 46 | #define ROML_BANK 0 47 | #define ROMH_BANK 0 48 | 49 | #define MR_BANK 1 50 | #define MW_BANK 1 51 | #define IOR_BANK 1 52 | #define IOW_BANK 1 53 | #define RX_BANK 1 54 | #define RESET_BANK 1 55 | #define BUSREQ_BANK 1 56 | #define BUSACK_BANK 1 57 | 58 | #define LMR_LBUSREQ ((0xFF & ~BUSREQ) & ~MR) 59 | #define LMW_LBUSREQ ((0xFF & ~BUSREQ) & ~MW) 60 | 61 | #define ADDR_DATA 0x20 62 | #define ADDR_ADDRESS 0x21 63 | #define ADDR_CONTROL 0x22 64 | 65 | char *i2cbus = "/dev/i2c-1"; 66 | int i2cfile; 67 | uint8_t lastMidAddrSet; 68 | uint8_t lastMidAddr; 69 | 70 | static void writereg(uint8_t addr, uint8_t reg, uint8_t bits) 71 | { 72 | ioctl(i2cfile, I2C_SLAVE, addr); 73 | i2c_smbus_write_byte_data(i2cfile, reg, bits); 74 | } 75 | 76 | static uint8_t readreg(uint8_t addr, uint8_t reg) 77 | { 78 | ioctl(i2cfile, I2C_SLAVE, addr); 79 | return i2c_smbus_read_byte_data(i2cfile, reg); 80 | } 81 | 82 | static void set_iodir(uint8_t addr, uint8_t bank, uint8_t bits) 83 | { 84 | writereg(addr, bank+IODIR, bits); 85 | } 86 | 87 | static void set_pullup(uint8_t addr, uint8_t bank, uint8_t bits) 88 | { 89 | writereg(addr, bank+GPPU, bits); 90 | } 91 | 92 | static void set_gpio(uint8_t addr, uint8_t bank, uint8_t bits) 93 | { 94 | writereg(addr, bank+MCP23017_GPIO, bits); 95 | } 96 | 97 | static uint8_t get_gpio(uint8_t addr, uint8_t bank) 98 | { 99 | return readreg(addr, bank + MCP23017_GPIO); 100 | } 101 | 102 | static void _release_bus() 103 | { 104 | set_iodir(ADDR_ADDRESS, 0, 0xFF); 105 | set_iodir(ADDR_ADDRESS, 1, 0xFF); 106 | set_iodir(ADDR_DATA, 0, 0xFF); 107 | set_iodir(ADDR_DATA, 1, 0xFF); 108 | set_iodir(ADDR_CONTROL, 0, 0xFF); // A16..19 and CS are inputs 109 | set_iodir(ADDR_CONTROL, 1, 0xFF); // even BUSRQ is an input 110 | 111 | //self.log("wait for not busack") 112 | while (1) { 113 | uint8_t bits = get_gpio(ADDR_CONTROL, BUSACK_BANK); 114 | if ((bits & BUSACK) != 0) { 115 | break; 116 | } 117 | } 118 | } 119 | 120 | static void _take_bus() 121 | { 122 | set_gpio(ADDR_CONTROL, BUSREQ_BANK, LBUSREQ); // BUSREQ lo 123 | set_iodir(ADDR_CONTROL, BUSREQ_BANK, LBUSREQ); // BUSREQ is an output 124 | 125 | //self.log("wait for busack") 126 | while (1) { 127 | uint8_t bits = get_gpio(ADDR_CONTROL, BUSACK_BANK); 128 | if ((bits & BUSACK) == 0) { 129 | break; 130 | } 131 | } 132 | 133 | set_iodir(ADDR_ADDRESS, 0, 0x00); 134 | set_iodir(ADDR_ADDRESS, 1, 0x00); 135 | set_iodir(ADDR_DATA, 0, 0xFF); 136 | set_iodir(ADDR_DATA, 1, 0xFF); 137 | 138 | set_gpio(ADDR_CONTROL, 0, RAML | RAMH | ROML | ROMH); // chip selects high. A16..A19 low 139 | set_iodir(ADDR_CONTROL, 0, 0x00); // A16..A19 and CS outputs 140 | 141 | set_gpio(ADDR_CONTROL, 1, MR | MW | IOR | IOW); // BUSRQ low 142 | set_iodir(ADDR_CONTROL, 1, RX | BUSACK | RESET); // MR, MW, IOR, IOW, BUSRQ outputs 143 | } 144 | 145 | static uint8_t _seg(uint32_t addr) 146 | { 147 | uint16_t seg = addr>>16; 148 | if (seg>=0x100) { 149 | seg = seg >> 8; 150 | } 151 | return seg; 152 | } 153 | 154 | static void _mem_read_start(uint32_t addr) 155 | { 156 | lastMidAddrSet = 0; 157 | set_gpio(ADDR_CONTROL, RAML_BANK, _seg(addr) | ROML | ROMH ); // assert RAML and RAMH low. ROML and ROMH high 158 | } 159 | 160 | static void _mem_read_end(void) 161 | { 162 | set_gpio(ADDR_CONTROL, RAML_BANK, 0xFF); // release all CS and A16..A19 163 | } 164 | 165 | static uint16_t _mem_read_fast(uint32_t addr) 166 | { 167 | uint16_t result; 168 | uint8_t midAddr; 169 | 170 | midAddr = (addr>>8) & 0xFF; // A8..A15 171 | if ((midAddr != lastMidAddr) || (!lastMidAddrSet)) { 172 | set_gpio(ADDR_ADDRESS, 1, midAddr); 173 | lastMidAddr = midAddr; 174 | lastMidAddrSet = 1; 175 | } 176 | 177 | set_gpio(ADDR_ADDRESS, 0, addr & 0xFF); // A0..A7 178 | set_gpio(ADDR_CONTROL, MR_BANK, LMR_LBUSREQ); // assert MR and hold busreq 179 | result = ((uint16_t)(get_gpio(ADDR_DATA,1)<<8)) | ((uint16_t) get_gpio(ADDR_DATA, 0)); 180 | set_gpio(ADDR_CONTROL,MR_BANK, LBUSREQ); // release MR and hold busreq 181 | return result; 182 | } 183 | 184 | static uint16_t _mem_read(uint32_t addr) 185 | { 186 | uint16_t result; 187 | _mem_read_start(addr); 188 | result = _mem_read_fast(addr); 189 | _mem_read_end(); 190 | return result; 191 | } 192 | 193 | static void _mem_write_start(uint32_t addr) 194 | { 195 | lastMidAddrSet = 0; 196 | set_gpio(ADDR_CONTROL, RAML_BANK, _seg(addr) | ROML | ROMH ); // assert RAML and RAMH low. ROML and ROMH high/ 197 | set_iodir(ADDR_DATA, 0, 0x00); // iodir to write data 198 | set_iodir(ADDR_DATA, 1, 0x00); // iodir to write data 199 | } 200 | 201 | static void _mem_write_end() 202 | { 203 | set_iodir(ADDR_DATA, 0, 0xFF); // iodir back to read data 204 | set_iodir(ADDR_DATA, 1, 0xFF); // iodir back to read data 205 | set_gpio(ADDR_CONTROL, RAML_BANK, 0xFF); // release all CS and A16..A19 206 | } 207 | 208 | static void _mem_write_fast(uint32_t addr, uint16_t val) 209 | { 210 | uint8_t midAddr; 211 | 212 | midAddr = (addr>>8) & 0xFF; // A8..A15 213 | if ((midAddr != lastMidAddr) || (!lastMidAddrSet)) { 214 | set_gpio(ADDR_ADDRESS, 1, midAddr); 215 | lastMidAddr = midAddr; 216 | lastMidAddrSet = 1; 217 | } 218 | 219 | set_gpio(ADDR_ADDRESS, 0, addr & 0xFF); // A0..A7 220 | set_gpio(ADDR_DATA, 0, val & 0xFF); 221 | set_gpio(ADDR_DATA, 1, val >> 8); 222 | set_gpio(ADDR_CONTROL, MW_BANK, LMW_LBUSREQ); // assert MW and hold busreq 223 | set_gpio(ADDR_CONTROL, MW_BANK, LBUSREQ); // release MW and hold busreq 224 | } 225 | 226 | static void _mem_write(uint32_t addr, uint16_t val) 227 | { 228 | _mem_write_start(addr); 229 | _mem_write_fast(addr, val); 230 | _mem_write_end(); 231 | } 232 | 233 | static PyObject *supervisor_direct_init(PyObject *self, PyObject *args) 234 | { 235 | i2cfile = open(i2cbus, O_RDWR); 236 | 237 | set_pullup(ADDR_CONTROL, 0, 0xFF); // weak pullups on all control lines 238 | set_pullup(ADDR_CONTROL, 1, 0xFF); // weak pullups on all control lines 239 | 240 | _release_bus(); 241 | 242 | return Py_BuildValue(""); 243 | } 244 | 245 | static PyObject *supervisor_direct_release_bus(PyObject *self, PyObject *args) 246 | { 247 | _release_bus(); 248 | 249 | return Py_BuildValue(""); 250 | } 251 | 252 | static PyObject *supervisor_direct_take_bus(PyObject *self, PyObject *args) 253 | { 254 | _take_bus(); 255 | 256 | return Py_BuildValue(""); 257 | } 258 | 259 | static PyObject *supervisor_direct_mem_read_start(PyObject *self, PyObject *args) 260 | { 261 | uint32_t addr; 262 | if (!PyArg_ParseTuple(args, "I", &addr)) { 263 | return NULL; 264 | } 265 | 266 | _mem_read_start(addr); 267 | 268 | return Py_BuildValue(""); 269 | } 270 | 271 | static PyObject *supervisor_direct_mem_read_end(PyObject *self, PyObject *args) 272 | { 273 | _mem_read_end(); 274 | 275 | return Py_BuildValue(""); 276 | } 277 | 278 | static PyObject *supervisor_direct_mem_read_fast(PyObject *self, PyObject *args) 279 | { 280 | uint32_t addr; 281 | uint16_t val; 282 | 283 | if (!PyArg_ParseTuple(args, "I", &addr)) { 284 | return NULL; 285 | } 286 | 287 | val = _mem_read_fast(addr); 288 | 289 | return Py_BuildValue("H", val); 290 | } 291 | 292 | static PyObject *supervisor_direct_mem_read(PyObject *self, PyObject *args) 293 | { 294 | uint32_t addr; 295 | uint16_t val; 296 | 297 | if (!PyArg_ParseTuple(args, "I", &addr)) { 298 | return NULL; 299 | } 300 | 301 | val = _mem_read(addr); 302 | 303 | return Py_BuildValue("H", val); 304 | } 305 | 306 | static PyObject *supervisor_direct_mem_write_start(PyObject *self, PyObject *args) 307 | { 308 | uint32_t addr; 309 | if (!PyArg_ParseTuple(args, "I", &addr)) { 310 | return NULL; 311 | } 312 | 313 | _mem_write_start(addr); 314 | 315 | return Py_BuildValue(""); 316 | } 317 | 318 | static PyObject *supervisor_direct_mem_write_end(PyObject *self, PyObject *args) 319 | { 320 | _mem_write_end(); 321 | 322 | return Py_BuildValue(""); 323 | } 324 | 325 | static PyObject *supervisor_direct_mem_write_fast(PyObject *self, PyObject *args) 326 | { 327 | uint32_t addr; 328 | uint16_t val; 329 | 330 | if (!PyArg_ParseTuple(args, "IH", &addr, &val)) { 331 | return NULL; 332 | } 333 | 334 | _mem_write_fast(addr, val); 335 | 336 | return Py_BuildValue(""); 337 | } 338 | 339 | static PyObject *supervisor_direct_mem_write_buffer(PyObject *self, PyObject *args) 340 | { 341 | uint32_t addr; 342 | Py_buffer pybuf; 343 | unsigned char bytes[128]; 344 | int res; 345 | 346 | if (!PyArg_ParseTuple(args, "Iy*", &addr, &pybuf)) { 347 | return NULL; 348 | } 349 | 350 | res = PyBuffer_ToContiguous(bytes, &pybuf, 128, 'C'); 351 | if (res!=0) { 352 | return NULL; 353 | } 354 | 355 | for (int i=0; i<64; i++) { 356 | unsigned short w; 357 | w = (bytes[i*2]<<8) | bytes[i*2+1]; 358 | _mem_write_fast(addr,w); 359 | addr += 2; 360 | } 361 | 362 | PyBuffer_Release(&pybuf); 363 | 364 | return Py_BuildValue(""); 365 | } 366 | 367 | static PyObject *supervisor_direct_mem_write(PyObject *self, PyObject *args) 368 | { 369 | uint32_t addr; 370 | uint16_t val; 371 | 372 | if (!PyArg_ParseTuple(args, "IH", &addr, &val)) { 373 | return NULL; 374 | } 375 | 376 | _mem_write(addr, val); 377 | 378 | return Py_BuildValue(""); 379 | } 380 | 381 | static PyMethodDef supervisor_direct_methods[] = { 382 | {"init", supervisor_direct_init, METH_VARARGS, "Initialize"}, 383 | {"release_bus", supervisor_direct_release_bus, METH_VARARGS, "Release bus"}, 384 | {"take_bus", supervisor_direct_take_bus, METH_VARARGS, "Take the bus"}, 385 | {"mem_read_start", supervisor_direct_mem_read_start, METH_VARARGS, "Start multiple memory reads"}, 386 | {"mem_read_end", supervisor_direct_mem_read_end, METH_VARARGS, "End multiple memory reads"}, 387 | {"mem_read_fast", supervisor_direct_mem_read_fast, METH_VARARGS, "Read one memory"}, 388 | {"mem_read", supervisor_direct_mem_read, METH_VARARGS, "start, read, end"}, 389 | {"mem_write_start", supervisor_direct_mem_write_start, METH_VARARGS, "Start multiple memory writes"}, 390 | {"mem_write_end", supervisor_direct_mem_write_end, METH_VARARGS, "End multiple memory writes"}, 391 | {"mem_write_fast", supervisor_direct_mem_write_fast, METH_VARARGS, "Write one memory"}, 392 | {"mem_write_buffer", supervisor_direct_mem_write_buffer, METH_VARARGS, "Write one memory"}, 393 | {"mem_write", supervisor_direct_mem_write, METH_VARARGS, "start, write, end"}, 394 | {NULL, NULL, 0, NULL} 395 | }; 396 | 397 | static struct PyModuleDef supervisor_direct_module = 398 | { 399 | PyModuleDef_HEAD_INIT, 400 | "supervisor_direct_ext", /* name of module */ 401 | "", /* module documentation, may be NULL */ 402 | -1, /* size of per-interpreter state of the module, or -1 if the module keeps state in global variables. */ 403 | supervisor_direct_methods 404 | }; 405 | 406 | PyMODINIT_FUNC PyInit_supervisor_direct_ext(void) 407 | { 408 | return PyModule_Create(&supervisor_direct_module); 409 | } 410 | -------------------------------------------------------------------------------- /smbpi/supervisor_h8_ext.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "i2c-utils.h" 8 | 9 | // mcp23017 registers 10 | #define IODIR 0x00 11 | #define IPOL 0x02 12 | #define GPINTEN 0x04 13 | #define DEFVAL 0x06 14 | #define INTCON 0x08 15 | #define IOCON 0x0A 16 | #define GPPU 0x0C 17 | #define INTF 0x0E 18 | #define MCP23017_GPIO 0x12 19 | #define OLAT 0x14 20 | 21 | // mcp23017 bank A 22 | #define A8 0x01 23 | #define A9 0x02 24 | #define A10 0x04 25 | #define A11 0x08 26 | #define A12 0x10 27 | #define A13 0x20 28 | #define A14 0x40 29 | #define A15 0x80 30 | 31 | // mcp23017 bank B 32 | // HOLD is active low 33 | // HLDA is active high 34 | #define HOLD 0x01 35 | #define HLDA 0x02 36 | #define RESET 0x04 37 | #define M1 0x08 38 | #define ROMDIS 0x10 39 | #define INT1 0x20 40 | #define INT2 0x40 41 | #define INTJPR 0x80 42 | 43 | #define LHOLD (0xFF & ~HOLD) 44 | #define LHOLD_LRESET ((0xFF & ~HOLD) & ~RESET) 45 | #define LRESET (0xFF & ~RESET) 46 | #define HRESET RESET 47 | 48 | #define A8_BANK 0 49 | #define A9_BANK 0 50 | #define A10_BANK 0 51 | #define A11_BANK 0 52 | #define A12_BANK 0 53 | #define A13_BANK 0 54 | #define A14_BANK 0 55 | #define A15_BANK 0 56 | 57 | #define HOLD_BANK 1 58 | #define HLDA_BANK 1 59 | #define RESET_BANK 1 60 | 61 | // down to just one io expander in this supervisor 62 | #define ADDR_CONTROL 0x21 63 | #define ADDR_ADDRESS 0x21 64 | 65 | #define PIN_D0 16 66 | #define PIN_D1 17 67 | #define PIN_D2 18 68 | #define PIN_D3 19 69 | #define PIN_D4 20 70 | #define PIN_D5 21 71 | #define PIN_D6 22 72 | #define PIN_D7 23 73 | 74 | #define PIN_A0 24 75 | #define PIN_A1 25 76 | #define PIN_A2 26 77 | #define PIN_A3 27 78 | #define PIN_A4 4 79 | #define PIN_A5 5 80 | #define PIN_A6 6 81 | #define PIN_A7 7 82 | 83 | #define PIN_LA 13 84 | #define PIN_LDIR 10 85 | #define PIN_MW 9 86 | #define PIN_LG 11 87 | #define PIN_MR 8 88 | 89 | #define PIN_IOR 0 90 | #define PIN_IOW 1 91 | 92 | #define PIN_CLKINT 12 93 | 94 | // High on the 74LVC245 will be H8 data to pi data 95 | #define DATADIR_READ 1 96 | #define DATADIR_WRITE 0 97 | 98 | static int datapins[] = {PIN_D0, PIN_D1, PIN_D2, PIN_D3, 99 | PIN_D4, PIN_D5, PIN_D6, PIN_D7}; 100 | 101 | 102 | char *i2cbus = "/dev/i2c-1"; 103 | int i2cfile; 104 | uint16_t isTaken; 105 | uint8_t lastMidAddrSet; 106 | uint8_t lastMidAddr; 107 | 108 | void short_delay(void) 109 | { 110 | // Just do nothing for a while. This is to allow the RAM some time to do it's work. 111 | // 112 | int j; 113 | 114 | for (j=0; j<10; j++) { 115 | asm("nop"); 116 | } 117 | } 118 | 119 | static void writereg(uint8_t addr, uint8_t reg, uint8_t bits) 120 | { 121 | ioctl(i2cfile, I2C_SLAVE, addr); 122 | i2c_smbus_write_byte_data(i2cfile, reg, bits); 123 | } 124 | 125 | static uint8_t readreg(uint8_t addr, uint8_t reg) 126 | { 127 | ioctl(i2cfile, I2C_SLAVE, addr); 128 | return i2c_smbus_read_byte_data(i2cfile, reg); 129 | } 130 | 131 | static void set_iodir(uint8_t addr, uint8_t bank, uint8_t bits) 132 | { 133 | writereg(addr, bank+IODIR, bits); 134 | } 135 | 136 | static void set_pullup(uint8_t addr, uint8_t bank, uint8_t bits) 137 | { 138 | writereg(addr, bank+GPPU, bits); 139 | } 140 | 141 | static void set_gpio(uint8_t addr, uint8_t bank, uint8_t bits) 142 | { 143 | writereg(addr, bank+MCP23017_GPIO, bits); 144 | } 145 | 146 | static uint8_t get_gpio(uint8_t addr, uint8_t bank) 147 | { 148 | return readreg(addr, bank + MCP23017_GPIO); 149 | } 150 | 151 | static void _databus_config_read() 152 | { 153 | for (int i=0; i<8; i++) { 154 | gpioSetMode(datapins[i], PI_INPUT); 155 | } 156 | gpioWrite(PIN_LDIR, DATADIR_READ); 157 | } 158 | 159 | static void _databus_config_write() 160 | { 161 | gpioWrite(PIN_LDIR, DATADIR_WRITE); 162 | for (int i=0; i<8; i++) { 163 | gpioSetMode(datapins[i], PI_OUTPUT); 164 | } 165 | } 166 | 167 | static uint16_t _data_read() 168 | { 169 | uint32_t data = gpioRead_Bits_0_31(); 170 | return ((~(data >> 16)) & 0xFF); // Note: Inverted 171 | } 172 | 173 | static void _data_write(uint16_t val) 174 | { 175 | uint32_t data = ((~val) & 0xFF) << 16; // Note: Inverted 176 | 177 | gpioWrite_Bits_0_31_Clear(0x00FF0000); 178 | gpioWrite_Bits_0_31_Set(data); 179 | } 180 | 181 | static void _set_address(uint32_t addr) 182 | { 183 | uint8_t midAddr; 184 | 185 | // Note: Inverted 186 | midAddr = (~(addr>>8)) & 0xFF; // A8..A15. 187 | if ((midAddr != lastMidAddr) || (!lastMidAddrSet)) { 188 | set_gpio(ADDR_ADDRESS, A8_BANK, midAddr); 189 | lastMidAddr = midAddr; 190 | lastMidAddrSet = 1; 191 | } 192 | 193 | // optimize me!! 194 | gpioWrite(PIN_A0, (addr & 1) ? 0 : 1); // NOTE: Inverted 195 | gpioWrite(PIN_A1, (addr & 2) ? 0 : 1); 196 | gpioWrite(PIN_A2, (addr & 4) ? 0 : 1); 197 | gpioWrite(PIN_A3, (addr & 8) ? 0 : 1); 198 | gpioWrite(PIN_A4, (addr & 16) ? 0 : 1); 199 | gpioWrite(PIN_A5, (addr & 32) ? 0 : 1); 200 | gpioWrite(PIN_A6, (addr & 64) ? 0 : 1); 201 | gpioWrite(PIN_A7, (addr & 128) ? 0 : 1); 202 | } 203 | 204 | static void _mrDown() 205 | { 206 | gpioWrite(PIN_MR, 1); // Note: Inverted 207 | } 208 | 209 | static void _mrUp() 210 | { 211 | gpioWrite(PIN_MR, 0); // Note: Inverted 212 | } 213 | 214 | static void _mwDown() 215 | { 216 | gpioWrite(PIN_MW, 1); // Note: Inverted 217 | } 218 | 219 | static void _mwUp() 220 | { 221 | gpioWrite(PIN_MW, 0); // Note: Inverted 222 | } 223 | 224 | static void _iorDown() 225 | { 226 | gpioWrite(PIN_IOR, 1); // Note: Inverted 227 | } 228 | 229 | static void _iorUp() 230 | { 231 | gpioWrite(PIN_IOR, 0); // Note: Inverted 232 | } 233 | 234 | static void _iowDown() 235 | { 236 | gpioWrite(PIN_IOW, 1); // Note: Inverted 237 | } 238 | 239 | static void _iowUp() 240 | { 241 | gpioWrite(PIN_IOW, 0); // Note: Inverted 242 | } 243 | 244 | static void _release_bus(uint8_t reset) 245 | { 246 | if (isTaken<=0) { 247 | printf("WARNING: _release_bus while isTaken==%d\n", isTaken); 248 | } 249 | isTaken--; 250 | 251 | if (reset) { 252 | set_gpio(ADDR_CONTROL, RESET_BANK, LHOLD_LRESET); // HOLD is still low, set RESET to low 253 | set_iodir(ADDR_CONTROL, RESET_BANK, LHOLD_LRESET); // HOLD and RESET are both outputs 254 | } 255 | 256 | gpioWrite(PIN_LA, 1); // release the MR/MW/A2-4 245 257 | 258 | set_iodir(ADDR_ADDRESS, 0, 0xFF); // A8..15 are inputs 259 | set_iodir(ADDR_CONTROL, 1, 0xFF); // even BUSRQ is an input. It'll be pulled to default by the CPU board. 260 | 261 | //self.log("wait for not HLDA"); 262 | while (1) { 263 | uint8_t bits = get_gpio(ADDR_CONTROL, HLDA_BANK); 264 | if ((bits & HLDA) == 0) { 265 | break; 266 | } 267 | } 268 | } 269 | 270 | static void _take_bus() 271 | { 272 | if (isTaken>0) { 273 | printf("WARNING: _take_bus while isTaken==%d\n", isTaken); 274 | } 275 | isTaken++; 276 | 277 | set_gpio(ADDR_CONTROL, HOLD_BANK, LHOLD); // HOLD is low 278 | set_iodir(ADDR_CONTROL, HOLD_BANK, LHOLD); // HOLD is an output 279 | 280 | //self.log("wait for HLDA"); 281 | while (1) { 282 | uint8_t bits = get_gpio(ADDR_CONTROL, HLDA_BANK); 283 | if ((bits & HLDA) != 0) { 284 | break; 285 | } 286 | } 287 | 288 | set_iodir(ADDR_ADDRESS, A8_BANK, 0x00); // Make A8..15 outputs 289 | 290 | _mrUp(); 291 | _mwUp(); 292 | _iorUp(); 293 | _iowUp(); 294 | gpioWrite(PIN_LA, 0); // assert the MR/MW/A0-7 245 295 | } 296 | 297 | static void _mem_read_start(uint32_t addr) 298 | { 299 | lastMidAddrSet = 0; 300 | } 301 | 302 | static void _mem_read_end(void) 303 | { 304 | } 305 | 306 | static uint16_t _mem_read_fast(uint32_t addr) 307 | { 308 | uint16_t result; 309 | 310 | _set_address(addr); 311 | short_delay(); 312 | _mrDown(); 313 | short_delay(); 314 | result = _data_read(); 315 | _mrUp(); 316 | return result; 317 | } 318 | 319 | static uint16_t _mem_read(uint32_t addr) 320 | { 321 | uint16_t result; 322 | _mem_read_start(addr); 323 | result = _mem_read_fast(addr); 324 | _mem_read_end(); 325 | return result; 326 | } 327 | 328 | static void _mem_write_start(uint32_t addr) 329 | { 330 | lastMidAddrSet = 0; 331 | _databus_config_write(); 332 | } 333 | 334 | static void _mem_write_end() 335 | { 336 | _databus_config_read(); 337 | } 338 | 339 | static void _mem_write_fast(uint32_t addr, uint16_t val) 340 | { 341 | _set_address(addr); 342 | _data_write(val); 343 | short_delay(); 344 | _mwDown(); 345 | short_delay(); 346 | _mwUp(); 347 | short_delay(); 348 | } 349 | 350 | static void _mem_write(uint32_t addr, uint16_t val) 351 | { 352 | _mem_write_start(addr); 353 | _mem_write_fast(addr, val); 354 | _mem_write_end(); 355 | } 356 | 357 | static PyObject *supervisor_h8_init(PyObject *self, PyObject *args) 358 | { 359 | i2cfile = open(i2cbus, O_RDWR); 360 | 361 | if (gpioInitialise() < 0) { 362 | Py_RETURN_FALSE; 363 | } 364 | 365 | 366 | set_pullup(ADDR_CONTROL, 0, 0xFF); // weak pullups on all control lines 367 | 368 | gpioSetMode(PIN_LDIR, PI_OUTPUT); // configure LDIR first, so _databus_config_read can do its thing 369 | 370 | _databus_config_read(); // configure data buffers for read 371 | 372 | gpioSetMode(PIN_MR, PI_OUTPUT); // now configure all the other outputs 373 | gpioSetMode(PIN_MW, PI_OUTPUT); 374 | gpioSetMode(PIN_IOR, PI_OUTPUT); 375 | gpioSetMode(PIN_IOW, PI_OUTPUT); 376 | gpioSetMode(PIN_A0, PI_OUTPUT); 377 | gpioSetMode(PIN_A1, PI_OUTPUT); 378 | gpioSetMode(PIN_A2, PI_OUTPUT); 379 | gpioSetMode(PIN_A3, PI_OUTPUT); 380 | gpioSetMode(PIN_A4, PI_OUTPUT); 381 | gpioSetMode(PIN_A5, PI_OUTPUT); 382 | gpioSetMode(PIN_A6, PI_OUTPUT); 383 | gpioSetMode(PIN_A7, PI_OUTPUT); 384 | 385 | _mrUp(); 386 | _mwUp(); 387 | _iorUp(); 388 | _iowUp(); 389 | gpioWrite(PIN_LA, 1); // disable the address/MR/MW buffer 390 | gpioSetMode(PIN_LA, PI_OUTPUT); 391 | 392 | gpioWrite(PIN_LG, 0); // enable the data buffer. Can be on all the time so we can snoop. 393 | gpioSetMode(PIN_LG, PI_OUTPUT); 394 | 395 | isTaken = 1; 396 | _release_bus(0); 397 | 398 | Py_RETURN_TRUE; 399 | } 400 | 401 | static PyObject *supervisor_h8_release_bus(PyObject *self, PyObject *args) 402 | { 403 | int reset; // not "bool x" 404 | 405 | if (!PyArg_ParseTuple(args, "p", &reset)) { 406 | return NULL; 407 | } 408 | 409 | _release_bus(reset); 410 | 411 | return Py_BuildValue(""); 412 | } 413 | 414 | static PyObject *supervisor_h8_take_bus(PyObject *self, PyObject *args) 415 | { 416 | _take_bus(); 417 | 418 | return Py_BuildValue(""); 419 | } 420 | 421 | static PyObject *supervisor_h8_is_taken(PyObject *self, PyObject *args) 422 | { 423 | if (isTaken>0) { 424 | Py_RETURN_TRUE; 425 | } else { 426 | Py_RETURN_FALSE; 427 | } 428 | } 429 | 430 | // this is probably not faster than just calling mem_snoop from python 431 | static PyObject *supervisor_h8_wait_signal(PyObject *self, PyObject *args) 432 | { 433 | uint8_t hit1=0; 434 | uint8_t hit2=0; 435 | struct timespec tStart; 436 | struct timespec tNow; 437 | long elapsed_ms; 438 | 439 | clock_gettime(CLOCK_REALTIME, &tStart); 440 | while (1) { 441 | uint16_t m = _data_read(); 442 | if (m == 0x73E7) { 443 | hit1=1; 444 | } else if (m == 0xF912) { 445 | hit2=1; 446 | } 447 | if (hit1 && hit2) { 448 | Py_RETURN_TRUE; 449 | } 450 | clock_gettime(CLOCK_REALTIME, &tNow); 451 | elapsed_ms = round((tNow.tv_nsec - tStart.tv_nsec)/1.0e6); 452 | if (elapsed_ms > 100) { 453 | Py_RETURN_FALSE; 454 | } 455 | } 456 | } 457 | 458 | static PyObject *supervisor_h8_mem_read_start(PyObject *self, PyObject *args) 459 | { 460 | uint32_t addr; 461 | if (!PyArg_ParseTuple(args, "I", &addr)) { 462 | return NULL; 463 | } 464 | 465 | _mem_read_start(addr); 466 | 467 | return Py_BuildValue(""); 468 | } 469 | 470 | static PyObject *supervisor_h8_mem_read_end(PyObject *self, PyObject *args) 471 | { 472 | _mem_read_end(); 473 | 474 | return Py_BuildValue(""); 475 | } 476 | 477 | static PyObject *supervisor_h8_mem_read_fast(PyObject *self, PyObject *args) 478 | { 479 | uint32_t addr; 480 | uint16_t val; 481 | 482 | if (!PyArg_ParseTuple(args, "I", &addr)) { 483 | return NULL; 484 | } 485 | 486 | val = _mem_read_fast(addr); 487 | 488 | return Py_BuildValue("H", val); 489 | } 490 | 491 | static PyObject *supervisor_h8_mem_read(PyObject *self, PyObject *args) 492 | { 493 | uint32_t addr; 494 | uint16_t val; 495 | 496 | if (!PyArg_ParseTuple(args, "I", &addr)) { 497 | return NULL; 498 | } 499 | 500 | val = _mem_read(addr); 501 | 502 | return Py_BuildValue("H", val); 503 | } 504 | 505 | static PyObject *supervisor_h8_mem_snoop(PyObject *self, PyObject *args) 506 | { 507 | uint16_t val; 508 | 509 | val = _data_read(); 510 | 511 | return Py_BuildValue("H", val); 512 | } 513 | 514 | static PyObject *supervisor_h8_mem_write_start(PyObject *self, PyObject *args) 515 | { 516 | uint32_t addr; 517 | if (!PyArg_ParseTuple(args, "I", &addr)) { 518 | return NULL; 519 | } 520 | 521 | _mem_write_start(addr); 522 | 523 | return Py_BuildValue(""); 524 | } 525 | 526 | static PyObject *supervisor_h8_mem_write_end(PyObject *self, PyObject *args) 527 | { 528 | _mem_write_end(); 529 | 530 | return Py_BuildValue(""); 531 | } 532 | 533 | static PyObject *supervisor_h8_mem_write_fast(PyObject *self, PyObject *args) 534 | { 535 | uint32_t addr; 536 | uint16_t val; 537 | 538 | if (!PyArg_ParseTuple(args, "IH", &addr, &val)) { 539 | return NULL; 540 | } 541 | 542 | _mem_write_fast(addr, val); 543 | 544 | return Py_BuildValue(""); 545 | } 546 | 547 | static PyObject *supervisor_h8_mem_write_buffer(PyObject *self, PyObject *args) 548 | { 549 | uint32_t addr; 550 | Py_buffer pybuf; 551 | unsigned char bytes[128]; 552 | int res; 553 | 554 | if (!PyArg_ParseTuple(args, "Iy*", &addr, &pybuf)) { 555 | return NULL; 556 | } 557 | 558 | res = PyBuffer_ToContiguous(bytes, &pybuf, 128, 'C'); 559 | if (res!=0) { 560 | return NULL; 561 | } 562 | 563 | for (int i=0; i<64; i++) { 564 | unsigned short w; 565 | w = (bytes[i*2]<<8) | bytes[i*2+1]; 566 | _mem_write_fast(addr,w); 567 | addr += 2; 568 | } 569 | 570 | PyBuffer_Release(&pybuf); 571 | 572 | return Py_BuildValue(""); 573 | } 574 | 575 | static PyObject *supervisor_h8_mem_write(PyObject *self, PyObject *args) 576 | { 577 | uint32_t addr; 578 | uint16_t val; 579 | 580 | if (!PyArg_ParseTuple(args, "IH", &addr, &val)) { 581 | return NULL; 582 | } 583 | 584 | _mem_write(addr, val); 585 | 586 | return Py_BuildValue(""); 587 | } 588 | 589 | static PyMethodDef supervisor_h8_methods[] = { 590 | {"init", supervisor_h8_init, METH_VARARGS, "Initialize"}, 591 | {"release_bus", supervisor_h8_release_bus, METH_VARARGS, "Release bus"}, 592 | {"take_bus", supervisor_h8_take_bus, METH_VARARGS, "Take the bus"}, 593 | {"is_taken", supervisor_h8_is_taken, METH_VARARGS, "Returns true if bus is taken"}, 594 | {"mem_read_start", supervisor_h8_mem_read_start, METH_VARARGS, "Start multiple memory reads"}, 595 | {"mem_read_end", supervisor_h8_mem_read_end, METH_VARARGS, "End multiple memory reads"}, 596 | {"mem_read_fast", supervisor_h8_mem_read_fast, METH_VARARGS, "Read one memory"}, 597 | {"mem_read", supervisor_h8_mem_read, METH_VARARGS, "start, read, end"}, 598 | {"mem_write_start", supervisor_h8_mem_write_start, METH_VARARGS, "Start multiple memory writes"}, 599 | {"mem_write_end", supervisor_h8_mem_write_end, METH_VARARGS, "End multiple memory writes"}, 600 | {"mem_write_fast", supervisor_h8_mem_write_fast, METH_VARARGS, "Write one memory"}, 601 | {"mem_write_buffer", supervisor_h8_mem_write_buffer, METH_VARARGS, "Write one memory"}, 602 | {"mem_write", supervisor_h8_mem_write, METH_VARARGS, "start, write, end"}, 603 | {"mem_snoop", supervisor_h8_mem_snoop, METH_VARARGS, "snoop the data bus"}, 604 | {"wait_signal", supervisor_h8_wait_signal, METH_VARARGS, "wait until woke"}, 605 | {NULL, NULL, 0, NULL} 606 | }; 607 | 608 | static struct PyModuleDef supervisor_h8_module = 609 | { 610 | PyModuleDef_HEAD_INIT, 611 | "supervisor_h8_ext", /* name of module */ 612 | "", /* module documentation, may be NULL */ 613 | -1, /* size of per-interpreter state of the module, or -1 if the module keeps state in global variables. */ 614 | supervisor_h8_methods 615 | }; 616 | 617 | PyMODINIT_FUNC PyInit_supervisor_h8_ext(void) 618 | { 619 | return PyModule_Create(&supervisor_h8_module); 620 | } 621 | -------------------------------------------------------------------------------- /smbpi/tlc59116.py: -------------------------------------------------------------------------------- 1 | """ 2 | TLC59116 Driver 3 | Scott Baker, http://www.smbaker.com/ 4 | 5 | This program includes a main() function and can be run from the command 6 | line, for example: 7 | python tlc59116.py led 0 pwm 128 8 | 9 | It can also be included as a library. Just import the module, and create 10 | a TLC59116 object. See the main() function for an example how that's 11 | done. 12 | """ 13 | 14 | from __future__ import print_function 15 | import sys 16 | import time 17 | 18 | REG_MODE1 = 0 19 | REG_MODE2 = 1 20 | REG_GRPPWM = 0x12 21 | REG_GRPFREQ = 0x13 22 | REG_LEDOUT0 = 0x14 23 | 24 | MODE1_OSC_OFF = 16 25 | MODE1_ALL_CALL = 1 26 | 27 | MODE2_DMBLNK = 0x20 28 | 29 | STATE_OFF = 0 30 | STATE_ON = 1 31 | STATE_PWM = 2 32 | STATE_GRP = 3 33 | 34 | class TLC59116(object): 35 | def __init__(self, bus, addr): 36 | self.addr = addr 37 | self.bus = bus 38 | self.mode1 = self.read_reg(REG_MODE1) #default: MODE1_OSC_OFF | MODE1_ALL_CALL 39 | self.mode2 = self.read_reg(REG_MODE2) #detault: 0 40 | 41 | def read_reg(self, reg): 42 | return self.bus.read_byte_data(self.addr, reg) 43 | 44 | def write_reg(self, reg, bits): 45 | self.bus.write_byte_data(self.addr, reg, bits) 46 | 47 | def set_oscillator(self, enable): 48 | if enable: 49 | self.mode1 = self.mode1 & (~MODE1_OSC_OFF) 50 | else: 51 | self.mode1 = self.mode1 | MODE1_OSC_OFF 52 | self.write_reg(REG_MODE1, self.mode1) 53 | 54 | def set_led_state(self, led, state): 55 | reg = (led / 4) + REG_LEDOUT0 56 | shift = (led % 4) * 2 57 | v = self.read_reg(reg) 58 | v = v & (~(3 << shift)) | (state << shift) 59 | # print("write mode reg %x %x" % (reg, v)) 60 | self.write_reg(reg, v) 61 | 62 | def set_led_pwm(self, led, brightness): 63 | self.write_reg(led+2, brightness) 64 | 65 | def set_blink(self, blink): 66 | if blink: 67 | self.mode2 = self.mode2 | MODE2_DMBLNK 68 | else: 69 | self.mode2 = self.mode2 & (~MODE2_DMBLNK) 70 | self.write_reg(REG_MODE2, self.mode2) 71 | 72 | def set_grpfreq(self, freq): 73 | self.write_reg(REG_GRPFREQ, freq) 74 | 75 | def set_grppwm(self, freq): 76 | self.write_reg(REG_GRPPWM, freq) 77 | 78 | def help(): 79 | print("tcl59116.py ") 80 | print() 81 | print("commands:") 82 | print(" led [off|on|pwm|grp] ") 83 | print(" blink ") 84 | print(" noblink") 85 | print(" grppwm ") 86 | print(" cylon") 87 | print(" oscoff") 88 | 89 | def main(): 90 | import smbus 91 | 92 | bus = smbus.SMBus(1) 93 | leds = TLC59116(bus, 0x60) 94 | 95 | if (len(sys.argv) <= 1): 96 | help() 97 | sys.exit(0) 98 | 99 | if (sys.argv[1] == "led"): 100 | if (sys.argv[2]=="all"): 101 | led = "all" 102 | else: 103 | led = int(sys.argv[2]) 104 | 105 | mode = sys.argv[3] 106 | 107 | if (mode=="off"): 108 | mode = STATE_OFF 109 | elif (mode=="on"): 110 | mode = STATE_ON 111 | elif (mode=="pwm"): 112 | mode = STATE_PWM 113 | elif (mode=="grp"): 114 | mode = STATE_GRP 115 | else: 116 | raise Exception("unknown mode") 117 | 118 | if (mode in [STATE_PWM, STATE_GRP]): 119 | pwm = int(sys.argv[4]) 120 | else: 121 | pwm = None 122 | 123 | leds.set_oscillator(True) 124 | 125 | if (led=="all"): 126 | for i in range(0,15): 127 | leds.set_led_state(i, mode) 128 | if pwm: 129 | leds.set_led_pwm(i, pwm) 130 | else: 131 | leds.set_led_state(led, mode) 132 | if pwm: 133 | leds.set_led_pwm(led, pwm) 134 | elif (sys.argv[1] == "blink"): 135 | leds.set_blink(True) 136 | leds.set_grppwm(128) 137 | leds.set_grpfreq(int(sys.argv[2])) 138 | elif (sys.argv[1] == "noblink"): 139 | leds.set_blink(False) 140 | leds.set_grppwm(255) 141 | elif (sys.argv[1] == "grppwm"): 142 | leds.set_blink(False) 143 | leds.set_grppwm(int(sys.argv[2])) 144 | elif (sys.argv[1] == "cylon"): 145 | leds.set_oscillator(True) 146 | for i in range(0,16): 147 | leds.set_led_state(i, STATE_GRP) 148 | while True: 149 | for i in range(0,16): 150 | leds.set_led_pwm(i, 255) 151 | time.sleep(0.1) 152 | leds.set_led_pwm(i, 0) 153 | for i in range(1, 15): 154 | leds.set_led_pwm(15-i, 255) 155 | time.sleep(0.1) 156 | leds.set_led_pwm(15-i, 0) 157 | elif (sys.argv[1] == "noosc"): 158 | leds.set_oscillator(False) 159 | else: 160 | help() 161 | 162 | 163 | if __name__ == "__main__": 164 | main() 165 | -------------------------------------------------------------------------------- /smbpi/ups.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | import crc8 3 | import time 4 | import traceback 5 | 6 | from ic2_with_crc import I2CWithCrc, I2CError, ReceiveCRCError, ReceiveSizeError, ReceiveUninitializedError, CRCError, NoMoreRetriesError 7 | 8 | class UPS(I2CWithCrc): 9 | def __init__(self, bus=None, addr=0x4, pi=None, sdaPin=2, sclPin=3): 10 | I2CWithCrc.__init__(self, bus, addr, pi, sdaPin, sclPin) 11 | 12 | try: 13 | self.r1 = self.readR1() 14 | self.r2 = self.readR2() 15 | print("XXX", self.r1, self.r2) 16 | except: 17 | print("WARNING: Failed to read R1 and R2") 18 | traceback.print_exc() 19 | 20 | self.states = {0: "DISABLED", 21 | 1: "WAIT_OFF", 22 | 2: "WAIT_ON", 23 | 3: "POWERUP", 24 | 4: "RUNNING", 25 | 5: "FAIL_SHUTDOWN", 26 | 6: "FAIL_SHUTDOWN_DELAY", 27 | 7: "CYCLE_DELAY"} 28 | 29 | def vConv(self, x): 30 | return float(x)*2.56*(self.r1+self.r2)/self.r2/256.0 31 | 32 | def setCountdown(self, ms): 33 | if (ms is not None) and (ms>0) and (ms<100): 34 | # the minimum is 100ms 35 | ms=100 36 | return self.writereg(9, ms/100) 37 | 38 | def readR1(self): 39 | return self.readreg(16) 40 | 41 | def readR2(self): 42 | return self.readreg(17) 43 | 44 | def readCountdown(self): 45 | return self.readreg(9) 46 | 47 | def readVIN(self): 48 | return self.vConv(self.readreg(3)) 49 | 50 | def readVUPS(self): 51 | return self.vConv(self.readreg(5)) 52 | 53 | def readOnThresh(self): 54 | return self.vConv(self.readreg(7)) 55 | 56 | def readOffThresh(self): 57 | return self.vConv(self.readreg(8)) 58 | 59 | def readPowerUpThresh(self): 60 | return self.vConv(self.readreg(14)) 61 | 62 | def readShutdownThresh(self): 63 | return self.vConv(self.readreg(15)) 64 | 65 | def readMosfet(self): 66 | return self.readreg(6) 67 | 68 | def setMosfet(self, v): 69 | self.writeReg(6, v) 70 | 71 | def readState(self): 72 | return self.readreg(10) 73 | 74 | def readRunCounter(self): 75 | return self.readreg(13) 76 | 77 | def readStateStr(self): 78 | state = self.readState() 79 | return self.states.get(state, str(state)) 80 | 81 | def testWrite(self, x): 82 | self.writereg(0, x) 83 | 84 | def diags(ups): 85 | print(ups.readreg(0), ups.readreg(2), ups.readreg(4), ups.readreg(6), ups.readreg(7), ups.readreg(8)) 86 | print("onThresh=%0.2f, offThresh=%0.2f" % (ups.readOnThresh(), ups.readOffThresh())) 87 | 88 | lastVUPS = None 89 | lastVIN = None 90 | lastState = None 91 | lastCountdown = None 92 | lastErrorCount = 0 93 | count = 0 94 | while True: 95 | try: 96 | VUPS = ups.readVUPS() 97 | VIN = ups.readVIN() 98 | state = ups.readStateStr() 99 | runCounter = ups.readRunCounter() 100 | countdown = ups.readCountdown() 101 | except NoMoreRetriesError: 102 | continue 103 | 104 | try: 105 | ups.testWrite(count % 256) 106 | except NoMoreRetriesError: 107 | pass 108 | 109 | if (VUPS != lastVUPS) or (VIN != lastVIN) or (state != lastState) or (countdown != lastCountdown): 110 | print("%-20s, vin=%0.2f, vups=%0.2f rc=%d cd=%d" % (state, VIN, VUPS, runCounter, countdown)) 111 | lastVUPS = VUPS 112 | lastVIN = VIN 113 | state = lastState 114 | 115 | errorCount = ups.errorCount 116 | if errorCount != lastErrorCount: 117 | print("ERR: success=%d, io=%d, crc=%d, rcv_crc=%d, rcv_size=%d, rcv_uninit=%d" % (ups.errorSuccess, ups.errorIO, ups.errorCRC, ups.errorReceiveCRC, ups.errorReceiveSize, ups.errorReceiveUninitialized)) 118 | lastErrorCount = errorCount 119 | 120 | time.sleep(0.01) 121 | 122 | def main(): 123 | #import smbus 124 | #bus = smbus.SMBus(1) 125 | #ups = UPS(bus=bus) 126 | 127 | import pigpio 128 | pi = pigpio.pi() 129 | ups = UPS(pi=pi) 130 | 131 | diags(ups) 132 | 133 | 134 | if __name__ == "__main__": 135 | main() 136 | -------------------------------------------------------------------------------- /smbpi/version.py: -------------------------------------------------------------------------------- 1 | __version__ = "1.0.1.dev0" 2 | -------------------------------------------------------------------------------- /smbpi/vfd.py: -------------------------------------------------------------------------------- 1 | """ 2 | VFD Driver 3 | Scott Baker, http://www.smbaker.com/ 4 | 5 | VFD Interface, using SPI mode 6 | """ 7 | 8 | from __future__ import print_function 9 | import spidev 10 | import sys 11 | import time 12 | 13 | class VFD: 14 | def __init__(self, spi_num, spi_ce): 15 | self.spi = spidev.SpiDev() 16 | self.spi.open(spi_num, spi_ce) 17 | # on raspberry pi zero w buster, speed was set to 1250000 and didn't work 18 | self.spi.max_speed_hz = 500000 19 | self.setDisplay(True, False, False) 20 | self.setDirection(True, False) 21 | 22 | def write(self, data, rs): 23 | if rs: 24 | self.spi.writebytes([0xFA, data]) 25 | else: 26 | self.spi.writebytes([0xF8, data]) 27 | time.sleep(0.00001) 28 | 29 | def writeCmd(self, c): 30 | self.write(c, False) 31 | 32 | def writeStr(self, s): 33 | for c in s: 34 | self.write(ord(c), True) 35 | 36 | def cls(self): 37 | self.writeCmd(0x01) 38 | time.sleep(0.005) 39 | 40 | def setPosition(self, x, y): 41 | self.writeCmd(0x80 | (0x40*y + x)) 42 | time.sleep(0.005) 43 | 44 | def setDirection(self, leftToRight, autoScroll): 45 | cmd = 4 46 | if leftToRight: 47 | cmd = cmd | 2 48 | if autoScroll: 49 | cmd = cmd | 1 50 | 51 | self.writeCmd(cmd) 52 | 53 | def setDisplay(self, display, cursor, blink): 54 | cmd = 8 55 | if display: 56 | cmd = cmd | 4 57 | if cursor: 58 | cmd = cmd | 2 59 | if blink: 60 | cmd = cmd | 1 61 | 62 | self.writeCmd(cmd) 63 | 64 | def usage(): 65 | print("vfd.py [args...]") 66 | print(" write ") 67 | print(" goto ") 68 | print(" cls") 69 | sys.exit(-1) 70 | 71 | def main(): 72 | vfd = VFD(0,0) 73 | 74 | if len(sys.argv)<2: 75 | usage() 76 | 77 | cmd=sys.argv[1] 78 | if (cmd=="write"): 79 | if len(sys.argv)!=3: 80 | usage() 81 | vfd.writeStr(sys.argv[2]) 82 | elif (cmd=="goto"): 83 | if len(sys.argv)!=4: 84 | usage() 85 | vfd.setPosition(int(sys.argv[2]), int(sys.argv[3])) 86 | elif (cmd=="cls"): 87 | vfd.cls() 88 | else: 89 | usage() 90 | 91 | if __name__ == "__main__": 92 | main() 93 | -------------------------------------------------------------------------------- /smbpi/vfdcontrol.py: -------------------------------------------------------------------------------- 1 | """ 2 | vfdcontrol.py 3 | 4 | Driver for Scott's VFD + Encoder pcboard 5 | 6 | http://www.smbaker.com/ 7 | 8 | NOTE: To prevent dropping encoder steps, add the following to /boot/config.txt 9 | dtparam=i2c1=on 10 | dtparam=i2c1_baudrate=400000 11 | """ 12 | 13 | from __future__ import print_function 14 | import math 15 | import threading 16 | import time 17 | from ioexpand import MCP23017, IOCON_MIRROR 18 | import RPi.GPIO as GPIO 19 | 20 | BIT_ENC_A = 0x01 21 | BIT_ENC_B = 0x02 22 | BIT_ENC_SW = 0x04 23 | BIT_ENC_BL = 0x08 24 | BIT_ENC_GR = 0x10 25 | BIT_ENC_RD = 0x20 26 | BIT_BUTTON1 = 0x40 27 | BIT_BUTTON2 = 0x80 28 | 29 | BIT_RS = 0x01 30 | BIT_E = 0x02 31 | BIT_DB4 = 0x04 32 | BIT_DB5 = 0x08 33 | BIT_DB6 = 0x10 34 | BIT_DB7 = 0x20 35 | BIT_WR = 0x40 36 | BIT_BUTTON3 = 0x80 37 | 38 | COLOR_NONE = 0 39 | COLOR_RED = BIT_ENC_RD 40 | COLOR_GREEN = BIT_ENC_GR 41 | COLOR_BLUE = BIT_ENC_BL 42 | COLOR_WHITE = BIT_ENC_RD | BIT_ENC_GR | BIT_ENC_BL 43 | COLOR_YELLOW = BIT_ENC_RD | BIT_ENC_GR 44 | COLOR_CYAN = BIT_ENC_BL | BIT_ENC_GR 45 | COLOR_MAGENTA = BIT_ENC_BL | BIT_ENC_RD 46 | 47 | # amount of time to hold E high or low. Seems like 0 works just fine, the display can be clocked as fast as we can 48 | # clock it. 49 | E_TICK = 0 # 0.00001 50 | 51 | class VFDController(object): 52 | 53 | def __init__(self, io, four_line=False): 54 | self.io = io 55 | 56 | self.last_a = 0 57 | self.last_b = 0 58 | 59 | self.lock = threading.Lock() 60 | 61 | self.io.set_iodir(0, BIT_ENC_A | BIT_ENC_B | BIT_ENC_SW | BIT_BUTTON1 | BIT_BUTTON2) 62 | self.io.set_iodir(1, BIT_BUTTON3) 63 | 64 | self.io.set_pullup(0, BIT_ENC_A | BIT_ENC_B | BIT_ENC_SW | BIT_BUTTON1 | BIT_BUTTON2) 65 | self.io.set_pullup(1, BIT_BUTTON3) 66 | 67 | self.io.set_gpio(1,0) 68 | 69 | # encoder init 70 | self.four_line = four_line 71 | self.int_pin = None 72 | self.poll_input(forced=True) 73 | self.last_delta = 0 74 | self.r_seq = self.rotation_sequence() 75 | self.steps_per_cycle = 4 # 4 steps between detents 76 | self.remainder = 0 77 | 78 | self.reset() 79 | 80 | self.poller = InputPoller(self) 81 | self.poller.start() 82 | 83 | def writeNibble(self, data, rs=0): 84 | self.io.set_gpio(1, rs | (data << 2) | BIT_E) # raise E to write 85 | if (E_TICK): time.sleep(E_TICK) 86 | self.io.set_gpio(1, rs | (data << 2)) # clear E 87 | if (E_TICK): time.sleep(E_TICK) 88 | 89 | def readNibble(self, rs=0): 90 | self.io.set_gpio(1, rs | BIT_WR | BIT_E) 91 | if (E_TICK): time.sleep(E_TICK) 92 | v = (self.io.get_gpio(1) >> 2) & 0x0F 93 | self.io.set_gpio(1, rs | BIT_WR) 94 | if (E_TICK): time.sleep(E_TICK) 95 | return v 96 | 97 | def waitNotBusy(self): 98 | with self.lock: 99 | self.io.set_iodir(1, BIT_DB4 | BIT_DB5 | BIT_DB6 | BIT_DB7 | BIT_BUTTON3) 100 | self.io.set_gpio(1, BIT_WR) 101 | while True: 102 | v = self.readNibble(rs=0) << 4 103 | v = v + self.readNibble(rs=0) 104 | if (v & 0x80) == 0: 105 | break 106 | self.io.set_gpio(1, 0) 107 | self.io.set_iodir(1, BIT_BUTTON3) 108 | 109 | 110 | def writeCmd(self, c): 111 | with self.lock: 112 | self.writeNibble(c>>4, rs=0) 113 | self.writeNibble(c&0x0F, rs=0) 114 | self.waitNotBusy() 115 | 116 | def writeData(self, c): 117 | with self.lock: 118 | self.writeNibble(c>>4, rs=1) 119 | self.writeNibble(c&0x0F, rs=1) 120 | #self.waitNotBusy() 121 | 122 | def writeStr(self, s): 123 | for c in s: 124 | self.writeData(ord(c)) 125 | 126 | def reset(self): 127 | self.writeNibble(0x3, rs=0) 128 | time.sleep(0.02) 129 | self.writeNibble(0x3, rs=0) 130 | time.sleep(0.01) 131 | self.writeNibble(0x3, rs=0) 132 | time.sleep(0.001) 133 | 134 | self.writeNibble(0x2, rs=0) 135 | self.waitNotBusy() 136 | self.writeCmd(0x28) # DL, 4-bit / 2-line 137 | self.setDisplay(display=True, cursor=True, blink=False) 138 | 139 | def cls(self): 140 | self.writeCmd(0x01) 141 | time.sleep(0.005) 142 | 143 | def setPosition(self, x, y): 144 | if self.four_line: 145 | self.writeCmd(0x80 | (0x20*y + x)) 146 | else: 147 | self.writeCmd(0x80 | (0x40*y + x)) 148 | 149 | time.sleep(0.005) 150 | 151 | def setDirection(self, leftToRight, autoScroll): 152 | cmd = 4 153 | if leftToRight: 154 | cmd = cmd | 2 155 | if autoScroll: 156 | cmd = cmd | 1 157 | 158 | self.writeCmd(cmd) 159 | 160 | def setDisplay(self, display, cursor, blink): 161 | cmd = 8 162 | if display: 163 | cmd = cmd | 4 164 | if cursor: 165 | cmd = cmd | 2 166 | if blink: 167 | cmd = cmd | 1 168 | 169 | self.writeCmd(cmd) 170 | 171 | self.last_display = display 172 | self.last_cursor = cursor 173 | self.last_blink = blink 174 | 175 | def setDisplayCached(self, display, cursor, blink): 176 | if (self.last_display != display) or \ 177 | (self.last_cursor != cursor) or \ 178 | (self.last_blink != blink): 179 | self.setDisplay(display, cursor, blink) 180 | 181 | # ----------------------------------------------------------------------------------------------------------------- 182 | # encoder stuff 183 | # ----------------------------------------------------------------------------------------------------------------- 184 | 185 | def set_color(self, color): 186 | color = ~color & (BIT_ENC_RD | BIT_ENC_GR | BIT_ENC_BL) 187 | with self.lock: 188 | self.io.set_gpio(0, color) 189 | 190 | def poll_input(self, forced=False): 191 | # If interrupts are enabled and there is no interrupt, return 192 | if (not forced) and (self.int_pin): 193 | if GPIO.input(self.int_pin) == 1: 194 | return 195 | 196 | with self.lock: 197 | bits = self.io.get_gpio(0) 198 | bits1 = self.io.get_gpio(1) 199 | 200 | self.a_state = (bits & BIT_ENC_A) != 0 201 | self.b_state = (bits & BIT_ENC_B) != 0 202 | self.button_enc_state = (bits & BIT_ENC_SW) != 0 203 | self.button1_state = (bits & BIT_BUTTON1) != 0 204 | self.button2_state = (bits & BIT_BUTTON2) != 0 205 | self.button3_state = (bits1 & BIT_BUTTON3) != 0 206 | 207 | if (self.last_a != self.a_state) or (self.last_b != self.b_state): 208 | #print("XXX A", self.a_state, "B", self.b_state) 209 | self.last_a = self.a_state 210 | self.last_b = self.b_state 211 | 212 | def enable_interrupts(self, pin): 213 | self.io.set_interrupt(0, BIT_ENC_A | BIT_ENC_B | BIT_ENC_SW | BIT_BUTTON1 | BIT_BUTTON2) 214 | self.io.set_intcon(0, 0) # all bits int-on-change 215 | self.io.set_interrupt(1, BIT_BUTTON3) 216 | self.io.set_intcon(1, 0) # all bits int-on-change 217 | self.io.set_config(IOCON_MIRROR) # trigger both int pins together 218 | 219 | GPIO.setup(pin, GPIO.IN) 220 | self.int_pin = pin 221 | 222 | 223 | def clear_interrupts(self): 224 | # reading both gpio will clear the interrupt 225 | self.io.get_gpio(0) 226 | self.io.get_gpio(1) 227 | 228 | def rotation_sequence(self): 229 | r_seq = (self.a_state ^ self.b_state) | self.b_state << 1 230 | return r_seq 231 | 232 | # Returns offset values of -2,-1,0,1,2 233 | def get_delta(self): 234 | delta = 0 235 | r_seq = self.rotation_sequence() 236 | if r_seq != self.r_seq: 237 | delta = (r_seq - self.r_seq) % 4 238 | if delta==3: 239 | delta = -1 240 | elif delta==2: 241 | delta = int(math.copysign(delta, self.last_delta)) # same direction as previous, 2 steps 242 | 243 | self.last_delta = delta 244 | self.r_seq = r_seq 245 | 246 | return delta 247 | 248 | def get_cycles(self): 249 | self.remainder += self.get_delta() 250 | cycles = self.remainder // self.steps_per_cycle 251 | self.remainder %= self.steps_per_cycle # remainder always remains positive 252 | return cycles 253 | 254 | def get_switchstate(self): 255 | # BasicEncoder doesn't have a switch 256 | return 0 257 | 258 | 259 | class Debouncer(object): 260 | # see http://www.embedded.com/electronics-blogs/break-points/4024981/My-favorite-software-debouncers 261 | # 262 | # I had some issues implementing the debouncer in this article, namely the step 263 | # debounced_state = debounced_state ^j 264 | # This just oscillated for me, so I computed result=debounced_state^j and then set debounced_state to j 265 | # instead. 266 | 267 | def __init__(self): 268 | self.index = 0 269 | self.max_checks = 10 270 | self.state = [] 271 | self.debounced_state = 0xFF 272 | for i in range(0, self.max_checks): 273 | self.state.append(0xFF) 274 | 275 | def debounce(self, in_state): 276 | # Computes new self.debounced_state, and returns a bitmask where a bit is true if that button went from 0 277 | # to 1. 278 | self.state[self.index] = in_state 279 | self.index += 1 280 | j = 0xFF 281 | for i in range(0, self.max_checks): 282 | j = j & self.state[i] 283 | res = (self.debounced_state ^ j) & j 284 | self.debounced_state = j 285 | if (self.index >= self.max_checks): 286 | self.index = 0 287 | return res 288 | 289 | def debounce_list(self, l): 290 | in_state = 0 291 | for x in l: 292 | in_state = in_state << 1 293 | if x: 294 | in_state = in_state | 1 295 | 296 | debounced_state = self.debounce(in_state) 297 | 298 | debounce_l = [] 299 | for x in l: 300 | if (debounced_state & 1) != 0: 301 | debounce_l.append(True) 302 | else: 303 | debounce_l.append(False) 304 | debounced_state = debounced_state >> 1 305 | 306 | debounce_l.reverse() 307 | return debounce_l 308 | 309 | class InputPoller(threading.Thread): 310 | def __init__(self, encoder): 311 | threading.Thread.__init__(self) 312 | self.lock = threading.Lock() 313 | self.stopping = False 314 | self.encoder = encoder 315 | self.daemon = True 316 | self.delta = 0 317 | self.delay = 0.001 318 | self.debouncer = Debouncer() 319 | 320 | self.button1_event = False 321 | self.button2_event = False 322 | self.button3_event = False 323 | self.button_enc_event = False 324 | 325 | def run(self): 326 | self.lastSwitchState = self.encoder.get_switchstate() 327 | while not self.stopping: 328 | self.encoder.poll_input() 329 | delta = self.encoder.get_cycles() 330 | with self.lock: 331 | self.delta += delta 332 | 333 | [button1_db, button2_db, button3_db, button_enc_db] = \ 334 | self.debouncer.debounce_list([self.encoder.button1_state,self.encoder.button2_state, 335 | self.encoder.button3_state,self.encoder.button_enc_state]) 336 | 337 | self.button1_event = self.button1_event or button1_db 338 | self.button2_event = self.button2_event or button2_db 339 | self.button3_event = self.button3_event or button3_db 340 | self.button_enc_event = self.button_enc_event or button_enc_db 341 | time.sleep(self.delay) 342 | 343 | # get_delta, get_upEvent, and get_downEvent return events that occurred on 344 | # the encoder. As a side effect, the corresponding event will be reset. 345 | 346 | def get_delta(self): 347 | with self.lock: 348 | delta = self.delta 349 | self.delta = 0 350 | return delta 351 | 352 | def get_button1_event(self): 353 | with self.lock: 354 | res = self.button1_event 355 | self.button1_event = False 356 | return res 357 | 358 | def get_button2_event(self): 359 | with self.lock: 360 | res = self.button2_event 361 | self.button2_event = False 362 | return res 363 | 364 | def get_button3_event(self): 365 | with self.lock: 366 | res = self.button3_event 367 | self.button3_event = False 368 | return res 369 | 370 | def get_button_enc_event(self): 371 | with self.lock: 372 | res = self.button_enc_event 373 | self.button_enc_event = False 374 | return res 375 | 376 | def set_color(self, color, v): 377 | self.encoder.set_color(color, v) 378 | 379 | """ 380 | BufferedDisplay 381 | 382 | The start of a buffered display object, to only send the diff to the VFD. Needs work. 383 | """ 384 | class BufferedDisplay(object): 385 | def __init__(self, display): 386 | self.display = display 387 | 388 | self.width = 16 389 | self.height = 2 390 | 391 | self.buf_lines = [" " * self.width, " " * self.width] 392 | self.buf_last = [" " * self.width, " " * self.width] 393 | 394 | def bufSetPosition(self, x, y): 395 | buf_x = x 396 | buf_y = y 397 | 398 | def scroll(self): 399 | for i in range(0, self.height - 1): 400 | self.buf_lines[i] = self.buf_lines[i + 1] 401 | 402 | def bufWrite(self, str): 403 | for ch in str: 404 | self.buf_lines[self.buf_y][self.buf_x] = ch 405 | self.buf_x = self.buf_x + 1 406 | if (self.buf_x >= self.width): 407 | self.buf_x = 0 408 | self.buf_y = self.buf_y + 1 409 | if (self.buf_y >= self.height): 410 | self.scroll() 411 | self.buf_y = self.height-1 412 | self.buf_lines[self.buf_y] = " " * self.width 413 | 414 | def bufUpdate(self): 415 | pass 416 | # implement this... 417 | 418 | def trimpad(x, l): 419 | if len(x) < l: 420 | x = x + " " * (l-len(x)) 421 | return x[:l] 422 | 423 | """ 424 | main: A simple datetime demo. 425 | """ 426 | 427 | def main(): 428 | import smbus 429 | from datetime import datetime 430 | 431 | bus = smbus.SMBus(1) 432 | display = VFDController(MCP23017(bus, 0x20), four_line=False) 433 | 434 | #GPIO.setmode(GPIO.BCM) 435 | #display.enable_interrupts(26) 436 | 437 | display.setDisplay(True, False, False) 438 | display.cls() 439 | 440 | while True: 441 | (dates, times) = str(datetime.now()).split(" ") 442 | display.setPosition(0,0) 443 | display.writeStr(trimpad(dates,16)) 444 | display.setPosition(0,1) 445 | display.writeStr(trimpad(times, 16)) 446 | 447 | if display.poller.get_button1_event(): 448 | print("button1") 449 | 450 | if display.poller.get_button2_event(): 451 | print("button2") 452 | 453 | if display.poller.get_button3_event(): 454 | print("button3") 455 | 456 | delta = display.poller.get_delta() 457 | if delta!=0: 458 | print(delta) 459 | 460 | time.sleep(0.01) 461 | 462 | 463 | if __name__ == "__main__": 464 | main() -------------------------------------------------------------------------------- /smbpi/wd37c65_direct_ext.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "micros.h" 7 | 8 | #define RESULT_OKAY 0 9 | #define RESULT_TIMEOUT_EXEC 0x14 10 | #define RESULT_OVER_DRAIN 0x16 11 | #define RESULT_OVER_CMDRES 0x17 12 | #define RESULT_TIMEOUT_READRES 0x18 13 | #define RESULT_READ_ERROR 0x19 14 | #define RESULT_WRITE_ERROR 0x20 15 | #define RESULT_SHORT 0x21 16 | #define RESULT_LONG 0x22 17 | #define RESULT_INPROCESS 0x23 18 | 19 | #define REG_MSR 0 20 | #define REG_DATA 1 21 | 22 | #define CS_FDC 0 23 | #define CS_DOR 1 24 | #define CS_DCR 2 25 | 26 | #define WD_WR 6 27 | #define WD_RD 5 28 | #define WD_A0 12 29 | #define WD_CS 13 30 | #define WD_DOR 19 31 | #define WD_CCR 16 32 | #define WD_DACK 26 33 | #define WD_TC 20 34 | #define WD_RESET 21 35 | #define WD_DC 7 36 | 37 | const int WD_DATAPINS[] = { 17, 18, 27, 22, 23, 24, 25, 4 }; 38 | const int WD_DATAPINS_REVERSED[] = { 4, 25, 24, 23, 22, 27, 18, 17 }; 39 | 40 | int EnableMyMicros = FALSE; 41 | 42 | #define myDigitalWrite(x,y) digitalWrite(x,y) 43 | #define myDigitalRead(x) digitalRead(x) 44 | #define myPinModeInput(x) pinMode(x,INPUT) 45 | #define myPinModeOutput(x) pinMode(x,OUTPUT) 46 | 47 | void myDelayMicroseconds(int x) 48 | { 49 | if (EnableMyMicros) { 50 | delayMicros(x); 51 | } else { 52 | // wiringpi 53 | delayMicroseconds(x); 54 | } 55 | } 56 | 57 | void wd_config_input(void) 58 | { 59 | int i; 60 | for (i=0; i<8; i++) { 61 | myPinModeInput(WD_DATAPINS[i]); 62 | } 63 | } 64 | 65 | void wd_config_output(void) 66 | { 67 | int i; 68 | for (i=0; i<8; i++) { 69 | myPinModeOutput(WD_DATAPINS[i]); 70 | } 71 | } 72 | 73 | void wd_set_addr(unsigned int addr) 74 | { 75 | myDigitalWrite(WD_A0, addr); 76 | } 77 | 78 | void wd_set_rd(unsigned int value) 79 | { 80 | myDigitalWrite(WD_RD,value); 81 | } 82 | 83 | void wd_set_wr(unsigned int value) 84 | { 85 | myDigitalWrite(WD_WR,value); 86 | } 87 | 88 | void wd_set_cs(unsigned int cs, unsigned int value) 89 | { 90 | switch(cs) { 91 | case CS_FDC: 92 | myDigitalWrite(WD_CS, value); 93 | break; 94 | 95 | case CS_DOR: 96 | myDigitalWrite(WD_DOR, value); 97 | break; 98 | 99 | case CS_DCR: 100 | myDigitalWrite(WD_CCR, value); 101 | break; 102 | } 103 | } 104 | 105 | void wd_set_data(unsigned int data) 106 | { 107 | int i; 108 | for (i=0; i<8; i++) { 109 | myDigitalWrite(WD_DATAPINS[i], data & 0x01); 110 | data = data >> 1; 111 | } 112 | } 113 | 114 | unsigned int wd_get_data(void) 115 | { 116 | int i; 117 | int data = 0; 118 | for (i=0; i<8; i++) { 119 | data = data << 1; 120 | data = data | myDigitalRead(WD_DATAPINS_REVERSED[i]); 121 | } 122 | return data; 123 | } 124 | 125 | unsigned int wd_get_tc(void) 126 | { 127 | return myDigitalRead(WD_TC); // XXX this is an output not an input... 128 | } 129 | 130 | unsigned int wd_get_dc(void) 131 | { 132 | return myDigitalRead(WD_DC); 133 | } 134 | 135 | void wd_pulse_cs_wr(unsigned int cs) 136 | { 137 | unsigned int i; 138 | 139 | wd_set_cs(cs, 0); 140 | wd_set_wr(0); 141 | 142 | // it's all fun and games until somebody loses an edge... 143 | for (i=0; i<1000; i++) { 144 | asm("nop"); 145 | } 146 | 147 | wd_set_wr(1); 148 | wd_set_cs(cs, 1); 149 | } 150 | 151 | unsigned int wd_pulse_cs_rd(unsigned int cs) 152 | { 153 | unsigned int i, v; 154 | 155 | wd_set_cs(cs, 0); 156 | wd_set_rd(0); 157 | 158 | // it's all fun and games until somebody loses an edge... 159 | for (i=0; i<1000; i++) { 160 | asm("nop"); 161 | } 162 | 163 | v = wd_get_data(); 164 | 165 | wd_set_rd(1); 166 | wd_set_cs(cs, 1); 167 | 168 | return v; 169 | } 170 | 171 | unsigned int wd_read_data(void) 172 | { 173 | unsigned int data; 174 | 175 | wd_config_input(); 176 | wd_set_addr(REG_DATA); 177 | data = wd_pulse_cs_rd(CS_FDC); 178 | 179 | return data; 180 | } 181 | 182 | unsigned int wd_read_msr(void) 183 | { 184 | unsigned int msr; 185 | 186 | wd_config_input(); 187 | wd_set_addr(REG_MSR); 188 | msr = wd_pulse_cs_rd(CS_FDC); 189 | 190 | return msr; 191 | } 192 | 193 | unsigned int wd_write_data(unsigned int data) 194 | { 195 | //fprintf(stderr, "write data %02X\n", data); 196 | wd_config_output(); 197 | wd_set_addr(REG_DATA); 198 | wd_set_data(data); 199 | wd_pulse_cs_wr(CS_FDC); 200 | wd_config_input(); 201 | 202 | return data; 203 | } 204 | 205 | unsigned int wd_write_dor(unsigned int data) 206 | { 207 | //fprintf(stderr, "write dor %02X\n", data); 208 | wd_config_output(); 209 | wd_set_addr(0); 210 | wd_set_data(data); 211 | wd_pulse_cs_wr(CS_DOR); 212 | wd_config_input(); 213 | 214 | return data; 215 | } 216 | 217 | unsigned int wd_write_dcr(unsigned int data) 218 | { 219 | //fprintf(stderr, "write dcr %02X\n", data); 220 | wd_config_output(); 221 | wd_set_addr(0); 222 | wd_set_data(data); 223 | wd_pulse_cs_wr(CS_DCR); 224 | wd_config_input(); 225 | 226 | return data; 227 | } 228 | 229 | unsigned int wd_wait_msr(unsigned int mask, unsigned int val) 230 | { 231 | unsigned int msr; 232 | unsigned int timeout = 65535 * 3; 233 | 234 | wd_config_input(); 235 | while (TRUE) { 236 | // wiringPi 237 | myDelayMicroseconds(3); 238 | 239 | msr = wd_read_msr(); 240 | if ((msr & mask) == val) { 241 | return 0; 242 | } 243 | 244 | timeout--; 245 | if (timeout == 0) { 246 | fprintf(stderr, "waitmsr timedout with %02X\n", msr); 247 | return RESULT_TIMEOUT_EXEC; 248 | } 249 | } 250 | } 251 | 252 | unsigned int wd_drain(void) 253 | { 254 | unsigned int msr; 255 | unsigned int max = 1024; 256 | 257 | wd_config_input(); 258 | while (max > 0) { 259 | // wiringPi 260 | myDelayMicroseconds(10); 261 | 262 | msr = wd_read_msr(); 263 | if ((msr & 0xC0) != 0xC0) { 264 | // no data 265 | return 0; 266 | } 267 | 268 | // there is a byte 269 | wd_read_data(); 270 | max --; 271 | } 272 | 273 | // something weird must have happened 274 | return RESULT_OVER_DRAIN; 275 | } 276 | 277 | void wd_init(void) 278 | { 279 | unsigned int i; 280 | 281 | myDigitalWrite(WD_A0, 1); 282 | myDigitalWrite(WD_CS, 1); 283 | myDigitalWrite(WD_DOR, 1); 284 | myDigitalWrite(WD_CCR, 1); 285 | myDigitalWrite(WD_RD, 1); 286 | myDigitalWrite(WD_WR, 1); 287 | myDigitalWrite(WD_RESET, 0); // start with reset deasserted 288 | myDigitalWrite(WD_TC, 1); 289 | myDigitalWrite(WD_DACK, 1); 290 | myPinModeOutput(WD_A0); 291 | myPinModeOutput(WD_CS); 292 | myPinModeOutput(WD_DOR); 293 | myPinModeOutput(WD_CCR); 294 | myPinModeOutput(WD_RD); 295 | myPinModeOutput(WD_WR); 296 | myPinModeOutput(WD_RESET); 297 | myPinModeOutput(WD_TC); 298 | myPinModeOutput(WD_DACK); 299 | 300 | for (i=0; i<8; i++) { 301 | pullUpDnControl(WD_DATAPINS[i], PUD_DOWN); 302 | } 303 | 304 | myDelayMicroseconds(10); 305 | myDigitalWrite(WD_RESET, 1); // assert reset 306 | myDelayMicroseconds(100); 307 | myDigitalWrite(WD_RESET, 0); // deassert reset 308 | myDelayMicroseconds(1000); 309 | } 310 | 311 | void wd_reset(unsigned int dor) 312 | { 313 | int i; 314 | 315 | wd_write_dor(0); 316 | myDelayMicroseconds(17); 317 | wd_write_dor(dor); 318 | 319 | // 2.4ms delay 320 | for (i=0; i<240; i++) { 321 | // wiringPi 322 | myDelayMicroseconds(10); 323 | } 324 | } 325 | 326 | void wd_pulse_dack(void) 327 | { 328 | myDigitalWrite(WD_DACK, 0); 329 | myDelayMicroseconds(1); 330 | myDigitalWrite(WD_DACK, 1); 331 | } 332 | 333 | void short_delay(void) 334 | { 335 | // Just do nothing for a while. This is to allow the RAM some time to do it's work. 336 | // 337 | int j; 338 | 339 | for (j=0; j<1; j++) { 340 | asm("nop"); 341 | } 342 | } 343 | 344 | static PyObject *wd_direct_init(PyObject *self, PyObject *args) 345 | { 346 | if (!PyArg_ParseTuple(args, "")) { 347 | return NULL; 348 | } 349 | wd_init(); 350 | return Py_BuildValue(""); 351 | } 352 | 353 | static PyObject *wd_direct_reset(PyObject *self, PyObject *args) 354 | { 355 | unsigned int dor; 356 | if (!PyArg_ParseTuple(args, "i", &dor)) { 357 | return NULL; 358 | } 359 | wd_reset(dor); 360 | return Py_BuildValue(""); 361 | } 362 | 363 | static PyObject *wd_direct_pulse_dack(PyObject *self, PyObject *args) 364 | { 365 | if (!PyArg_ParseTuple(args, "")) { 366 | return NULL; 367 | } 368 | wd_pulse_dack(); 369 | return Py_BuildValue(""); 370 | } 371 | 372 | static PyObject *wd_direct_get_tc(PyObject *self, PyObject *args) 373 | { 374 | if (!PyArg_ParseTuple(args, "")) { 375 | return NULL; 376 | } 377 | return Py_BuildValue("i", wd_get_tc()); 378 | } 379 | 380 | static PyObject *wd_direct_get_dc(PyObject *self, PyObject *args) 381 | { 382 | if (!PyArg_ParseTuple(args, "")) { 383 | return NULL; 384 | } 385 | return Py_BuildValue("i", wd_get_dc()); 386 | } 387 | 388 | static PyObject *wd_direct_set_addr(PyObject *self, PyObject *args) 389 | { 390 | int addr; 391 | if (!PyArg_ParseTuple(args, "i", &addr)) { 392 | return NULL; 393 | } 394 | wd_set_addr(addr); 395 | return Py_BuildValue(""); 396 | } 397 | 398 | static PyObject *wd_direct_read_byte(PyObject *self, PyObject *args) 399 | { 400 | unsigned int cs, addr; 401 | unsigned int data; 402 | if (!PyArg_ParseTuple(args, "ii", &cs, &addr)) { 403 | return NULL; 404 | } 405 | data = wd_read_data(); 406 | return Py_BuildValue("i", data); 407 | } 408 | 409 | static PyObject *wd_direct_write_byte(PyObject *self, PyObject *args) 410 | { 411 | unsigned int cs, addr, data; 412 | if (!PyArg_ParseTuple(args, "iii", &cs, &addr, &data)) { 413 | return NULL; 414 | } 415 | wd_config_output(); 416 | wd_set_addr(addr); 417 | wd_set_data(data); 418 | wd_pulse_cs_wr(cs); 419 | wd_config_input(); 420 | return Py_BuildValue(""); 421 | } 422 | 423 | static PyObject *wd_direct_write_data(PyObject *self, PyObject *args) 424 | { 425 | unsigned int data; 426 | if (!PyArg_ParseTuple(args, "i", &data)) { 427 | return NULL; 428 | } 429 | wd_write_data(data); 430 | return Py_BuildValue(""); 431 | } 432 | 433 | static PyObject *wd_direct_write_dor(PyObject *self, PyObject *args) 434 | { 435 | unsigned int data; 436 | if (!PyArg_ParseTuple(args, "i", &data)) { 437 | return NULL; 438 | } 439 | wd_write_dor(data); 440 | return Py_BuildValue(""); 441 | } 442 | 443 | static PyObject *wd_direct_write_dcr(PyObject *self, PyObject *args) 444 | { 445 | unsigned int data; 446 | if (!PyArg_ParseTuple(args, "i", &data)) { 447 | return NULL; 448 | } 449 | wd_write_dcr(data); 450 | return Py_BuildValue(""); 451 | } 452 | 453 | static PyObject *wd_direct_read_block(PyObject *self, PyObject *args) 454 | { 455 | unsigned int count; 456 | char buf[1024]; 457 | int i; 458 | 459 | if (!PyArg_ParseTuple(args, "i", &count)) { 460 | return NULL; 461 | } 462 | 463 | if (count > 1024) { 464 | // throw exception? 465 | return NULL; 466 | } 467 | 468 | for (i=0; i0) { 504 | unsigned int msr; 505 | 506 | // wiringPi 507 | myDelayMicroseconds(10); 508 | 509 | msr = wd_read_msr(); 510 | //fprintf(stderr, "readRes msr %2X\n", msr); 511 | if ((msr & 0xF0) == 0xD0) { 512 | // RQM=1, DIO=1, BUSY=1 ... byte is ready to read 513 | buf[count] = wd_read_data(); 514 | count++; 515 | maxTime = 10000; 516 | } else if ((msr & 0xF0) == 0x80) { 517 | // RQM=1, DIO=0, BUSY=0 ... fdc is waiting for next command ... we are done 518 | return Py_BuildValue("is#", 0, buf, count); 519 | } else { 520 | maxTime--; 521 | } 522 | 523 | if (count>128) { 524 | return Py_BuildValue("is#", RESULT_OVER_CMDRES, buf, count); 525 | } 526 | } 527 | 528 | return Py_BuildValue("is#", RESULT_TIMEOUT_READRES, buf, count); 529 | } 530 | 531 | static PyObject *wd_direct_write_block(PyObject *self, PyObject *args) 532 | { 533 | const char *buf; 534 | unsigned int buf_len, count; 535 | unsigned int autoTerminate; 536 | int i; 537 | 538 | if (!PyArg_ParseTuple(args, "s#ii", &buf, &buf_len, &count, &autoTerminate)) { 539 | return NULL; 540 | } 541 | 542 | for (i=0; i