7-segment LED display driver Based on a PIC16F627A (or 628A or 648A) chip. --------------------------------------------------------------------- Basic features: - Uses BasicBus serial connection, default device ID = 0. - Supports up to 6 7-segment(-plus-decimal-point) LED modules - Modules may be common-anode or common-cathode - Also needs one current-limiting resistor per segment and one current-switching transistor per LED module. --------------------------------------------------------------------- Pinout: 1: Segment Select (SS)2 (seg C) PIC RA2 2: SS3 (seg D) PIC RA3 * 3: Module Select (MS)0 PIC RA4, open-drain output 4: Module Polarity (MP) PIC RA5, input-only 5: Ground (Vss) * 6: SS4 (seg E) PIC RB0 7: Data In (DI) Input data line, driven by bus master. PIC RB1/RX/DT * 8: SS7 (seg dp) PIC RB2 * 9: MS1 PIC RB3 10: MS2 PIC RB4 11: MS3 PIC RB5 12: MS4 PIC RB6 13: MS5 PIC RB7 14: Vdd (3.5 - 5.5 V) 15: SS5 (seg F) PIC RA6 16: SS6 (seg G) PIC RA7 17: SS0 (seg A) PIC RA0 18: SS1 (seg B) PIC RA1 (* Still need to update in the schematic) The MP line (Module Polarity) must be wired low for common-anode and high for common-cathode. The output registers are MS (Module Select) and SS (Segment Select). MS must be wired to the bases of up to 6 transistors. Use NPN transistors with common-anode LED modules, PNP transistors for common-cathode modules. 200 mA maximum current is recommended (e.g., 2N3904/2N3906). Wire the collectors to Vdd (common-anode) or Vss (common-cathode), and the emitters to the modules' common pins. SS must be wired to the module segments, through current-limiting resistors appropriate for a single segment. All segments are wired to the same pin of SS. Modules are numbered from 0-7, and may be physically ordered in any order, though they should be arranged with 0 on the far left if you want to use scrolling input. Unused modules can be simply ignored. Segments are named as follows: AAA F B GGG E C DDD dp --------------------------------------------------------------------- Architecture The internal architecture contains: - A command processor, reading and interpreting commands off the bus. - The display buffer: Two banks of 16 bytes of segment bits. Bank 0: |_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_| (normal) | Bank 1: |_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_| (flashing) 0 8| 15 The first 9 positions correspond to the 9 possible output modules. What appears in these positions is output to the corresponding LED modules. The remaining 7 positions are for future expansion. By default, the two banks are kept in sync -- their contents are identical. The actual values used for display are taken from the current output bank, which changes twice per second. So, modules that have different contents in the two banks will alternate between those two display values. If one bank's contents are blank at a given position, that position will flash on and off. - The cursor, indicating the current module for updates. The cursor consists of an index (0-15) and a bank (0 or 1). When the cursor is in bank 0, writing a value also copies it to the same position in bank 1. When the cursor is in bank 1, writing a value only writes it to bank 1. Normally, when a value is written to a given position, the cursor is incremented afterwards. If the cursor position is at or past the module count (i.e., pointing one position past the last visible module), the display buffer contents (both banks) are shifted left one position and the cursor is decremented before the value is written. This allows the display contents to continually scroll as more values are written, so a CLEAR command is unnecessary. To disable scrolling and cursor advancement, set MODULES to 0. --------------------------------------------------------------------- Protocol See the BasicBus spec for the General Commands. Commands specific to this device: RAW The format code may be: 0x00: Segment patterns. Bit 0 = segment A, up to bit 7 = decimal point. 0x01: 8-bit data to be represented in hexadecimal nibbles. E.g., 0x7D is shown as "7D" on the display. Since each byte takes two display positions, the cursor is moved two positions for each input byte. 0x02: 7-bit ASCII codes. Glyphs are represented as unambiguously as possible. Where there is no unambiguous interpretation (e.g., '@'), a rough approximation is used. If the high bit is set, the character is set to flash. That is, a space (' ') is written to bank 1 instead of duplicating the same character to bank 1. Special characters: \b [backspace] Move the cursor position back one. \r [0x0D = CR] Move the cursor to position 0. \n [0x0A = LF] Same as CLEAR. DLE [0x10] Move to bank 0. Same as BANK 0. DC1 [0x11] Move to bank 1. Same as BANK 1. SYN [0x16] Same as RESET_FLASH. '.' If the decimal point is off in the previous module, just turn it on. Otherwise, just inserts the period as normal. (Result: combines this with the previous digit if possible.) RESET Format: 0b00000000 Resets the chip to its power-on state. Specifically: - all modules are activated - the display buffer is cleared, as with the CLEAR command. CLEAR Format: 0b00000001 Clears display buffer for all modules, and resets the cursor to point to module 0, bank 0. BANK Format: 0b0000100b, where b is the bank, 0-1. Sets the cursor bank to b, leaving the index unchanged. RESET_FLASH Format: 0b00000100 Resets the flash timer, and displays bank 0. Often useful when you're updating flashing positions rapidly, to ensure values are seen. MODULES Format: 0b0001mmmm, where m is the number of LED modules in use, 0-9. Sets the number of LED modules. Modules 0 to m-1 will be used until the next MODULES command. Other modules will be deactivated, and left blank. Values outside the range 0-9 will be read as 9. Setting a value of 0 modules means the cursor is never advanced after writing. This can be useful to write a series of values to the same module in sequence. CURSOR Format: 0b0010cccc, where c is the new cursor value, 0-15. Moves the cursor to the given module index in the current bank. NIBBLE Format: 0b0101nnnn, where n is a hex nibble, 0-16. Puts the character corresponding to the hex nibble in the current position. A shortcut when outputting numeric data. FLASH Format: 0b0110mmmm, where m is a module number, 0-9. Sets the specified display value in bank 1 to space (' '), without changing the cursor or bank numbers. Effectively makes the given position flash. Examples To set up a single 4-digit display, oriented left-to-right: TARGET ALL 0x9F RESET 0x00 MODULES 4 0x14 To ouptut "12AB" using raw hex mode: RAW HEX 0xC1 data 0x12 data 0xAB end of data 0xFF To do the same operation using nibble commands: NIBBLE 1 0x51 NIBBLE 2 0x52 NIBBLE A 0x5A NIBBLE B 0x5B To do the same operation using raw ASCII mode: RAW ASCII 0xC2 1 0x31 2 0x32 A 0x41 B 0x42 end of data 0xFF This entails transmitting more serial bytes, but may be more convenient for the user. To output "FEFF" using raw hex mode: RAW HEX 0xC1 data 0xFE end of data 0xFF RAW HEX 0xC1 data 0xFF [not EOD because it's the first byte] end of data 0xFF Note that this uses the same number of bytes as does ASCII mode, because of the necessary escape codes. To implement a "wait" indicator that moves a segment around in the rightmost module: CURSOR 3 0x23 MODULES 0 0x10 [don't advance the cursor] RAW SEGMENTS 0xC0 segment A 0x01 segment B 0x02 segment C 0x04 segment D 0x08 segment E 0x10 segment F 0x20 [loop back to segment A...] ... end of data 0xFF MODULES 4 0x14 The caller is responsible for interposing a suitable delay between each segment command. To make a "wait" indicator that alternates two small boxes in the top and bottom half of the rightmost indicator: CURSOR 3 0x23 MODULES 0 0x10 [don't advance the cursor] RAW SEGMENTS 0xC0 segments ABFG 0xC3 end of data 0xFF BANK 1 0x09 RAW SEGMENTS 0xC0 segments CDEG 0xAC end of data 0xFF BANK 0 0x08 [clean up] MODULES 4 0x14 [clean up] This cursor will continue to alternate without intervention from the caller. Since these indicators correspond to the interpretations of ASCII characters '*' and 'o', this could also be written as: CURSOR 3 0x23 MODULES 0 0x10 [don't advance the cursor] RAW ASCII 0xC2 '*' 0x2A DC1 = BANK 1 0x11 [flash bank] 'o'; 0x6F DLE = BANK 0 0x10 [main bank] end of data 0xFF MODULES 4 0x14 [clean up] To change the display to read flashing "1200": CLEAR 0x01 RAW ASCII 0xC2 1+flash 0xB1 2+flash 0xB2 0+flash 0xB0 0+flash 0xB0 end of data 0xFF To make whatever's currently on the display flash: FLASH 0 0x60 FLASH 1 0x61 FLASH 2 0x62 FLASH 3 0x63 To make whatever's currently on the display flash to dashes: CURSOR 0 0x20 BANK 1 0x09 RAW ASCII 0xC2 '-' 0x2D '-' 0x2D '-' 0x2D '-' 0x2D end of data 0xFF BANK 0 0x08 [clean up] To make the display flash "LEdS/gOOd": CLEAR 0x01 RAW ASCII 0xC2 'L' 0x4C 'E' 0x45 'D' 0x44 'S' 0x53 \r 0x0D DC1 = BANK 1 0x11 'G' 0x47 'O' 0x4F 'O' 0x4F 'D' 0x44 DLE = BANK 0 0x10 end of data 0xFF To assign IDs to two different displays, and display "one" on the first and "two" on the second: One-time setup: Connect the first display to the bus. SETID 1 0xA1 Connect the second display to the bus. SETID 2 0xA2 Connect both displays. TARGET 1 0x81 CLEAR 0x01 RAW ASCII 0xC2 o 0x6F n 0x6E e 0x65 end of data 0xFF TARGET 2 0x82 CLEAR 0x01 RAW ASCII 0xC2 t 0x74 w 0x77 o 0x6F end of data 0xFF --------------------------------------------------------------------- Implementation The main work is done in an interrupt service routine. The PIC timer 0 is used to provide an interrupt for updating the LED outputs, which are lit round-robin, one module at a time. The PIC timer 1 is used to implement flashing. When a byte is received on the serial bus, the UART provides an interrupt, and the command is processed. The command processor parses incoming commands and maintains the following variables: - The command mode, which can be: - Ignoring: waiting for a 0xFF to finish another device's RAW mode. - Untargeted: waiting to be targeted by the bus. - Targeted: targeted, not in a raw mode. - Raw-segment: reading segment data in RAW mode. - Raw-Hex: reading hex data in RAW mode. This converts the nibbles to ASCII, then looks them up in EEPROM to convert them to segment data. - Raw-ASCII: reading ASCII data in RAW mode. This looks up the 7-bit values in a 128-byte table in EEPROM, converting them to segment data. - The display buffer: 16 bytes for bank 0 and 16 bytes for bank 1. - The cursor, indicating the current module and bank for updates. - The number of modules. The timer-0 interrupt processor does the following tasks on each interrupt: - Keeps track of the currently-lit module M. Increments it on each cycle. - Sets up PORTA and PORTB to drive the module select and segment lines. Invert the module lines if Module Polarity is 1 (common-cathode); otherwise, invert the segment lines. The timer-1 interrupt processor simply toggles the display bank, then restarts the timer. --------------------------------------------------------------------- TBD C macros, with and without prefixes auto baud detect? Scroll right and left in the 16-byte (or longer) buffer with SI/SO? Marquee: auto-scroll in a continuous loop. RAW mode 2 (7-bit ASCII) could be more efficiently implemented using a Targeted code point, so the other slaves don't have to be looking for 0xFF to end RAW mode. On the other hand, it probably doesn't actually cost anything.