Source code for pyBusPirateLite.BitBang

# Created by Sean Nelson on 2009-10-14.
# Copyright 2009 Sean Nelson <audiohacked@gmail.com>
# 
# Overhauled and edited by Garrett Berg on 2011- 1 - 22
# Copyright 2011 Garrett Berg <cloudform511@gmail.com>
# 
# pyBusPirate is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# 
# pyBusPirate is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with pyBusPirate.  If not, see <http://www.gnu.org/licenses/>.

from .base import BusPirate, ProtocolError


[docs]class BitBang(BusPirate): """ Provide access to the Bus Pirate bitbang mode""" def __init__(self, portname='', speed=115200, timeout=0.1, connect=True): """ This constructor by default conntects to the first buspirate it can find. If you don't want that, set connect to False. Parameters ---------- portname : str Name of comport (/dev/bus_pirate or COM3) speed : int Communication speed, use default of 115200 timeout : int Timeout in s to wait for reply Examples -------- >>> bb = BitBang() """ super().__init__(portname, speed, timeout, connect) @property def outputs(self): """ Returns ------- byte Current state of the pins PIN_AUX, PIN_MOSI, PIN_CLK, PIN_MISO, PIN_CS """ self.write(0x40 | ~ self.pins_direction & 0x1f) # map input->1, output->0 **TODO** self.timeout(self.minDelay * 10) return ord(self.response(1, binary=True)) & 0x1f @outputs.setter def outputs(self, pinlist=0): """ Configure pins as input our output Notes ----- The Bus pirate responds to each direction update with a byte showing the current state of the pins, regardless of direction. This is useful for open collector I/O modes. Used in every mode to configure pins. In bb it configures as either input or output, in the other modes it normally configures peripherals such as power supply and the aux pin Parameters ---------- pinlist : byte List of pins to be set as outputs (default: all inputs) PIN_AUX, PIN_MOSI, PIN_CLK, PIN_MISO, PIN_CS Returns ------- byte Current state of the pins PIN_AUX, PIN_MOSI, PIN_CLK, PIN_MISO, PIN_CS """ self.pins_direction = pinlist & 0x1f self.write(0x40 | ~ self.pins_direction & 0x1f) # map input->1, output->0 self.timeout(self.minDelay * 10) self.response(1, binary=True) @property def pins(self): """ Get pins status Returns ------- byte Current state of the pins PIN_POWER, PIN_PULLUP, PIN_AUX, PIN_MOSI, PIN_CLK, PIN_MISO, PIN_CS """ self.write(0x80 | (self.pins_state & 0x7f)) self.timeout(self.minDelay * 10) self.pins_state = ord(self.response(1, binary=True)) & 0x7f return self.pins_state @pins.setter def pins(self, pinlist=0): """ Set pins to high or low Notes ----- The lower 7bits of the command byte control the Bus Pirate pins and peripherals. Bitbang works like a player piano or bitmap. The Bus Pirate pins map to the bits in the command byte as follows: 1|POWER|PULLUP|AUX|MOSI|CLK|MISO|CS The Bus pirate responds to each update with a byte in the same format that shows the current state of the pins. Parameters ---------- pinlist : byte List of pins to be set high PIN_POWER, PIN_PULLUP, PIN_AUX, PIN_MOSI, PIN_CLK, PIN_MISO, PIN_CS """ self.pins_state = pinlist & 0x7f self.write(0x80 | self.pins_state) self.timeout(self.minDelay * 10) self.pins_state = ord(self.response(1, binary=True)) & 0x7f @property def adc(self): """Returns the voltage from ADC pin Returns ------- float Voltage measured at ADC pin """ self.write(0x14) self.timeout(self.minDelay) ret = self.response(2, binary=True) voltage = (ret[0] << 8) + ret[1] voltage = (voltage * 6.6) / 1024 return voltage
[docs] def start_getting_adc_voltages(self): """Start continuously getting adc voltages. Notes ----- use memberfunction enter_bb to exit, use get_next_adc_voltage to get the next one. """ self.write(0x15)
[docs] def get_next_adc_voltage(self): ret = self.response(2, binary=True) voltage = (ret[0] << 8) + ret[1] voltage = (voltage * 6.6) / 1024 if voltage < 10: """sometimes the input gets out of sync. This is the best error checking currently available, firmware will probably be updated to expect a 101 or something in the top byte, which will be better error checking""" self.recurse_end() return voltage self.response(1, binary=True) # get an additional byte and then flush self.port.flushInput() return self.recurse(self.get_next_adc_voltage)
[docs] def stop_getting_adc_voltages(self): """I was encountering problems resetting out of adc mode, so I wrote this little function""" self.port.flushInput() for i in range(5): self.write(0x00) #r, w, e = select.select([self.port], [], [], 0.01); r = self.response(1, binary=True) if r: break; self.port.flushInput() self.enter_bb() return 1
[docs] def selftest(self, complete=False): """ Self test Parameters ---------- complete: bool Requires jumpers between +5 and Vpu, +3.3 and ADC Notes ----- Self-tests are access from the binary bitbang mode. There are actually two self-tests available. T he full test is the same as self-test in the user terminal, it requires jumpers between two sets of pins in order to test some features. The short test eliminates the six checks that require jumpers. After the test is complete, the Bus Pirate responds with the number of errors. It also echoes any input plus the number of errors. The MODE LED blinks if the test was successful, or remains solid if there were errors. Exit the self-test by sending 0xff, the Bus Pirate will respond 0x01 and return to binary bitbang mode. Returns ------- int Number of errors """ self.port.flushInput() if complete is True: self.write(0x11) else: self.write(0x10) self.timeout(1) errors = self.response(1, binary=True) self.write(0xff) resp = self.response(1, binary=True) if resp != b'\x01': raise ProtocolError('Self test did not return to bitbang mode') self.timeout(self.minDelay) return ord(errors)
[docs] def enable_PWM(self, frequency, dutycycle=.5): """ Enable PWM output Parameters ---------- frequency: float PWM frequency in Hz dutycycle: float Duty cycle between 0 (0%) and 1 (100%) Notes ----- Configure and enable pulse-width modulation output in the AUX pin. Requires a 5 byte configuration sequence. Responds 0x01 after a complete sequence is received. The PWM remains active after leaving binary bitbang mode! Equations to calculate the PWM frequency and period are in the PIC24F output compare manual. Bit 0 and 1 of the first configuration byte set the prescaler value. The Next two bytes set the duty cycle register, high 8bits first. The final two bytes set the period register, high 8bits first. Parameter calculation stolen from http://codepad.org/qtYpZmIF """ if dutycycle > 1: raise ValueError('Duty cycle should be between 0 and 1') Fosc = 24e6 Tcy = 2.0 / Fosc PwmPeriod = 1.0 / frequency # find needed prescaler PrescalerList = {0: 1, 1: 8, 2: 64, 3: 256} for n in range(4): Prescaler = PrescalerList[n] PRy = PwmPeriod * 1.0 / (Tcy * Prescaler) PRy = int(PRy - 1) OCR = int(PRy * dutycycle) if PRy < (2 ** 16 - 1): break # valid value for PRy, keep values else: raise ValueError('frequency requested is invalid') prescaler = Prescaler dutycycle = OCR period = PRy self.write(0x12) self.write(prescaler) self.write((dutycycle >> 8) & 0xFF) self.write(dutycycle & 0xFF) self.write((period >> 8) & 0xFF) self.write(period & 0xFF) self.timeout(self.minDelay * 10) if self.response(1, binary=True) != b'\x01': raise ValueError("Could not setup PWM mode")
[docs] def disable_PWM(self): """ Clear/disable PWM """ self.write(0x13) self.timeout(self.minDelay * 10) if self.response(1, binary=True) != b'\x01': raise ValueError("Could not disable PWM mode")