LED Display 7219 #

Modul 7-segmentového displeja je riadený obvodom MAX7219/MAX7221. Komunikácia s displejom je pomocou zbernice SPI, radič displeja podporuje sériové reťazenie obvodov na zbernici SPI (Daisy-Chain).

_images/display_led_max7219.png

Obr. 45 Modul displeja.#

Štandardné zapojenie modulu podľa doporučenia výrobcu je na obrázku.

_images/ic_max7219_2.png

Obr. 46 Zapojenie displeja.#

Príklad pripojenie modulu displeja ku kitu NUCLEO-64.

DIN  -> PB15   
CLK  -> PB13
CS   -> PB1
+5V
GND 
_images/spi2_led_max7219.png

Obr. 47 Pripojenie modulu displeja.#

Komunikácia #

Radič displeja MAX7219 umožňuje pripojenie 8-znakového 7-segmentového displeja so spoločnou katódou, sĺpcového LED displeja alebo 64 samostatných LED. Obvod obsahuje BCD dekóder pre 7-segmentové znaky, obvody pre multiplexovanie znakov displeja, riadenie jasu a statickú pamäť RAM 8x8 pre každý znak displeja. Riadnie displeja umožňuje adresovať každý znak displeja individuálne, bez potreby prepísania obsahu celého displeja. Obvod je k mikroprocesoru pripojený pomocou rozhrania SPI s maximálnou frekvenciou hodím 10MHz.

_images/ic_max7219_1.png

Obr. 48 Vnútorné zapojenie obvodu MAX7219.#

Obvod obsahuje 16-bitový posuvný register, ktorého obsah je po naplnení z rozhrania SPI dekódovaný podľa nasledujúceho formátu

_images/ic_max7219_3.png

Obr. 49 Sériový formát dát v posuvnom registri.#

Popis polí vo formáte dát

  • D15 … D12 - nepoužité bity

  • D11 … D08 - adresa registru alebo znaku

  • D07 … D00 - dáta pre register alebo adresovaný znak

Adresné bity (D11 … D08) majú nasledujúci význam

_images/ic_max7219_4.png

Obr. 50 Dekódovanie adresných bitov.#

Popis registrov

  • No-Op - používa sa pri sériovom radení registrov, kedy v sekvencii dát chceme osloviť niektorý z radičov a požadujeme, aby ostatné nereagovali

  • Digit 0 … Digit 7 - adresa znaku

  • Decode Mode - nastavenie dekódovania dát pre zobrazenie znaku na displeji v BCD kóde alebo priamym riadením LED

  • Intensity - nastavenie jasu displeja

  • Scan Limit - počet zobrazovaných znakov

  • Shutdown - zhasnutie displeja a prechod do módu s malou spotrebou

Dáta pre register Decode Mode určujú formát dekódovania znakov. Hodnota 0xFF nastavuje dekódovanie znakov v BCD kóde, hodnote 0x00 priame zobrazenie LED.

_images/ic_max7219_6.png

Obr. 51 Nastavenie typu dekódovania dát pre znaky displeja.#

Programovanie #

Pre obsluhu modulu 7-segmentového displaje je vytvorená knižnica lib_max7219

Zdrojový kód knižnice
  1'''
  2MicroPython kniznica pre riadenie modulu 7-segmentového displeja
  3----------------------------------------------------------------
  4
  5Upravené s pouzitím nasledujucich zdrojov
  6
  7zdroj:   MicroPython driver for MAX7219 with 7-segment modules
  8link:    https://github.com/JennaSys/micropython-max7219
  9autor:   John Sheehan
 10mail:    jennasyseng@gmail.com
 11licencia:MIT
 12
 13zdroj:   Segmented LED Display - ASCII Library
 14link:    https://github.com/dmadison/LED-Segment-ASCII
 15autor:   Dave Madison
 16licencia:MIT
 17
 18
 19Pripojenie modulu ku kitu NUCLEO-64:
 20
 21DIN  -> PB15   
 22CLK  -> PB13
 23CS   -> PB1
 24+5V
 25GND     
 26'''
 27
 28from pyb import Pin, SPI
 29import time
 30
 31# konfiguracia SPI
 32SPI_BUS = 2  
 33SPI_BAUDRATE = 100000
 34SPI_CS =  'PB1' # D3
 35
 36# konfiguracia displeja
 37MAX7219_DIGITS = 8
 38
 39MAX7219_REG_NOOP = 0x0
 40MAX7219_REG_DIGIT0 = 0x1
 41MAX7219_REG_DIGIT1 = 0x2
 42MAX7219_REG_DIGIT2 = 0x3
 43MAX7219_REG_DIGIT3 = 0x4
 44MAX7219_REG_DIGIT4 = 0x5
 45MAX7219_REG_DIGIT5 = 0x6
 46MAX7219_REG_DIGIT6 = 0x7
 47MAX7219_REG_DIGIT7 = 0x8
 48MAX7219_REG_DECODEMODE = 0x9
 49MAX7219_REG_INTENSITY = 0xA
 50MAX7219_REG_SCANLIMIT = 0xB
 51MAX7219_REG_SHUTDOWN = 0xC
 52MAX7219_REG_DISPLAYTEST = 0xF
 53
 54# 7 Segment bit order: DP-G-F-E-D-C-B-A
 55char_map = {
 56    ' ': 0b00000000,
 57    '!': 0b10000110,
 58    '"': 0b00100010,
 59    '#': 0b01111110,
 60    '$': 0b01101101,
 61    '%': 0b11010010,
 62    '&': 0b01000110,
 63    "'": 0b00100000,
 64    '(': 0b00101001,
 65    ')': 0b00001011,
 66    '*': 0b00100001,
 67    '+': 0b01110000,
 68    ',': 0b00010000,
 69    '-': 0b01000000,
 70    '.': 0b10000000,
 71    '/': 0b01010010,
 72    '0': 0b00111111,
 73    '1': 0b00000110,
 74    '2': 0b01011011,
 75    '3': 0b01001111,
 76    '4': 0b01100110,
 77    '5': 0b01101101,
 78    '6': 0b01111101,
 79    '7': 0b00000111,
 80    '8': 0b01111111,
 81    '9': 0b01101111,
 82    ':': 0b00001001,
 83    ';': 0b00001101,
 84    '<': 0b01100001,
 85    '=': 0b01001000,
 86    '>': 0b01000011,
 87    '?': 0b11010011,
 88    '@': 0b01011111,
 89    'A': 0b01110111,
 90    'B': 0b01111100,
 91    'C': 0b00111001,
 92    'D': 0b01011110,
 93    'E': 0b01111001,
 94    'F': 0b01110001,
 95    'G': 0b00111101,
 96    'H': 0b01110110,
 97    'I': 0b00110000,
 98    'J': 0b00011110,
 99    'K': 0b01110101,
100    'L': 0b00111000,
101    'M': 0b00010101,
102    'N': 0b00110111,
103    'O': 0b00111111,
104    'P': 0b01110011,
105    'Q': 0b01101011,
106    'R': 0b00110011,
107    'S': 0b01101101,
108    'T': 0b01111000,
109    'U': 0b00111110,
110    'V': 0b00111110,
111    'W': 0b00101010,
112    'X': 0b01110110,
113    'Y': 0b01101110,
114    'Z': 0b01011011,
115    '[': 0b00111001,
116    '\\': 0b01100100,
117    ']': 0b00001111,
118    '^': 0b00100011,
119    '_': 0b00001000,
120    '`': 0b00000010,
121    'a': 0b01011111,
122    'b': 0b01111100,
123    'c': 0b01011000,
124    'd': 0b01011110,
125    'e': 0b01111011,
126    'f': 0b01110001,
127    'g': 0b01101111,
128    'h': 0b01110100,
129    'i': 0b00010000,
130    'j': 0b00001100,
131    'k': 0b01110101,
132    'l': 0b00110000,
133    'm': 0b00010100,
134    'n': 0b01010100,
135    'o': 0b01011100,
136    'p': 0b01110011,
137    'q': 0b01100111,
138    'r': 0b01010000,
139    's': 0b01101101,
140    't': 0b01111000,
141    'u': 0b00011100,
142    'v': 0b00011100,
143    'w': 0b00010100,
144    'x': 0b01110110,
145    'y': 0b01101110,
146    'z': 0b01011011,
147    '{': 0b01000110,
148    '|': 0b00110000,
149    '}': 0b01110000,
150    '~': 0b00000001
151}
152
153
154def get_char(char):
155    return char_map.get(str(char), char_map.get('_'))
156
157
158def get_char2(char):
159    # 7 Segment bit order: DP-A-B-C-D-E-F-G
160    bits = get_char(char)
161    tmp = '{:08b}'.format(bits)
162    return int(''.join(['0b', tmp[0], ''.join(reversed(tmp[1:]))]), 2)
163    
164    
165
166class SevenSegment:
167    def __init__(self, digits=8, scan_digits=MAX7219_DIGITS, baudrate=SPI_BAUDRATE, cs=SPI_CS, spi_bus=SPI_BUS, reverse=False):
168        """
169        Constructor:
170        `digits` should be the total number of individual digits being displayed
171        `scan_digits` is the number of digits each individual max7219 displays
172        `baudrate` defaults to 100KHz, note that excessive rates may result in instability (and is probably unnecessary)
173        `cs` is the GPIO port to use for the chip select line of the SPI bus - defaults to GPIO 0 / D3
174        `spi_bus` is the SPI bus on the controller to utilize - defaults to SPI bus 1
175        `reverse` changes the write-order of characters for displays where digits are wired R-to-L instead of L-to-R
176        """
177
178        self.digits = digits
179        self.devices = -(-digits // scan_digits)  # ceiling integer division
180        self.scan_digits = scan_digits
181        self.reverse = reverse
182        self._buffer = [0] * digits
183        #self._spi = SPI(spi_bus, baudrate=baudrate, polarity=0, phase=0)
184        self._spi = SPI(spi_bus, SPI.CONTROLLER, baudrate=baudrate, polarity=0, phase=0, crc=None)
185        self._cs = Pin(cs, Pin.OUT, value=1)
186
187        self.command(MAX7219_REG_SCANLIMIT, scan_digits - 1)  # digits to display on each device  0-7
188        self.command(MAX7219_REG_DECODEMODE, 0)   # use segments (not digits)
189        self.command(MAX7219_REG_DISPLAYTEST, 0)  # no display test
190        self.command(MAX7219_REG_SHUTDOWN, 1)     # not blanking mode
191        self.brightness(7)                        # intensity: range: 0..15
192        self.clear()
193
194    def command(self, register, data):
195        """Sets a specific register some data, replicated for all cascaded devices."""
196        self._write([register, data] * self.devices)
197
198    def _write(self, data):
199        """Send the bytes (which should be comprised of alternating command, data values) over the SPI device."""
200        self._cs.off()
201        self._spi.write(bytes(data))
202        self._cs.on()
203
204    def clear(self, flush=True):
205        """Clears the buffer and if specified, flushes the display."""
206        self._buffer = [0] * self.digits
207        if flush:
208            self.flush()
209
210    def flush(self):
211        """For each digit, cascade out the contents of the buffer cells to the SPI device."""
212        buffer = self._buffer.copy()
213        if self.reverse:
214            buffer.reverse()
215
216        for dev in range(self.devices):
217            if self.reverse:
218                current_dev = self.devices - dev - 1
219            else:
220                current_dev = dev
221
222            for pos in range(self.scan_digits):
223                self._write([pos + MAX7219_REG_DIGIT0, buffer[pos + (current_dev * self.scan_digits)]] + ([MAX7219_REG_NOOP, 0] * dev))
224
225    def brightness(self, intensity):
226        """Sets the brightness level of all cascaded devices to the same intensity level, ranging from 0..15."""
227        self.command(MAX7219_REG_INTENSITY, intensity)
228
229    def letter(self, position, char, dot=False, flush=True):
230        """Looks up the appropriate character representation for char and updates the buffer, flushes by default."""
231        value = get_char2(char) | (dot << 7)
232        self._buffer[position] = value
233
234        if flush:
235            self.flush()
236
237    def text(self, text):
238        """Outputs the text (as near as possible) on the specific device."""
239        self.clear(False)
240        text = text[:self.digits]  # make sure we don't overrun the buffer
241        for pos, char in enumerate(text):
242            self.letter(pos, char, flush=False)
243
244        self.flush()
245
246    def number(self, val):
247        """Formats the value according to the parameters supplied, and displays it."""
248        self.clear(False)
249        strval = ''
250        if isinstance(val, (int, float)):
251            strval = str(val)
252        elif isinstance(val, str):
253            if val.replace('.', '', 1).strip().isdigit():
254                strval = val
255
256        if '.' in strval:
257            strval = strval[:self.digits + 1]
258        else:
259            strval = strval[:self.digits]
260
261        pos = 0
262        for char in strval:
263            dot = False
264            if char == '.':
265                continue
266            else:
267                if pos < len(strval) - 1:
268                    if strval[pos + 1] == '.':
269                        dot = True
270                self.letter(pos, char, dot, False)
271                pos += 1
272
273        self.flush()
274
275    def scroll(self, rotate=True, reverse=False, flush=True):
276        """Shifts buffer contents left or right (reverse), with option to wrap around (rotate)."""
277        if reverse:
278            tmp = self._buffer.pop()
279            if rotate:
280                self._buffer.insert(0, tmp)
281            else:
282                self._buffer.insert(0, 0x00)
283        else:
284            tmp = self._buffer.pop(0)
285            if rotate:
286                self._buffer.append(tmp)
287            else:
288                self._buffer.append(0x00)
289
290        if flush:
291            self.flush()
292
293    def message(self, text, delay=0.4):
294        """Transitions the text message across the devices from left-to-right."""
295        self.clear(False)
296        for char in text:
297            time.sleep(delay)
298            self.scroll(rotate=False, flush=False)
299            self.letter(self.digits - 1, char)
Aplikačné rozhranie

Konštruktor:

SevenSegment(digits=8, 
             scan_digits=MAX7219_DIGITS, 
             baudrate=SPI_BAUDRATE, 
             cs=SPI_CS, 
             spi_bus=SPI_BUS, 
             reverse=False)

Metódy:

clear()
brightness(intensity)
command(register, data)
text(text)
number(val)
scroll(rotate, reverse, flush)
message(text, delay)
Inicializácia

Uložte knižnicu lib_max7219 do lokálneho pracovného adresáru a spustite nasledujúci skript, ktorý knižnicu nahrá do prostredia MicroPythonu.

  mpremote fs mkdir lib
  mpremote fs cp lib_max7219.py :./lib/lib_max7219.py

Príklady #

Komunikácia s displejom #

Základná komunikácia s displejom pomocou API funkcií.

from pyb import Pin
from time import sleep
from lib.lib_max7219 import *

display = SevenSegment(digits=8, scan_digits=8, cs='PB1', spi_bus=2, reverse=True)
display.clear()

sleep(0.5)
display.text("HELLO")

sleep(1)
display.number(3.14159)

sleep(1)
display.message("--- HELLO ---        ")

sleep(1)
display.clear()
for i in range(32):
    s = f'{i:2d}--{i:04x}'
    display.text(s)
    sleep(0.5)
    

Sériové zapojenie displejov #

Príklad komunikácie s displejmi zapojenými v sérii pomocou priameho zápisu do registrov. V programe nie je použité dekódovanie znakov pomocou dekóderu radiča, funkcia get_char2() prevádza kód znaku do kódu aktívnych segmentov pre zobrazenie na displeji.

from pyb import Pin
from time import sleep
from lib.lib_max7219 import *

display = SevenSegment(digits=16, scan_digits=8, cs='PB1', spi_bus=2, reverse=True)
display.clear()
display.brightness(5) 
  
display.command(0x09,0x00)     # mod bez dekodovania
s1 = '    1234'
s2 = '--AHOJ--'
for x in range(8):
    display._write([8-x, get_char2(s2[x]), 
                    8-x, get_char2(s1[x])] )
    sleep(0.25)