├── LICENSE.txt ├── README.md ├── bin ├── ad9850-test.py ├── hmc5883-test.py ├── load-test.py ├── mpu6050-test.py ├── nrf24-chat.py ├── nrf24-dump.py ├── nrf24-recv.py ├── nrf24-send.py ├── nrf24-test.py └── si4702-test.py ├── setup.py └── ucdev ├── __init__.py ├── ad9850.py ├── common.py ├── cy7c65211 ├── __init__.py ├── device.py └── header.py ├── hmc5883.py ├── mpu6050.py ├── nrf24.py ├── register.py └── si4702.py /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Taisuke Yamada 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | python-ucdev 2 | ============ 3 | 4 | Python library to access Cypress CY7C6521x (CY7C65211, CY7C65213, or CY7C65215) USB-Serial bridge (USB to UART/I2C/SPI/GPIO) chip. 5 | Also includes library to access various chips over I2C/SPI. Currently includes driver for 6 | 7 | - Nordic nRF24L01 wireless tranceiver (SPI) 8 | - InvenSense MPU-6050 3-axis accelerometer + 3-axis gyroscope (I2C) 9 | - Honeywell HMC5883L 3-axis magnetometer (I2C) 10 | - Si4702 FM radio receiver (I2C for now, additional SPI-mode ongoing) 11 | 12 | ## Usage (CY7C6521x) 13 | 14 | >>> from ucdev.cy7c65211 import CyUSBSerial, CyGPIO, CySPI 15 | >>> 16 | >>> # load DLL provided by Cypress 17 | >>> lib = CyUSBSerial(lib="cyusbserial") 18 | >>> 19 | >>> # use first device found 20 | >>> dev = lib.find().next() 21 | >>> 22 | >>> # access GPIO 23 | >>> gpio = CyGPIO(dev) 24 | >>> gpio.set(3, 1) 25 | >>> ret = gpio.get(3) 26 | >>> 27 | >>> # access each GPIO pin 28 | >>> pin = gpio.pin(3) 29 | >>> pin.set(1) 30 | >>> ret = pin.get() 31 | >>> 32 | >>> # access SPI 33 | >>> spi = CySPI(dev) 34 | >>> ret = spi.send("any-data-to-be-clocked-out") 35 | 36 | ## Usage (nRF24L01) 37 | 38 | >>> from ucdev.nrf24 import * 39 | >>> 40 | >>> tx = nRF24(CySPI(dev), CE=CyGPIO(dev).pin(0)) 41 | >>> tx.reset(MODE_SB|DIR_SEND) 42 | >>> tx.TX_ADDR = tx.RX_ADDR_P0 = 0xB3B4B5B6C2 43 | >>> tx.send("some-payload-len-of-max-32-bytes") 44 | >>> 45 | >>> print tx.FIFO_STATUS.TX_EMPTY 46 | >>> print tx.CONFIG 47 | 48 | ## Note 49 | This requires cyusbserial.dll (or libcyusbserial.so) library 50 | provided by Cypress. 51 | 52 | Current development focuses on GPIO and SPI features to 53 | use Nordic nRF24 wireless tranceiver chip. See sample scripts 54 | under bin/ folder for the detail. 55 | -------------------------------------------------------------------------------- /bin/ad9850-test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | To run and test this script, connection betwen CY7C65211 and 6 | AD9850 must be done as below: 7 | 8 | Cypress nRF24L01 9 | ---------------------- 10 | GPIO 0 <---> RESET 11 | SSEL <---> FU_UD 12 | MOSI <---> DATA 13 | SCLK <---> W_CLK 14 | 15 | """ 16 | 17 | import sys, os 18 | import time 19 | from IPython import embed 20 | 21 | from ucdev.cy7c65211 import * 22 | from ucdev.ad9850 import * 23 | 24 | #dll = "c:/app/Cypress/Cypress-USB-Serial/library/lib/cyusbserial.dll" 25 | dll = "cyusbserial" 26 | lib = CyUSBSerial(lib = dll) 27 | 28 | dev = lib.find().next() 29 | ctl = CyGPIO(dev) 30 | dds = AD9850(CySPI(dev), RESET=ctl.pin(0)) 31 | dds.reset() 32 | dds.send(FREQ=0xFFFF) 33 | 34 | embed() 35 | -------------------------------------------------------------------------------- /bin/hmc5883-test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | from ucdev.cy7c65211 import * 5 | from ucdev.hmc5883 import * 6 | from IPython import embed 7 | 8 | #dll = "c:/app/Cypress/Cypress-USB-Serial/library/lib/cyusbserial.dll" 9 | dll = "cyusbserial" 10 | lib = CyUSBSerial(lib = dll) 11 | dev = lib.find().next() 12 | ffi = lib.ffi 13 | 14 | i2c = CyI2C(dev) 15 | i2c.debug = 1 16 | 17 | hmc = HMC5883(i2c) 18 | 19 | embed() 20 | -------------------------------------------------------------------------------- /bin/load-test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | To run and test this script, plug CY7C65211 in to the USB port 6 | 7 | """ 8 | 9 | from __future__ import print_function 10 | 11 | import sys, os 12 | import time 13 | from threading import * 14 | from IPython import embed 15 | 16 | from ucdev.cy7c65211 import * 17 | from ucdev.nrf24 import * 18 | 19 | dll = "cyusbserial" 20 | lib = CyUSBSerial(lib = dll) 21 | 22 | for i in lib.find(vid=0x04b4, pid=0x0004): 23 | print(i) 24 | 25 | embed() 26 | -------------------------------------------------------------------------------- /bin/mpu6050-test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | from __future__ import print_function 5 | 6 | import os 7 | import sys 8 | import time 9 | 10 | from struct import pack, unpack 11 | from argparse import ArgumentParser 12 | 13 | from ucdev.cy7c65211 import CyUSBSerial, CyI2C 14 | from ucdev.mpu6050 import * 15 | 16 | from IPython import embed 17 | 18 | import logging 19 | log = logging.getLogger(__name__) 20 | 21 | def find_dev(ctx): 22 | #dll = "c:/app/Cypress/Cypress-USB-Serial/library/lib/cyusbserial.dll" 23 | dll = os.getenv("CYUSBSERIAL_DLL") or "cyusbserial" 24 | lib = CyUSBSerial(lib=dll) 25 | found = list(lib.find(vid=ctx.opt.vid, pid=ctx.opt.pid)) 26 | return found[ctx.opt.nth] 27 | 28 | def main(ctx): 29 | dev = find_dev(ctx) 30 | i2c = CyI2C(dev) 31 | mpu = MPU6050(i2c, address=ctx.opt.addr) 32 | 33 | print(mpu.WHO_AM_I) 34 | print(mpu.PWR_MGMT_1) 35 | 36 | # powerup 37 | mpu.PWR_MGMT_1.SLEEP = 0 38 | 39 | # dump accel data for some time 40 | for i in range(ctx.opt.time * 10): 41 | print(mpu.ACCEL_XOUT_H) 42 | time.sleep(0.1) 43 | print("=== Data dump done. Entering IPython ===") 44 | 45 | embed() 46 | 47 | def to_int(v): 48 | return int(v, 0) 49 | 50 | if __name__ == '__main__' and '__file__' in globals(): 51 | ap = ArgumentParser() 52 | ap.add_argument('-D', '--debug', default='INFO') 53 | ap.add_argument('-V', '--vid', type=to_int, default=0x04b4) 54 | ap.add_argument('-P', '--pid', type=to_int, default=0x0004) 55 | ap.add_argument('-A', '--addr', type=to_int, default=0x68) 56 | ap.add_argument('-n', '--nth', type=int, default=0) 57 | ap.add_argument('-t', '--time', type=int, default=1) 58 | ap.add_argument('args', nargs='*') 59 | 60 | # parse args 61 | ctx = lambda:0 62 | ctx.opt = ap.parse_args() 63 | 64 | # setup logger 65 | logging.basicConfig(level=eval('logging.' + ctx.opt.debug)) 66 | 67 | main(ctx) 68 | 69 | -------------------------------------------------------------------------------- /bin/nrf24-chat.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8-unix -*- 3 | 4 | usage = """ 5 | To run and test this script, connection betwen CY7C65211 and nRF24L01 6 | must be done as below: 7 | 8 | Cypress nRF24L01 9 | ---------------------- 10 | GPIO 0 <---> CE 11 | GPIO 1 <---> IRQ 12 | SSEL <---> CSN 13 | MISO <---> MO 14 | MOSI <---> MI 15 | SCLK <---> SCK 16 | 17 | """ 18 | 19 | import os 20 | import sys 21 | import time 22 | import select 23 | 24 | from argparse import ArgumentParser 25 | from ucdev.cy7c65211 import CyUSBSerial, CyGPIO, CySPI 26 | from ucdev.nrf24 import * 27 | 28 | import logging 29 | log = logging.getLogger(__name__) 30 | 31 | from IPython import embed 32 | 33 | def find_dev(ctx): 34 | dll = os.getenv("CYUSBSERIAL_DLL") or "cyusbserial" 35 | lib = CyUSBSerial(lib=dll) 36 | found = list(lib.find(vid=ctx.opt.vid, pid=ctx.opt.pid)) 37 | return found[ctx.opt.nth] 38 | 39 | def main(ctx): 40 | dev = find_dev(ctx) 41 | io = CyGPIO(dev) 42 | rf = nRF24(CySPI(dev), CE=io.pin(0), IRQ=io.pin(1)) 43 | 44 | # set basic mode 45 | mode = DIR_RECV 46 | mode |= eval("MODE_%s" % ctx.opt.mode.upper()) 47 | if ctx.opt.rate: 48 | mode |= eval("RATE_%s" % ctx.opt.rate.upper()) 49 | rf.reset(mode) 50 | 51 | # set address and channel 52 | for i,addr in enumerate(ctx.opt.rx): 53 | log.debug("RX_ADDR_P{i}: {addr:010X}".format(**locals())) 54 | setattr(rf, "RX_ADDR_P" + str(i), addr) 55 | log.debug("TX_ADDR: {ctx.opt.tx:010X}".format(**locals())) 56 | rf.TX_ADDR = ctx.opt.tx 57 | rf.RF_CH = ctx.opt.freq - 2400 58 | 59 | # TODO: Try doing multi-node chat with promisc mode 60 | #rf.SETUP_AW.AW = 1 61 | #rf.CONFIG.EN_CRC = 0 62 | 63 | # send/recv loop 64 | while True: 65 | fd = select.select([sys.stdin], [], [], 0.0) 66 | 67 | if fd[0]: 68 | input = fd[0][0].readline().strip() 69 | log.info("send: %s" % input) 70 | rf.set_mode(DIR_SEND) 71 | rf.send(input.ljust(32)[:32]) 72 | rf.set_mode(DIR_RECV) 73 | 74 | rc, buf = rf.recv() 75 | if rc and rc.RX_DR and buf: 76 | log.info("recv: %s" % buf) 77 | 78 | def to_int(v): 79 | return int(v, 0) 80 | 81 | if __name__ == '__main__' and '__file__' in globals(): 82 | ap = ArgumentParser() 83 | ap.add_argument('-D', '--debug', default='INFO') 84 | ap.add_argument('-V', '--vid', type=to_int, default=0x04b4) 85 | ap.add_argument('-P', '--pid', type=to_int, default=0x0004) 86 | ap.add_argument('-n', '--nth', type=int, default=0) 87 | ap.add_argument('-T', '--tx', type=to_int, default=0xE7E7E7E7E7) 88 | ap.add_argument('-R', '--rx', action='append', type=to_int, default=[]) 89 | ap.add_argument('-f', '--freq', type=int, default=2405) 90 | ap.add_argument('-m', '--mode', default='SB') 91 | ap.add_argument('-r', '--rate') 92 | ap.add_argument('args', nargs='*') 93 | 94 | # parse args 95 | ctx = lambda:0 96 | ctx.opt = ap.parse_args() 97 | 98 | # default RX address(es) 99 | if len(ctx.opt.rx) == 0: 100 | # NOTE: 101 | # In ESB mode, TX_ADDR and RX_ADDR_P0 must be same for Auto-ACK 102 | ctx.opt.rx = [0xE7E7E7E7E7] 103 | if len(ctx.opt.rx) == 1 and ctx.opt.mode == 'ESB': 104 | # NOTE: 105 | # I had to set RX addr in RX_ADDR_P1, not P0. 106 | # It seems ESB mode expects P0 to have same address as TX_ADDR to 107 | # receive Auto-ACK, although it's only used by ESB TX node. 108 | ctx.opt.rx = [0, ctx.opt.rx[0]] 109 | 110 | # setup logger 111 | logging.basicConfig(level=eval('logging.' + ctx.opt.debug)) 112 | 113 | main(ctx) 114 | -------------------------------------------------------------------------------- /bin/nrf24-dump.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | usage = """ 5 | nRF24 sniffer 6 | 7 | ref: 8 | - http://yveaux.blogspot.jp/2014/07/nrf24l01-sniffer-part-1.html 9 | - https://github.com/Yveaux/NRF24_Sniffer/ 10 | 11 | To run and test this script, connection betwen CY7C65211 and nRF24L01 12 | must be done as below: 13 | 14 | Cypress nRF24L01 15 | ---------------------- 16 | GPIO 0 <---> CE 17 | GPIO 1 <---> IRQ 18 | SSEL <---> CSN 19 | MISO <---> MO 20 | MOSI <---> MI 21 | SCLK <---> SCK 22 | 23 | """ 24 | 25 | import os 26 | import sys 27 | import time 28 | 29 | from argparse import ArgumentParser 30 | from ucdev.cy7c65211 import * 31 | from ucdev.nrf24 import * 32 | 33 | import logging 34 | log = logging.getLogger(__name__) 35 | 36 | from IPython import embed 37 | 38 | def find_dev(ctx): 39 | dll = os.getenv("CYUSBSERIAL_DLL") or "cyusbserial" 40 | lib = CyUSBSerial(lib=dll) 41 | found = list(lib.find(vid=ctx.opt.vid, pid=ctx.opt.pid)) 42 | return found[ctx.opt.nth] 43 | 44 | def main(ctx): 45 | dev = find_dev(ctx) 46 | io = CyGPIO(dev) 47 | rf = nRF24(CySPI(dev), CE=io.pin(0), IRQ=io.pin(1)) 48 | 49 | # set basic mode 50 | mode = DIR_RECV 51 | mode |= eval("MODE_%s" % ctx.opt.mode.upper()) 52 | if ctx.opt.rate: 53 | mode |= eval("RATE_%s" % ctx.opt.rate.upper()) 54 | rf.reset(mode) 55 | 56 | # set address and channel 57 | for i,addr in enumerate(ctx.opt.rx): 58 | log.debug("RX_ADDR_P{i}: {addr:010X}".format(**locals())) 59 | setattr(rf, "RX_ADDR_P" + str(i), addr) 60 | rf.RF_CH = ctx.opt.freq - 2400 61 | 62 | # 63 | # configure promisc mode 64 | # 65 | # While 0x00 is unofficial, it is known to enable "2-byte address 66 | # match" mode. With this configuration, it is now possible to 67 | # sniff all nRF24 packet with 2-byte RX_ADDR 0x0055. This is because 68 | # nRF24 uses radio preamble of 0xAA or 0x55 and default background 69 | # tends to generate 0x00 byte in the air. 70 | # 71 | # Right after radio preamble, nRF24 uses MAC address as a SYNC pattern. 72 | # This means sniffing allows you to capture MAC address in payload. 73 | # 74 | # ref: 75 | # - http://travisgoodspeed.blogspot.jp/2011/02/promiscuity-is-nrf24l01s-duty.html 76 | # 77 | rf.SETUP_AW.AW = max(0, ctx.opt.match - 2) 78 | 79 | # 80 | # disable CRC 81 | # 82 | # In this fake "promisc mode", CRC will never match as we are only 83 | # matching against part of incoming MAC address (as part of SYNC frame), 84 | # making CRC computed with remaining part of MAC as payload. 85 | # 86 | rf.CONFIG.EN_CRC = 0 87 | 88 | # recv loop 89 | while True: 90 | rc, buf = rf.recv(32) 91 | if not buf: continue 92 | 93 | if ctx.opt.dump == 'mac': 94 | print("".join(map(lambda v: "%02X" % v, buf[0:5]))) 95 | elif ctx.opt.dump == 'hex': 96 | print("".join(map(lambda v: "%02X" % v, buf))) 97 | elif ctx.opt.dump == 'ascii': 98 | print("".join(map(lambda v: chr(v) if (0x20 < v and v < 0x7e) else ' ', buf))) 99 | else: 100 | sys.stdout.write(buf) 101 | 102 | def to_int(v): 103 | return int(v, 0) 104 | 105 | if __name__ == '__main__' and '__file__' in globals(): 106 | ap = ArgumentParser() 107 | ap.add_argument('-D', '--debug', default='INFO') 108 | ap.add_argument('-V', '--vid', type=to_int, default=0x04b4) 109 | ap.add_argument('-P', '--pid', type=to_int, default=0x0004) 110 | ap.add_argument('-n', '--nth', type=int) 111 | ap.add_argument('-R', '--rx', action='append', type=to_int, default=[]) 112 | ap.add_argument('-M', '--match', type=int, default=2) 113 | ap.add_argument('-f', '--freq', metavar='FREQ', type=int, default=2405) 114 | ap.add_argument('-m', '--mode', default='SB') 115 | ap.add_argument('-r', '--rate') 116 | ap.add_argument('-d', '--dump', default='raw') 117 | ap.add_argument('args', nargs='*') 118 | 119 | # parse args 120 | ctx = lambda:0 121 | ctx.opt = ap.parse_args() 122 | 123 | # default address(es) 124 | if len(ctx.opt.rx) == 0: ctx.opt.rx.append(0x0055) 125 | if len(ctx.opt.rx) == 1: ctx.opt.rx.append(ctx.opt.rx[0]) 126 | 127 | # setup logger 128 | logging.basicConfig(level=eval('logging.' + ctx.opt.debug)) 129 | 130 | main(ctx) 131 | -------------------------------------------------------------------------------- /bin/nrf24-recv.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8-unix -*- 3 | 4 | usage = """ 5 | To run and test this script, connection betwen CY7C65211 and nRF24L01 6 | must be done as below: 7 | 8 | Cypress nRF24L01 9 | ---------------------- 10 | GPIO 0 <---> CE 11 | GPIO 1 <---> IRQ 12 | SSEL <---> CSN 13 | MISO <---> MO 14 | MOSI <---> MI 15 | SCLK <---> SCK 16 | 17 | """ 18 | 19 | import os 20 | import sys 21 | import time 22 | 23 | from argparse import ArgumentParser 24 | from ucdev.cy7c65211 import CyUSBSerial, CyGPIO, CySPI 25 | from ucdev.nrf24 import * 26 | 27 | import logging 28 | log = logging.getLogger(__name__) 29 | 30 | from IPython import embed 31 | 32 | def find_dev(ctx): 33 | dll = os.getenv("CYUSBSERIAL_DLL") or "cyusbserial" 34 | lib = CyUSBSerial(lib=dll) 35 | found = list(lib.find(vid=ctx.opt.vid, pid=ctx.opt.pid)) 36 | return found[ctx.opt.nth] 37 | 38 | def main(ctx): 39 | dev = find_dev(ctx) 40 | io = CyGPIO(dev) 41 | rf = nRF24(CySPI(dev), CE=io.pin(0), IRQ=io.pin(1)) 42 | 43 | # set basic mode 44 | mode = DIR_RECV 45 | mode |= eval("MODE_%s" % ctx.opt.mode.upper()) 46 | if ctx.opt.rate: 47 | mode |= eval("RATE_%s" % ctx.opt.rate.upper()) 48 | rf.reset(mode) 49 | 50 | # set address and channel 51 | for i,addr in enumerate(ctx.opt.rx): 52 | log.debug("RX_ADDR_P{i}: {addr:010X}".format(**locals())) 53 | setattr(rf, "RX_ADDR_P" + str(i), addr) 54 | if ctx.opt.tx: 55 | log.debug("TX_ADDR: {ctx.opt.tx:010X}".format(**locals())) 56 | rf.TX_ADDR = ctx.opt.tx 57 | rf.RF_CH = ctx.opt.freq - 2400 58 | 59 | # recv loop 60 | while True: 61 | rc, buf = rf.recv() 62 | if rc and rc.RX_DR and buf: 63 | sys.stdout.write(buf) 64 | 65 | def to_int(v): 66 | return int(v, 0) 67 | 68 | if __name__ == '__main__' and '__file__' in globals(): 69 | ap = ArgumentParser() 70 | ap.add_argument('-D', '--debug', default='INFO') 71 | ap.add_argument('-V', '--vid', type=to_int, default=0x04b4) 72 | ap.add_argument('-P', '--pid', type=to_int, default=0x0004) 73 | ap.add_argument('-n', '--nth', type=int) 74 | ap.add_argument('-T', '--tx', type=to_int) 75 | ap.add_argument('-R', '--rx', action='append', type=to_int, default=[]) 76 | ap.add_argument('-f', '--freq', type=int, default=2405) 77 | ap.add_argument('-m', '--mode', default='SB') 78 | ap.add_argument('-r', '--rate') 79 | ap.add_argument('args', nargs='*') 80 | 81 | # parse args 82 | ctx = lambda:0 83 | ctx.opt = ap.parse_args() 84 | 85 | # default RX address(es) 86 | if len(ctx.opt.rx) == 0: 87 | ctx.opt.rx = [0xE7E7E7E7E7] 88 | if len(ctx.opt.rx) == 1 and ctx.opt.mode == 'ESB': 89 | # NOTE: 90 | # I had to set RX addr in RX_ADDR_P1, not P0. 91 | # It seems ESB mode expects P0 to have same address as TX_ADDR to 92 | # receive Auto-ACK, although it's only used by ESB TX node. 93 | ctx.opt.rx = [0, ctx.opt.rx[0]] 94 | 95 | # setup logger 96 | logging.basicConfig(level=eval('logging.' + ctx.opt.debug)) 97 | 98 | main(ctx) 99 | -------------------------------------------------------------------------------- /bin/nrf24-send.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8-unix -*- 3 | 4 | usage = """ 5 | To run and test this script, connection betwen CY7C65211 and nRF24L01 6 | must be done as below: 7 | 8 | Cypress nRF24L01 9 | ---------------------- 10 | GPIO 0 <---> CE 11 | GPIO 1 <---> IRQ 12 | SSEL <---> CSN 13 | MISO <---> MO 14 | MOSI <---> MI 15 | SCLK <---> SCK 16 | 17 | """ 18 | 19 | import os 20 | import sys 21 | import time 22 | 23 | from argparse import ArgumentParser 24 | from ucdev.cy7c65211 import CyUSBSerial, CyGPIO, CySPI 25 | from ucdev.nrf24 import * 26 | 27 | import logging 28 | log = logging.getLogger(__name__) 29 | 30 | from IPython import embed 31 | 32 | def find_dev(ctx): 33 | dll = os.getenv("CYUSBSERIAL_DLL") or "cyusbserial" 34 | lib = CyUSBSerial(lib=dll) 35 | found = list(lib.find(vid=ctx.opt.vid, pid=ctx.opt.pid)) 36 | return found[ctx.opt.nth] 37 | 38 | def main(ctx): 39 | dev = find_dev(ctx) 40 | io = CyGPIO(dev) 41 | rf = nRF24(CySPI(dev), CE=io.pin(0), IRQ=io.pin(1)) 42 | 43 | # set basic mode 44 | mode = DIR_SEND 45 | mode |= eval("MODE_%s" % ctx.opt.mode.upper()) 46 | if ctx.opt.rate: 47 | mode |= eval("RATE_%s" % ctx.opt.rate.upper()) 48 | rf.reset(mode) 49 | 50 | # set address and channel 51 | for i,addr in enumerate(ctx.opt.rx): 52 | log.debug("RX_ADDR_P{i}: {addr:010X}".format(**locals())) 53 | setattr(rf, "RX_ADDR_P" + str(i), addr) 54 | log.debug("TX_ADDR: {ctx.opt.tx:010X}".format(**locals())) 55 | rf.TX_ADDR = ctx.opt.tx 56 | rf.RF_CH = ctx.opt.freq - 2400 57 | 58 | # send loop 59 | buf = sys.stdin.read(32) 60 | while buf: 61 | while not rf.FIFO_STATUS.TX_EMPTY: 62 | rf.flush() 63 | rf.send(buf) 64 | sys.stdout.write('.') 65 | sys.stdout.flush() 66 | buf = sys.stdin.read(32) 67 | 68 | def to_int(v): 69 | return int(v, 0) 70 | 71 | if __name__ == '__main__' and '__file__' in globals(): 72 | ap = ArgumentParser() 73 | ap.add_argument('-D', '--debug', default='INFO') 74 | ap.add_argument('-V', '--vid', type=to_int, default=0x04b4) 75 | ap.add_argument('-P', '--pid', type=to_int, default=0x0004) 76 | ap.add_argument('-n', '--nth', type=int) 77 | ap.add_argument('-T', '--tx', type=to_int, default=0xE7E7E7E7E7) 78 | ap.add_argument('-R', '--rx', action='append', type=to_int, default=[]) 79 | ap.add_argument('-f', '--freq', type=int, default=2405) 80 | ap.add_argument('-m', '--mode', default='SB') 81 | ap.add_argument('-r', '--rate') 82 | ap.add_argument('args', nargs='*') 83 | 84 | # parse args 85 | ctx = lambda:0 86 | ctx.opt = ap.parse_args() 87 | 88 | # NOTE: TX_ADDR and RX_ADDR_P0 must be same in ESB mode 89 | if ctx.opt.mode == 'ESB' and not ctx.opt.rx: 90 | ctx.opt.rx = [ctx.opt.tx] 91 | 92 | # setup logger 93 | logging.basicConfig(level=eval('logging.' + ctx.opt.debug)) 94 | 95 | main(ctx) 96 | -------------------------------------------------------------------------------- /bin/nrf24-test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | usage = """ 5 | nRF24 test carrier wave 6 | 7 | To run and test this script, connection betwen CY7C65211 and nRF24L01 8 | must be done as below: 9 | 10 | Cypress nRF24L01 11 | ---------------------- 12 | GPIO 0 <---> CE 13 | GPIO 1 <---> IRQ 14 | SSEL <---> CSN 15 | MISO <---> MO 16 | MOSI <---> MI 17 | SCLK <---> SCK 18 | 19 | """ 20 | 21 | import os 22 | import sys 23 | import time 24 | 25 | from argparse import ArgumentParser 26 | from ucdev.cy7c65211 import * 27 | from ucdev.nrf24 import * 28 | 29 | import logging 30 | log = logging.getLogger(__name__) 31 | 32 | from IPython import embed 33 | 34 | def find_dev(ctx): 35 | dll = os.getenv("CYUSBSERIAL_DLL") or "cyusbserial" 36 | lib = CyUSBSerial(lib=dll) 37 | found = list(lib.find(vid=ctx.opt.vid, pid=ctx.opt.pid)) 38 | return found[ctx.opt.nth] 39 | 40 | def main(ctx): 41 | dev = find_dev(ctx) 42 | io = CyGPIO(dev) 43 | rf = nRF24(CySPI(dev), CE=io.pin(0), IRQ=io.pin(1)) 44 | 45 | # set carrier wave test mode 46 | rf.reset(MODE_TEST) 47 | 48 | log.info("Carrier wave at {ctx.opt.freq}[MHz]".format(**locals())) 49 | rf.RF_CH = ctx.opt.freq - 2400 50 | rf.CE = 1 51 | 52 | # loop 53 | while True: 54 | sys.stdout.write('.') 55 | sys.stdout.flush() 56 | time.sleep(1) 57 | 58 | def to_int(v): 59 | return int(v, 0) 60 | 61 | if __name__ == '__main__' and '__file__' in globals(): 62 | ap = ArgumentParser() 63 | ap.add_argument('-D', '--debug', default='INFO') 64 | ap.add_argument('-V', '--vid', type=to_int, default=0x04b4) 65 | ap.add_argument('-P', '--pid', type=to_int, default=0x0004) 66 | ap.add_argument('-n', '--nth', type=int) 67 | ap.add_argument('-f', '--freq', type=int, default=2405) 68 | ap.add_argument('args', nargs='*') 69 | 70 | # parse args 71 | ctx = lambda:0 72 | ctx.opt = ap.parse_args() 73 | 74 | # setup logger 75 | logging.basicConfig(level=eval('logging.' + ctx.opt.debug)) 76 | 77 | main(ctx) 78 | -------------------------------------------------------------------------------- /bin/si4702-test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | from __future__ import print_function 5 | 6 | import os 7 | import sys 8 | import time 9 | 10 | from struct import pack, unpack 11 | from argparse import ArgumentParser 12 | 13 | from ucdev.cy7c65211 import CyUSBSerial, CyI2C 14 | from ucdev.si4702 import * 15 | 16 | from IPython import embed 17 | 18 | import logging 19 | log = logging.getLogger(__name__) 20 | 21 | def find_dev(ctx): 22 | #dll = "c:/app/Cypress/Cypress-USB-Serial/library/lib/cyusbserial.dll" 23 | dll = os.getenv("CYUSBSERIAL_DLL") or "cyusbserial" 24 | lib = CyUSBSerial(lib=dll) 25 | found = list(lib.find(vid=ctx.opt.vid, pid=ctx.opt.pid)) 26 | return found[ctx.opt.nth] 27 | 28 | def main(ctx): 29 | dev = find_dev(ctx) 30 | i2c = CyI2C(dev) 31 | si = SI4702(i2c) 32 | 33 | # enable internal oscillator before powerup 34 | si.TEST1.XOSCEN = 1 35 | time.sleep(0.5) 36 | 37 | # clear RDS data (see errata) 38 | si.RDSD = 0x0000 39 | 40 | # power on 41 | si.POWERCFG = POWERCFG(ENABLE=1, DISABLE=0) 42 | time.sleep(0.1) 43 | 44 | print(si.DEVICEID) 45 | print(si.CHIPID) 46 | 47 | # set region to Japan, and maximize volume 48 | si.SYSCONFIG2 = SYSCONFIG2(BAND=0b01, SPACE=0b01, VOLUME=0xF) 49 | 50 | # NOTE: 51 | # Freq = SPACE * CHAN + Limit, where: 52 | # - Limit = 76MHz if BAND=0b01 53 | # - SPACE = 0.1MHz if SPACE=0b01 54 | si.CHANNEL = CHANNEL(TUNE=1, CHAN=int((ctx.opt.freq - 76.0) / 0.1)) 55 | 56 | # STC bit gets set after SEEK=1 or TUNE=1 operation starts. 57 | # It must be cleared by having both SEEK=0 and TUNE=0. 58 | while not si.STATUSRSSI.STC: 59 | time.sleep(0.1) 60 | si.CHANNEL.TUNE = 0 61 | 62 | # disable mute 63 | si.POWERCFG.DMUTE = 1 64 | 65 | embed() 66 | 67 | def to_int(v): 68 | return int(v, 0) 69 | 70 | if __name__ == '__main__' and '__file__' in globals(): 71 | ap = ArgumentParser() 72 | ap.add_argument('-D', '--debug', default='INFO') 73 | ap.add_argument('-V', '--vid', type=to_int, default=0x04b4) 74 | ap.add_argument('-P', '--pid', type=to_int, default=0x0004) 75 | ap.add_argument('-n', '--nth', type=int, default=0) 76 | ap.add_argument('-f', '--freq', type=float, default=80.0) 77 | ap.add_argument('args', nargs='*') 78 | 79 | # parse args 80 | ctx = lambda:0 81 | ctx.opt = ap.parse_args() 82 | 83 | # setup logger 84 | logging.basicConfig(level=eval('logging.' + ctx.opt.debug)) 85 | 86 | main(ctx) 87 | 88 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | setup(name='ucdev', 4 | version='0.0.3', 5 | description='Library to access various I2C/SPI/GPIO-accessible chips over Cypress CY7C65211/3/5 USB-to-UART/I2C/SPI/GPIO bridge.', 6 | long_description=open('README.md').read(), 7 | url='https://github.com/tai/python-ucdev/', 8 | author='Taisuke Yamada', 9 | author_email='tai@remove-if-not-spam.rakugaki.org', 10 | license='MIT', 11 | packages=['ucdev'], 12 | classifiers=[ 13 | 'License :: OSI Approved :: MIT License', 14 | 'Intended Audience :: Developers', 15 | 'Programming Language :: Python :: 2', 16 | 'Programming Language :: Python :: 2.7', 17 | 'Programming Language :: Python :: 3', 18 | 'Topic :: Scientific/Engineering', 19 | 'Topic :: Software Development :: Embedded Systems', 20 | 'Topic :: System :: Hardware', 21 | ], 22 | install_requires=[ 23 | 'bitstring', 24 | 'cffi', 25 | 'IPython', 26 | ] 27 | ) 28 | -------------------------------------------------------------------------------- /ucdev/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8-unix -*- 2 | 3 | -------------------------------------------------------------------------------- /ucdev/ad9850.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8-unix -*- 3 | 4 | from bitstring import * 5 | from register import Register 6 | 7 | W = Register("PHASE:5 POWER_DOWN CONTROL:2 FREQ:32", 0x00) 8 | 9 | """ 10 | Usage: 11 | spi = CySPI(dev) 12 | gpio = CyGPIO(dev) 13 | dds = AD9850(spi, RESET=gpio.pin(0)) 14 | dds.reset() 15 | dds.send(FREQ=0x0000FFFF, PHASE=0b00000) 16 | 17 | """ 18 | class AD9850(object): 19 | CE = property(lambda s:s.__ce.get(), lambda s,v:s.__ce.set(1 if v else 0)) 20 | RESET = property(lambda s:s.__rs.get(), lambda s,v:s.__rs.set(1 if v else 0)) 21 | 22 | def __init__(self, spi, CE=None, RESET=None): 23 | self.spi = spi 24 | self.__ce = CE 25 | self.__rs = RESET 26 | self.debug = False 27 | 28 | def reset(self, freq=100000): 29 | self.reset_spi(freq) 30 | # RESET pulse must be at least (5 * REFCLK) pulse width. 31 | # This is 500ns when REFCLK == 10MHz (which is MUCH slower than 32 | # usual REFCLK), so following should be good enough... 33 | self.RESET = 0 34 | self.RESET = 1 35 | self.RESET = 0 36 | 37 | def reset_spi(self, freq): 38 | spi = self.spi 39 | 40 | rc = spi.set_config({ 41 | 'frequency': freq, 42 | 'dataWidth': 8, 43 | 'protocol': spi.MOTOROLA, 44 | 'isMsbFirst': True, 45 | 'isMaster': True, 46 | 'isContinuousMode': True, 47 | 'isCpha': False, 48 | 'isCpol': False, 49 | }) 50 | if rc != 0: 51 | raise Exception("ERROR: SPI init failed=%d" % rc) 52 | 53 | def send(self, *arg, **kw): 54 | tmp = W(*arg, **kw).value 55 | tmp.reverse() 56 | if self.debug: 57 | print(tmp.bin) 58 | self.spi.send(tmp.bytes) 59 | -------------------------------------------------------------------------------- /ucdev/common.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8-unix -*- 2 | 3 | class SPI(object): 4 | pass 5 | 6 | class I2C(object): 7 | pass 8 | 9 | class GPIO(object): 10 | def pin(self, nr): 11 | return GPIOPin(self, nr) 12 | 13 | class GPIOPin(object): 14 | def __init__(self, port, nr): 15 | self.port = port 16 | self.nr = nr 17 | 18 | def get(self): 19 | return self.port.get(self.nr) 20 | 21 | def set(self, val): 22 | return self.port.set(self.nr, val) 23 | -------------------------------------------------------------------------------- /ucdev/cy7c65211/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8-unix -*- 2 | from .device import * 3 | -------------------------------------------------------------------------------- /ucdev/cy7c65211/device.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8-unix -*- 2 | 3 | import sys, os 4 | import platform 5 | 6 | from cffi import FFI 7 | from ucdev.common import GPIO, SPI, I2C 8 | 9 | from .header import src as cdef_src 10 | 11 | import logging 12 | log = logging.getLogger(__name__) 13 | 14 | class CyUSBSerial(object): 15 | __self = None 16 | 17 | def __new__(cls, lib=None, ffi=None): 18 | if not cls.__self: 19 | if not ffi: 20 | ffi = FFI() 21 | obj = super(CyUSBSerial, cls).__new__(cls) 22 | obj.ffi = ffi 23 | obj.ffi.cdef(cdef_src) 24 | obj.api = ffi.dlopen(lib if lib else "cyusbserial") 25 | 26 | # initialize if API exists 27 | if hasattr(obj.api, 'CyLibraryInit'): 28 | rc = obj.api.CyLibraryInit() 29 | if rc != obj.api.CY_SUCCESS: 30 | raise Exception("ERROR: CyLibraryInit=%d" % rc) 31 | 32 | cls.__self = obj 33 | return cls.__self 34 | 35 | def __del__(self): 36 | # finalize if API exists 37 | if self.api and hasattr(self.api, 'CyLibraryExit'): 38 | self.api.CyLibraryExit() 39 | 40 | def find(self, finder=None, vid=None, pid=None): 41 | ffi, api = self.ffi, self.api 42 | 43 | os = platform.system() 44 | nr = ffi.new("UINT8 *") 45 | rc = api.CyGetListofDevices(nr) 46 | 47 | info = ffi.new("CY_DEVICE_INFO *") 48 | 49 | for devno in range(0, nr[0]): 50 | rc = api.CyGetDeviceInfo(devno, info) 51 | 52 | if os == 'Windows' and info.deviceBlock != api.SerialBlock_SCB0: 53 | continue 54 | 55 | found = True 56 | 57 | if finder: 58 | found = finder(info) 59 | elif vid or pid: 60 | iv = info.vidPid.vid 61 | ip = info.vidPid.pid 62 | 63 | found = (vid, pid) in ((iv, ip), (iv, None), (None, ip)) 64 | 65 | if found: 66 | yield CyUSBSerialDevice(self, devno, 0) 67 | 68 | ###################################################################### 69 | 70 | class CyUSBSerialDevice(object): 71 | def __init__(self, lib, devno, ifnum): 72 | self.lib = lib 73 | self.devno = devno 74 | self.ifnum = ifnum 75 | self.dev = None 76 | 77 | self.raise_on_error = True 78 | 79 | # import API symbols from the library 80 | dummy = self.CY_SUCCESS 81 | self.__dict__.update(lib.api.__dict__) 82 | 83 | # delegate API calls to the library 84 | def __getattr__(self, key): 85 | lib, api = self.lib, self.lib.api 86 | val = getattr(api, key) 87 | 88 | # wrap API so device handle is handled automatically 89 | if callable(val): 90 | def wrap(self, name, func): 91 | def wrapper(*args, **kwargs): 92 | # automatically open handle on first call 93 | if not self.dev: 94 | self.open() 95 | 96 | # delegate API call 97 | rc = func(self.dev, *args, **kwargs) 98 | 99 | if self.raise_on_error and rc != api.CY_SUCCESS: 100 | sym = self.err_to_sym(rc) 101 | msg = "ERROR: {0}={1}, {2}".format(name, rc, sym) 102 | raise Exception(msg) 103 | 104 | # invalidate handle to force reopen on next call 105 | elif name in ('CyCyclePort', 'CyResetDevice'): 106 | self.dev = None 107 | 108 | return rc 109 | return wrapper 110 | val = wrap(self, key, val) 111 | 112 | # save as local attribute to help ipython completion 113 | setattr(self, key, val) 114 | 115 | return val 116 | 117 | def __del__(self, *args): 118 | self.close() 119 | 120 | def err_to_sym(self, rc): 121 | for k, v in vars(self.lib.api).items(): 122 | if k.startswith("CY_ERROR") and v == rc: 123 | return k 124 | return "UNKNOWN" 125 | 126 | def open(self): 127 | lib, ffi, api = self.lib, self.lib.ffi, self.lib.api 128 | rc = api.CY_SUCCESS 129 | if not self.dev: 130 | dev = ffi.new("CY_HANDLE *") 131 | rc = api.CyOpen(self.devno, self.ifnum, dev) 132 | self.dev = dev[0] 133 | return rc 134 | 135 | def close(self): 136 | lib, ffi, api = self.lib, self.lib.ffi, self.lib.api 137 | if self.dev: 138 | api.CyClose(self.dev) 139 | self.dev = None 140 | 141 | ###################################################################### 142 | 143 | class CyI2C(SPI): 144 | def __init__(self, dev): 145 | if not isinstance(dev, CyUSBSerialDevice): 146 | msg = "ERROR: Not a CyUSBSerialDevice object: %s" % str(dev) 147 | raise Exception(msg) 148 | self.dev = dev 149 | 150 | def set_config(self, config): 151 | dev = self.dev 152 | ffi = dev.lib.ffi 153 | api = dev.lib.api 154 | cfg = ffi.new("CY_I2C_CONFIG *", config) 155 | return dev.CySetI2cConfig(cfg) 156 | 157 | def get_config(self): 158 | dev = self.dev 159 | ffi = dev.lib.ffi 160 | api = dev.lib.api 161 | 162 | cfg = ffi.new("CY_I2C_CONFIG *") 163 | rc = dev.CyGetI2cConfig(cfg) 164 | if rc != api.CY_SUCCESS: 165 | raise Exception("ERROR: CyGetI2cConfig=%d" % rc) 166 | 167 | ret = {} 168 | for k, v in ffi.typeof(cfg[0]).fields: 169 | ret[k] = getattr(cfg, k) 170 | return ret 171 | 172 | def prepare(self, slaveAddress, isStopBit=1, isNakBit=0): 173 | dev = self.dev 174 | ffi = dev.lib.ffi 175 | api = dev.lib.api 176 | 177 | cfg = ffi.new("CY_I2C_DATA_CONFIG *", 178 | (slaveAddress, isStopBit, isNakBit)) 179 | return cfg 180 | 181 | def reset(self, resetMode=0): 182 | dev = self.dev 183 | ffi = dev.lib.ffi 184 | api = dev.lib.api 185 | 186 | rc = dev.CyI2cReset(resetMode) 187 | log.debug("rc=%d" % rc) 188 | 189 | def read(self, cfg, data, timeout=1000): 190 | dev = self.dev 191 | ffi = dev.lib.ffi 192 | api = dev.lib.api 193 | 194 | rlen = len(data) 195 | rbuf = ffi.new("UCHAR[%d]" % rlen) 196 | rcdb = ffi.new("CY_DATA_BUFFER *", (rbuf, rlen, 0)) 197 | 198 | rc = dev.CyI2cRead(cfg, rcdb, timeout) 199 | log.debug("r:" + " ".join(["{:08b}".format(i) for i in rbuf])) 200 | 201 | return bytearray(ffi.buffer(rcdb.buffer, rcdb.transferCount)[0:]) 202 | 203 | def write(self, cfg, data, timeout=1000): 204 | dev = self.dev 205 | ffi = dev.lib.ffi 206 | api = dev.lib.api 207 | 208 | wlen = len(data) 209 | wbuf = ffi.new("UCHAR[%d]" % wlen, bytes(data)) 210 | wcdb = ffi.new("CY_DATA_BUFFER *", (wbuf, wlen, 0)) 211 | 212 | log.debug("w:" + " ".join(["{:08b}".format(i) for i in wbuf])) 213 | rc = dev.CyI2cWrite(cfg, wcdb, timeout) 214 | 215 | return rc 216 | 217 | ###################################################################### 218 | 219 | class CySPI(SPI): 220 | MOTOROLA = TI = NS = None 221 | 222 | # Used for GPIO-based chip-select 223 | CSN = property(lambda s:s.__csn.get() if s.__csn else None, 224 | lambda s,v:s.__csn.set(1 if v else 0) if s.__csn else None) 225 | 226 | def __init__(self, dev, CSN=None): 227 | if not isinstance(dev, CyUSBSerialDevice): 228 | msg = "ERROR: Not a CyUSBSerialDevice object: %s" % str(dev) 229 | raise Exception(msg) 230 | self.dev = dev 231 | self.__csn = CSN 232 | 233 | # FIXME: 234 | # - These should be set on load-time, not run-time. 235 | CySPI.MOTOROLA = dev.lib.api.CY_SPI_MOTOROLA 236 | CySPI.TI = dev.lib.api.CY_SPI_TI 237 | CySPI.NS = dev.lib.api.CY_SPI_NS 238 | 239 | def set_config(self, config): 240 | dev = self.dev 241 | ffi = dev.lib.ffi 242 | api = dev.lib.api 243 | cfg = ffi.new("CY_SPI_CONFIG *", config) 244 | return dev.CySetSpiConfig(cfg) 245 | 246 | def get_config(self): 247 | dev = self.dev 248 | ffi = dev.lib.ffi 249 | api = dev.lib.api 250 | 251 | cfg = ffi.new("CY_SPI_CONFIG *") 252 | rc = dev.CyGetSpiConfig(cfg) 253 | if rc != api.CY_SUCCESS: 254 | raise Exception("ERROR: CyGetSpiConfig=%d" % rc) 255 | 256 | ret = {} 257 | for k, v in ffi.typeof(cfg[0]).fields: 258 | ret[k] = getattr(cfg, k) 259 | return ret 260 | 261 | def send(self, data, timeout=1000): 262 | dev = self.dev 263 | ffi = dev.lib.ffi 264 | 265 | wlen = len(data) 266 | wbuf = ffi.new("UCHAR[%d]" % wlen, bytes(data)) 267 | wcdb = ffi.new("CY_DATA_BUFFER *", (wbuf, wlen, 0)) 268 | 269 | rlen = len(data) 270 | rbuf = ffi.new("UCHAR[%d]" % rlen) 271 | rcdb = ffi.new("CY_DATA_BUFFER *", (rbuf, rlen, 0)) 272 | 273 | log.debug("w:" + " ".join(["{:08b}".format(i) for i in wbuf])) 274 | 275 | self.CSN = 1 276 | rc = dev.CySpiReadWrite(rcdb, wcdb, timeout) 277 | self.CSN = 0 278 | 279 | log.debug("r:" + " ".join(["{:08b}".format(i) for i in rbuf])) 280 | 281 | return bytearray(ffi.buffer(rcdb.buffer, rcdb.transferCount)[0:]) 282 | 283 | ###################################################################### 284 | 285 | class CyGPIO(GPIO): 286 | def __init__(self, dev): 287 | if not isinstance(dev, CyUSBSerialDevice): 288 | msg = "ERROR: Not a CyUSBSerialDevice object: %s" % str(dev) 289 | raise Exception(msg) 290 | self.dev = dev 291 | 292 | def set(self, pin, val): 293 | dev = self.dev 294 | api = dev.lib.api 295 | 296 | ret = dev.CySetGpioValue(pin, val) 297 | if ret != api.CY_SUCCESS: 298 | sym = dev.err_to_sym(ret) 299 | msg = "ERROR: CySetGpioValue={0}, {1}".format(ret, sym) 300 | raise Exception(msg) 301 | 302 | def get(self, pin): 303 | dev = self.dev 304 | api = dev.lib.api 305 | ffi = dev.lib.ffi 306 | 307 | val = ffi.new("UINT8 *") 308 | ret = dev.CyGetGpioValue(pin, val) 309 | if ret != api.CY_SUCCESS: 310 | sym = dev.err_to_sym(ret) 311 | msg = "ERROR: CyGetGpioValue={0}, {1]".format(ret, sym) 312 | raise Exception(msg) 313 | return val[0] 314 | 315 | ###################################################################### 316 | 317 | # export symbols 318 | __all__ = [i for i in list(locals()) if i.startswith("Cy")] 319 | -------------------------------------------------------------------------------- /ucdev/cy7c65211/header.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8-unix -*- 2 | 3 | import platform 4 | 5 | ###################################################################### 6 | # Platform specific headers 7 | ###################################################################### 8 | 9 | if platform.system() == 'Linux': 10 | src = """ 11 | typedef bool BOOL; 12 | """ 13 | 14 | ###################################################################### 15 | # Common headers 16 | ###################################################################### 17 | 18 | src += """ 19 | #define CY_STRING_DESCRIPTOR_SIZE 256 20 | #define CY_MAX_DEVICE_INTERFACE 5 21 | #define CY_US_VERSION_MAJOR 1 22 | #define CY_US_VERSION_MINOR 0 23 | #define CY_US_VERSION_PATCH 0 24 | #define CY_US_VERSION 1 25 | #define CY_US_VERSION_BUILD 74 26 | typedef unsigned int UINT32; 27 | typedef unsigned char UINT8; 28 | typedef unsigned short UINT16; 29 | typedef char CHAR; 30 | typedef unsigned char UCHAR; 31 | typedef void* CY_HANDLE; 32 | typedef void (*CY_EVENT_NOTIFICATION_CB_FN)(UINT16 eventsNotified); 33 | typedef struct _CY_VID_PID { 34 | UINT16 vid; 35 | UINT16 pid; 36 | } CY_VID_PID, *PCY_VID_PID; 37 | typedef struct _CY_LIBRARY_VERSION { 38 | UINT8 majorVersion; 39 | UINT8 minorVersion; 40 | UINT16 patch; 41 | UINT8 buildNumber; 42 | } CY_LIBRARY_VERSION, *PCY_LIBRARY_VERSION; 43 | typedef struct _CY_FIRMWARE_VERSION { 44 | UINT8 majorVersion; 45 | UINT8 minorVersion; 46 | UINT16 patchNumber; 47 | UINT32 buildNumber; 48 | } CY_FIRMWARE_VERSION, *PCY_FIRMWARE_VERSION; 49 | typedef enum _CY_DEVICE_CLASS{ 50 | CY_CLASS_DISABLED = 0, 51 | CY_CLASS_CDC = 0x02, 52 | CY_CLASS_PHDC = 0x0F, 53 | CY_CLASS_VENDOR = 0xFF 54 | } CY_DEVICE_CLASS; 55 | typedef enum _CY_DEVICE_TYPE { 56 | CY_TYPE_DISABLED = 0, 57 | CY_TYPE_UART, 58 | CY_TYPE_SPI, 59 | CY_TYPE_I2C, 60 | CY_TYPE_JTAG, 61 | CY_TYPE_MFG 62 | } CY_DEVICE_TYPE; 63 | typedef enum _CY_DEVICE_SERIAL_BLOCK 64 | { 65 | SerialBlock_SCB0 = 0, 66 | SerialBlock_SCB1, 67 | SerialBlock_MFG 68 | } CY_DEVICE_SERIAL_BLOCK; 69 | typedef struct _CY_DEVICE_INFO { 70 | CY_VID_PID vidPid; 71 | UCHAR numInterfaces; 72 | UCHAR manufacturerName [256]; 73 | UCHAR productName [256]; 74 | UCHAR serialNum [256]; 75 | UCHAR deviceFriendlyName [256]; 76 | CY_DEVICE_TYPE deviceType [5]; 77 | CY_DEVICE_CLASS deviceClass [5]; 78 | CY_DEVICE_SERIAL_BLOCK deviceBlock; 79 | } CY_DEVICE_INFO,*PCY_DEVICE_INFO; 80 | typedef struct _CY_DATA_BUFFER { 81 | UCHAR *buffer; 82 | UINT32 length; 83 | UINT32 transferCount; 84 | } CY_DATA_BUFFER,*PCY_DATA_BUFFER; 85 | typedef enum _CY_RETURN_STATUS{ 86 | CY_SUCCESS = 0, 87 | CY_ERROR_ACCESS_DENIED, 88 | CY_ERROR_DRIVER_INIT_FAILED, 89 | CY_ERROR_DEVICE_INFO_FETCH_FAILED, 90 | CY_ERROR_DRIVER_OPEN_FAILED, 91 | CY_ERROR_INVALID_PARAMETER, 92 | CY_ERROR_REQUEST_FAILED, 93 | CY_ERROR_DOWNLOAD_FAILED, 94 | CY_ERROR_FIRMWARE_INVALID_SIGNATURE, 95 | CY_ERROR_INVALID_FIRMWARE, 96 | CY_ERROR_DEVICE_NOT_FOUND, 97 | CY_ERROR_IO_TIMEOUT, 98 | CY_ERROR_PIPE_HALTED, 99 | CY_ERROR_BUFFER_OVERFLOW, 100 | CY_ERROR_INVALID_HANDLE, 101 | CY_ERROR_ALLOCATION_FAILED, 102 | CY_ERROR_I2C_DEVICE_BUSY, 103 | CY_ERROR_I2C_NAK_ERROR, 104 | CY_ERROR_I2C_ARBITRATION_ERROR, 105 | CY_ERROR_I2C_BUS_ERROR, 106 | CY_ERROR_I2C_BUS_BUSY, 107 | CY_ERROR_I2C_STOP_BIT_SET, 108 | CY_ERROR_STATUS_MONITOR_EXIST 109 | } CY_RETURN_STATUS; 110 | typedef struct _CY_I2C_CONFIG{ 111 | UINT32 frequency; 112 | UINT8 slaveAddress; 113 | BOOL isMaster; 114 | BOOL isClockStretch; 115 | } CY_I2C_CONFIG,*PCY_I2C_CONFIG; 116 | typedef struct _CY_I2C_DATA_CONFIG 117 | { 118 | UCHAR slaveAddress; 119 | BOOL isStopBit; 120 | BOOL isNakBit; 121 | } CY_I2C_DATA_CONFIG, *PCY_I2C_DATA_CONFIG; 122 | typedef enum _CY_SPI_PROTOCOL { 123 | CY_SPI_MOTOROLA = 0, 124 | CY_SPI_TI, 125 | CY_SPI_NS 126 | } CY_SPI_PROTOCOL; 127 | typedef struct _CY_SPI_CONFIG 128 | { 129 | UINT32 frequency; 130 | UCHAR dataWidth; 131 | CY_SPI_PROTOCOL protocol ; 132 | BOOL isMsbFirst; 133 | BOOL isMaster; 134 | BOOL isContinuousMode; 135 | BOOL isSelectPrecede; 136 | BOOL isCpha; 137 | BOOL isCpol; 138 | }CY_SPI_CONFIG,*PCY_SPI_CONFIG; 139 | typedef enum _CY_UART_BAUD_RATE 140 | { 141 | CY_UART_BAUD_300 = 300, 142 | CY_UART_BAUD_600 = 600, 143 | CY_UART_BAUD_1200 = 1200, 144 | CY_UART_BAUD_2400 = 2400, 145 | CY_UART_BAUD_4800 = 4800, 146 | CY_UART_BAUD_9600 = 9600, 147 | CY_UART_BAUD_14400 = 14400, 148 | CY_UART_BAUD_19200 = 19200, 149 | CY_UART_BAUD_38400 = 38400, 150 | CY_UART_BAUD_56000 = 56000, 151 | CY_UART_BAUD_57600 = 57600, 152 | CY_UART_BAUD_115200 = 115200, 153 | CY_UART_BAUD_230400 = 230400, 154 | CY_UART_BAUD_460800 = 460800, 155 | CY_UART_BAUD_921600 = 921600, 156 | CY_UART_BAUD_1000000 = 1000000, 157 | CY_UART_BAUD_3000000 = 3000000, 158 | }CY_UART_BAUD_RATE; 159 | typedef enum _CY_UART_PARITY_MODE { 160 | CY_DATA_PARITY_DISABLE = 0, 161 | CY_DATA_PARITY_ODD, 162 | CY_DATA_PARITY_EVEN, 163 | CY_DATA_PARITY_MARK, 164 | CY_DATA_PARITY_SPACE 165 | } CY_UART_PARITY_MODE; 166 | typedef enum _CY_UART_STOP_BIT { 167 | CY_UART_ONE_STOP_BIT = 1, 168 | CY_UART_TWO_STOP_BIT 169 | } CY_UART_STOP_BIT; 170 | typedef enum _CY_FLOW_CONTROL_MODES { 171 | CY_UART_FLOW_CONTROL_DISABLE = 0, 172 | CY_UART_FLOW_CONTROL_DSR, 173 | CY_UART_FLOW_CONTROL_RTS_CTS, 174 | CY_UART_FLOW_CONTROL_ALL 175 | } CY_FLOW_CONTROL_MODES; 176 | typedef struct _CY_UART_CONFIG { 177 | CY_UART_BAUD_RATE baudRate; 178 | UINT8 dataWidth; 179 | CY_UART_STOP_BIT stopBits; 180 | CY_UART_PARITY_MODE parityMode; 181 | BOOL isDropOnRxErrors; 182 | } CY_UART_CONFIG,*PCY_UART_CONFIG; 183 | typedef enum _CY_CALLBACK_EVENTS { 184 | CY_UART_CTS_BIT = 0x01, 185 | CY_UART_DSR_BIT = 0x02, 186 | CY_UART_BREAK_BIT = 0x04, 187 | CY_UART_RING_SIGNAL_BIT = 0x08, 188 | CY_UART_FRAME_ERROR_BIT = 0x10, 189 | CY_UART_PARITY_ERROR_BIT = 0x20, 190 | CY_UART_DATA_OVERRUN_BIT = 0x40, 191 | CY_UART_DCD_BIT = 0x100, 192 | CY_SPI_TX_UNDERFLOW_BIT = 0x200, 193 | CY_SPI_BUS_ERROR_BIT = 0x400, 194 | CY_ERROR_EVENT_FAILED_BIT = 0x800 195 | } CY_CALLBACK_EVENTS; 196 | CY_RETURN_STATUS CyLibraryInit (); 197 | CY_RETURN_STATUS CyLibraryExit (); 198 | CY_RETURN_STATUS CyGetListofDevices ( 199 | UINT8* numDevices 200 | ); 201 | CY_RETURN_STATUS CyGetDeviceInfo( 202 | UINT8 deviceNumber, 203 | CY_DEVICE_INFO *deviceInfo 204 | ); 205 | CY_RETURN_STATUS CyGetDeviceInfoVidPid ( 206 | CY_VID_PID vidPid, 207 | UINT8 *deviceIdList, 208 | CY_DEVICE_INFO *deviceInfoList, 209 | UINT8 *deviceCount, 210 | UINT8 infoListLength 211 | ); 212 | CY_RETURN_STATUS CyOpen ( 213 | UINT8 deviceNumber, 214 | UINT8 interfaceNum, 215 | CY_HANDLE *handle 216 | ); 217 | CY_RETURN_STATUS CyClose ( 218 | CY_HANDLE handle 219 | ); 220 | CY_RETURN_STATUS CyCyclePort ( 221 | CY_HANDLE handle 222 | ); 223 | CY_RETURN_STATUS CySetGpioValue ( 224 | CY_HANDLE handle, 225 | UINT8 gpioNumber, 226 | UINT8 value 227 | ); 228 | CY_RETURN_STATUS CyGetGpioValue ( 229 | CY_HANDLE handle, 230 | UINT8 gpioNumber, 231 | UINT8 *value 232 | ); 233 | CY_RETURN_STATUS CySetEventNotification( 234 | CY_HANDLE handle, 235 | CY_EVENT_NOTIFICATION_CB_FN notificationCbFn 236 | ); 237 | CY_RETURN_STATUS CyAbortEventNotification( 238 | CY_HANDLE handle 239 | ); 240 | CY_RETURN_STATUS CyGetLibraryVersion ( 241 | CY_HANDLE handle, 242 | PCY_LIBRARY_VERSION version 243 | ); 244 | CY_RETURN_STATUS CyGetFirmwareVersion ( 245 | CY_HANDLE handle, 246 | PCY_FIRMWARE_VERSION firmwareVersion 247 | ); 248 | CY_RETURN_STATUS CyResetDevice ( 249 | CY_HANDLE handle 250 | ); 251 | CY_RETURN_STATUS CyProgUserFlash ( 252 | CY_HANDLE handle, 253 | CY_DATA_BUFFER *progBuffer, 254 | UINT32 flashAddress, 255 | UINT32 timeout 256 | ); 257 | CY_RETURN_STATUS CyReadUserFlash ( 258 | CY_HANDLE handle, 259 | CY_DATA_BUFFER *readBuffer, 260 | UINT32 flashAddress, 261 | UINT32 timeout 262 | ); 263 | CY_RETURN_STATUS CyGetSignature ( 264 | CY_HANDLE handle, 265 | UCHAR *pSignature 266 | ); 267 | CY_RETURN_STATUS CyGetUartConfig ( 268 | CY_HANDLE handle, 269 | CY_UART_CONFIG *uartConfig 270 | ); 271 | CY_RETURN_STATUS CySetUartConfig ( 272 | CY_HANDLE handle, 273 | CY_UART_CONFIG *uartConfig 274 | ); 275 | CY_RETURN_STATUS CyUartRead ( 276 | CY_HANDLE handle, 277 | CY_DATA_BUFFER* readBuffer, 278 | UINT32 timeout 279 | ); 280 | CY_RETURN_STATUS CyUartWrite ( 281 | CY_HANDLE handle, 282 | CY_DATA_BUFFER* writeBuffer, 283 | UINT32 timeout 284 | ); 285 | CY_RETURN_STATUS CyUartSetHwFlowControl( 286 | CY_HANDLE handle, 287 | CY_FLOW_CONTROL_MODES mode 288 | ); 289 | CY_RETURN_STATUS CyUartGetHwFlowControl( 290 | CY_HANDLE handle, 291 | CY_FLOW_CONTROL_MODES *mode 292 | ); 293 | CY_RETURN_STATUS CyUartSetRts( 294 | CY_HANDLE handle 295 | ); 296 | CY_RETURN_STATUS CyUartClearRts( 297 | CY_HANDLE handle 298 | ); 299 | CY_RETURN_STATUS CyUartSetDtr( 300 | CY_HANDLE handle 301 | ); 302 | CY_RETURN_STATUS CyUartClearDtr( 303 | CY_HANDLE handle 304 | ); 305 | CY_RETURN_STATUS CyUartSetBreak( 306 | CY_HANDLE handle, 307 | UINT16 timeout 308 | ); 309 | CY_RETURN_STATUS CyGetI2cConfig ( 310 | CY_HANDLE handle, 311 | CY_I2C_CONFIG *i2cConfig 312 | ); 313 | CY_RETURN_STATUS CySetI2cConfig ( 314 | CY_HANDLE handle, 315 | CY_I2C_CONFIG *i2cConfig 316 | ); 317 | CY_RETURN_STATUS CyI2cRead ( 318 | CY_HANDLE handle, 319 | CY_I2C_DATA_CONFIG *dataConfig, 320 | CY_DATA_BUFFER *readBuffer, 321 | UINT32 timeout 322 | ); 323 | CY_RETURN_STATUS CyI2cWrite ( 324 | CY_HANDLE handle, 325 | CY_I2C_DATA_CONFIG *dataConfig, 326 | CY_DATA_BUFFER *writeBuffer, 327 | UINT32 timeout 328 | ); 329 | CY_RETURN_STATUS CyI2cReset( 330 | CY_HANDLE handle, 331 | BOOL resetMode 332 | ); 333 | CY_RETURN_STATUS CyGetSpiConfig ( 334 | CY_HANDLE handle, 335 | CY_SPI_CONFIG *spiConfig 336 | ); 337 | CY_RETURN_STATUS CySetSpiConfig ( 338 | CY_HANDLE handle, 339 | CY_SPI_CONFIG *spiConfig 340 | ); 341 | CY_RETURN_STATUS CySpiReadWrite ( 342 | CY_HANDLE handle, 343 | CY_DATA_BUFFER* readBuffer, 344 | CY_DATA_BUFFER* writeBuffer, 345 | UINT32 timeout 346 | ); 347 | CY_RETURN_STATUS CyJtagEnable ( 348 | CY_HANDLE handle 349 | ); 350 | CY_RETURN_STATUS CyJtagDisable ( 351 | CY_HANDLE handle 352 | ); 353 | CY_RETURN_STATUS CyJtagWrite ( 354 | CY_HANDLE handle, 355 | CY_DATA_BUFFER *writeBuffer, 356 | UINT32 timeout 357 | ); 358 | CY_RETURN_STATUS CyJtagRead ( 359 | CY_HANDLE handle, 360 | CY_DATA_BUFFER *readBuffer, 361 | UINT32 timeout 362 | ); 363 | CY_RETURN_STATUS CyPhdcClrFeature ( 364 | CY_HANDLE handle 365 | ); 366 | CY_RETURN_STATUS CyPhdcSetFeature ( 367 | CY_HANDLE handle 368 | ); 369 | CY_RETURN_STATUS CyPhdcGetStatus ( 370 | CY_HANDLE handle, 371 | UINT16 *dataStatus 372 | ); 373 | """ 374 | -------------------------------------------------------------------------------- /ucdev/hmc5883.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8-unix -*- 3 | 4 | from bitstring import * 5 | 6 | # HMC5883 registers 7 | 8 | CRA = 0 # 00 Configuration Register A Read/Write 9 | CRB = 1 # 01 Configuration Register B Read/Write 10 | MR = 2 # 02 Mode Register Read/Write 11 | DXRA = 3 # 03 Data Output X MSB Register Read 12 | DXRB = 4 # 04 Data Output X LSB Register Read 13 | DYRA = 5 # 05 Data Output Z MSB Register Read 14 | DYRB = 6 # 06 Data Output Z LSB Register Read 15 | DZRA = 7 # 07 Data Output Y MSB Register Read 16 | DZRB = 8 # 08 Data Output Y LSB Register Read 17 | SR = 9 # 09 Status Register Read 18 | IRA = 10 # 10 Identification Register A Read 19 | IRB = 11 # 11 Identification Register B Read 20 | IRC = 12 # 12 Identification Register C Read 21 | 22 | MODE_CONTINUOUS = 0 23 | MODE_SINGLE = 1 24 | 25 | class ValueObject(): 26 | pass 27 | 28 | class HMC5883(): 29 | def __init__(self, i2c, address=0x1E): 30 | self.i2c = i2c 31 | self.cfg = i2c.prepare(slaveAddress=address, isStopBit=0, isNakBit=0) 32 | 33 | def get_reg(self, reg): 34 | self.write(pack(' 0: 114 | kw[f_name] = makeprop(r_bitlen, f_bitlen, f_offset) 115 | f_offset += f_bitlen 116 | r_fields.reverse() 117 | 118 | # dynamically generate class for this register configuration 119 | sub = type(cls.__name__, (cls, ), kw) 120 | sub.__fields = [k for k,v in r_fields if k] 121 | sub.__length = r_bitlen 122 | 123 | obj = int.__new__(sub, address) 124 | protect_object(obj) 125 | return obj 126 | 127 | @property 128 | def fields(self): 129 | return list(self.__fields) 130 | 131 | @property 132 | def length(self): 133 | return self.__length 134 | 135 | """ 136 | Returns a new register instance with given initial value. 137 | """ 138 | def __call__(self, *args, **kwargs): 139 | reg = RegisterValue(self, 0) 140 | if args: 141 | reg.value = args[0] 142 | for k, v in kwargs.items(): 143 | setattr(reg, k, v) 144 | return reg 145 | 146 | class RegisterValue(object): 147 | def __new__(cls, reg, value): 148 | if cls is not RegisterValue: 149 | return object.__new__(cls) 150 | 151 | def makeprop(field): 152 | def fget(self): 153 | fval = (self.__value & field) >> field.offset 154 | return Bits(uint=fval.uint, length=field.bitlen) 155 | def fset(self, val): 156 | curval = self.__value 157 | newval = to_bits(val, curval.length) << field.offset 158 | curval ^= field & curval 159 | self.__value = curval | newval 160 | self.__notify() 161 | return property(fget, fset) 162 | 163 | kw = {} 164 | for f_name in reg.fields: 165 | field = getattr(reg, f_name) 166 | kw[f_name] = makeprop(field) 167 | 168 | obj = type(cls.__name__, (cls, ), kw)(reg, value) 169 | obj.__reg = reg 170 | obj.__mon = {} 171 | obj.value = value 172 | 173 | protect_object(obj) 174 | return obj 175 | 176 | @property 177 | def length(self): 178 | return self.__reg.length 179 | 180 | @property 181 | def value(self): 182 | return BitArray(bytes=self.__value.tobytes()) 183 | 184 | @value.setter 185 | def value(self, value): 186 | self.__value = to_bits(value, self.__reg.length) 187 | self.__notify() 188 | 189 | @property 190 | def fields(self): 191 | return self.__reg.fields 192 | 193 | def subscribe(self, func): 194 | self.__mon[func] = 1 195 | 196 | def unsubscribe(self, func): 197 | if self.__mon.has_key(func): 198 | del self.__mon[func] 199 | 200 | def __notify(self, *args, **kwargs): 201 | for func in self.__mon.keys(): 202 | func(self, *args, **kwargs) 203 | 204 | def __repr__(self): 205 | rep = [] 206 | for f_name in self.fields: 207 | field = getattr(self, f_name) 208 | rep.append("{0}={1}".format(f_name, field)) 209 | return "(" + ", ".join(rep) + ")" 210 | 211 | """ 212 | Returns a new register value instance with the same initial value. 213 | """ 214 | def __call__(self, *args, **kwargs): 215 | reg = RegisterValue(self.__reg, args[0] if args else self.value) 216 | for k, v in kwargs.items(): 217 | setattr(reg, k, v) 218 | return reg 219 | 220 | def __and__(self, v): 221 | return self.value & to_bits(v, self.length) 222 | 223 | def __or__(self, v): 224 | return self.value | to_bits(v, self.length) 225 | 226 | def __xor__(self, v): 227 | return self.value ^ to_bits(v, self.length) 228 | 229 | def __nonzero__(self): 230 | return self.value.uint 231 | 232 | if __name__ == "__main__": 233 | from IPython import embed 234 | 235 | def handle_exception(atype, value, tb): 236 | if hasattr(sys, 'ps1') or not sys.stderr.isatty(): 237 | # we are in interactive mode or we don't have a tty-like 238 | # device, so we call the default hook 239 | sys.__excepthook__(atype, value, tb) 240 | else: 241 | # we are NOT in interactive mode, print the exception... 242 | import traceback 243 | traceback.print_exception(atype, value, tb) 244 | print 245 | 246 | # ...then start the debugger in post-mortem mode. 247 | from IPython import embed 248 | embed() 249 | sys.excepthook = handle_exception 250 | 251 | REG = Register("FOO:3 :1 BAR:4", 0x12) 252 | print(REG) 253 | print(REG.FOO) 254 | print(REG.BAR) 255 | 256 | reg = REG(0xAC) 257 | print(reg) 258 | print(reg.FOO) 259 | print(reg.BAR) 260 | 261 | embed() 262 | -------------------------------------------------------------------------------- /ucdev/si4702.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8-unix -*- 3 | """ 4 | I2C driver for Si4702 FM radio tuner chip. 5 | 6 | This chip can be controlled by either SPI or I2C, and 7 | this driver takes care of I2C part. It's not really an I2C, 8 | but made close enough to be driven by I2C master. 9 | 10 | It turned out the setup I have forces me to take complicated 11 | sequence to enter I2C mode. So this driver is not well tested. 12 | 13 | """ 14 | 15 | from struct import pack, unpack 16 | from ucdev.register import Value, Register 17 | 18 | import logging 19 | log = logging.getLogger(__name__) 20 | 21 | from IPython import embed 22 | 23 | ###################################################################### 24 | # Si4702 registers 25 | # 26 | # Register configuration of Si4702-B16 and Si4702-03-C19 differs, 27 | # but "C" rev seems to be upper compatible. 28 | ###################################################################### 29 | 30 | DEVICEID = Register("PN:4 MFGID:12", 0x00) 31 | CHIPID = Register("REV:6 DEV:4 FIRMWARE:6", 0x01) 32 | POWERCFG = Register( 33 | "DSMUTE DMUTE MONO : RDSM SKMODE SEEKUP SEEK : DISABLE :5 ENABLE", 0x02) 34 | CHANNEL = Register("TUNE :5 CHAN:10", 0x03) 35 | SYSCONFIG1 = Register( 36 | "RDSIEN STCIEN : RDS DE AGCD :2 BLNDADJ:2 GPIO3:2 GPIO2:2 GPIO1:2", 0x04) 37 | SYSCONFIG2 = Register("SEEKTH:8 BAND:2 SPACE:2 VOLUME:4", 0x05) 38 | SYSCONFIG3 = Register("SMUTER:2 SMUTEA:2 :3 VOLEXT SKSNR:4 SKCNT:4", 0x06) 39 | TEST1 = Register("XOSCEN AHIZEN :14", 0x07) 40 | TEST2 = Register(":16", 0x08) 41 | BOOTCONFIG = Register(":16", 0x09) 42 | STATUSRSSI = Register("RDSR STC SF_BL AFCRL RDSS BLERA:2 ST RSSI:8", 0x0A) 43 | READCHAN = Register("BLERB:2 BLERC:2 BLERD:2 READCHAN:10", 0x0B) 44 | RDSA = Register("RDSA:16", 0x0C) 45 | RDSB = Register("RDSB:16", 0x0D) 46 | RDSC = Register("RDSC:16", 0x0E) 47 | RDSD = Register("RDSD:16", 0x0F) 48 | 49 | ###################################################################### 50 | 51 | class SI4702(object): 52 | def __init__(self, i2c, address=0b0010000): 53 | self.i2c = i2c 54 | 55 | # 56 | # AN230 - 2.3. 2-Wire Control Interface 57 | # 58 | # The control word is latched internally on rising SCLK edges 59 | # and is eight bits in length, comprised of a seven bit device 60 | # address equal to 0010000b and a read/write bit (read = 1 and 61 | # write = 0). The ordering of the control word is A6:A0, R/W as 62 | # shown below. The device remains in the read or write state 63 | # until the STOP condition is received. 64 | # 65 | self.cfg = i2c.prepare(slaveAddress=address, isStopBit=1, isNakBit=1) 66 | 67 | # Read registers into "shadow registers" to apply further writes. 68 | self.sreg = self.get_all_regs() 69 | 70 | def read(self, len=1): 71 | buf = bytearray(len) 72 | return self.i2c.read(self.cfg, buf) 73 | 74 | def write(self, data): 75 | return self.i2c.write(self.cfg, data) 76 | 77 | def get_all_regs(self): 78 | # 79 | # AN230 - 2.3. 2-Wire Control Interface 80 | # 81 | # Device register addresses are incremented by an internal 82 | # address counter, starting at the upper byte of register 0Ah, 83 | # followed by the lower byte of register 0Ah, and wrapping 84 | # back to 00h at the end of the register file. 85 | # 86 | tmp = self.read(32) 87 | buf = list(unpack('>16H', tmp)) 88 | loc = (0x0 - 0xA) & 0xF 89 | return buf[loc:] + buf[:loc] 90 | 91 | def get_reg(self, reg, size=None): 92 | regs = self.get_all_regs() 93 | val = regs[reg] 94 | 95 | return reg(val) if isinstance(reg, Register) else val 96 | 97 | def set_reg(self, reg, *arg, **kw): 98 | # update shadow buffer 99 | newval = reg(*arg, **kw).value.uint 100 | 101 | self.sreg = self.get_all_regs() 102 | self.sreg[reg] = newval 103 | 104 | # 105 | # AN230 - 2.3. 2-Wire Control Interface 106 | # 107 | # Device register addresses are incremented by an internal 108 | # address counter, starting with the upper byte of register 109 | # 02h, followed by the lower byte of register 02h, and 110 | # wrapping back to 00h at the end of the register file. 111 | # 112 | 113 | # 114 | # As decribe above, I always need to start writing from 115 | # 02h register. So except 02h register itself, I do a full 116 | # writeback to all R/W registers, from 02h to 09h (inclusive). 117 | # 118 | if reg == 0x2: 119 | # register 02h can be written independently 120 | return self.write(pack('>H', newval)) 121 | else: 122 | # do a full writeback to 02h - 09h 123 | return self.write(pack('>8H', *self.sreg[0x2:(0x9 + 1)])) 124 | 125 | def add_register(cls): 126 | def makeprop(reg): 127 | def fget(self): 128 | tmp = self.get_reg(reg) 129 | def update_hook(v): 130 | self.set_reg(reg, v) 131 | tmp.subscribe(update_hook) 132 | return tmp 133 | def fset(self, v): 134 | self.set_reg(reg, v) 135 | return property(fget, fset) 136 | 137 | for name, reg in globals().items(): 138 | if not hasattr(cls, name) and isinstance(reg, Register): 139 | setattr(cls, name, makeprop(reg)) 140 | 141 | add_register(SI4702) 142 | 143 | # export symbols 144 | __all__ = [i for i in list(locals()) if i.isupper()] 145 | __all__ += [i for i in list(locals()) if i.startswith("SI47")] 146 | --------------------------------------------------------------------------------