diff options
Diffstat (limited to 'drivers')
| -rw-r--r-- | drivers/dht/dht.c | 4 | ||||
| -rw-r--r-- | drivers/display/lcd160cr.py | 474 | ||||
| -rw-r--r-- | drivers/display/lcd160cr_test.py | 171 | ||||
| -rw-r--r-- | drivers/display/ssd1306.py | 161 | ||||
| -rw-r--r-- | drivers/memory/spiflash.c | 200 | ||||
| -rw-r--r-- | drivers/memory/spiflash.h | 42 | ||||
| -rw-r--r-- | drivers/nrf24l01/nrf24l01.py | 114 | ||||
| -rw-r--r-- | drivers/nrf24l01/nrf24l01test.py | 4 | ||||
| -rw-r--r-- | drivers/onewire/ds18x20.py | 6 | ||||
| -rw-r--r-- | drivers/sdcard/sdcard.py | 26 |
10 files changed, 1134 insertions, 68 deletions
diff --git a/drivers/dht/dht.c b/drivers/dht/dht.c index 1f0cffc6f..6bdda44b4 100644 --- a/drivers/dht/dht.c +++ b/drivers/dht/dht.c @@ -65,7 +65,7 @@ STATIC mp_obj_t dht_readinto(mp_obj_t pin_in, mp_obj_t buf_in) { // time pulse, should be 80us ticks = machine_time_pulse_us(pin, 1, 150); - if (ticks == (mp_uint_t)-1) { + if ((mp_int_t)ticks < 0) { goto timeout; } @@ -73,7 +73,7 @@ STATIC mp_obj_t dht_readinto(mp_obj_t pin_in, mp_obj_t buf_in) { uint8_t *buf = bufinfo.buf; for (int i = 0; i < 40; ++i) { ticks = machine_time_pulse_us(pin, 1, 100); - if (ticks == (mp_uint_t)-1) { + if ((mp_int_t)ticks < 0) { goto timeout; } buf[i / 8] = (buf[i / 8] << 1) | (ticks > 48); diff --git a/drivers/display/lcd160cr.py b/drivers/display/lcd160cr.py new file mode 100644 index 000000000..dd9ab9985 --- /dev/null +++ b/drivers/display/lcd160cr.py @@ -0,0 +1,474 @@ +# Driver for official MicroPython LCD160CR display +# MIT license; Copyright (c) 2017 Damien P. George + +from micropython import const +from utime import sleep_ms +from ustruct import calcsize, pack_into +import uerrno, machine + +# for set_orient +PORTRAIT = const(0) +LANDSCAPE = const(1) +PORTRAIT_UPSIDEDOWN = const(2) +LANDSCAPE_UPSIDEDOWN = const(3) + +# for set_startup_deco; can be or'd +STARTUP_DECO_NONE = const(0) +STARTUP_DECO_MLOGO = const(1) +STARTUP_DECO_INFO = const(2) + +_uart_baud_table = { + 2400: 0, + 4800: 1, + 9600: 2, + 19200: 3, + 38400: 4, + 57600: 5, + 115200: 6, + 230400: 7, + 460800: 8, +} + +class LCD160CR: + def __init__(self, connect=None, *, pwr=None, i2c=None, spi=None, i2c_addr=98): + if connect in ('X', 'Y', 'XY', 'YX'): + i = connect[-1] + j = connect[0] + y = j + '4' + elif connect == 'C': + i = 2 + j = 2 + y = 'A7' + else: + if pwr is None or i2c is None or spi is None: + raise ValueError('must specify valid "connect" or all of "pwr", "i2c" and "spi"') + + if pwr is None: + pwr = machine.Pin(y, machine.Pin.OUT) + if i2c is None: + i2c = machine.I2C(i, freq=1000000) + if spi is None: + spi = machine.SPI(j, baudrate=13500000, polarity=0, phase=0) + + if not pwr.value(): + pwr(1) + sleep_ms(10) + # else: + # alread have power + # lets be optimistic... + + # set connections + self.pwr = pwr + self.i2c = i2c + self.spi = spi + self.i2c_addr = i2c_addr + + # create temp buffers and memoryviews + self.buf16 = bytearray(16) + self.buf19 = bytearray(19) + self.buf = [None] * 10 + for i in range(1, 10): + self.buf[i] = memoryview(self.buf16)[0:i] + self.buf1 = self.buf[1] + self.array4 = [0, 0, 0, 0] + + # set default orientation and window + self.set_orient(PORTRAIT) + self._fcmd2b('<BBBBBB', 0x76, 0, 0, self.w, self.h) # viewport 'v' + self._fcmd2b('<BBBBBB', 0x79, 0, 0, self.w, self.h) # window 'y' + + def _send(self, cmd): + i = self.i2c.writeto(self.i2c_addr, cmd) + if i == len(cmd): + return + cmd = memoryview(cmd) + n = len(cmd) + while True: + i += self.i2c.writeto(self.i2c_addr, cmd[i:]) + if i == n: + return + sleep_ms(10) + + def _fcmd2(self, fmt, a0, a1=0, a2=0): + buf = self.buf[calcsize(fmt)] + pack_into(fmt, buf, 0, 2, a0, a1, a2) + self._send(buf) + + def _fcmd2b(self, fmt, a0, a1, a2, a3, a4=0): + buf = self.buf[calcsize(fmt)] + pack_into(fmt, buf, 0, 2, a0, a1, a2, a3, a4) + self._send(buf) + + def _waitfor(self, n, buf): + t = 5000 + while t: + self.i2c.readfrom_into(self.i2c_addr, self.buf1) + if self.buf1[0] >= n: + self.i2c.readfrom_into(self.i2c_addr, buf) + return + t -= 1 + sleep_ms(1) + raise OSError(uerrno.ETIMEDOUT) + + def oflush(self, n=255): + t = 5000 + while t: + self.i2c.readfrom_into(self.i2c_addr + 1, self.buf1) + r = self.buf1[0] + if r >= n: + return + t -= 1 + machine.idle() + raise OSError(uerrno.ETIMEDOUT) + + def iflush(self): + t = 5000 + while t: + self.i2c.readfrom_into(self.i2c_addr, self.buf16) + if self.buf16[0] == 0: + return + t -= 1 + sleep_ms(1) + raise OSError(uerrno.ETIMEDOUT) + + #### MISC METHODS #### + + @staticmethod + def rgb(r, g, b): + return ((b & 0xf8) << 8) | ((g & 0xfc) << 3) | (r >> 3) + + @staticmethod + def clip_line(c, w, h): + while True: + ca = ce = 0 + if c[1] < 0: + ca |= 8 + elif c[1] > h: + ca |= 4 + if c[0] < 0: + ca |= 1 + elif c[0] > w: + ca |= 2 + if c[3] < 0: + ce |= 8 + elif c[3] > h: + ce |= 4 + if c[2] < 0: + ce |= 1 + elif c[2] > w: + ce |= 2 + if ca & ce: + return False + elif ca | ce: + ca |= ce + if ca & 1: + if c[2] < c[0]: + c[0], c[2] = c[2], c[0] + c[1], c[3] = c[3], c[1] + c[1] += ((-c[0]) * (c[3] - c[1])) // (c[2] - c[0]) + c[0] = 0 + elif ca & 2: + if c[2] < c[0]: + c[0], c[2] = c[2], c[0] + c[1], c[3] = c[3], c[1] + c[3] += ((w - 1 - c[2]) * (c[3] - c[1])) // (c[2] - c[0]) + c[2] = w - 1 + elif ca & 4: + if c[0] == c[2]: + if c[1] >= h: + c[1] = h - 1 + if c[3] >= h: + c[3] = h - 1 + else: + if c[3] < c[1]: + c[0], c[2] = c[2], c[0] + c[1], c[3] = c[3], c[1] + c[2] += ((h - 1 - c[3]) * (c[2] - c[0])) // (c[3] - c[1]) + c[3] = h - 1 + else: + if c[0] == c[2]: + if c[1] < 0: + c[1] = 0 + if c[3] < 0: + c[3] = 0 + else: + if c[3] < c[1]: + c[0], c[2] = c[2], c[0] + c[1], c[3] = c[3], c[1] + c[0] += ((-c[1]) * (c[2] - c[0])) // (c[3] - c[1]) + c[1] = 0 + else: + return True + + #### SETUP COMMANDS #### + + def set_power(self, on): + self.pwr(on) + sleep_ms(15) + + def set_orient(self, orient): + self._fcmd2('<BBB', 0x14, (orient & 3) + 4) + # update width and height variables + self.iflush() + self._send(b'\x02g0') + self._waitfor(4, self.buf[5]) + self.w = self.buf[5][1] + self.h = self.buf[5][2] + + def set_brightness(self, value): + self._fcmd2('<BBB', 0x16, value) + + def set_i2c_addr(self, addr): + # 0x0e set i2c addr + if addr & 3: + raise ValueError('must specify mod 4 aligned address') + self._fcmd2('<BBW', 0x0e, 0x433249 | (addr << 24)) + + def set_uart_baudrate(self, baudrate): + try: + baudrate = _uart_baud_table[baudrate] + except KeyError: + raise ValueError('invalid baudrate') + self._fcmd2('<BBB', 0x18, baudrate) + + def set_startup_deco(self, value): + self._fcmd2('<BBB', 0x19, value) + + def save_to_flash(self): + self._send(b'\x02fn') + + #### PIXEL ACCESS #### + + def set_pixel(self, x, y, c): + self._fcmd2b('<BBBBH', 0x41, x, y, c) + + def get_pixel(self, x, y): + self._fcmd2('<BBBB', 0x61, x, y) + t = 1000 + while t: + self.i2c.readfrom_into(self.i2c_addr, self.buf1) + if self.buf1[0] >= 2: + self.i2c.readfrom_into(self.i2c_addr, self.buf[3]) + return self.buf[3][1] | self.buf[3][2] << 8 + t -= 1 + sleep_ms(1) + raise OSError(uerrno.ETIMEDOUT) + + def get_line(self, x, y, buf): + l = len(buf) // 2 + self._fcmd2b('<BBBBB', 0x10, l, x, y) + l *= 2 + t = 1000 + while t: + self.i2c.readfrom_into(self.i2c_addr, self.buf1) + if self.buf1[0] >= l: + self.i2c.readfrom_into(self.i2c_addr, buf) + return + t -= 1 + sleep_ms(1) + raise OSError(uerrno.ETIMEDOUT) + + def screen_dump(self, buf, x=0, y=0, w=None, h=None): + if w is None: + w = self.w - x + if h is None: + h = self.h - y + if w <= 127: + line = bytearray(2 * w + 1) + line2 = None + else: + # split line if more than 254 bytes needed + buflen = (w + 1) // 2 + line = bytearray(2 * buflen + 1) + line2 = memoryview(line)[:2 * (w - buflen) + 1] + for i in range(min(len(buf) // (2 * w), h)): + ix = i * w * 2 + self.get_line(x, y + i, line) + buf[ix:ix + len(line) - 1] = memoryview(line)[1:] + ix += len(line) - 1 + if line2: + self.get_line(x + buflen, y + i, line2) + buf[ix:ix + len(line2) - 1] = memoryview(line2)[1:] + ix += len(line2) - 1 + + def screen_load(self, buf): + l = self.w * self.h * 2+2 + self._fcmd2b('<BBHBBB', 0x70, l, 16, self.w, self.h) + n = 0 + ar = memoryview(buf) + while n < len(buf): + if len(buf) - n >= 0x200: + self._send(ar[n:n + 0x200]) + n += 0x200 + else: + self._send(ar[n:]) + while n < self.w * self.h * 2: + self._send(b'\x00') + n += 1 + + #### TEXT COMMANDS #### + + def set_pos(self, x, y): + self._fcmd2('<BBBB', 0x58, x, y) + + def set_text_color(self, fg, bg): + self._fcmd2('<BBHH', 0x63, fg, bg) + + def set_font(self, font, scale=0, bold=0, trans=0, scroll=0): + self._fcmd2('<BBBB', 0x46, (scroll << 7) | (trans << 6) | ((font & 3) << 4) | (bold & 0xf), scale & 0xff) + + def write(self, s): + # TODO: eventually check for room in LCD input queue + self._send(s) + + #### PRIMITIVE DRAWING COMMANDS #### + + def set_pen(self, line, fill): + self._fcmd2('<BBHH', 0x50, line, fill) + + def erase(self): + self._send(b'\x02\x45') + + def dot(self, x, y): + if 0 <= x < self.w and 0 <= y < self.h: + self._fcmd2('<BBBB', 0x4b, x, y) + + def rect(self, x, y, w, h, cmd=0x72): + if x + w <= 0 or y + h <= 0 or x >= self.w or y >= self.h: + return + elif x < 0 or y < 0: + left = top = True + if x < 0: + left = False + w += x + x = 0 + if y < 0: + top = False + h += y + y = 0 + if cmd == 0x51 or cmd == 0x72: + # draw interior + self._fcmd2b('<BBBBBB', 0x51, x, y, min(w, 255), min(h, 255)) + if cmd == 0x57 or cmd == 0x72: + # draw outline + if left: + self._fcmd2b('<BBBBBB', 0x57, x, y, 1, min(h, 255)) + if top: + self._fcmd2b('<BBBBBB', 0x57, x, y, min(w, 255), 1) + if x + w < self.w: + self._fcmd2b('<BBBBBB', 0x57, x + w, y, 1, min(h, 255)) + if y + h < self.h: + self._fcmd2b('<BBBBBB', 0x57, x, y + h, min(w, 255), 1) + else: + self._fcmd2b('<BBBBBB', cmd, x, y, min(w, 255), min(h, 255)) + + def rect_outline(self, x, y, w, h): + self.rect(x, y, w, h, 0x57) + + def rect_interior(self, x, y, w, h): + self.rect(x, y, w, h, 0x51) + + def line(self, x1, y1, x2, y2): + ar4 = self.array4 + ar4[0] = x1 + ar4[1] = y1 + ar4[2] = x2 + ar4[3] = y2 + if self.clip_line(ar4, self.w, self.h): + self._fcmd2b('<BBBBBB', 0x4c, ar4[0], ar4[1], ar4[2], ar4[3]) + + def dot_no_clip(self, x, y): + self._fcmd2('<BBBB', 0x4b, x, y) + + def rect_no_clip(self, x, y, w, h): + self._fcmd2b('<BBBBBB', 0x72, x, y, w, h) + + def rect_outline_no_clip(self, x, y, w, h): + self._fcmd2b('<BBBBBB', 0x57, x, y, w, h) + + def rect_interior_no_clip(self, x, y, w, h): + self._fcmd2b('<BBBBBB', 0x51, x, y, w, h) + + def line_no_clip(self, x1, y1, x2, y2): + self._fcmd2b('<BBBBBB', 0x4c, x1, y1, x2, y2) + + def poly_dot(self, data): + if len(data) & 1: + raise ValueError('must specify even number of bytes') + self._fcmd2('<BBB', 0x71, len(data) // 2) + self._send(data) + + def poly_line(self, data): + if len(data) & 1: + raise ValueError('must specify even number of bytes') + self._fcmd2('<BBB', 0x78, len(data) // 2) + self._send(data) + + #### TOUCH COMMANDS #### + + def touch_config(self, calib=False, save=False, irq=None): + self._fcmd2('<BBBB', 0x7a, (irq is not None) << 2 | save << 1 | calib, bool(irq) << 7) + + def is_touched(self): + self._send(b'\x02T') + b = self.buf[4] + self._waitfor(3, b) + return b[1] >> 7 != 0 + + def get_touch(self): + self._send(b'\x02T') # implicit LCD output flush + b = self.buf[4] + self._waitfor(3, b) + return b[1] >> 7, b[2], b[3] + + #### ADVANCED COMMANDS #### + + def set_spi_win(self, x, y, w, h): + pack_into('<BBBHHHHHHHH', self.buf19, 0, 2, 0x55, 10, x, y, x + w - 1, y + h - 1, 0, 0, 0, 0xffff) + self._send(self.buf19) + + def fast_spi(self, flush=True): + if flush: + self.oflush() + self._send(b'\x02\x12') + return self.spi + + def show_framebuf(self, buf): + self.fast_spi().write(buf) + + def set_scroll(self, on): + self._fcmd2('<BBB', 0x15, on) + + def set_scroll_win(self, win, x=-1, y=0, w=0, h=0, vec=0, pat=0, fill=0x07e0, color=0): + pack_into('<BBBHHHHHHHH', self.buf19, 0, 2, 0x55, win, x, y, w, h, vec, pat, fill, color) + self._send(self.buf19) + + def set_scroll_win_param(self, win, param, value): + self._fcmd2b('<BBBBH', 0x75, win, param, value) + + def set_scroll_buf(self, s): + l = len(s) + if l > 32: + raise ValueError('length must be 32 or less') + self._fcmd2('<BBB', 0x11, l) + self._send(s) + + def jpeg_start(self, l): + if l > 0xffff: + raise ValueError('length must be 65535 or less') + self.oflush() + self._fcmd2('<BBH', 0x6a, l) + + def jpeg_data(self, buf): + self._send(buf) + + def jpeg(self, buf): + self.jpeg_start(len(buf)) + self.jpeg_data(buf) + + def feed_wdt(self): + self._send(b'\x02\x17') + + def reset(self): + self._send(b'\x02Y\xef\xbe\xad\xde') + sleep_ms(15) diff --git a/drivers/display/lcd160cr_test.py b/drivers/display/lcd160cr_test.py new file mode 100644 index 000000000..8b7ef4555 --- /dev/null +++ b/drivers/display/lcd160cr_test.py @@ -0,0 +1,171 @@ +# Driver test for official MicroPython LCD160CR display +# MIT license; Copyright (c) 2017 Damien P. George + +import time, math, framebuf, lcd160cr + +def get_lcd(lcd): + if type(lcd) is str: + lcd = lcd160cr.LCD160CR(lcd) + return lcd + +def show_adc(lcd, adc): + data = [adc.read_core_temp(), adc.read_core_vbat(), 3.3] + try: + data[2] = adc.read_vref() + except: + pass + for i in range(3): + lcd.set_text_color((825, 1625, 1600)[i], 0) + if lcd.h == 160: + lcd.set_font(2) + lcd.set_pos(0, 100 + i * 16) + else: + lcd.set_font(2, trans=1) + lcd.set_pos(0, lcd.h-60 + i * 16) + lcd.write('%4s: ' % ('TEMP', 'VBAT', 'VREF')[i]) + if i > 0: + s = '%6.3fV' % data[i] + else: + s = '%5.1f°C' % data[i] + if lcd.h == 160: + lcd.set_font(1, bold=0, scale=1) + else: + lcd.set_font(1, bold=0, scale=1, trans=1) + lcd.set_pos(45, lcd.h-60 + i * 16) + lcd.write(s) + +def test_features(lcd, orient=lcd160cr.PORTRAIT): + # if we run on pyboard then use ADC and RTC features + try: + import pyb + adc = pyb.ADCAll(12, 0xf0000) + rtc = pyb.RTC() + except: + adc = None + rtc = None + + # set orientation and clear screen + lcd = get_lcd(lcd) + lcd.set_orient(orient) + lcd.set_pen(0, 0) + lcd.erase() + + # create M-logo + mlogo = framebuf.FrameBuffer(bytearray(17 * 17 * 2), 17, 17, framebuf.RGB565) + mlogo.fill(0) + mlogo.fill_rect(1, 1, 15, 15, 0xffffff) + mlogo.vline(4, 4, 12, 0) + mlogo.vline(8, 1, 12, 0) + mlogo.vline(12, 4, 12, 0) + mlogo.vline(14, 13, 2, 0) + + # create inline framebuf + offx = 14 + offy = 19 + w = 100 + h = 75 + fbuf = framebuf.FrameBuffer(bytearray(w * h * 2), w, h, framebuf.RGB565) + lcd.set_spi_win(offx, offy, w, h) + + # initialise loop parameters + tx = ty = 0 + t0 = time.ticks_us() + + for i in range(300): + # update position of cross-hair + t, tx2, ty2 = lcd.get_touch() + if t: + tx2 -= offx + ty2 -= offy + if tx2 >= 0 and ty2 >= 0 and tx2 < w and ty2 < h: + tx, ty = tx2, ty2 + else: + tx = (tx + 1) % w + ty = (ty + 1) % h + + # create and show the inline framebuf + fbuf.fill(lcd.rgb(128 + int(64 * math.cos(0.1 * i)), 128, 192)) + fbuf.line(w // 2, h // 2, + w // 2 + int(40 * math.cos(0.2 * i)), + h // 2 + int(40 * math.sin(0.2 * i)), + lcd.rgb(128, 255, 64)) + fbuf.hline(0, ty, w, lcd.rgb(64, 64, 64)) + fbuf.vline(tx, 0, h, lcd.rgb(64, 64, 64)) + fbuf.rect(tx - 3, ty - 3, 7, 7, lcd.rgb(64, 64, 64)) + for phase in (-0.2, 0, 0.2): + x = w // 2 - 8 + int(50 * math.cos(0.05 * i + phase)) + y = h // 2 - 8 + int(32 * math.sin(0.05 * i + phase)) + fbuf.blit(mlogo, x, y) + for j in range(-3, 3): + fbuf.text('MicroPython', + 5, h // 2 + 9 * j + int(20 * math.sin(0.1 * (i + j))), + lcd.rgb(128 + 10 * j, 0, 128 - 10 * j)) + lcd.show_framebuf(fbuf) + + # show results from the ADC + if adc: + show_adc(lcd, adc) + + # show the time + if rtc: + lcd.set_pos(2, 0) + lcd.set_font(1) + t = rtc.datetime() + lcd.write('%4d-%02d-%02d %2d:%02d:%02d.%01d' % (t[0], t[1], t[2], t[4], t[5], t[6], t[7] // 100000)) + + # compute the frame rate + t1 = time.ticks_us() + dt = time.ticks_diff(t1, t0) + t0 = t1 + + # show the frame rate + lcd.set_pos(2, 9) + lcd.write('%.2f fps' % (1000000 / dt)) + +def test_mandel(lcd, orient=lcd160cr.PORTRAIT): + # set orientation and clear screen + lcd = get_lcd(lcd) + lcd.set_orient(orient) + lcd.set_pen(0, 0xffff) + lcd.erase() + + # function to compute Mandelbrot pixels + def in_set(c): + z = 0 + for i in range(32): + z = z * z + c + if abs(z) > 100: + return i + return 0 + + # cache width and height of LCD + w = lcd.w + h = lcd.h + + # create the buffer for each line and set SPI parameters + line = bytearray(w * 2) + lcd.set_spi_win(0, 0, w, h) + spi = lcd.fast_spi() + + # draw the Mandelbrot set line-by-line + hh = ((h - 1) / 3.2) + ww = ((w - 1) / 2.4) + for v in range(h): + for u in range(w): + c = in_set((v / hh - 2.3) + (u / ww - 1.2) * 1j) + if c < 16: + rgb = c << 12 | c << 6 + else: + rgb = 0xf800 | c << 6 + line[2 * u] = rgb + line[2 * u + 1] = rgb >> 8 + spi.write(line) + +def test_all(lcd, orient=lcd160cr.PORTRAIT): + lcd = get_lcd(lcd) + test_features(lcd, orient) + test_mandel(lcd, orient) + +print('To run all tests: test_all(<lcd>)') +print('Individual tests are: test_features, test_mandel') +print('<lcd> argument should be a connection, eg "X", or an LCD160CR object') diff --git a/drivers/display/ssd1306.py b/drivers/display/ssd1306.py new file mode 100644 index 000000000..53bcb0d2d --- /dev/null +++ b/drivers/display/ssd1306.py @@ -0,0 +1,161 @@ +# MicroPython SSD1306 OLED driver, I2C and SPI interfaces + +from micropython import const +import time +import framebuf + + +# register definitions +SET_CONTRAST = const(0x81) +SET_ENTIRE_ON = const(0xa4) +SET_NORM_INV = const(0xa6) +SET_DISP = const(0xae) +SET_MEM_ADDR = const(0x20) +SET_COL_ADDR = const(0x21) +SET_PAGE_ADDR = const(0x22) +SET_DISP_START_LINE = const(0x40) +SET_SEG_REMAP = const(0xa0) +SET_MUX_RATIO = const(0xa8) +SET_COM_OUT_DIR = const(0xc0) +SET_DISP_OFFSET = const(0xd3) +SET_COM_PIN_CFG = const(0xda) +SET_DISP_CLK_DIV = const(0xd5) +SET_PRECHARGE = const(0xd9) +SET_VCOM_DESEL = const(0xdb) +SET_CHARGE_PUMP = const(0x8d) + + +class SSD1306: + def __init__(self, width, height, external_vcc): + self.width = width + self.height = height + self.external_vcc = external_vcc + self.pages = self.height // 8 + self.buffer = bytearray(self.pages * self.width) + self.framebuf = framebuf.FrameBuffer(self.buffer, self.width, self.height, framebuf.MVLSB) + self.poweron() + self.init_display() + + def init_display(self): + for cmd in ( + SET_DISP | 0x00, # off + # address setting + SET_MEM_ADDR, 0x00, # horizontal + # resolution and layout + SET_DISP_START_LINE | 0x00, + SET_SEG_REMAP | 0x01, # column addr 127 mapped to SEG0 + SET_MUX_RATIO, self.height - 1, + SET_COM_OUT_DIR | 0x08, # scan from COM[N] to COM0 + SET_DISP_OFFSET, 0x00, + SET_COM_PIN_CFG, 0x02 if self.height == 32 else 0x12, + # timing and driving scheme + SET_DISP_CLK_DIV, 0x80, + SET_PRECHARGE, 0x22 if self.external_vcc else 0xf1, + SET_VCOM_DESEL, 0x30, # 0.83*Vcc + # display + SET_CONTRAST, 0xff, # maximum + SET_ENTIRE_ON, # output follows RAM contents + SET_NORM_INV, # not inverted + # charge pump + SET_CHARGE_PUMP, 0x10 if self.external_vcc else 0x14, + SET_DISP | 0x01): # on + self.write_cmd(cmd) + self.fill(0) + self.show() + + def poweroff(self): + self.write_cmd(SET_DISP | 0x00) + + def contrast(self, contrast): + self.write_cmd(SET_CONTRAST) + self.write_cmd(contrast) + + def invert(self, invert): + self.write_cmd(SET_NORM_INV | (invert & 1)) + + def show(self): + x0 = 0 + x1 = self.width - 1 + if self.width == 64: + # displays with width of 64 pixels are shifted by 32 + x0 += 32 + x1 += 32 + self.write_cmd(SET_COL_ADDR) + self.write_cmd(x0) + self.write_cmd(x1) + self.write_cmd(SET_PAGE_ADDR) + self.write_cmd(0) + self.write_cmd(self.pages - 1) + self.write_data(self.buffer) + + def fill(self, col): + self.framebuf.fill(col) + + def pixel(self, x, y, col): + self.framebuf.pixel(x, y, col) + + def scroll(self, dx, dy): + self.framebuf.scroll(dx, dy) + + def text(self, string, x, y, col=1): + self.framebuf.text(string, x, y, col) + + +class SSD1306_I2C(SSD1306): + def __init__(self, width, height, i2c, addr=0x3c, external_vcc=False): + self.i2c = i2c + self.addr = addr + self.temp = bytearray(2) + super().__init__(width, height, external_vcc) + + def write_cmd(self, cmd): + self.temp[0] = 0x80 # Co=1, D/C#=0 + self.temp[1] = cmd + self.i2c.writeto(self.addr, self.temp) + + def write_data(self, buf): + self.temp[0] = self.addr << 1 + self.temp[1] = 0x40 # Co=0, D/C#=1 + self.i2c.start() + self.i2c.write(self.temp) + self.i2c.write(buf) + self.i2c.stop() + + def poweron(self): + pass + + +class SSD1306_SPI(SSD1306): + def __init__(self, width, height, spi, dc, res, cs, external_vcc=False): + self.rate = 10 * 1024 * 1024 + dc.init(dc.OUT, value=0) + res.init(res.OUT, value=0) + cs.init(cs.OUT, value=1) + self.spi = spi + self.dc = dc + self.res = res + self.cs = cs + super().__init__(width, height, external_vcc) + + def write_cmd(self, cmd): + self.spi.init(baudrate=self.rate, polarity=0, phase=0) + self.cs(1) + self.dc(0) + self.cs(0) + self.spi.write(bytearray([cmd])) + self.cs(1) + + def write_data(self, buf): + self.spi.init(baudrate=self.rate, polarity=0, phase=0) + self.cs(1) + self.dc(1) + self.cs(0) + self.spi.write(buf) + self.cs(1) + + def poweron(self): + self.res(1) + time.sleep_ms(1) + self.res(0) + time.sleep_ms(10) + self.res(1) diff --git a/drivers/memory/spiflash.c b/drivers/memory/spiflash.c new file mode 100644 index 000000000..214610e0a --- /dev/null +++ b/drivers/memory/spiflash.c @@ -0,0 +1,200 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016-2017 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include <stdio.h> +#include <string.h> + +#include "py/mperrno.h" +#include "py/mphal.h" +#include "extmod/machine_spi.h" +#include "drivers/memory/spiflash.h" + +#define CMD_WRITE (0x02) +#define CMD_READ (0x03) +#define CMD_WRDI (0x04) +#define CMD_RDSR (0x05) +#define CMD_WREN (0x06) +#define CMD_SEC_ERASE (0x20) +#define WAIT_SR_TIMEOUT (1000000) + +#define PAGE_SIZE (256) // maximum bytes we can write in one SPI transfer +#define SECTOR_SIZE (4096) // size of erase sector + +// Note: this code is not reentrant with this shared buffer +STATIC uint8_t buf[SECTOR_SIZE]; + +void mp_spiflash_init(mp_spiflash_t *self) { + mp_hal_pin_write(self->cs, 1); + mp_hal_pin_output(self->cs); + mp_hal_pin_write(self->spi.sck, 0); + mp_hal_pin_output(self->spi.sck); + mp_hal_pin_output(self->spi.mosi); + mp_hal_pin_input(self->spi.miso); +} + +STATIC void mp_spiflash_acquire_bus(mp_spiflash_t *self) { + // can be used for actions needed to acquire bus + (void)self; +} + +STATIC void mp_spiflash_release_bus(mp_spiflash_t *self) { + // can be used for actions needed to release bus + (void)self; +} + +STATIC void mp_spiflash_transfer(mp_spiflash_t *self, size_t len, const uint8_t *src, uint8_t *dest) { + mp_machine_soft_spi_transfer(&self->spi.base, len, src, dest); +} + +STATIC int mp_spiflash_wait_sr(mp_spiflash_t *self, uint8_t mask, uint8_t val, uint32_t timeout) { + uint8_t cmd[1] = {CMD_RDSR}; + mp_hal_pin_write(self->cs, 0); + mp_spiflash_transfer(self, 1, cmd, NULL); + for (; timeout; --timeout) { + mp_spiflash_transfer(self, 1, cmd, cmd); + if ((cmd[0] & mask) == val) { + break; + } + } + mp_hal_pin_write(self->cs, 1); + if ((cmd[0] & mask) == val) { + return 0; // success + } else if (timeout == 0) { + return -MP_ETIMEDOUT; + } else { + return -MP_EIO; + } +} + +STATIC int mp_spiflash_wait_wel1(mp_spiflash_t *self) { + return mp_spiflash_wait_sr(self, 2, 2, WAIT_SR_TIMEOUT); +} + +STATIC int mp_spiflash_wait_wip0(mp_spiflash_t *self) { + return mp_spiflash_wait_sr(self, 1, 0, WAIT_SR_TIMEOUT); +} + +STATIC void mp_spiflash_write_cmd(mp_spiflash_t *self, uint8_t cmd) { + mp_hal_pin_write(self->cs, 0); + mp_spiflash_transfer(self, 1, &cmd, NULL); + mp_hal_pin_write(self->cs, 1); +} + +STATIC int mp_spiflash_erase_sector(mp_spiflash_t *self, uint32_t addr) { + // enable writes + mp_spiflash_write_cmd(self, CMD_WREN); + + // wait WEL=1 + int ret = mp_spiflash_wait_wel1(self); + if (ret != 0) { + return ret; + } + + // erase the sector + mp_hal_pin_write(self->cs, 0); + uint8_t cmd[4] = {CMD_SEC_ERASE, addr >> 16, addr >> 8, addr}; + mp_spiflash_transfer(self, 4, cmd, NULL); + mp_hal_pin_write(self->cs, 1); + + // wait WIP=0 + return mp_spiflash_wait_wip0(self); +} + +STATIC int mp_spiflash_write_page(mp_spiflash_t *self, uint32_t addr, const uint8_t *src) { + // enable writes + mp_spiflash_write_cmd(self, CMD_WREN); + + // wait WEL=1 + int ret = mp_spiflash_wait_wel1(self); + if (ret != 0) { + return ret; + } + + // write the page + mp_hal_pin_write(self->cs, 0); + uint8_t cmd[4] = {CMD_WRITE, addr >> 16, addr >> 8, addr}; + mp_spiflash_transfer(self, 4, cmd, NULL); + mp_spiflash_transfer(self, PAGE_SIZE, src, NULL); + mp_hal_pin_write(self->cs, 1); + + // wait WIP=0 + return mp_spiflash_wait_wip0(self); +} + +void mp_spiflash_read(mp_spiflash_t *self, uint32_t addr, size_t len, uint8_t *dest) { + mp_spiflash_acquire_bus(self); + uint8_t cmd[4] = {CMD_READ, addr >> 16, addr >> 8, addr}; + mp_hal_pin_write(self->cs, 0); + mp_spiflash_transfer(self, 4, cmd, NULL); + mp_spiflash_transfer(self, len, dest, dest); + mp_hal_pin_write(self->cs, 1); + mp_spiflash_release_bus(self); +} + +int mp_spiflash_write(mp_spiflash_t *self, uint32_t addr, size_t len, const uint8_t *src) { + // TODO optimise so we don't need to erase multiple times for successive writes to a sector + + // align to 4096 sector + uint32_t offset = addr & 0xfff; + addr = (addr >> 12) << 12; + + // restriction for now, so we don't need to erase multiple pages + if (offset + len > sizeof(buf)) { + printf("mp_spiflash_write: len is too large\n"); + return -MP_EIO; + } + + mp_spiflash_acquire_bus(self); + + // read sector + uint8_t cmd[4] = {CMD_READ, addr >> 16, addr >> 8, addr}; + mp_hal_pin_write(self->cs, 0); + mp_spiflash_transfer(self, 4, cmd, NULL); + mp_spiflash_transfer(self, SECTOR_SIZE, buf, buf); + mp_hal_pin_write(self->cs, 1); + + // erase sector + int ret = mp_spiflash_erase_sector(self, addr); + if (ret != 0) { + mp_spiflash_release_bus(self); + return ret; + } + + // copy new block into buffer + memcpy(buf + offset, src, len); + + // write sector in pages of 256 bytes + for (int i = 0; i < SECTOR_SIZE; i += 256) { + ret = mp_spiflash_write_page(self, addr + i, buf + i); + if (ret != 0) { + mp_spiflash_release_bus(self); + return ret; + } + } + + mp_spiflash_release_bus(self); + return 0; // success +} diff --git a/drivers/memory/spiflash.h b/drivers/memory/spiflash.h new file mode 100644 index 000000000..d2e817450 --- /dev/null +++ b/drivers/memory/spiflash.h @@ -0,0 +1,42 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef MICROPY_INCLUDED_DRIVERS_MEMORY_SPIFLASH_H +#define MICROPY_INCLUDED_DRIVERS_MEMORY_SPIFLASH_H + +#include "extmod/machine_spi.h" + +typedef struct _mp_spiflash_t { + mp_hal_pin_obj_t cs; + // TODO replace with generic SPI object + mp_machine_soft_spi_obj_t spi; +} mp_spiflash_t; + +void mp_spiflash_init(mp_spiflash_t *self); +void mp_spiflash_read(mp_spiflash_t *self, uint32_t addr, size_t len, uint8_t *dest); +int mp_spiflash_write(mp_spiflash_t *self, uint32_t addr, size_t len, const uint8_t *src); + +#endif // MICROPY_INCLUDED_DRIVERS_MEMORY_SPIFLASH_H diff --git a/drivers/nrf24l01/nrf24l01.py b/drivers/nrf24l01/nrf24l01.py index 3c79650bf..b45c137c6 100644 --- a/drivers/nrf24l01/nrf24l01.py +++ b/drivers/nrf24l01/nrf24l01.py @@ -2,7 +2,7 @@ """ from micropython import const -import pyb +import utime # nRF24L01+ registers CONFIG = const(0x00) @@ -53,22 +53,24 @@ class NRF24L01: def __init__(self, spi, cs, ce, channel=46, payload_size=16): assert payload_size <= 32 - # init the SPI bus and pins - spi.init(spi.MASTER, baudrate=4000000, polarity=0, phase=0, firstbit=spi.MSB) - cs.init(cs.OUT_PP, cs.PULL_NONE) - ce.init(ce.OUT_PP, ce.PULL_NONE) + self.buf = bytearray(1) # store the pins self.spi = spi self.cs = cs self.ce = ce + # init the SPI bus and pins + self.init_spi(4000000) + cs.init(cs.OUT, value=1) + ce.init(ce.OUT, value=0) + # reset everything - self.ce.low() - self.cs.high() + self.ce(0) + self.cs(1) self.payload_size = payload_size self.pipe0_read_addr = None - pyb.delay(5) + utime.sleep_ms(5) # set address width to 5 bytes and check for device present self.reg_write(SETUP_AW, 0b11) @@ -98,29 +100,45 @@ class NRF24L01: self.flush_rx() self.flush_tx() + def init_spi(self, baudrate): + try: + master = self.spi.MASTER + except AttributeError: + self.spi.init(baudrate=baudrate, polarity=0, phase=0) + else: + self.spi.init(master, baudrate=baudrate, polarity=0, phase=0) + def reg_read(self, reg): - self.cs.low() - self.spi.send_recv(reg) - buf = self.spi.recv(1) - self.cs.high() - return buf[0] - - def reg_write(self, reg, buf): - self.cs.low() - status = self.spi.send_recv(0x20 | reg)[0] - self.spi.send(buf) - self.cs.high() - return status + self.cs(0) + self.spi.readinto(self.buf, reg) + self.spi.readinto(self.buf) + self.cs(1) + return self.buf[0] + + def reg_write_bytes(self, reg, buf): + self.cs(0) + self.spi.readinto(self.buf, 0x20 | reg) + self.spi.write(buf) + self.cs(1) + return self.buf[0] + + def reg_write(self, reg, value): + self.cs(0) + self.spi.readinto(self.buf, 0x20 | reg) + ret = self.buf[0] + self.spi.readinto(self.buf, value) + self.cs(1) + return ret def flush_rx(self): - self.cs.low() - self.spi.send(FLUSH_RX) - self.cs.high() + self.cs(0) + self.spi.readinto(self.buf, FLUSH_RX) + self.cs(1) def flush_tx(self): - self.cs.low() - self.spi.send(FLUSH_TX) - self.cs.high() + self.cs(0) + self.spi.readinto(self.buf, FLUSH_TX) + self.cs(1) # power is one of POWER_x defines; speed is one of SPEED_x defines def set_power_speed(self, power, speed): @@ -144,8 +162,8 @@ class NRF24L01: # address should be a bytes object 5 bytes long def open_tx_pipe(self, address): assert len(address) == 5 - self.reg_write(RX_ADDR_P0, address) - self.reg_write(TX_ADDR, address) + self.reg_write_bytes(RX_ADDR_P0, address) + self.reg_write_bytes(TX_ADDR, address) self.reg_write(RX_PW_P0, self.payload_size) # address should be a bytes object 5 bytes long @@ -157,7 +175,7 @@ class NRF24L01: if pipe_id == 0: self.pipe0_read_addr = address if pipe_id < 2: - self.reg_write(RX_ADDR_P0 + pipe_id, address) + self.reg_write_bytes(RX_ADDR_P0 + pipe_id, address) else: self.reg_write(RX_ADDR_P0 + pipe_id, address[0]) self.reg_write(RX_PW_P0 + pipe_id, self.payload_size) @@ -168,15 +186,15 @@ class NRF24L01: self.reg_write(STATUS, RX_DR | TX_DS | MAX_RT) if self.pipe0_read_addr is not None: - self.reg_write(RX_ADDR_P0, self.pipe0_read_addr) + self.reg_write_bytes(RX_ADDR_P0, self.pipe0_read_addr) self.flush_rx() self.flush_tx() - self.ce.high() - pyb.udelay(130) + self.ce(1) + utime.sleep_us(130) def stop_listening(self): - self.ce.low() + self.ce(0) self.flush_tx() self.flush_rx() @@ -186,10 +204,10 @@ class NRF24L01: def recv(self): # get the data - self.cs.low() - self.spi.send(R_RX_PAYLOAD) - buf = self.spi.recv(self.payload_size) - self.cs.high() + self.cs(0) + self.spi.readinto(self.buf, R_RX_PAYLOAD) + buf = self.spi.read(self.payload_size) + self.cs(1) # clear RX ready flag self.reg_write(STATUS, RX_DR) @@ -198,9 +216,9 @@ class NRF24L01: # blocking wait for tx complete def send(self, buf, timeout=500): send_nonblock = self.send_start(buf) - start = pyb.millis() + start = utime.ticks_ms() result = None - while result is None and pyb.elapsed_millis(start) < timeout: + while result is None and utime.ticks_diff(utime.ticks_ms(), start) < timeout: result = self.send_done() # 1 == success, 2 == fail if result == 2: raise OSError("send failed") @@ -209,19 +227,19 @@ class NRF24L01: def send_start(self, buf): # power up self.reg_write(CONFIG, (self.reg_read(CONFIG) | PWR_UP) & ~PRIM_RX) - pyb.udelay(150) + utime.sleep_us(150) # send the data - self.cs.low() - self.spi.send(W_TX_PAYLOAD) - self.spi.send(buf) + self.cs(0) + self.spi.readinto(self.buf, W_TX_PAYLOAD) + self.spi.write(buf) if len(buf) < self.payload_size: - self.spi.send(b'\x00' * (self.payload_size - len(buf))) # pad out data - self.cs.high() + self.spi.write(b'\x00' * (self.payload_size - len(buf))) # pad out data + self.cs(1) # enable the chip so it can send the data - self.ce.high() - pyb.udelay(15) # needs to be >10us - self.ce.low() + self.ce(1) + utime.sleep_us(15) # needs to be >10us + self.ce(0) # returns None if send still in progress, 1 for success, 2 for fail def send_done(self): diff --git a/drivers/nrf24l01/nrf24l01test.py b/drivers/nrf24l01/nrf24l01test.py index 264bc21c8..a25194d38 100644 --- a/drivers/nrf24l01/nrf24l01test.py +++ b/drivers/nrf24l01/nrf24l01test.py @@ -43,7 +43,7 @@ def master(): timeout = True if timeout: - print('failed, respones timed out') + print('failed, response timed out') num_failures += 1 else: @@ -57,7 +57,7 @@ def master(): # delay then loop pyb.delay(250) - print('master finished sending; succeses=%d, failures=%d' % (num_successes, num_failures)) + print('master finished sending; successes=%d, failures=%d' % (num_successes, num_failures)) def slave(): nrf = NRF24L01(SPI(2), Pin('Y5'), Pin('Y4'), payload_size=8) diff --git a/drivers/onewire/ds18x20.py b/drivers/onewire/ds18x20.py index 342f4b28a..72adcbfd4 100644 --- a/drivers/onewire/ds18x20.py +++ b/drivers/onewire/ds18x20.py @@ -7,11 +7,11 @@ temperature sensors. It supports multiple devices on the same 1-wire bus. The following example assumes the ground of your DS18x20 is connected to Y11, vcc is connected to Y9 and the data pin is connected to Y10. ->>> from pyb import Pin +>>> from machine import Pin >>> gnd = Pin('Y11', Pin.OUT_PP) ->>> gnd.low() +>>> gnd.off() >>> vcc = Pin('Y9', Pin.OUT_PP) ->>> vcc.high() +>>> vcc.on() >>> from ds18x20 import DS18X20 >>> d = DS18X20(Pin('Y10')) diff --git a/drivers/sdcard/sdcard.py b/drivers/sdcard/sdcard.py index 191630aeb..e749d5376 100644 --- a/drivers/sdcard/sdcard.py +++ b/drivers/sdcard/sdcard.py @@ -130,7 +130,7 @@ class SDCard: raise OSError("timeout waiting for v2 card") def cmd(self, cmd, arg, crc, final=0, release=True): - self.cs.low() + self.cs(0) # create and send the command buf = self.cmdbuf @@ -142,7 +142,7 @@ class SDCard: buf[5] = crc self.spi.write(buf) - # wait for the repsonse (response[7] == 0) + # wait for the response (response[7] == 0) for i in range(_CMD_TIMEOUT): response = self.spi.read(1, 0xff)[0] if not (response & 0x80): @@ -150,12 +150,12 @@ class SDCard: for j in range(final): self.spi.write(b'\xff') if release: - self.cs.high() + self.cs(1) self.spi.write(b'\xff') return response # timeout - self.cs.high() + self.cs(1) self.spi.write(b'\xff') return -1 @@ -164,15 +164,15 @@ class SDCard: self.spi.read(1, 0xff) # ignore stuff byte for _ in range(_CMD_TIMEOUT): if self.spi.read(1, 0xff)[0] == 0xff: - self.cs.high() + self.cs(1) self.spi.write(b'\xff') return 0 # OK - self.cs.high() + self.cs(1) self.spi.write(b'\xff') return 1 # timeout def readinto(self, buf): - self.cs.low() + self.cs(0) # read until start byte (0xff) while self.spi.read(1, 0xff)[0] != 0xfe: @@ -186,11 +186,11 @@ class SDCard: self.spi.write(b'\xff') self.spi.write(b'\xff') - self.cs.high() + self.cs(1) self.spi.write(b'\xff') def write(self, token, buf): - self.cs.low() + self.cs(0) # send: start of block, data, checksum self.spi.read(1, token) @@ -200,7 +200,7 @@ class SDCard: # check the response if (self.spi.read(1, 0xff)[0] & 0x1f) != 0x05: - self.cs.high() + self.cs(1) self.spi.write(b'\xff') return @@ -208,18 +208,18 @@ class SDCard: while self.spi.read(1, 0xff)[0] == 0: pass - self.cs.high() + self.cs(1) self.spi.write(b'\xff') def write_token(self, token): - self.cs.low() + self.cs(0) self.spi.read(1, token) self.spi.write(b'\xff') # wait for write to finish while self.spi.read(1, 0xff)[0] == 0x00: pass - self.cs.high() + self.cs(1) self.spi.write(b'\xff') def count(self): |
