Pixel scrolling of a text message on 8x8 LED matrix displays with a MAX7219

Firstly, apologies if there is already an answer to this query somewhere in this wonderful set of forums, but if there is I haven't been able to find it.

What I'm trying to do is display a message of about 18 characters on four 8x8 LED matrix displays (i.e. 32x8 pixels) using pixel by pixel (column by column) scrolling. The hardware I'm using is (four off of) the FC-16 board & display shown in the attached pictures, which is available from a well known internet auction site for less than 89p (yes really!) per board complete with soldered MAX7219 and the LED matrix display (price when bought as three sets of four boards).

I really want to stick with this hardware because it's easy to understand and simple to drive from any PICAXE chip including the 08M2+ which is what I'm currently using for testing (and cheap). Later, I want to scale up the display to maybe 50 or 60 cms in height (using discrete LEDs) if I can get it working on the small matrix displays first.

This short bit of video shows the sort of effect I'm after but just with a simple chevron pattern:

But displaying a message such as this:
by scrolling one pixel/column at a time is taxing my programming skills somewhat. The code I used for this display draws heavily on westaust55's excellent piece: https://picaxeforum.co.uk/threads/getting-started-with-the-max7219-eight-digit-7-segment-led-driver.23293/ which helped greatly in getting something working very quickly. I would though point out that there are a couple of minor errors in the 08M2 code in this article: "SYMBOL DIn_7219 = pinC.3" - pin 3 is an input only, and "DIRSB = %11100000", should be DIRSC?

My code for the text display is:
Code:
#PICAXE 08M2
 SETFREQ M32
; Code to write "This is a test screen."
; to a 4-digit matrix display (8x8 LEDs)
; in three screens.
;
; SYMBOL Definitions
;
; Control signal lines from PICAXE to MAX7219
SYMBOL Load_7219  = C.1
SYMBOL Clk_7219   = C.2
SYMBOL DIn_7219   = pinC.4
;
; MAX7219 Registers - these are the rows in the 8x8 matrix.
; Row0 is the top row and Row7 is the bottom.
;
SYMBOL No_Op    = 0
SYMBOL Row0       = 1
SYMBOL Row1       = 2
SYMBOL Row2       = 3
SYMBOL Row3       = 4
SYMBOL Row4       = 5
SYMBOL Row5       = 6
SYMBOL Row6       = 7
SYMBOL Row7       = 8
;
SYMBOL Decode     = 9
SYMBOL Intens     = 10
SYMBOL ScanLim    = 11
SYMBOL ShutDwn    = 12
SYMBOL DigTest    = 15
;
; MAX7219 Data Constants
SYMBOL Set_Off    = 0
SYMBOL Set_On     = 1
SYMBOL No_Digits  = 7   ; Scan limit for rows (all 8).
SYMBOL Dec_Digits = 0   ; turn off decoding of digits for 7-segment display.
SYMBOL Init_Inten = 1   ; 0 (=1/32 PWM on time) to 15 (=31/32 PWM on time).
;
; Define variables
;
SYMBOL Data_7219  = b0
SYMBOL Register   = b1
SYMBOL Character  = b2
SYMBOL RowCount   = b3
SYMBOL Counter    = b4
SYMBOL Latch      = b5
;
; EEPROM character definitions would ideally be used to generate
; the required bit patterns but it's rather complicated.
;
;EEPROM (" " ,3 ,%00000000 ,%00000000 ,%00000000)
;EEPROM ("T" ,5 ,%10000000 ,%10000000 ,%11111110 ,%10000000 ,%10000000)
;EEPROM ("a" ,4 ,%00000100 ,%00101010 ,%00101010 ,%00011110)
;EEPROM ("e" ,4 ,%00011100 ,%00101010 ,%00101010 ,%00011000)
;EEPROM ("h" ,4 ,%11111110 ,%00100000 ,%00100000 ,%00011110)
;EEPROM ("i" ,3 ,%00100010 ,%10111110 ,%00000010)
;EEPROM ("c" ,4 ,%00011100 ,%00100010 ,%00100010 ,%00010100)
;EEPROM ("n" ,4 ,%00111110 ,%00100000 ,%00100000 ,%00011110)
;EEPROM ("r" ,4 ,%00111110 ,%00010000 ,%00100000 ,%00100000)
;EEPROM ("t" ,3 ,%00100000 ,%11111100 ,%00100010)
;EEPROM ("s" ,4 ,%00010010 ,%00101010 ,%00101010 ,%00100100)
;
; These EEPROM definitions define a row at a time for the whole display.
; Screen #1
EEPROM ($7D , $04 , $00 , $80) ; Row 0, top
EEPROM ($11 , $00 , $00 , $00)
EEPROM ($11 , $CC , $71 , $8E)
EEPROM ($11 , $24 , $80 , $90)
EEPROM ($11 , $24 , $60 , $8C)
EEPROM ($11 , $24 , $10 , $82)
EEPROM ($11 , $2E , $E1 , $DC)
EEPROM ($00 , $00 , $00 , $00) ; Row 7, bottom
; Screen #2
EEPROM ($00 , $08 , $00 , $20) ; Row 0, top
EEPROM ($00 , $08 , $00 , $20)
EEPROM ($06 , $1C , $C7 , $70)
EEPROM ($01 , $09 , $28 , $20)
EEPROM ($07 , $09 , $E6 , $20)
EEPROM ($09 , $09 , $01 , $20)
EEPROM ($07 , $04 , $CE , $10)
EEPROM ($00 , $00 , $00 , $00) ; Row 7, bottom
; Screen #3
EEPROM ($00 , $00 , $00 , $00) ; Row 0, top
EEPROM ($00 , $00 , $00 , $00)
EEPROM ($73 , $2C , $C6 , $70)
EEPROM ($84 , $B1 , $29 , $48)
EEPROM ($64 , $21 , $EF , $48)
EEPROM ($14 , $A1 , $08 , $4B)
EEPROM ($E3 , $20 , $C6 , $4B)
EEPROM ($00 , $00 , $00 , $00) ; Row 7, bottom
;
;===============================================
; Initialise the PICAXE Pins for communication with the MAX7219
;===============================================
Init:
    ; DIRSB = %11100000 wrong for an 08M2?
    DIRSC = %00010110
    LOW Load_7219
    LOW Clk_7219
    DIn_7219 = 0
    PAUSE 1000
    GOSUB Initialise7219
;
;===============================================
; Main Program loop
;===============================================   
Main:
    PAUSE 5000
    GOSUB Screen1
    PAUSE 10000
    GOSUB Screen2
    PAUSE 10000
    GOSUB Screen3
    PAUSE 10000
    ;GOSUB OverwriteB
    GOTO main
END
;
;===============================================
; Subroutines
;===============================================   
; Subroutine to initialise the MAX7219 7-Seg LED display driver
Initialise7219:
FOR Character = 1 TO 4
    Register = Decode  : Data_7219 = Dec_Digits : GOSUB ShiftTo7219
NEXT Character
    PULSOUT C.1, 1 ' minimum high duration is 50 ns -still okay at 64 MHz
FOR Character = 1 TO 4
    Register = Intens  : Data_7219 = Init_Inten : GOSUB ShiftTo7219
NEXT Character
    PULSOUT C.1, 1 ' minimum high duration is 50 ns -still okay at 64 MHz
FOR Character = 1 TO 4
    Register = ScanLim : Data_7219 = No_Digits  : GOSUB ShiftTo7219
NEXT Character
    PULSOUT C.1, 1 ' minimum high duration is 50 ns -still okay at 64 MHz
FOR Character = 1 TO 4
    Register = ShutDwn : Data_7219 = Set_On    : GOSUB ShiftTo7219
NEXT Character
    PULSOUT C.1, 1 ' minimum high duration is 50 ns -still okay at 64 MHz
FOR Character = 1 TO 4
    Register = DigTest : Data_7219 = Set_Off     : GOSUB ShiftTo7219
NEXT Character
    PULSOUT C.1, 1 ' minimum high duration is 50 ns -still okay at 64 MHz
RETURN
;
; Subroutine to shift the register address and data
; values out to one of the four cascaded MAX7219s.
ShiftTo7219:
    PULSOUT Clk_7219, 1 ; bit 15 is don't care
    PULSOUT Clk_7219, 1 ; bit 14 is don't care
    PULSOUT Clk_7219, 1 ; bit 13 is don't care
    PULSOUT Clk_7219, 1 ; bit 12 is don't care
    DIn_7219 = bit11 : PULSOUT Clk_7219, 1
    DIn_7219 = bit10 : PULSOUT Clk_7219, 1
    DIn_7219 = bit9 : PULSOUT Clk_7219, 1
    DIn_7219 = bit8 : PULSOUT Clk_7219, 1
    DIn_7219 = bit7 : PULSOUT Clk_7219, 1
    DIn_7219 = bit6 : PULSOUT Clk_7219, 1
    DIn_7219 = bit5 : PULSOUT Clk_7219, 1
    DIn_7219 = bit4 : PULSOUT Clk_7219, 1
    DIn_7219 = bit3 : PULSOUT Clk_7219, 1
    DIn_7219 = bit2 : PULSOUT Clk_7219, 1
    DIn_7219 = bit1 : PULSOUT Clk_7219, 1
    DIn_7219 = bit0 : PULSOUT Clk_7219, 1
    PAUSE 200   ; Deliberate reduction of write speed 
            ; so that the writing can be seen.
            ; Without this it's almost instantaneous.
;
; PULSOUT C.1, 1 ; Latches the data into the MAX7219s.
;
; The above is moved to the main routine so that all 4 devices are
; latched simultaneously with the 4 bytes of serial data.
    RETURN
;
; New versions of the display routine.
Screen1:
    FOR Counter = 0 TO 31
        READ Counter , Data_7219
        Register = Counter / 4
        Register = Register + 1 : GOSUB ShiftTo7219
        Latch = Counter // 4
        IF Latch = 3 THEN : PULSOUT C.1, 1 : ENDIF
        ; Latch the data into all four devices simultaneously.
    NEXT
RETURN
;
Screen2:
    FOR Counter = 32 TO 63
        READ Counter , Data_7219
        Register = Counter - 32
        Register = Register / 4
        Register = Register + 1 : GOSUB ShiftTo7219
        Latch = Counter // 4
        IF Latch = 3 THEN : PULSOUT C.1, 1 : ENDIF
    NEXT
RETURN
;
Screen3:
    FOR Counter = 64 TO 95
        READ Counter , Data_7219
        Register = Counter - 64
        Register = Register / 4
        Register = Register + 1 : GOSUB ShiftTo7219
        Latch = Counter // 4
        IF Latch = 3 THEN : PULSOUT C.1, 1 : ENDIF
    NEXT
RETURN
;
The codes in EEPROM have been constructed from the character definitions shown in the code (commented out) by manipulating them in Excel to read the row-by-row values needed to write to the display. The display can only be written to one row (of 32 pixels) at a time, which makes scrolling column by column quite challenging.

I think that the way to do it is to recreate the whole message as an array (of approximately 96 x 8 bits) in RAM, then copy eight 32 x 1 horizontal chunks of it to the display, moving the area to copy one pixel right each time the eight rows have been copied. See the diagram attached. I think this is the approach taken by this Arduino example: https://howtomechatronics.com/tutorials/arduino/8x8-led-matrix-max7219-tutorial-scrolling-text-android-control-via-bluetooth/ , but so far as I can see it only uses two display matrices (16 x 8) which simplifies things a bit.

Looking at the Arduino code I can get the gist of what's being done but code such as " while (*s != 0) { " leaves me a bit nonplussed - it's been 40 years since I did any 'C' coding and very little of it even then. So if anyone could maybe give me a bit of help with this please I'd be most grateful? If it helps, I only want to scroll a fixed message (similar in length to the test message) and not do variable messages on-the-fly as in the Arduino example. Sorry it's rather a long request.
 

Attachments

hippy

Technical Support
Staff member
It should be feasible. The easiest way is to represent each column sequential in memory so you can just scan through each item and set each column as you go, which seems to be how you also imagine it, for example -
Code:
   .-------------- %10000001  #------#
   | .------------ %11111111  ########
   | | .---------- %10000001  #------#
   | | | .-------- %00000000  --------
   | | | | .------ %11111110  #######-
   | | | | | .---- %00010001  ---#---#
   | | | | | | .-- %00010001  ---#---#
   | | | | | | |
Code:
0  # # # - - # # - -
1  - # - - # - - # -
2  - # - - # - - # -
3  - # - - # - - # -
4  - # - - # # # # -
5  - # - - # - - # -
6  - # - - # - - # -
7  # # # - # - - # -
The advantage of the top row mapping to the least significant bit of the byte value, which might be counter intuitive, is, when you tilt your head to the right, the binary byte values represent the pixels which can be read clearly, and the column output, top to bottom, reads left to right with head tilted. That's even more obvious when showing 0 as '-' and 1 as '#'. That makes it much easier to find any bit errors in the data being used.

Scrolling is then just a case of shifting or rotating that memory upwards or downwards, sending the new set of data to have it scrolling. The memory may be larger than the actual number of columns to allow blank space between the end and subsequent start of what is next scrolled in.

The memory doesn't necessarily have to be shifted or rotated, you can keep the memory static and vary where you will be starting from when updating the display, wrap round to the start when you reach the end while outputting.

And with that, it no longer actually has to be memory. The columns can be extracted from Data Eeprom, Table, or even obtained from LOOKUP tables.

So we have in theory solved the problem, easy-peasy. First part done.

The difficulty comes in the hardware wanting our 'column at a time' data in some other form; row at a time maybe, or its row zero may be at the bottom rather than top as we'd like it.

Some of that may be solvable by rotating each display, 90, 180 or 270 degrees. by turning the whole display upside down, or by shifting memory in the opposite direction. But there is probably going to be something left to resolve.

So what one has to do is work out the routine which can output that data, work out the data needed and where it comes from for each column or row and send that.

That way we have our memory in an easy to understand and easy to shift or scroll form, and easy to use output display routines.

Those routine may be easy through to a little complicated. It will depend on which is row and column 0 and which 7 and how the displays are orientated and ordered.

I would start with something simple which puts out a single 8x8 character in memory-held column order to just one display, no matter how it display, use something which allows what needs to be done to make it work easy to determine -
Code:
.---------------- %10000000  #-------
| .-------------- %01000000  -#------
| | .------------ %00100000  --#-----
| | | .---------- %00010001  ---#---#
| | | | .-------- %00001011  ----#-##
| | | | | .------ %00000111  -----###
| | | | | | .---- %00001111  ----####
| | | | | | | .-- %11011111  ##-#####
| | | | | | | |
Code:
- - - # # # # #
- - - - # # # #
- - - - - # # #
- - - - # - # #
- - - # - - - #
- - # - - - - -
- # - - - - - #
# - - - - - - #
That should be possible to do and I, and others can probably help with that. So it becomes a five part affair -

1) Define how data is held in memory - Done, but might need tweaking.

2) Determine what hardware is being used - Done.

3) Define how the hardware connects to the PICAXE - Done.

4) Write some simple code which puts out the test character.

5) Observe the results, tweak until it works.

So it's (4) which we need. I am not sure how well what you have fits the bill; it might help to tell us what you have does.

Also, do you want to use the code you have, modified as may be required, or are you happy any which way it works, are happy to build the code up from first principles ?

The later is often best because once, you understand it, it's easier to go onwards by yourself, add more, even tell others how to do it.

I would have to look at the datasheet to check how the MAX7219's are controlled, but the basic scrolling is easy enough to simulate -
Code:
Symbol reserveW0 = w0 ; b1:b0
Symbol scroll    = b2
Symbol column    = b3
Symbol index     = b4

Data( %10000000 ) ;  #-------
Data( %01000000 ) ; -#------
Data( %00100000 ) ; --#-----
Data( %00010001 ) ; ---#---#
Data( %00001011 ) ; ----#-##
Data( %00000111 ) ; -----###
Data( %00001111 ) ; ----####
Data( %11011111 ) ; ##-#####

TestProgram:
  Do
    For scroll = 0 To 10
      Gosub ShowCharacter
      SerTxd( CR, LF )
    Next
  Loop

ShowCharacter:
  For column = 0 To 7
    Gosub ShowColumn
  Next
  Return

ShowColumn:
  index = scroll + column // 10
  If index > 7 Then
    b0  = %00000000
  Else
    Read index, b0
  End If
  If bit7 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit6 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit5 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit4 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit3 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit2 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit1 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit0 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  SerTxd( CR, LF )
  Return
 
Thanks Hippy and Tex for your very quick and helpful replies. Noting your first point Hippy about the character definitions, the Arduino code does indeed store the characters in what at first sight appears to be an odd format, column by column with the least significant bit at the top of the character, e.g.
Code:
  5, 8, B00000001, B00000001, B01111111, B00000001, B00000001, // T
  4, 8, B01111111, B00000100, B00000100, B01111000, B00000000, // h
  3, 8, B01000100, B01111101, B01000000, B00000000, B00000000, // i
  4, 8, B01001000, B01010100, B01010100, B00100100, B00000000, // s
You ask what my code does Hippy, it's very simple: I've just represented each row of the 32 x 8 matrix (top down) as four bytes which are read from EEPROM and written sequentially to the display. It wouldn't make any sense to persevere with that approach. Other than the routines for transmitting the data to the MAX7219s, I don't feel that the code I have is much use and am happy to effectively start again from scratch.

As the message I want to display uses several characters multiple times, it makes sense to store character definitions (as above) together with the string of characters to display, "This is a test screen." for instance and generate the pixel representations as required.

I'm reasonably conversant with the MAX7219 data sheet and modes of operation and it's somewhat limited in what can be done. Basically the (up to) 8 characters in 7-segment display mode translate to the horizontal rows in the 8 x 8 pixel display. Daisy chaining the chips (and displays) requires all four devices to be latched simultaneously once the 32 bits for a given row have been written as four bytes (as I've tried to portray in my diagram). Digit 0 on a 7-segment display (register address 0xX1) translates to row 0, the top row. Rows could be written in any order but it makes sense to write from 0 to 7 because it's easier.

I shall try out your code suggestion Hippy on the simulator and see how I get on and whether I can extend it to the 32 bit long display.

Thanks also Tex, your code suggestions look a bit scary at first glance but if I can't get anywhere with Hippy's suggestion I'll look at them in more detail.
 

hippy

Technical Support
Staff member
Code:
B01111111, B00000100, B00000100, B01111000, B00000000, // h
That seems to match with what I was proposing, probably for the same reason. Split into columns ...
Code:
%01111111 ; -####### h
%00000100 ; -----#--
%00000100 ; -----#--
%01111000 ; -####---
The msb, left-most bit, is probably always zero to give a blank bottom row, to separate characters from the one below in a multi-line display, and to give a bit of a descender for g, j, p, q and y.

I would possibly go for a 6-pixel high character code ...
Code:
%00011100 ; ---###-- g
%10100010 ; #-#---#-
%10010010 ; #--#--#-
%01111100 ; -#####--

%00111111 ; --###### h
%00000100 ; -----#--
%00000010 ; ------#-
%00111100 ; --####--
How things look is very much in the eye of the beholder and one can argue endlessly over what looks best. So long as the basics are there it can always be tweaked later.

One tip I do have is to use an editor which supports overtyping, use a blank '%00000000 ; --------" character template, overtype the -'s with #'s, then fill in the 1's. Handily the PICAXE Editor supports overtyping.
 
Yes thanks Hippy, you're exactly right about the msb being mostly blank apart from the descenders, e.g. :
Code:
  4, 8, B00111110, B01000001, B01000001, B10111110, B00000000, // Q
  4, 8, B10011000, B10100100, B10100100, B01111000, B00000000, // g
  4, 8, B01000000, B10000000, B10000100, B01111101, B00000000, // j
  4, 8, B10011100, B10100000, B10100000, B01111100, B00000000, // y
To save re-inventing the wheel I'll stick with the character definitions from the Arduino example as they look fine on the small matrix displays.

I just tried simulating your example and note that on the serial terminal screen it writes horizontally but scrolls vertically so even if the character is viewed sideways this isn't how the MAX7219 works, it writes horizontally but the scrolling also needs to be horizontal. That said I'm sure it's only a minor tweak to modify your code. I'll start looking at it and see if I can make that change myself. I'll never learn anything if I just take other people's code and use it without understanding it.
 

hippy

Technical Support
Staff member
I just tried simulating your example and note that on the serial terminal screen it writes horizontally but scrolls vertically so even if the character is viewed sideways this isn't how the MAX7219 works
That is correct; that code just demonstrates that the memory can be scrolled.
 

hippy

Technical Support
Staff member
Looking at the MAX7219 datasheet using daisy-chained displays it appears you would send four bytes which represent the first row or column on each 8x8 display, issue a "load", and repeat eight times so all rows or columns were written.

So, to figure out how the hardware is physically configured, I would forget about the actual message part for now, and just send the following -

Send $0102, $0106, $010E, $011E, Load
Send $0200, $0200, $0200, $0200, Load
Send $0300, $0300, $0300, $0300, Load
Send $0400, $0400, $0400, $0400, Load
Send $0500, $0500, $0500, $0500, Load
Send $0600, $0600, $0600, $0600, Load
Send $0700, $0700, $0700, $0700, Load
Send $0800, $0800, $0800, $0800, Load

Each 8x8 display should show a single line, top, bottom, left or right, with the lit pixels at one end or the other of that line. From what that shows how the display is wired can be figured out, and from that what the output routine needs to be.
 
Last edited:
Yes thanks, I have the display sussed okay.

If you look at my original code, e.g. :
Code:
; These EEPROM definitions define a row at a time for the whole display.
; Screen #1
EEPROM ($7D , $04 , $00 , $80) ; Row 0, top
EEPROM ($11 , $00 , $00 , $00)
EEPROM ($11 , $CC , $71 , $8E)
EEPROM ($11 , $24 , $80 , $90)
EEPROM ($11 , $24 , $60 , $8C)
EEPROM ($11 , $24 , $10 , $82)
EEPROM ($11 , $2E , $E1 , $DC)
EEPROM ($00 , $00 , $00 , $00) ; Row 7, bottom
this writes the first screen "This is" which is fine.

However, it's not in a format which can be scrolled left a pixel at a time. I'd have to generate a whole new set of 32 bytes for each new position (32) of the text which would obviously not be feasible.

My problem at the moment is that using the character definitions as above (Monday's post)
Code:
  4, 8, B00111110, B01000001, B01000001, B10111110, B00000000, // Q
  4, 8, B10011000, B10100100, B10100100, B01111000, B00000000, // g
I need to take the eight most significant bits from each of the first eight columns and write them to a new byte to be sent to the display top row then repeat for the next eight, etc to write the msbs for all 32 columns simultaneously to the display.

I had thought it would be easy to merge bits from different bytes into one new one and no doubt there are examples of how to do it somewhere on the forum, but my first attempts at what I thought was easy proved not so simple as I thought.

All I'm trying to do is copy bitwise from b1 to b0 to see how it's done, but it doesn't work as I expected.
Code:
RdModWr:
  READ index, b1
  LET  b0  = %00000000
  LET Temp = %11111111
  IF bit15 = 1 THEN : LET Temp = Temp  AND %10000000 : ENDIF
  IF bit14 = 1 THEN : LET Temp = Temp  AND %01000000 : ENDIF
  IF bit13 = 1 THEN : LET Temp = Temp  AND %00100000 : ENDIF
  IF bit12 = 1 THEN : LET Temp = Temp  AND %00010000 : ENDIF
  IF bit11 = 1 THEN : LET Temp = Temp  AND %00001000 : ENDIF
  IF bit10 = 1 THEN : LET Temp = Temp  AND %00000100 : ENDIF
  IF bit8  = 1 THEN : LET Temp = Temp  AND %00000010 : ENDIF
  IF bit9  = 1 THEN : LET Temp = Temp  AND %00000001 : ENDIF
  LET b0 = Temp
  RETURN
I'm sure there will be a simple bit of code which does something like this, probably much shorter and more elegant, but I haven't managed to find anything.

A little help here would be appreciated please?
 
Ha!
Brain working again and basic logic of the AND function sorted out with simple diagram.

Code now works as intended though if there's a more efficient way of doing something similar that would be interesting?

I'm always amazed at the experts' use of very short bits of code that do clever things in very few commands.
Code:
RdModWr:
  READ index, b1
  LET Temp = %11111111
  IF bit15 = 0 THEN : LET Temp = Temp  AND %01111111 : ENDIF
  IF bit14 = 0 THEN : LET Temp = Temp  AND %10111111 : ENDIF
  IF bit13 = 0 THEN : LET Temp = Temp  AND %11011111 : ENDIF
  IF bit12 = 0 THEN : LET Temp = Temp  AND %11101111 : ENDIF
  IF bit11 = 0 THEN : LET Temp = Temp  AND %11110111 : ENDIF
  IF bit10 = 0 THEN : LET Temp = Temp  AND %11111011 : ENDIF
  IF bit8  = 0 THEN : LET Temp = Temp  AND %11111101 : ENDIF
  IF bit9  = 0 THEN : LET Temp = Temp  AND %11111110 : ENDIF
  LET b0 = Temp
  RETURN
 

hippy

Technical Support
Staff member
Code:
  LET Temp = %11111111
  IF bit15 = 0 THEN : LET Temp = Temp  AND %01111111 : ENDIF
  IF bit14 = 0 THEN : LET Temp = Temp  AND %10111111 : ENDIF
  IF bit13 = 0 THEN : LET Temp = Temp  AND %11011111 : ENDIF
  IF bit12 = 0 THEN : LET Temp = Temp  AND %11101111 : ENDIF
  IF bit11 = 0 THEN : LET Temp = Temp  AND %11110111 : ENDIF
  IF bit10 = 0 THEN : LET Temp = Temp  AND %11111011 : ENDIF
  IF bit8  = 0 THEN : LET Temp = Temp  AND %11111101 : ENDIF
  IF bit9  = 0 THEN : LET Temp = Temp  AND %11111110 : ENDIF
  LET b0 = Temp
It's not exactly clear what that is intended to do. As written it is swapping the two lsb's. If that is just a typo with 'bit8' and 'bit9' swapped round then it's merely doing "Let b0 = b1".

If it is some esoteric way to bitwise copy, as it seems it may be, then it probably is that the 'bit8' and 'bit9' are swapped.

If it's intended to reverse the bits of b1 with the result in b0 then the easiest code where there is no REV command available is -
Code:
bit0 = bit15
bit1 = bit14
bit2 = bit13
bit3 = bit12
bit4 = bit11
bit5 = bit10
bit6 = bit9
bit7 = bit8
Where data is held in on-chip Eeprom and needs to be reversed one trick is to reverse the bits in Eeprom when the program is first run; read each byte, reverse it, write it back. Check the value of the first byte or some flag in Eeprom to indicated if it has been reversed or not so it only gets reversed after a new download. That won't have a significant effect on Eeprom lifetime.

That way the code can be entered in the most readable and understandable way, even if it is reversed. And, because that is corrected the first time the program runs, there is no reversing which needs to be done in the rest of the code.

One just has to remember that how the data is defined is not how it will be when the program runs.
 
Doh! Yes, just a typo (bit8/9) and the code is purely to test writing to specific bits within a byte as that isn't possible with a standard command.

As I mentioned earlier: "I need to take the eight most significant bits from each of the first eight columns and write them to a new byte to be sent to the display top row then repeat for the next eight, etc to write the msbs for all 32 columns simultaneously to the display. "

So I needed to test how to do that, but I think I've nearly got that sorted now thanks.
 

hippy

Technical Support
Staff member
If we assume you have 8 consecutive 8-bit bytes as below in RAM and we want to form a single 'b0' byte containing %abcdefgh ...
Code:
<start> %-------a
        %-------b
        %-------c
        %-------d
        %-------e
        %-------f
        %-------g
        %-------h
You can do this ...
Code:
bPtr = <start>
mask = %00000001

bit7 = @bPtrInc & mask Max 1
bit6 = @bPtrInc & mask Max 1
bit5 = @bPtrInc & mask Max 1
bit4 = @bPtrInc & mask Max 1
bit3 = @bPtrInc & mask Max 1
bit2 = @bPtrInc & mask Max 1
bit1 = @bPtrInc & mask Max 1
bit0 = @bPtrInc & mask Max 1
Change 'mask' to get other bits. Change 'bit7-bit0' order to 'bit0-bit7' to reverse those if need be.

It gets a little more complicated if the data is in Eeprom or Table, ...
Code:
bPtr = <start>
mask = %00000001

Read bPtr, b1 : bPtr = bPtr + 1 : bit7 = b1 & mask Max 1 
Read bPtr, b1 : bPtr = bPtr + 1 : bit6 = b1 & mask Max 1 
Read bPtr, b1 : bPtr = bPtr + 1 : bit5 = b1 & mask Max 1 
Read bPtr, b1 : bPtr = bPtr + 1 : bit4 = b1 & mask Max 1 
Read bPtr, b1 : bPtr = bPtr + 1 : bit3 = b1 & mask Max 1 
Read bPtr, b1 : bPtr = bPtr + 1 : bit2 = b1 & mask Max 1 
Read bPtr, b1 : bPtr = bPtr + 1 : bit1 = b1 & mask Max 1 
Read bPtr, b1 : bPtr = bPtr + 1 : bit0 = b1 & mask Max 1
You can try this program in simulation, which does do the character rotation which was lacking in my earlier program by accumulating bits as suggested here -
Code:
#Picaxe 08M2

Symbol mask = b2

Data( %10000000 ) ; #-------
Data( %01000000 ) ; -#------
Data( %00100000 ) ; --#-----
Data( %00010001 ) ; ---#---#
Data( %00001011 ) ; ----#-##
Data( %00000111 ) ; -----###
Data( %00001111 ) ; ----####
Data( %11011111 ) ; ##-#####

TestProgram:
  mask = %00000001
  Do
    bPtr = 0
    Gosub GetByte
    Gosub ShowByte
    mask = mask * 2
  Loop Until mask = 0
  End

GetByte:
  Read bPtr, b1 : bPtr = bPtr + 1 : bit7 = b1 & mask Max 1 
  Read bPtr, b1 : bPtr = bPtr + 1 : bit6 = b1 & mask Max 1 
  Read bPtr, b1 : bPtr = bPtr + 1 : bit5 = b1 & mask Max 1 
  Read bPtr, b1 : bPtr = bPtr + 1 : bit4 = b1 & mask Max 1 
  Read bPtr, b1 : bPtr = bPtr + 1 : bit3 = b1 & mask Max 1 
  Read bPtr, b1 : bPtr = bPtr + 1 : bit2 = b1 & mask Max 1 
  Read bPtr, b1 : bPtr = bPtr + 1 : bit1 = b1 & mask Max 1 
  Read bPtr, b1 : bPtr = bPtr + 1 : bit0 = b1 & mask Max 1
  Return

ShowByte:
  If bit7 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit6 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit5 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit4 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit3 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit2 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit1 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit0 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  SerTxd( CR, LF )
  Return
 
Last edited:

AllyCat

Senior Member
Hi,

My first attempt at a "quick and dirty" solution (well the first that worked and looked presentable ;) ). Not as pretty as hippy's, but it should work for any consecutive direct or indirect memory locations and easily mirrored or flipped by using the STEP -1 versions.
Code:
b10=$FF: b11=1: b12=1: b13=1: b14=0: b15=0: b16=0: b17=0 ; Test pattern
symbol source = b2
for source = 10 to 17        ; MIRROR
;;; for source = 17 to 10 step -1
    peek source , b0
;;;      for bptr = 20 to 27        ; FLIP (INVERT)  
    for bptr = 27 to 20 step -1
        @bptr = bit7 + @bptr + @bptr
        b0 = bit7 + b0 + b0
    next
next
sertxd(#b20," ",#b21," ",#b22," ",#b23," ",#b24," ",#b25," ",#b26," ",#b27," ")
The "bit7" can be replaced by "bn / 128" if you don't have a bit-addressable variable, and PEEK / POKE if you don't want to use @bptr.

Cheers, Alan.
 
Thanks Alan & Hippy, your code options look infinitely neater than what I'v achieved so far, and being more concise will no doubt run faster as well. I'll have a look at putting one or other into my program tomorrow and see how I get on.
 

BESQUEUT

Senior Member
However, it's not in a format which can be scrolled left a pixel at a time. I'd have to generate a whole new set of 32 bytes for each new position (32) of the text which would obviously not be feasible.
My proposal to scroll a bitmap memory stored, one pixel at a time.
Test matrix is 32x3 but can be more lines...
Code:
#simspeed 2

symbol I=b10
symbol Line=b11
symbol Start=100

bptr=Start
@bptrinc=%00000010 ' first line of Pixels
@bptrinc=%00000000
@bptrinc=%00000000
@bptrinc=%00111000

@bptrinc=%00000110 ' second line of Pixels
@bptrinc=%00000000
@bptrinc=%00000000
@bptrinc=%01111000

@bptrinc=%00001110 ' third line of Pixels
@bptrinc=%00000000
@bptrinc=%00000000
@bptrinc=%11111000

' etc...

for I=1 to 32
    for Line =0 to 2
        bptr=Line*4+Start
        b0=@bPtrInc
        b1=@bPtrInc
        b2=@bPtrInc
        b3=@bPtrInc
        gosub printbits
      
        b4=bit15    'Rotate bits...
        w0=w0*2+bit31
        w1=w1*2+b4
      
        bptr=Line*4+Start
        @bPtrInc=b0    ' store rotated bits
        @bPtrInc=b1
        @bPtrInc=b2
        @bPtrInc=b3

    next Line
    sertxd (13,10)
  
next I
end


PrintBits:
    sertxd(#bit31,#bit30,#bit29,#bit28,#bit27,#bit26,#bit25,#bit24," ")
    sertxd(#bit23,#bit22,#bit21,#bit20,#bit19,#bit18,#bit17,#bit16," ")
    sertxd(#bit15,#bit14,#bit13,#bit12,#bit11,#bit10,#bit9,#bit8," ")
    sertxd(#bit7,#bit6,#bit5,#bit4,#bit3,#bit2,#bit1,#bit0,13,10)
    return
 

hippy

Technical Support
Staff member
The conflict here is between code where it is easy to specify a message, and edit if it needs to be changed, and having simple code for decoding and output. It's one or the other.

In all three examples below the 'ShowRow' routine is exactly the same so it is effectively fixed MAX7219 hardware. And it does match one of the sixteen possible hardware configurations it could be, even if not the specific one being used in this project.

The difference between the three is the 'Data' definitions and the 'ReadRow' routines. The 'Data' needs to match what 'ReadRow' does. All 'ReadRow' routines deliver the exact same results that the 'ShowRow' routine requires.

This first code is that which has a 'ReadRow' which is simplest, grabs a full 32-bit row from consecutive locations and outputs that.

As noted, 'Data' must match that, so good luck figuring out what the display will show without running the code. And you will need even better luck figuring out how to change it when it's not quite right or needs tweaking - As it does because I did make an accidental mistake along the line as you will see when you run it.
Code:
Data( %10010000 ) ; #--#----
Data( %00100100 ) ; --#--#--
Data( %00000010 ) ; ------#-
Data( %00000100 ) ; -----#--

Data( %10010000 ) ; #--#----
Data( %10100100 ) ; #-#--#--
Data( %01100010 ) ; -##---#-
Data( %01100100 ) ; -##--#--

Data( %10010000 ) ; #--#----
Data( %10100100 ) ; #-#--#--
Data( %10010010 ) ; #--#--#-
Data( %10010101 ) ; #--#-#-#

Data( %11110011 ) ; ####--##
Data( %10100100 ) ; #-#--#--
Data( %10010010 ) ; #--#--#-
Data( %10010110 ) ; #--#-##-

Data( %10010100 ) ; #--#-#--
Data( %00100100 ) ; --#--#--
Data( %10010010 ) ; #--#--#-
Data( %10010100 ) ; #--#-#--

Data( %10010111 ) ; #--#-###
Data( %10100100 ) ; #-#--#--
Data( %10010010 ) ; #--#--#-
Data( %10010110 ) ; #--#-##-

Data( %10010100 ) ; #--#-#--
Data( %00100100 ) ; --#--#--
Data( %10010000 ) ; #--#----
Data( %10010101 ) ; #--#-#-#

Data( %10010011 ) ; #--#--##
Data( %00010010 ) ; ---#--#-
Data( %01100010 ) ; -##---#-
Data( %01100101 ) ; -##--#-#

TestProgram:
  For b27 = 1 To 8
    Gosub ReadRow
    Gosub ShowRow
  Next
  SerTxd( CR, LF )
  End

ReadRow:
  Read bPtr, b3 : bPtr = bPtr + 1
  Read bPtr, b2 : bPtr = bPtr + 1
  Read bPtr, b1 : bPtr = bPtr + 1
  Read bPtr, b0 : bPtr = bPtr + 1
  Return

ShowRow:
  If bit31 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit30 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit29 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit28 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit27 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit26 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit25 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit24 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit23 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit22 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit21 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit20 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit19 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit18 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit17 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit16 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit15 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit14 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit13 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit12 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit11 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit10 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit9  = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit8  = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit7  = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit6  = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit5  = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit4  = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit3  = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit2  = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit1  = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit0  = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  SerTxd( CR, LF )
  Return
The display should show "Hello! Ok" and fill all 32 columns. That defective "e" genuinely wasn't deliberate. It actually arose through an error in the following code, but it's not easy to find nor fix in the above.

Added : Actually it is a bit easier if one uses multi-byte Data statements -
Code:
Data( %10010000,%00100100,%00000010,%00000100 )
Data( %10010000,%10100100,%01100010,%01100100 )
Data( %10010000,%10100100,%10010010,%10010101 )
Data( %11110011,%10100100,%10010010,%10010110 )
Data( %10010100,%00100100,%10010010,%10010100 )
Data( %10010111,%10100100,%10010010,%10010110 )
Data( %10010100,%00100100,%10010000,%10010101 )
Data( %10010011,%00010010,%01100010,%01100101 )
[Split across multiple posts to overcome forum limits]
 
Last edited:

hippy

Technical Support
Staff member
Here's a version which does a row at a time as above, but in 8x8 display blocks, so it is easier to see what is being put out. The 'ReadRow' routine has changed, but not by much.

The text which will be displayed is easier to see and comprehend. Where that "e" error is becomes more obvious, is easier to fix. But if one wanted to make the horizontal bar of the initial "H" one pixel wider, it means having to change almost every Data value.
Code:
Data( %10010000 ) ; #--#----
Data( %10010000 ) ; #--#----
Data( %10010000 ) ; #--#----
Data( %11110011 ) ; ####--##
Data( %10010100 ) ; #--#-#--
Data( %10010111 ) ; #--#-###
Data( %10010100 ) ; #--#-#--
Data( %10010011 ) ; #--#--##

Data( %00100100 ) ; --#--#--
Data( %10100100 ) ; #-#--#--
Data( %10100100 ) ; #-#--#--
Data( %10100100 ) ; #-#--#--
Data( %00100100 ) ; --#--#--
Data( %10100100 ) ; #-#--#--
Data( %00100100 ) ; --#--#--
Data( %00010010 ) ; ---#--#-

Data( %00000010 ) ; ------#-
Data( %01100010 ) ; -##---#-
Data( %10010010 ) ; #--#--#-
Data( %10010010 ) ; #--#--#-
Data( %10010010 ) ; #--#--#-
Data( %10010010 ) ; #--#--#-
Data( %10010000 ) ; #--#----
Data( %01100010 ) ; -##---#-

Data( %00000100 ) ; -----#--
Data( %01100100 ) ; -##--#--
Data( %10010101 ) ; #--#-#-#
Data( %10010110 ) ; #--#-##-
Data( %10010100 ) ; #--#-#--
Data( %10010110 ) ; #--#-##-
Data( %10010101 ) ; #--#-#-#
Data( %01100101 ) ; -##--#-#

TestProgram:
  For b27 = 1 To 8
    Gosub ReadRow
    Gosub ShowRow
  Next
  SerTxd( CR, LF )
  End

ReadRow:
  Read bPtr, b3 : bPtr = bPtr + 8
  Read bPtr, b2 : bPtr = bPtr + 8
  Read bPtr, b1 : bPtr = bPtr + 8
  Read bPtr, b0 : bPtr = bPtr + 8
  bPtr = bPtr - 31
  Return

ShowRow:
  If bit31 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit30 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit29 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit28 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit27 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit26 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit25 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit24 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit23 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit22 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit21 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit20 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit19 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit18 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit17 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit16 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit15 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit14 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit13 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit12 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit11 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit10 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit9  = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit8  = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit7  = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit6  = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit5  = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit4  = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit3  = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit2  = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit1  = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit0  = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  SerTxd( CR, LF )
  Return
 

hippy

Technical Support
Staff member
Here's the version I would use. The Data is readable and logical. Errors in every column can be easily spotted and corrected, columns can easily be added or removed. The cost is that the 'ReadRow' routine is more complicated, but it's not that complicated, just the same thing repeated 32 times.
Code:
Data( %11111111 ) ; ########
Data( %00001000 ) ; ----#---
Data( %00001000 ) ; ----#---
Data( %11111111 ) ; ########
Data( %00000000 ) ; --------
Data( %01111100 ) ; -#####--
Data( %10010010 ) ; #--#--#-
Data( %10010010 ) ; #--#--#-
Data( %01011100 ) ; -#-###--
Data( %00000000 ) ; --------
Data( %01111111 ) ; -#######
Data( %10000000 ) ; #-------
Data( %00000000 ) ; --------
Data( %11111111 ) ; ########
Data( %10000000 ) ; #-------
Data( %00000000 ) ; --------
Data( %01111100 ) ; -#####--
Data( %10000010 ) ; #-----#-
Data( %10000010 ) ; #-----#-
Data( %01111100 ) ; -#####--
Data( %00000000 ) ; --------
Data( %00000000 ) ; --------
Data( %10011111 ) ; #--#####
Data( %00000000 ) ; --------
Data( %01111100 ) ; -#####--
Data( %10000010 ) ; #-----#-
Data( %10000010 ) ; #-----#-
Data( %01111100 ) ; -#####--
Data( %00000000 ) ; --------
Data( %11111111 ) ; ########
Data( %00101000 ) ; --#-#---
Data( %11000100 ) ; ##---#--

Symbol mask = b27
Symbol temp = b26

TestProgram:
  mask = %00000001
  Do
    Gosub ReadRow
    Gosub ShowRow
    mask = mask * 2
  Loop Until mask = 0
  SerTxd( CR, LF )
  End

ReadRow:
  bPtr = 0
  Read bPtr, temp : bPtr = bPtr + 1 : bit31 = temp & mask Max 1
  Read bPtr, temp : bPtr = bPtr + 1 : bit30 = temp & mask Max 1
  Read bPtr, temp : bPtr = bPtr + 1 : bit29 = temp & mask Max 1
  Read bPtr, temp : bPtr = bPtr + 1 : bit28 = temp & mask Max 1
  Read bPtr, temp : bPtr = bPtr + 1 : bit27 = temp & mask Max 1
  Read bPtr, temp : bPtr = bPtr + 1 : bit26 = temp & mask Max 1
  Read bPtr, temp : bPtr = bPtr + 1 : bit25 = temp & mask Max 1
  Read bPtr, temp : bPtr = bPtr + 1 : bit24 = temp & mask Max 1
  Read bPtr, temp : bPtr = bPtr + 1 : bit23 = temp & mask Max 1
  Read bPtr, temp : bPtr = bPtr + 1 : bit22 = temp & mask Max 1
  Read bPtr, temp : bPtr = bPtr + 1 : bit21 = temp & mask Max 1
  Read bPtr, temp : bPtr = bPtr + 1 : bit20 = temp & mask Max 1
  Read bPtr, temp : bPtr = bPtr + 1 : bit19 = temp & mask Max 1
  Read bPtr, temp : bPtr = bPtr + 1 : bit18 = temp & mask Max 1
  Read bPtr, temp : bPtr = bPtr + 1 : bit17 = temp & mask Max 1
  Read bPtr, temp : bPtr = bPtr + 1 : bit16 = temp & mask Max 1
  Read bPtr, temp : bPtr = bPtr + 1 : bit15 = temp & mask Max 1
  Read bPtr, temp : bPtr = bPtr + 1 : bit14 = temp & mask Max 1
  Read bPtr, temp : bPtr = bPtr + 1 : bit13 = temp & mask Max 1
  Read bPtr, temp : bPtr = bPtr + 1 : bit12 = temp & mask Max 1
  Read bPtr, temp : bPtr = bPtr + 1 : bit11 = temp & mask Max 1
  Read bPtr, temp : bPtr = bPtr + 1 : bit10 = temp & mask Max 1
  Read bPtr, temp : bPtr = bPtr + 1 : bit9  = temp & mask Max 1
  Read bPtr, temp : bPtr = bPtr + 1 : bit8  = temp & mask Max 1
  Read bPtr, temp : bPtr = bPtr + 1 : bit7  = temp & mask Max 1
  Read bPtr, temp : bPtr = bPtr + 1 : bit6  = temp & mask Max 1
  Read bPtr, temp : bPtr = bPtr + 1 : bit5  = temp & mask Max 1
  Read bPtr, temp : bPtr = bPtr + 1 : bit4  = temp & mask Max 1
  Read bPtr, temp : bPtr = bPtr + 1 : bit3  = temp & mask Max 1
  Read bPtr, temp : bPtr = bPtr + 1 : bit2  = temp & mask Max 1
  Read bPtr, temp : bPtr = bPtr + 1 : bit1  = temp & mask Max 1
  Read bPtr, temp : bPtr = bPtr + 1 : bit0  = temp & mask Max 1
  Return

ShowRow:
  If bit31 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit30 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit29 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit28 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit27 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit26 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit25 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit24 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit23 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit22 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit21 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit20 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit19 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit18 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit17 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit16 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit15 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit14 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit13 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit12 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit11 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit10 = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit9  = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit8  = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit7  = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit6  = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit5  = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit4  = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit3  = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit2  = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit1  = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  If bit0  = 0 Then : SerTxd("-") : Else : SerTxd("#") : End If
  SerTxd( CR, LF )
  Return
To me it doesn't make sense to create code which makes life easier for the MAX7219 when one needs to be making life easier for the programmer who has to specify what's shown, has to debug, fix, change and tweak that.

There are some deliberate errors in the Data used; the desired "Hello! Ok" shows as "Hello !ok" and one might want to tweak other things; make that "k" better, wider, dropping the lower curl on the l's to allow that, make the H wider.

With the last code it's pretty easy. A lot harder to do with the second, near impossible with the first.
 

hippy

Technical Support
Staff member
Here's the improved Data for my third example -
Code:
Data( %11111111 ) ; ########
Data( %00001000 ) ; ----#---
Data( %00001000 ) ; ----#---
Data( %00001000 ) ; ----#---
Data( %11111111 ) ; ########
Data( %00000000 ) ; --------
Data( %01111100 ) ; -#####--
Data( %10010010 ) ; #--#--#-
Data( %10010010 ) ; #--#--#-
Data( %01011100 ) ; -#-###--
Data( %00000000 ) ; --------
Data( %11111111 ) ; ########
Data( %00000000 ) ; --------
Data( %11111111 ) ; ########
Data( %00000000 ) ; --------
Data( %01111100 ) ; -#####--
Data( %10000010 ) ; #-----#-
Data( %10000010 ) ; #-----#-
Data( %01111100 ) ; -#####--
Data( %00000000 ) ; --------
Data( %10011111 ) ; #--#####
Data( %00000000 ) ; --------
Data( %00000000 ) ; --------
Data( %01111110 ) ; -######-
Data( %10000001 ) ; #------#
Data( %10000001 ) ; #------#
Data( %01111110 ) ; -######-
Data( %00000000 ) ; --------
Data( %11111111 ) ; ########
Data( %00110000 ) ; --##----
Data( %01001000 ) ; -#--#---
Data( %10000000 ) ; #-------
 

hippy

Technical Support
Staff member
And here's the changes to my third example needed to make it scroll to the left -
Code:
Symbol scroll = b25

TestProgram:
  Do
    mask = %00000001
    Do
      Gosub ReadRow
      Gosub ShowRow
      mask = mask * 2
    Loop Until mask = 0
    SerTxd( CR, LF )
    scroll = scroll + 1
  Loop

ReadRow:
  bPtr = scroll // 35
More columns can be added as Data to make the message longer.

That "35" should be the number of columns in the message plus however blank pixels there should be between the end of the message and the start of the same re-entering from the right.

If that number needs to be more than 255 the code would require tweaking to support that.

So, apart from adding a PAUSE to set the scroll speed, the only thing left to turn that into a usable program with actual MAX7219 hardware is to figure out how the 'ShowRow' should be. That requires knowing how the physical display is wired up on the PCB, which of the main sixteen possibilities it is -

8x8 blocks in '1234' or '4321' order
Data written in row or column order
Row order 0 to 7 or 7 to 0
Column order 0 to 7 or 7 to 0

Technically there are some 40 billion possibilities, but I'm guessing it's one of the obvious combinations rather than any less obvious possibilities. But one never knows; design the PCB for easy tracking and leave the software to sort that out is a standard approach - what we are in fact doing !

It should be possible to create a single routine which works no matter what the configuration is.
 
Wow!

Many thanks Hippy, that works perfectly with the test texts I have already defined. I really like the ingenious use of the mask byte.

There is a slight complication in that the current ShiftTo7219 routine (adapted from WestAust55's original MAX7219 code) for writing to the display uses b0 for defining the MAX7219 register to write to and b1 for the data, i.e.
Code:
ShiftTo7219:
    PULSOUT Clk_7219, 1 ; bit 15 is don't care
    PULSOUT Clk_7219, 1 ; bit 14 is don't care
    PULSOUT Clk_7219, 1 ; bit 13 is don't care
    PULSOUT Clk_7219, 1 ; bit 12 is don't care
    DIn_7219 = bit11 : PULSOUT Clk_7219, 1
    DIn_7219 = bit10 : PULSOUT Clk_7219, 1
    DIn_7219 = bit9 : PULSOUT Clk_7219, 1
    DIn_7219 = bit8 : PULSOUT Clk_7219, 1
    DIn_7219 = bit7 : PULSOUT Clk_7219, 1
    DIn_7219 = bit6 : PULSOUT Clk_7219, 1
    DIn_7219 = bit5 : PULSOUT Clk_7219, 1
    DIn_7219 = bit4 : PULSOUT Clk_7219, 1
    DIn_7219 = bit3 : PULSOUT Clk_7219, 1
    DIn_7219 = bit2 : PULSOUT Clk_7219, 1
    DIn_7219 = bit1 : PULSOUT Clk_7219, 1
    DIn_7219 = bit0 : PULSOUT Clk_7219, 1
;
; PULSOUT C.1, 1 ; Latches the data into the MAX7219s.
;
; The above is moved to the main routine so that all 4 devices are
; latched simultaneously with the 4 bytes of serial data.

RETURN
But I can work around that I think because when writing to the display the register bits (bit8 - bit11) in the existing routine are fixed for each display row (e.g. %XXXX0001 defines the top row) and obviously the data bits will need to point to the relevant byte. So bits 24 - 31 get transmitted first to the leftmost matrix and eventually bits 0 - 7 to the rightmost 8 x 8 matrix.

I'll give that a go and see how it works. Thanks again.
 

BESQUEUT

Senior Member
Here's the improved Data for my third example -
Code:
Data( %11111111 ) ; ########
Data( %00001000 ) ; ----#---
Data( %00001000 ) ; ----#---
Data( %00001000 ) ; ----#---
Data( %11111111 ) ; ########
Data( %00000000 ) ; --------
Data( %01111100 ) ; -#####--
Data( %10010010 ) ; #--#--#-
Data( %10010010 ) ; #--#--#-
Data( %01011100 ) ; -#-###--
Data( %00000000 ) ; --------
Data( %11111111 ) ; ########
Data( %00000000 ) ; --------
Data( %11111111 ) ; ########
Data( %00000000 ) ; --------
Data( %01111100 ) ; -#####--
Data( %10000010 ) ; #-----#-
Data( %10000010 ) ; #-----#-
Data( %01111100 ) ; -#####--
Data( %00000000 ) ; --------
Data( %10011111 ) ; #--#####
Data( %00000000 ) ; --------
Data( %00000000 ) ; --------
Data( %01111110 ) ; -######-
Data( %10000001 ) ; #------#
Data( %10000001 ) ; #------#
Data( %01111110 ) ; -######-
Data( %00000000 ) ; --------
Data( %11111111 ) ; ########
Data( %00110000 ) ; --##----
Data( %01001000 ) ; -#--#---
Data( %10000000 ) ; #-------
Another way to do the same thing :
Code:
#include "symbin.bas"

Data( OOOOOOOO )
Data( ____O___ ) 
Data( ____O___ )
Data( OOOOOOOO ) 
Data( ________ ) 
Data( _OOOOO__ ) 
Data( O__O__O_ ) 
Data( O__O__O_ )
Data( _O__OO__ )
Data( ________ ) 
Data( OOOOOOOO )
Data( ________ ) 
Data( OOOOOOOO ) 
Data( ________ ) 
Data( _OOOOO__ ) 
Data( O_____O_ ) 
Data( O_____O_ ) 
Data( _OOOOO__ ) 
Data( ________ ) 
Data( ________ ) 
Data( O__OOOOO ) 
Data( ________ ) 
Data( _OOOOO__ )
Data( O_____O_ ) 
Data( O_____O_ ) 
Data( _OOOOO__ ) 
Data( ________ ) 
Data( OOOOOOOO ) 
Data( __O_O___ ) 
Data( _O___O__ )
Data( O_______ )
 

Attachments

hippy

Technical Support
Staff member
There is a slight complication in that the current ShiftTo7219 routine (adapted from WestAust55's original MAX7219 code) for writing to the display uses b0 for defining the MAX7219 register to write to and b1 for the data,
The usual trick there would be for the ShowRow routine to move b0,b1,b2,b3 to some other variables, then move them back in one at a time. Like ...
Code:
Send Row:
  b10 = b0
  b11 = b1
  b12 = b2
  b13 = b3

  ; Send first byte
  b0 = b10
  ; .... Lots of PULSOUT's etc
  
  ; Send second byte
  b0 = b11
  ; .... Lots of PULSOUT's etc

  ; etc
Another way to do the same thing :
That's a neat trick.
 

BESQUEUT

Senior Member
The usual trick there would be for the ShowRow routine to move b0,b1,b2,b3 to some other variables, then move them back in one at a time. Like ...
If PICAXE X2 :
Code:
Send Row:
push b3,b2,b1,b0

  ; Send first byte
   pop b0
  ; .... Lots of PULSOUT's etc

  ; Send second byte
  pop b0
  ; .... Lots of PULSOUT's etc

  ; etc
And with X2, you can use HSPIout...
That's a neat trick.
Thanks...
Can also be used the other direction to simplify code :
Code:
Data ( O__O____,__O_O___,____O___,___O____ )
Data ( O__O__OO,__O_O__O,O___O__O,O__O____ )
Data ( O__O_O__,O_O_O_O_,_O__O_O_,_O_O_O__ )
Data ( OOOO_O__,O_O_O_O_,_O__O_O_,_O_OO___ )
Data ( O__O_OOO,__O_O_O_,_O__O_O_,_O_O____ )
Data ( O__O_O__,__O_O_O_,_O__O_O_,_O_OO___ )
Data ( O__O_O__,O_O_O_O_,_O____O_,_O_O_O__ )
Data ( O__O__OO,__O_O__O,O___O__O,O__O__O_ )
Less readable, depending the used font, but we can also do that :
Rich (BB code):
Data ( HH____HH________,____HH__HH______,________HH______,______HH________ )
Data ( HH____HH____HHHH,____HH__HH____HH,HH______HH____HH,HH____HH________ )
Data ( HH____HH__HH____,HH__HH__HH__HH__,__HH____HH__HH__,__HH__HH__HH____ )
Data ( HHHHHHHH__HH____,HH__HH__HH__HH__,__HH____HH__HH__,__HH__HHHH______ )
Data ( HH____HH__HHHHHH,____HH__HH__HH__,__HH____HH__HH__,__HH__HH________ )
Data ( HH____HH__HH____,____HH__HH__HH__,__HH____HH__HH__,__HH__HHHH______ )
Data ( HH____HH__HH____,HH__HH__HH__HH__,__HH________HH__,__HH__HH__HH____ )
Data ( HH____HH____HHHH,____HH__HH____HH,HH______HH____HH,HH____HH____HH__ )
[/QUOTE]
 
Last edited:
After a dodgy start where I ended up with far too much code, I've got something working. Not quite right yet as the displays can sometimes get out of sync and not display anything, but it's 95% there. When I had issues with too much code I wondered if the Read bptr lines could be simplified, e.g.
Code:
Read bPtr, Temp : bPtr = bPtr + 1 : bit31 = Temp & Mask MAX 1
    
Read @bPtrInc, Temp : bit31 = Temp & Mask MAX 1
But that didn't work.

Anyway, it didn't matter because I just simplified things a bit and the code now takes less than half the available space on a 08M2, including the text definitions.

Here's a short bit of video with the same text as in my original setup:

I was pleased that I got all the right bits in the right order straight away. As you said Hippy, there are lots of possibilities for getting it wrong.

I'll put the working code in the 'User Projects - Audio/Visual' forum when I'm sure that it's more bomb proof, together with a description of how the hardware goes together - there are lots of ways that can be got wrong as well.

Thanks everyone for your expert help.
 

BESQUEUT

Senior Member
First step, successfully tested, quite fluid (actual aspect is better than video...) :
VIDEO
Code:
'-----------------  PICAXE   20X2 ------ connected to four 8x8 displays
' This program uses a PICAXE-20X2 to shift-out SPI data to
' four MAX7219 LED display drivers to display
' This code is for 4 matrix 8X8 displays

#Picaxe 20X2
#no_table
#include "symbin.bas"

#macro RAM(T1,T2,T3,T4)
    @bptrinc =T1
    @bptrinc =T2
    @bptrinc =T3
    @bptrinc =T4
#endmacro

setfreq m64
' Hardware interface to the MAX7219
let dirsB = %11111111
'                  40X2
'symbol sData     =  C.6 'B.5                     ' data out line to Max7219
'symbol clock     =  D.7 'B.7                     ' clock line
'symbol sLoad     =  C.7 'B.6                     ' pulse briefly to load data onto LED Matrix

'                  20X2
symbol sData     = B.4  'B.5                     ' data out line to Max7219
symbol clock     = B.2  'B.7                     ' clock line
symbol sLoad     = B.3  'B.6                     ' pulse briefly to load data onto LED Matrix


' Register addresses for the MAX7219
symbol decode = $09'9                ' decode register; specify digits to decode
symbol brite  = $0A                 ' intensity (brightness) register; 15 = 100%
symbol scan   = $0B'11                 ' scan-limit register; specify # of LEDs
symbol on_off = $0C '12                ' 1 = display on; 0 = display off
symbol TEST = $0F '15

'-----Variables ------
symbol outByte = b0                 ' data byte to be transmitted to the LED
symbol maxReg  = b1                 ' MAX register that receives the data byte
symbol forMax  = w0                 '


  LOW SData
  LOW Clock
  LOW sload
    
   pause 500
   gosub progrmax
   pause 500
   gosub cleard
   
symbol I=b10
symbol Line=b11
symbol Start=50

do
bptr=Start

RAM( O__O____,__O_O___,____O___,___O____ )
RAM( O__O__OO,__O_O__O,O___O__O,O__O____ )
RAM( O__O_O__,O_O_O_O_,_O__O_O_,_O_O_O__ )
RAM( OOOO_O__,O_O_O_O_,_O__O_O_,_O_OO___ )
RAM( O__O_OOO,__O_O_O_,_O__O_O_,_O_O____ )
RAM( O__O_O__,__O_O_O_,_O__O_O_,_O_OO___ )
RAM( O__O_O__,O_O_O_O_,_O____O_,_O_O_O__ )
RAM( O__O__OO,__O_O__O,O___O__O,O__O__O_ )


for I=1 to 32
    for Line =1 to 8
        bptr=Line-1*4+Start
        b3=@bPtrInc
        b2=@bPtrInc
        b1=@bPtrInc
        b0=@bPtrInc
        gosub printbits
       
        b4=bit15    'Rotate bits...
        w0=w0*2+bit31
        w1=w1*2+b4
       
        bptr=Line-1*4+Start
        @bPtrInc=b3    ' store rotated bits
        @bPtrInc=b2
        @bPtrInc=b1
        @bPtrInc=b0

    next Line
    pause 50
  next I
pause 10000
loop
end




CLEARD: ' --------------------- CLEAR ALL 4 MATRIX DISPLAYS ------------------
  b0 = %00000000 
  FOR b1 = 1 to 8
     shiftout clock,sData,MSBFirst_L,(b1,b0,b1,b0,b1,b0,b1,b0)
     pulsout sLoad,1
   NEXT b1
RETURN            

PROGRMAX: '------------------------- SET UP  DISPLAY-------------------------
low sload
FOR b15 = 1 to 4
   maxReg = TEST                
   outByte = 0    
   gosub shoutNP
   pause 100
  
   maxReg = scan          ' how many digits on the display  0 to 7  (we need eight for each 8X8 m)            
   outByte =7    
   gosub shoutNP
  
   maxReg = brite        ' set brightness to 0 to 15  (15 = 100%)  1 is still bright enough for my displays
   outByte =10
   gosub shoutNP
  
   maxReg = decode      ' set BCD decoding for digits 0 = no decode mode  MUST BE 0 for a matrix
   outByte = 0 
   gosub shoutNP
  
   maxReg = on_off      ' turn display off or on    1 = on
   outByte = 1
   gosub shoutNP  
NEXT b15
RETURN
SHOUTNP:
    shiftout clock,sData,MSBFirst_L,(b1,b0,b1,b0,b1,b0,b1,b0)
    pulsout sLoad,1  
return



PrintBits:
    shiftout clock,sData,MSBFirst_L,(Line,b3,line,b2,line,b1,line,b0)
        pulsout sLoad,1
return
Next steps :
1) a 32x8 pixel window scrolling a 100x8 bitmap
2) Code to write any text to bitmap...
 
Last edited:

BESQUEUT

Senior Member
After a dodgy start where I ended up with far too much code, I've got something working. Not quite right yet as the displays can sometimes get out of sync and not display anything, but it's 95% there. When I had issues with too much code I wondered if the Read bptr lines could be simplified, e.g.
Code:
Read bPtr, Temp : bPtr = bPtr + 1 : bit31 = Temp & Mask MAX 1
   
Read @bPtrInc, Temp : bit31 = Temp & Mask MAX 1
But that didn't work.

Anyway, it didn't matter because I just simplified things a bit and the code now takes less than half the available space on a 08M2, including the text definitions.

Here's a short bit of video with the same text as in my original setup:

I was pleased that I got all the right bits in the right order straight away. As you said Hippy, there are lots of possibilities for getting it wrong.

I'll put the working code in the 'User Projects - Audio/Visual' forum when I'm sure that it's more bomb proof, together with a description of how the hardware goes together - there are lots of ways that can be got wrong as well.

Thanks everyone for your expert help.
Beautiful.
Please can you publish whole code, even if not final...
 
What I thought might have been a software issue which was randomly blanking some of the 8x8 displays turned out to be a simple hardware issue: I hadn't set the current limit on the PSU to a high enough level for the initial all-pixels-on-at-full-brightness test, so the supply voltage dipped momentarily and some of the MAX7219s got confused as a result. With the PSU delivering +5V consistently, it all works fine now so I don't feel too embarrassed at publishing the code. It's obviously not as fast as the impressive 20X2 code above but it does the job (I require) with exceedingly simple and cheap hardware.

Code attached as files as the 10,000 character limit is exceeded if I enter the code directly. I will add a circuit diagram in due course.
 

Attachments

The (corrected) circuit diagram in its most basic form. I actually used an AXE092 prototype board for speed of assembly.

23116

As shown in my initial notes, the FC-16 boards with the MAX7219 & display can be fitted with straight 5-pin headers:
23113

and then simply linked together with five jumpers:
23114

In sets of four, these can be bought for less than 89p per board and with the 08M2 and a programming connector and two resistors on a little bit of PCB the total cost is around £5.
 

Attachments

Last edited:

BESQUEUT

Senior Member
What I thought might have been a software issue which was randomly blanking some of the 8x8 displays turned out to be a simple hardware issue: I hadn't set the current limit on the PSU to a high enough level for the initial all-pixels-on-at-full-brightness test, so the supply voltage dipped momentarily and some of the MAX7219s got confused as a result. With the PSU delivering +5V consistently, it all works fine now so I don't feel too embarrassed at publishing the code. It's obviously not as fast as the impressive 20X2 code above but it does the job (I require) with exceedingly simple and cheap hardware.

Code attached as files as the 10,000 character limit is exceeded if I enter the code directly. I will add a circuit diagram in due course.
Very interesting. Thanks.
YES : same evidence : 256 LEDs needs some juice...

The circuit diagram, slightly more complex than necessary because for speed of assembly I've used an AXE092 prototype board.
View attachment 23112

As shown in my initial notes, the FC-16 boards with the MAX7219 & display can be fitted with straight 5-pin headers:
View attachment 23113

and then simply linked together with five jumpers:
View attachment 23114

In sets of four, these can be bought for less than 89p per board and with the 08M2 and a programming connector and two resistors on a little bit of PCB the total cost is around £5.
I am using less than 4€ ready bords with 4 MAX7219 and connectors.
X2 is very convenient cause the HSPI commands and m64 ...

Not tested, but for :
Read @bPtrInc, Temp : bit31 = Temp & Mask MAX 1

I think that the correct syntax is :
Temp=@bPtrInc : bit31 = Temp & Mask MAX 1

So may be :
bit31 = @bPtrInc & Mask MAX 1
 
Last edited:
Thanks, I'll try those alternatives. Even if they're no faster, they look neater in the coding.

I've had a mix of HC-16 boards:

- the first one came with all the parts but nothing soldered on (and no instructions). I succeeded in soldering the MAX7219 and the cap & resistor (all surface mount) and the various connectors, sockets for the display etc, but without a guide I discovered later (when it didn't work) that I'd put the display sockets on the wrong side of the board so the connections were all wrong.

- the next boards I got had the surface mount components soldered in place but not the sockets or pin headers, and they came with 90° headers. Having now researched the orientation of the board I discovered which side of the board the display fitted and which way round it should be. I also fitted straight pin headers so that the boards could be linked with jumpers instead of cables.

- I also have on order some sets of four boards with displays. Until they arrive I din't know if they are fully assembled or need soldering, but the price is amazing. These sound like what you're using Besqueut.
 

hippy

Technical Support
Staff member
I wondered if the Read bptr lines could be simplified, e.g.
Code:
Read bPtr, Temp : bPtr = bPtr + 1 : bit31 = Temp & Mask MAX 1
    
Read @bPtrInc, Temp : bit31 = Temp & Mask MAX 1
But that didn't work.
No it won't. In the first 'bPtr' is being used as a variable, nothing more. That's a hangover from the original which accessed RAM.

When reading data from Data Eeprom it has to be -
Rich (BB code):
Read bPtr, Temp : bPtr = bPtr + 1 : bit31 = Temp & Mask MAX 1
When reading from RAM the following are all equivalent, each a successive optimisation on the first -
Rich (BB code):
Get bPtr, Temp : bPtr = bPtr + 1 : bit31 = Temp & Mask MAX 1
Temp = @bPtr : bPtr = bPtr + 1 : bit31 = Temp & Mask MAX 1
Temp = @bPtrInc : bit31 = Temp & Mask MAX 1
bit31 = @bPtrInc & Mask MAX 1
But there is no optimisation path from 'Read bPtr' to anything else. "Read @bPtr' and 'Read @bPtrInc' do something entirely different to what is required.

My recommendation, if using data held in Data Eeprom is to do a global substitution and rename 'bPtr' to 'index'.
 

BESQUEUT

Senior Member
No it won't. In the first 'bPtr' is being used as a variable, nothing more. That's a hangover from the original which accessed RAM.

When reading data from Data Eeprom it has to be -
Rich (BB code):
Read bPtr, Temp : bPtr = bPtr + 1 : bit31 = Temp & Mask MAX 1
When reading from RAM the following are all equivalent, each a successive optimisation on the first -
Rich (BB code):
Get bPtr, Temp : bPtr = bPtr + 1 : bit31 = Temp & Mask MAX 1
Temp = @bPtr : bPtr = bPtr + 1 : bit31 = Temp & Mask MAX 1
Temp = @bPtrInc : bit31 = Temp & Mask MAX 1
bit31 = @bPtrInc & Mask MAX 1
But there is no optimisation path from 'Read bPtr' to anything else. "Read @bPtr' and 'Read @bPtrInc' do something entirely different to what is required.

My recommendation, if using data held in Data Eeprom is to do a global substitution and rename 'bPtr' to 'index'.
Oups... I should have see that !
As my code is using RAM, I was misleaded...
Maybe we can use inc bPtr in place of bPtr=bPtr+1 but it is very little improvement.

As habitual, some macro to improve readability :
Rich (BB code):
#macro RegData(Reg,D)   '985 bytes
      FOR Character = 1 TO 4
            Register = Reg  : Data_7219 = D : GOSUB ShiftTo7219
      NEXT Character
      PULSOUT C.1, 1 ;
#EndMacro   
'...
; Subroutine to initialise the MAX7219 7-Seg LED display driver
Initialise7219:
      RegData(Decode,Dec_Digits)
      RegData(Intens,Init_Inten)
      RegData(ScanLim,No_Digits)
      RegData(ShutDwn,Set_On)
      RegData(DispTest,Set_On)
      PAUSE 10000
      RegData(DispTest,Set_Off)
RETURN
Proposal (non tested) to send RowCount :
WARNING : Rowcount is going from 1 to 8 ...
Rich (BB code):
FourPulse:
      DIn_7219 = 0                  ' 864 bytes
      PULSOUT Clk_7219, 1
      PULSOUT Clk_7219, 1
      PULSOUT Clk_7219, 1
      PULSOUT Clk_7219, 1
      DIn_7219 = RowCount & %1000 MAX 1 : PULSOUT Clk_7219, 1
      DIn_7219 = RowCount & %0100 MAX 1 : PULSOUT Clk_7219, 1
      DIn_7219 = RowCount & %0010 MAX 1 : PULSOUT Clk_7219, 1
      DIn_7219 = RowCount & %0001 MAX 1 : PULSOUT Clk_7219, 1
RETURN
 
Last edited:

BESQUEUT

Senior Member
I think we have no need to use bit variable.
So we can group reading and sending data (non tested) :
Rich (BB code):
Main:
;
Do
      RowCount = 1                  '463 bytes
      Mask = %00000001
      Do
            bPtr = scroll // 122          ; The value here defines the gap 
                                          ; before the message starts again.
            GOSUB WriteRow
            Mask = Mask * 2
            inc RowCount
      Loop Until Mask = 0
      PAUSE 400                     ; This defines the scrolling speed.
      Scroll = Scroll + 1
Loop
;
END
Rich (BB code):
; Subroutine to read data and shift the register address and data 
; values out to one of the four cascaded MAX7219s.
WriteRow:                     ' 826 bytes
      GOSUB WriteBlock
      GOSUB WriteBlock
      GOSUB WriteBlock
      GOSUB WriteBlock
      
      PULSOUT C.1, 1 ; Latches the data into all four MAX7219s.
      RETURN
;
WriteBlock:
      DIn_7219 = 0                  ' 864 bytes
      PULSOUT Clk_7219, 1
      PULSOUT Clk_7219, 1
      PULSOUT Clk_7219, 1
      PULSOUT Clk_7219, 1
      DIn_7219 = RowCount & %1000 MAX 1 : PULSOUT Clk_7219, 1
      DIn_7219 = RowCount & %0100 MAX 1 : PULSOUT Clk_7219, 1
      DIn_7219 = RowCount & %0010 MAX 1 : PULSOUT Clk_7219, 1
      DIn_7219 = RowCount & %0001 MAX 1 : PULSOUT Clk_7219, 1
      
      Read bPtr, Temp : inc bPtr : DIn_7219 = Temp & Mask MAX 1: PULSOUT Clk_7219, 1
      Read bPtr, Temp : inc bPtr : DIn_7219 = Temp & Mask MAX 1: PULSOUT Clk_7219, 1
      Read bPtr, Temp : inc bPtr : DIn_7219 = Temp & Mask MAX 1: PULSOUT Clk_7219, 1
      Read bPtr, Temp : inc bPtr : DIn_7219 = Temp & Mask MAX 1: PULSOUT Clk_7219, 1
      Read bPtr, Temp : inc bPtr : DIn_7219 = Temp & Mask MAX 1: PULSOUT Clk_7219, 1
      Read bPtr, Temp : inc bPtr : DIn_7219 = Temp & Mask MAX 1: PULSOUT Clk_7219, 1
      Read bPtr, Temp : inc bPtr : DIn_7219 = Temp & Mask MAX 1: PULSOUT Clk_7219, 1
      Read bPtr, Temp : inc bPtr : DIn_7219 = Temp & Mask MAX 1: PULSOUT Clk_7219, 1
RETURN
 
Last edited:

BESQUEUT

Senior Member
No Picaxe here to test, and I do not know if this is really better :
Rich (BB code):
WriteBlock:
      DIn_7219 = 0                  ' 385 bytes
      PULSOUT Clk_7219, 1
      PULSOUT Clk_7219, 1
      PULSOUT Clk_7219, 1
      PULSOUT Clk_7219, 1
      DIn_7219 = RowCount & %1000 MAX 1 : PULSOUT Clk_7219, 1
      DIn_7219 = RowCount & %0100 MAX 1 : PULSOUT Clk_7219, 1
      DIn_7219 = RowCount & %0010 MAX 1 : PULSOUT Clk_7219, 1
      DIn_7219 = RowCount & %0001 MAX 1 : PULSOUT Clk_7219, 1
      
      NextbPtr=bPtr+7
      for bPtr=bPtr to NextBptr
            Read bPtr, Temp : DIn_7219 = Temp & Mask MAX 1: PULSOUT Clk_7219, 1
      next bPtr
RETURN
 
Last edited:
Top