25. Lektion: I2C: Unterschied zwischen den VersionenAus Attraktor Wiki
Version vom 12. Dezember 2023, 17:38 UhrInhaltsverzeichnisDas ProtokollDas I2C Manual kann von hier heruntergeladen werden. Der Bus
Die DatenübertragungDie Übertragung erfolgt in Dateneinheiten. Eine Dateneinheit besteht aus 8 Datenbits und einem ACK-Bit. Dabei wird zuerst das MSB (most significant bit) also Bit 7 übertragen.
I2C Objekt erzeugenWie alles in Micropython ist auch I2C als Klasse implementiert. Deshalb muß für jeden I2C Bus eine Instanz erzeugt werden.
Hardware I2CDer Raspberry Pi Pico W enthält 2 Hardware I2C Einheiten (0 und 1). Die default Einstellungen findet man folgendermassen: from machine import I2C print(I2C(0)) print(I2C(1)) I2C(0, freq=399361, scl=5, sda=4, timeout=50000) # timeout in µs I2C(1, freq=399361, scl=7, sda=6, timeout=50000) Nun soll eine I2C Instanz erzeugt werden. Dabei können die Defaultwerte oder andere zulässige Werte benutzt werden. from machine import Pin, I2C i2c = I2C(0) # default assignment i2c = I2C(1, scl=Pin(3), sda=Pin(2), freq=400_000) Mit dem so erzeugten I2C-Objekt können wir außer einem Busscan noch nicht viel anfangen. i2c.scan() # scan for peripherals, returning a list of 7-bit addresses i2c.writeto(42, b'123') # write 3 bytes to peripheral with 7-bit address 42 i2c.readfrom(42, 4) # read 4 bytes from peripheral with 7-bit address 42 i2c.readfrom_mem(42, 8, 3) # read 3 bytes from memory of peripheral 42, # starting at memory-address 8 in the peripheral i2c.writeto_mem(42, 2, b'\x10') # write 1 byte to memory of peripheral 42 # starting at address 2 in the peripheral Die übrigen Methoden werden in Treibermodulen für I2C Geräte benötigt. Ein solches Modul ist z.B. ssd1306.py, das wir uns gleich noch näher ansehen werden. Scan Test>>> i2c.scan() [60] >>> hex(i2c.scan()) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: can't convert list to int >>> for adr in i2c.scan(): print(hex(adr)) 0x3c >>> Software I2CEin SoftI2C Objekt wird genauso erzeugt wie ein Hardware I2C Objekt, nur das die Pins und die Frequenz unbedingt angegeben werden müssen. Hier ist man frei in der Wahl der Pins. from machine import Pin, SoftI2C i2c = SoftI2C(scl=Pin(5), sda=Pin(4), freq=100_000[, timeout=50000]) i2c.scan() # scan for devices i2c.readfrom(0x3a, 4) # read 4 bytes from device with address 0x3a i2c.writeto(0x3a, '12') # write '12' to device with address 0x3a buf = bytearray(10) # create a buffer with 10 bytes i2c.writeto(0x3a, buf) # write the given buffer to the peripheral Ansonsten gilt das bei Hardware I2C dargestellte. SSD1306 Display mit I2C benutzenAuf unserem Demoboard befindet sich ein Display mit einem SSD1306 IC das über den I2C angesteuert wird. Auf dem Board ist SCL = GPIO21 SDA = GPIO20. Das entspricht nicht der default Belegung, aber ist eine mögliche Belegung für I2C0.
Eine Display Instanz erstellenWenn wir ein I2C Display nutzen wollen, so müssen wir zuerst eine I2C Instanz erstellen. Da wir nicht die default Pin nutzen, müssen wir die Pins angeben.
from machine import Pin, I2C from ssd1306 import SSD1306_I2C i2c = I2C(0, sda=Pin(20), scl=Pin(21)) oled = SSD1306_I2C(128, 64, i2c) Texte ausgebenDie Anzeige von Texten ist sehr einfach. Dafür gibt es die Methode .text(String, x, y, Farbe). oled.fill(0) oled.text('Hallo', 10, 10, 1) oled.show() Grafische AusgabenDie PraxisHier eine kleine Demo (): from machine import Pin, I2C from ssd1306 import SSD1306_I2C i2c = I2C(0, sda=Pin(20), scl=Pin(21)) oled = SSD1306_I2C(128, 64, i2c) oled.fill(0) oled.text("Attraktor e.V.", 10, 0) oled.text("Hello World!", 16, 32) oled.show() Hier ein etwas umfangreicheres Beispiel mit grafischen Elementen: from machine import Pin, I2C from ssd1306 import SSD1306_I2C i2c = I2C(0, sda=Pin(20), scl=Pin(21)) oled = SSD1306_I2C(128, 64, i2c) oled.fill(0) oled.fill_rect(0, 0, 32, 32, 1) oled.fill_rect(2, 2, 28, 28, 0) oled.vline(9, 8, 22, 1) oled.vline(16, 2, 22, 1) oled.vline(23, 8, 22, 1) oled.fill_rect(26, 24, 2, 4, 1) oled.text('MicroPython', 40, 0, 1) oled.text('SSD1306', 40, 12, 1) oled.text('OLED 128x64', 40, 24, 1) oled.show() Das SSD1306 ICSSD1306.py internNun wollen wir einen Blick in das Modul ssd1306.py werfen: # MicroPython SSD1306 OLED driver, I2C and SPI interfaces from micropython import const import framebuf # register definitions SET_CONTRAST = const(0x81) SET_ENTIRE_ON = const(0xA4) ... class SSD1306(framebuf.FrameBuffer): def __init__(self, width, height, external_vcc): ... self.init_display() def init_display(self): for cmd in (SET_DISP | 0x00, ... SET_DISP | 0x01) self.write_cmd(cmd) self.fill(0) self.show() def poweroff(self): self.write_cmd(SET_DISP | 0x00) def poweron(self): self.write_cmd(SET_DISP | 0x01) 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 ... self.write_data(self.buffer) Die Klasse SSD1306 erbt von der Klasse framebuf. Alle Methoden aus framebuf stehen deshalb auch in SSD1306 zur Verfügung. Dazu später.
class SSD1306_I2C(SSD1306): def __init__(self, width, height, i2c, addr=0x3C, external_vcc=False): ... 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.write_list[1] = buf self.i2c.writevto(self.addr, self.write_list) Das Modul framebufDas Modul framebuf ist im Lieferumfang von Micropython enthalten. Es ermöglicht es im Speicher ein Abbild der Displayausgabe vorzubereiten. >>> import framebuf >>> dir(framebuf) ['__class__', '__name__', '__dict__', 'FrameBuffer', 'FrameBuffer1', 'GS2_HMSB', 'GS4_HMSB', 'GS8', 'MONO_HLSB', 'MONO_HMSB', 'MONO_VLSB', 'MVLSB', 'RGB565'] >>> dir(framebuf.FrameBuffer) ['__class__', '__name__', '__bases__', '__dict__', 'blit', 'ellipse', 'fill', 'fill_rect', 'hline', 'line', 'pixel', 'poly', 'rect', 'scroll', 'text', 'vline'] >>> Die Display Instanz erbt alle Methoden aus FrameBuffer, SSD1306 und SSD1306_I2C. Die Methoden für das Displayfrom machine import Pin, I2C import ssd1306 # using default address 0x3C i2c = I2C(sda=Pin(4), scl=Pin(5)) display = ssd1306.SSD1306_I2C(128, 64, i2c) display.text('Hello, World!', 0, 0, 1) display.show() display.poweroff() # power off the display, pixels persist in memory display.poweron() # power on the display, pixels redrawn display.contrast(0) # dim display.contrast(255) # bright display.invert(1) # display inverted display.invert(0) # display normal display.rotate(True) # rotate 180 degrees display.rotate(False) # rotate 0 degrees display.show() # write the contents of the FrameBuffer to display memory display.fill(0) # fill entire screen with colour=0 display.pixel(0, 10) # get pixel at x=0, y=10 display.pixel(0, 10, 1) # set pixel at x=0, y=10 to colour=1 display.hline(0, 8, 4, 1) # draw horizontal line x=0, y=8, width=4, colour=1 display.vline(0, 8, 4, 1) # draw vertical line x=0, y=8, height=4, colour=1 display.line(0, 0, 127, 63, 1) # draw a line from 0,0 to 127,63 display.rect(10, 10, 107, 43, 1) # draw a rectangle outline 10,10 to 117,53, colour=1 display.fill_rect(10, 10, 107, 43, 1) # draw a solid rectangle 10,10 to 117,53, colour=1 display.text('Hello World', 0, 0, 1) # draw some text at x=0, y=0, colour=1 display.scroll(20, 0) # scroll 20 pixels to the right Bus Erweiterung
|