-
Notifications
You must be signed in to change notification settings - Fork 0
Hardware API
The aim is to make the pyb module obsolete, make all its functionality cleaner and more board-generic, and move such functionality elsewhere. Below is the current proposal, very much a work in progress.
The main aim is to provide Python modules/functions/classes that abstract the hardware in a Pythonic way.
The API should be Pythonic, obvious and relatively minimal. There should be a close mapping from functions to hardware and there should be as little magic as possible. The API should be as consistent across peripherals (Pin, UART, I2C, ADC, etc) as possible. There should usually be only one way to do something. A method name should do exactly what it says and no more (ie it shouldn't be heavily overloaded).
These use-cases need to be written:
- Basic I/O on a pin
- PWM on a pin
- ADC on a pin
- Using a Timer to do a one-shot callback
- Using a Timer to do a repeated callback
The classes to control the peripherals of the board will reside in a new module called hardware, therefore, by doing:
import hardware
dir(hardware)one can easily see what's supported on the board.
pin = Pin(id, mode, pull=Pin.PULL_NONE, *, value, drive, slew, alt, ...)
-
id, andmodeare mandatory and positional (modecan kw). -
pullis optional and positional (also can be named). - The rest of args are kwonly.
- Only
valueis required for a port to implement (initial value if given). - The rest are optional, and a port can define them and also define others.
Besides Pin.IN and Pin.OUT, mode can also take Pin.ALT and Pin.ALT_OPEN_DRAIN, but the alternate function itself cannot be configured via the Pin class, since this will be done via the other hardware classes (UART, SPI, I2C, etc.). However, Pin.ALT and Pin.ALT_OPEN_DRAIN exist to allow custom control of the pin settings after it has been taken by another peripheral. For example:
# create and initialize UART1 with TX and RX on GP1 and GP2 respectively.
UART(1, 9600, pins=['GP1', 'GP2'])
# enable the pull-ups on both UART1 pins
Pin('GP1', mode=Pin.ALT, pull=Pin.PULL_UP)
Pin('GP2', mode=Pin.ALT, pull=Pin.PULL_UP)-
pin.init(...)re init. -
pin.high()set high. -
pin.low()set low. -
pin.value()get value. -
pin.value(x)set value. -
pin()fast method to get the value of the pin. -
pin(value)fast method to set the value of the pin. pin.toggle()
Getters and setters
-
pin.id()get only. - need to clarify what this really returns. I'm assuming a board ID. pin.mode([mode])pin.pull([pull])pin.drive([drive])pin.slew([slew])
-
for mode:
Pin.IN,Pin.OUT,Pin.OPEN_DRAIN,Pin.ALT,Pin.ALT_OPEN_DRAIN -
for pull:
Pin.PULL_UP,Pin.PULL_DOWN,Pin.PULL_NONE, optional:Pin.PULL_UP_STRONG,Pin.PULL_DOWN_WEAK, ... -
for drive:
Pin.LOW_POWER,Pin.MED_POWER,Pin.HIGH_POWER -
for slew:
Pin.FAST_RISE,Pin.SLOW_RISE -
for interrupt (callback) mode:
pin.INT_RISING,pin.INT_FALLING,pin.INT_RISING_FALLING,pin.INT_HIGH_LEVEL,pin.INT_LOW_LEVEL
- Pins list:
Pin.board(mandatory) andPin.cpu(optional).
uart = UART(id, baudrate=9600, bits=8, parity=None, stop=0, *, pins, ...)
-
pinsis a 4 or 2 item list indicating the TX, RX, RTS and CTS pins (in that order). Any of the pins can beNoneif one wants the UART to operate with limited functionality. If theRTSpin is given the theRXpin must be given as well. The same applies toCTS. When no pins are given (or all areNone), then the default set ofTXandRXpins is taken, and hardware flow control will be disabled. Example
uart = UART(1, 9600, 8, pins=['GP1', 'GP2']) # No hardware flow control
uart = UART(1, 9600, 8, pins=['GP1', 'GP2', 'GP7', 'GP6']) # RTS/CTS flow control
uart = UART(1, 9600, 8, pins=['GP1', None]) # Tx only
uart = UART(1, 9600, 8, pins=[None, 'GP2']) # Rx only
uart = UART(1, 9600, 8, pins=['GP1']) # Raise
uart = UART(1, 9600, 8, pins=['GP1', 'GP2', 'GP7']) # Raise
uart = UART(1, 9600, 8, pins=['GP1', 'GP2', 'GP7', None]) # OK, RTS only
uart = UART(1, 9600, 8) # Default pins are used. No flow controluart.init(...)uart.read([nbytes])uart.readinto(buf[, nbytes])uart.readall()uart.readline([max_size])uart.readchar()uart.write(buf)uart.writechar(char)uart.sendbreak()
adc = ADC(id, *, bits, ...)
-
adc.init(...)re-init -
adc.readchannel(channel)read a specific channel -
apin = adc(pin)make an analog pin (will choose the correct channel for the pin) apin.read()apin.readtimed(buf, timer)
i2c = I2C(id, mode, *, baudrate, addr)
Master mode transfers (do we need the char methods?):
i2c.readfrom(addr, nbytes)i2c.readfrom_into(addr, buf)i2c.readcharfrom(addr)-
i2c.writeto(addr, buf, *, stop=True)stop is if we want to send a stop bit at the end i2c.writecharto(addr, chr, *, stop=True)
Master mode mem transfers:
i2c.readfrom_mem(addr, memaddr, nbytes, memaddr, *, addrsize=8)i2c.readfrom_mem_into(addr, memaddr, buf, *, addrsize=8)i2c.readcharfrom_mem(addr, memaddr, *, addrsize=8)i2c.writeto_mem(addr, memaddr, buf, *, addrsize=8)i2c.writecharto_mem(addr, memaddr, chr, *, addrsize=8)
Slave mode:
i2c.read(nbytes)i2c.readinto(buf)i2c.readchar()i2c.write(buf)i2c.writechar(chr)
spi = SPI(id, mode, *, baudrate, polarity=1, phase=0, bits=8, firstbit=SPI.MSB)
Methods:
spi.write(buf)spi.writechar(chr)spi.read(nbytes, *, write=0x00)spi.readinto(buf, *, write=0x00)spi.readchar(*, write=0x00)spi.write_readinto(write_buf, read_buf)
-
machine.freq([freq, ...])get or set CPU and/or bus frequencies -
machine.idle([... options])idle the CPU, may require external event to leave idle mode -
machine.sleep([... options])enter sleep mode that retains RAM and continues execution when woken -
machine.deepsleep([... options])enter sleep mode that may not retain RAM and may reset when woken
machine.SLEEPmachine.DEEPSLEEPmachine.PWR_ON_RESETmachine.HARD_RESETmachine.SOFT_RESETmachine.WDT_RESETmachine.DEEPSLEEP_RESETmachine.NETWORK_WAKEmachine.PIN_WAKEmachine.TIMER_WAKE
The sys module has uPy specific functions:
-
sys.dup_stdio([stream_obj])get or set duplication of global stdio, so that REPL can be redirected to UART or other (note: could allow to duplicate to multiple stream objs but that's arguably overkill and could anyway be done in Python by making a stream multiplexer)
The time module has uPy specific functions:
time.sleep_ms(ms)time.sleep_us(us)time.ticks_ms()time.ticks_us()time.ticks_cpu()time.ticks_diff(t0, t1)
The os module has uPy specific functions:
os.mount(block_dev, mount_point, *, readonly)os.umount(mount_point)os.mkfs(device or path, *, options...)