/ simpleio.py
simpleio.py
1 # The MIT License (MIT) 2 # 3 # Copyright (c) 2017 Scott Shawcroft for Adafruit Industries. 4 # 5 # Permission is hereby granted, free of charge, to any person obtaining a copy 6 # of this software and associated documentation files (the "Software"), to deal 7 # in the Software without restriction, including without limitation the rights 8 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 # copies of the Software, and to permit persons to whom the Software is 10 # furnished to do so, subject to the following conditions: 11 # 12 # The above copyright notice and this permission notice shall be included in 13 # all copies or substantial portions of the Software. 14 # 15 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 # THE SOFTWARE. 22 """ 23 `simpleio` - Simple, beginner friendly IO. 24 ================================================= 25 26 The `simpleio` module contains classes to provide simple access to IO. 27 28 * Author(s): Scott Shawcroft 29 """ 30 import time 31 import sys 32 try: 33 import audioio 34 except ImportError: 35 pass # not always supported by every board! 36 import array 37 import digitalio 38 import pulseio 39 40 def tone(pin, frequency, duration=1, length=100): 41 """ 42 Generates a square wave of the specified frequency on a pin 43 44 :param ~microcontroller.Pin Pin: Pin on which to output the tone 45 :param float frequency: Frequency of tone in Hz 46 :param int length: Variable size buffer (optional) 47 :param int duration: Duration of tone in seconds (optional) 48 """ 49 if length * frequency > 350000: 50 length = 350000 // frequency 51 try: 52 # pin with PWM 53 #pylint: disable=no-member 54 with pulseio.PWMOut(pin, frequency=int(frequency), variable_frequency=False) as pwm: 55 pwm.duty_cycle = 0x8000 56 time.sleep(duration) 57 #pylint: enable=no-member 58 except ValueError: 59 # pin without PWM 60 sample_length = length 61 square_wave = array.array("H", [0] * sample_length) 62 for i in range(sample_length / 2): 63 square_wave[i] = 0xFFFF 64 if sys.implementation.version[0] >= 3: 65 square_wave_sample = audioio.RawSample(square_wave) 66 square_wave_sample.sample_rate = int(len(square_wave) * frequency) 67 with audioio.AudioOut(pin) as dac: 68 if not dac.playing: 69 dac.play(square_wave_sample, loop=True) 70 time.sleep(duration) 71 dac.stop() 72 else: 73 sample_tone = audioio.AudioOut(pin, square_wave) 74 sample_tone.frequency = int(len(square_wave) * frequency) 75 if not sample_tone.playing: 76 sample_tone.play(loop=True) 77 time.sleep(duration) 78 sample_tone.stop() 79 80 81 82 def bitWrite(x, n, b): #pylint: disable-msg=invalid-name 83 """ 84 Based on the Arduino bitWrite function, changes a specific bit of a value to 0 or 1. 85 The return value is the original value with the changed bit. 86 This function is written for use with 8-bit shift registers 87 88 :param x: numeric value 89 :param n: position to change starting with least-significant (right-most) bit as 0 90 :param b: value to write (0 or 1) 91 """ 92 if b == 1: 93 x |= 1<<n & 255 94 else: 95 x &= ~(1 << n) & 255 96 return x 97 98 99 100 def shift_in(data_pin, clock, msb_first=True): 101 """ 102 Shifts in a byte of data one bit at a time. Starts from either the LSB or 103 MSB. 104 105 .. warning:: Data and clock are swapped compared to other CircuitPython libraries 106 in order to match Arduino. 107 108 :param ~digitalio.DigitalInOut data_pin: pin on which to input each bit 109 :param ~digitalio.DigitalInOut clock: toggles to signal data_pin reads 110 :param bool msb_first: True when the first bit is most significant 111 :return: returns the value read 112 :rtype: int 113 """ 114 115 value = 0 116 i = 0 117 118 for i in range(0, 8): 119 if msb_first: 120 value |= ((data_pin.value) << (7-i)) 121 else: 122 value |= ((data_pin.value) << i) 123 # toggle clock True/False 124 clock.value = True 125 clock.value = False 126 i += 1 127 return value 128 129 def shift_out(data_pin, clock, value, msb_first=True, bitcount=8): 130 """ 131 Shifts out a byte of data one bit at a time. Data gets written to a data 132 pin. Then, the clock pulses hi then low 133 134 .. warning:: Data and clock are swapped compared to other CircuitPython libraries 135 in order to match Arduino. 136 137 :param ~digitalio.DigitalInOut data_pin: value bits get output on this pin 138 :param ~digitalio.DigitalInOut clock: toggled once the data pin is set 139 :param bool msb_first: True when the first bit is most significant 140 :param int value: byte to be shifted 141 :param unsigned bitcount: number of bits to shift 142 143 Example for Metro M0 Express: 144 145 .. code-block:: python 146 147 import digitalio 148 import simpleio 149 from board import * 150 clock = digitalio.DigitalInOut(D12) 151 data_pin = digitalio.DigitalInOut(D11) 152 latchPin = digitalio.DigitalInOut(D10) 153 clock.direction = digitalio.Direction.OUTPUT 154 data_pin.direction = digitalio.Direction.OUTPUT 155 latchPin.direction = digitalio.Direction.OUTPUT 156 157 while True: 158 valueSend = 500 159 # shifting out least significant bits 160 # must toggle latchPin.value before and after shift_out to push to IC chip 161 # this sample code was tested using 162 latchPin.value = False 163 simpleio.shift_out(data_pin, clock, (valueSend>>8), msb_first = False) 164 latchPin.value = True 165 time.sleep(1.0) 166 latchPin.value = False 167 simpleio.shift_out(data_pin, clock, valueSend, msb_first = False) 168 latchPin.value = True 169 time.sleep(1.0) 170 171 # shifting out most significant bits 172 latchPin.value = False 173 simpleio.shift_out(data_pin, clock, (valueSend>>8)) 174 latchPin.value = True 175 time.sleep(1.0) 176 latchpin.value = False 177 simpleio.shift_out(data_pin, clock, valueSend) 178 latchpin.value = True 179 time.sleep(1.0) 180 """ 181 if bitcount < 0 or bitcount > 32: 182 raise ValueError('bitcount must be in range 0..32 inclusive') 183 184 if msb_first: 185 bitsequence = lambda: range(bitcount-1, -1, -1) 186 else: 187 bitsequence = lambda: range(0, bitcount) 188 189 for i in bitsequence(): 190 tmpval = bool(value & (1<<i)) 191 data_pin.value = tmpval 192 # toggle clock pin True/False 193 clock.value = True 194 clock.value = False 195 196 class DigitalOut: 197 """ 198 Simple digital output that is valid until reload. 199 200 :param pin microcontroller.Pin: output pin 201 :param value bool: default value 202 :param drive_mode digitalio.DriveMode: drive mode for the output 203 """ 204 def __init__(self, pin, **kwargs): 205 self.iopin = digitalio.DigitalInOut(pin) 206 self.iopin.switch_to_output(**kwargs) 207 208 @property 209 def value(self): 210 """The digital logic level of the output pin.""" 211 return self.iopin.value 212 213 @value.setter 214 def value(self, value): 215 self.iopin.value = value 216 217 class DigitalIn: 218 """ 219 Simple digital input that is valid until reload. 220 221 :param pin microcontroller.Pin: input pin 222 :param pull digitalio.Pull: pull configuration for the input 223 """ 224 def __init__(self, pin, **kwargs): 225 self.iopin = digitalio.DigitalInOut(pin) 226 self.iopin.switch_to_input(**kwargs) 227 228 @property 229 def value(self): 230 """The digital logic level of the input pin.""" 231 return self.iopin.value 232 233 @value.setter 234 def value(self, value): #pylint: disable-msg=no-self-use, unused-argument 235 raise AttributeError("Cannot set the value on a digital input.") 236 237 def map_range(x, in_min, in_max, out_min, out_max): 238 """ 239 Maps a number from one range to another. 240 Note: This implementation handles values < in_min differently than arduino's map function does. 241 242 :return: Returns value mapped to new range 243 :rtype: float 244 """ 245 in_range = in_max - in_min 246 in_delta = x - in_min 247 if in_range != 0: 248 mapped = in_delta / in_range 249 elif in_delta != 0: 250 mapped = in_delta 251 else: 252 mapped = .5 253 mapped *= out_max - out_min 254 mapped += out_min 255 if out_min <= out_max: 256 return max(min(mapped, out_max), out_min) 257 return min(max(mapped, out_max), out_min)