SSD1327

Anubis1985

Senior Member
Hello,
I created a program, tested on a PICAXE 20x2, to draw geometric shapes on an SSD1327 display.
However, I have a small problem that I can't seem to solve.
I can't read the contents of a segment.
Each segment read is always equal to 2, regardless of the value entered.
Here's how it works:
A segment controls two pixels in the horizontal direction (corresponding variable = x).Each value controls two pixels: the first pixel has its maximum brightness, and the second is off.If you want to do the opposite, you would need to set the value to 240 instead of 15.If you want to light both pixels at their maximum brightness, you would need to set it to 255.

240 = 11110000 'Left pixel lit at its maximum brightness.
15 = 00001111 'Right pixel lit at its maximum brightness.

Code:
hi2cout (SETSTARTLINE,15) 'Example to display one pixel

In my program, the content of a segment is read on line 182 and the segment is drawn on line 201 in DrawsPixel.

The image shows what the program does.
You can see that the lines are dotted horizontally because I can't retrieve the correct value for a segment.

Thank you in advance
 

Attachments

Last edited:
Hi,

Presumably you have taken into account the section on "Dummy Reads" in the Data Sheet?

Personally, I don't have any experience of the SSD1327, but some of the other "SSD..." chips (i.e. manufactured by Solomon Systech), such as the SSD1306 do NOT support READING of data in "Serial" Mode (which includes SPI and I2C), only in the "MCU Parallel" (6800 / 8080) modes. Interestingly some "SH..." chips such as the SH1106 do permit I2C reading of the display buffer.

I have n't found a ny specific statement in the SSD1327 Data Sheet that it can NOT Read data from the display buffer*, but the SPI interface does not appear to have a SPO (Data Out) pin and the I2C description (e.g. Section 8.1.5 ) does not include any Timing Diagrams for Reading. The I2C mode does have a SDA(Out) pin (D2, which must be linked to D1) but that may carry only the "Acknowledge" pulse and not any valid (pixel) data.

Therefore, I suspect that you need to keep a "local" copy of the display data within the PICaxe, but even the larger X2s don't appear to have enough "Scratchpad" to store only one bit per pixel for a whole 128 x 128 screen (i.e. 2048 bytes). Also, separate I2C RAM chips don't seem economically / conveniently available, but perhaps a simple I2C 24LC.... EEPROM could be suitable and not "wear out" too quickly?

UPDATE / EDIT: See the first paragraph in the Data Sheet (page 41) Section 9.1 Read/Write :"No data read is provided in serial mode operation."

Cheers, Alan.
 
Last edited:
I too agree with AllyCat, it is possible that reading is not supported when using I2C. ( See datasheet section 9.1, where it says "No data read is provided in serial mode operation. )

The easiest way to check is to write a small program that uses the simplest setup code. Use this to write and read from a few fixed GDDRAM locations.
 
Hi,

I've Updated my post #2 above several times and my (current) definitive conclusion is that the SSD1327 driver chip does NOT support "Serial Protocol" Reading (I2C or SPI), but there do appear to be very similar displays available using the SH1107 driver (which AFAIK does support Reading), for example from The PiHut or Ebay.

UPDATE: Unfortunately it appears that there may be many "Fake" 128x128 displays which claim to use the SH1107 but actually carry SSD1307 or similar chips. But I can't be sure whether THIS LINK is genuine or perhaps the result of "AI". However, the apparently genuine SH1107 Data Sheet does state on page 13:

I2C-bus Interface
The SH1107 can transfer data via a standard I2C-bus and has slave mode only in communication. The command or RAM data
can be written into the chip and the status and RAM data can be read out of the chip.


Cheers, Alan.
 
Last edited:
I also have a program I created for an SH1107 that has two font sizes (Numero_8) and (Numero_16).It also supports the graphics functions (DrawsPixel), (DrawsLine), (DrawsRectangle), and (DrawsCircle).You can enable or disable the fonts on demand to optimize memory usage. It's very comprehensive.


There are other examples that are disabled if you'd like to try them out.

Reading from RAM worked very well on this type of display.
This is the first display I programmed before moving on to the SSD1327.
 

Attachments

Last edited:
I carefully considered the section on "Dummy Readings" in the technical specifications. I created a minimal code for testing, but it didn't work. The values retrieved were always equal to 2, regardless of the stored values.

The initial idea was to create a library for picaxe X2 in order to make an ssd1327 usable, as I did for the SH1107.
I created several versions for the SH1107 on the official PICAXE French forum.

After several attempts to read the RAM of the SSD1327, I realized that it might not be possible.Indeed, I already had the idea of using several 24LC512 chips to store a copy of the pattern to be displayed to solve the problem, but given the amount of memory required, that doesn't seem feasible.

I just wanted to get other opinions. But apparently, this confirms what I thought.
 
Last edited:
I have a wild, off-the-wall, hare-brained idea !.

Parallel a 24LCxxx with the SSD1327. You will need to make the 24LCxxx address the same as the SSD1327, and you might need some gating and latching to handle 'reads'.

If it works, then 'writes' will go to both devices, but 'reads' will only get replies from the 24LCxxx.

This is a completely untested idea, but it might work.
 
Hi,
You will need to make the 24LCxxx address the same as the SSD1327,
I don't think that the I2C Slave Addresses of 24LC... or SSD13.... chips are fully configurable (because the high nibble is generally reserved for the "device type"). But writing the same data to two separate addresses IS practical, by changing the address in the hi2cout commands (SETSTARTLINE) from a Program Constant to a Variable. Surprisingly, the external HI2COUT commands can execute much faster than internal PEEKs and POKEs, so the code might still execute fast enough (see my thread which effectively stores the character shapes (pixels) in Program memory) .

However, IMHO particularly for "Library" routines (and PICaxe in general) the best solution is to write code which is compatible between SSD.... and SH.... display chips, with only the essential, minor configuration differences. Currently there is a dearth of code for the SSH... chips on the forum, because most programs have been configured for the SSD1306, using their (non-default) $2x "Scroll Window" commands, even though they offer almost no benefits for the PICaxe applications.

One of the problems in writing "Library" routines for PICaxe is that the different "families" (e.g. M2 versus X2) have different sizes and types of available memory. Particularly for the larger M2s, the TABLE MEMORY is usually the most suitable location for the "Font" bytes (because there are 512 bytes in each of the two available Program Banks/Slots). However, the 08M2 has none, and also the X2 chips have only 256 bytes which are "borrowed" from their Program memory allocation. Note that a "byte" which is stored in the Program area does not normally consume only a single byte of program space, and can actually depend on the value of the byte !

Cheers, Alan.
 
I don't think that the I2C Slave Addresses of 24LC... or SSD13.... chips are fully configurable (because the high nibble is generally reserved for the "device type").
I know the addresses can't be directly configured. I envisaged a circuit with some 'shift register' type of circuits which could translate one serial stream into another. However, this could end up using two or three logic chips, which seems a bit excessive.

Your note about the speed of hi2c instructions gave me another idea. What about setting a 28X2 in 'Slave Mode', duplicating the SSD chip address ?

Either way it seems like a 'labour of love' to get it working. It might better to use the OLED board in parallel mode, and use a few more i/o pins ?

Or better still, re-write the code to selectively update two pixels per write. This could easily solve the dotted horizontals, but might give artifacts when lines cross each other. The code @Anubis1985 has written so far shows a good understanding of the kind of logic needed, I'm sure he could do it.

Cheers,

Buzby
 
Hello,
I have a version of my program where the character set is stored in a 24LC512 to free up as much memory as possible on the PICAXE.
First, you need to install (Carracteres.bas) on the PICAXE so that it can write them to the EEPROM.

Circuit configuration:A red LED is connected to pin C.5 of the PICAXE 20x2.A green LED is connected to pin C.4.A WP (EEPROM write protection) and a 4.7kΩ resistor pulled upwards (+VCC) are connected to pin C.7.
When the character copying begins, the red LED is lit.When it's finished, the red LED turns off and the green LED turns on.

We start by copying the last characters, ending with the first. If we did the opposite, some characters would be copied incorrectly into the 24LC512, and small rectangles would be displayed instead.

Caution.
The characters n, o, p are copied incorrectly to the EEPROM. I haven't been able to figure out why.

You'll notice that in the program (Lib_SH1107_PICAXE_20X2_24LC512), pin c.7 is not mentioned.
Indeed, the goal is to avoid damaging the character set contained in the EEPROM through a programming error.
That's why wp is pulled upwards.

The image shows what the code does.
 

Attachments

Last edited:
Hi,
The characters n, o, p are copied incorrectly to the EEPROM. I haven't been able to figure out why.
The 24LC512 has "Page Boundaries" every 128 bytes, where the writing address wraps back to the start of the page (the letters "nop" are after the boundary at decimal address 384 = $180). This may be why you need to write the data groups in reverse sequence, but I would expect similar problems after writing to addresses 128 and 256, etc.. Note that the writei2c instruction is "deprecated" with hi2cout now preferred. Some smaller 24LC.... EEPROMs have smaller pages, so I normally store 5-byte-wide character data in groups of 3, with each 16th byte as zero/nul, which avoids any page boundaries being crossed.

However, for 5-byte-wide characters an additional EEPROM is hardly necessary - the full 96 ASCII characters can be stored in around 1k Program Bytes (if you DON'T use the SELECT ... CASE format ! ).

For writing "Library" routines, PICaxe has some much more efficient structures to help reduce the program size and often be much faster. Here are a few examples which I found in the listing from post #5 (untested but you should get the idea).

Code:
SetPosition:
if X > 127 then : X = 127 endif    ;  = 9 bytes
;   X = X max 127                          ; = 5 bytes

#IFDEF ORIGINAL     ; = 168 bytes
    select case X
    case 0   to 15  : ColumLowBits = X       : ColumHighBits = 0
    case 16  to 31  : ColumLowBits = X - 16  : ColumHighBits = 1
    case 32  to 47  : ColumLowBits = X - 32  : ColumHighBits = 2
    case 48  to 63  : ColumLowBits = X - 48  : ColumHighBits = 3
    case 64  to 79  : ColumLowBits = X - 64  : ColumHighBits = 4
    case 80  to 95  : ColumLowBits = X - 80  : ColumHighBits = 5
    case 96  to 111 : ColumLowBits = X - 96  : ColumHighBits = 6
    case 112 to 127 : ColumLowBits = X - 112 : ColumHighBits = 7
    'case 128 to 131 : ColumLowBits = X - 128 : ColumHighBits = 8 'SH1106
endselect
#ELSE       ; Compact
      ColumLowBits = X // 16 :  ColumHighBits = X / 16    ; = 12 bytes
#ENDIF

DrawsPixel:
 if Y > 127 then : Y = 127 endif     ; = 6 bytes
;  Y = Y max 127                             ; = 3 bytes

#IFDEF ORIGINAL                            ; = 353 bytes
     select case Y
     case   0 to   7 : Page = 176 : R_graphique = Y
     case   8 to  15 : Page = 177 : R_graphique = Y -   8
     case  16 to  23 : Page = 178 : R_graphique = Y -  16
     case  24 to  31 : Page = 179 : R_graphique = Y -  24
     case  32 to  39 : Page = 180 : R_graphique = Y -  32
     case  40 to  47 : Page = 181 : R_graphique = Y -  40
     case  48 to  55 : Page = 182 : R_graphique = Y -  48
     case  56 to  63 : Page = 183 : R_graphique = Y -  56
     case  64 to  71 : Page = 184 : R_graphique = Y -  64
     case  72 to  79 : Page = 185 : R_graphique = Y -  72
     case  80 to  87 : Page = 186 : R_graphique = Y -  80
     case  88 to  95 : Page = 187 : R_graphique = Y -  88
     case  96 to 103 : Page = 188 : R_graphique = Y -  96
     case 104 to 111 : Page = 189 : R_graphique = Y - 104
     case 112 to 119 : Page = 190 : R_graphique = Y - 112
     case 120 to 127 : Page = 191 : R_graphique = Y - 120
     endselect
#ELSE    ; Compact
      Page = Y / 8 + 176 : R_graphique = Y // 8            ; = 9 bytes
#ENDIF

#IFDEF ORIGINAL        ; = 102 bytes
     select case R_graphique
     case 0 : R_graphique = 0x01
     case 1 : R_graphique = 0x02
     case 2 : R_graphique = 0x04
     case 3 : R_graphique = 0x08
     case 4 : R_graphique = 0x10
     case 5 : R_graphique = 0x20
     case 6 : R_graphique = 0x40
     case 7 : R_graphique = 0x80
endselect
#ELSE            ; Compact
          lookup R_graphique,(1,2,4,8,16,32,64,128),R_graphique          ; = 9 bytes
#ENDIF
                
#IFDEF ORIGINAL        ; = 54 bytes             
     if bit0 = 1 then : bit8  = 1 endif
     if bit1 = 1 then : bit9  = 1 endif
     if bit2 = 1 then : bit10 = 1 endif
     if bit3 = 1 then : bit11 = 1 endif
     if bit4 = 1 then : bit12 = 1 endif
     if bit5 = 1 then : bit13 = 1 endif
     if bit6 = 1 then : bit14 = 1 endif
     if bit7 = 1 then : bit15 = 1 endif
#ELSE   ; Compact 
      b1 = b1 OR b0     ; = 4 bytes
#ENDIF

Also, I suspect that a subroutine (perhaps with a TABLE data structure and an ON .... GOTO ..... , etc.) could considerably reduce the size of all the similar sections like the following:
Code:
case 45 : NumASCII = Colonne        ; 75 bytes (used about 12 times)
                gosub Numero_8
                gosub SetPosition
                hi2cout (SETSTARTLINE, 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80)    '-H
                NumASCII = Colonne + 1
                gosub Numero_8
                gosub SetPosition
                hi2cout (SETSTARTLINE, 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01)   '-B
                x = x + 11 : gosub SetPosition

Cheers, Alan.
 
Last edited:
Hello,
I just tested the code to compress the program with my SH1107 and I can confirm that all the examples given work very well.
Therefore, I will be able to continue developing my program by adding additional graphics functions in the near future, which I will make available later.
 
Hello,
I just finished my structure for compressing 8x16 numbers, which you can see below. Indeed, it saves a lot of memory.
Thanks again for the tips.

The compressed version of my program, compatible with SH1107, is available here : #124

Code:
Numero_16:
      Select Case NumASCII
      case  0 to 15 : Colonne = NumASCII
 
      case 32 :       NumASCII = Colonne
                      gosub Numero_8
                      gosub SetPosition
                      gosub Esp_BH'EspaceH
                      NumASCII = Colonne + 1
                      gosub Numero_8
                      gosub SetPosition
                      gosub Esp_BH'EspaceB
                      x = x + 11 : gosub SetPosition
              
      case 43 to 57 : Pointer  = NumASCII - 43
                      NumASCII = Colonne
                      gosub Numero_8
                      gosub SetPosition
                      ON Pointer gosub Sym_PH,Esp_VD,Sym_MH,Esp_VD,Sym_DH,Num_0H,Num_1H,Num_2H,Num_3H,Num_4H,Num_5H,Num_6H,Num_7H,Num_8H,Num_9H
                      NumASCII = Colonne + 1
                      gosub Numero_8
                      gosub SetPosition
                      ON Pointer gosub Sym_PB,Sym_VB,Sym_MB,Sym_PO,Sym_DB,Num_0B,Num_1B,Num_2B,Num_3B,Num_4B,Num_5B,Num_6B,Num_7B,Num_8B,Num_9B
                      x = x + 11 : gosub SetPosition
    endselect
return
Esp_BH: hi2cout (SETSTARTLINE, Seg_0,Seg_0,Seg_0,Seg_0,Seg_0,Seg_0,Seg_0,Seg_0) : return 'Espace
Esp_VD: hi2cout (SETSTARTLINE, Seg_0,Seg_0,Seg_0)                       : return 'EspaceVide
Sym_PH: hi2cout (SETSTARTLINE, 0x80,0x80,0x80,0xf0,0xf0,0x80,0x80,0x80) : return '+H 43
Sym_PB: hi2cout (SETSTARTLINE, 0x01,0x01,0x01,0x0f,0x0f,0x01,0x01,0x01) : return '+B 43
Sym_VB: hi2cout (SETSTARTLINE, Seg_0,0xd8,0x78)                         : return ',B 44
Sym_MB: hi2cout (SETSTARTLINE, 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01) : return '-B 45
Sym_MH: hi2cout (SETSTARTLINE, 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80) : return '-H 45
Sym_PO: hi2cout (SETSTARTLINE, Seg_0,0x60,0x60)                         : return '.PO 46
Sym_DB: hi2cout (SETSTARTLINE, 0x60,0x78,0x1E,0x07,0x01,0x00,0x00,0x00) : return '/B 47
Sym_DH: hi2cout (SETSTARTLINE, 0x00,0x00,0x00,0x80,0xF0,0x7C,0x0E,0x02) : return '/H 47
Num_0B: hi2cout (SETSTARTLINE, 0x1f,0x3f,0x70,0x60,0x60,0x70,0x3f,0x1f) : return
Num_0H: hi2cout (SETSTARTLINE, 0xf8,0xfc,0x0e,0x06,0x06,0x0e,0xfc,0xf8) : return
Num_1B: hi2cout (SETSTARTLINE, 0x60,0x60,0x60,0x7f,0x7f,0x60,0x60,0x60) : return
Num_1H: hi2cout (SETSTARTLINE, 0x10,0x30,0x18,0xfc,0xfe,0x00,0x00,0x00) : return
Num_2B: hi2cout (SETSTARTLINE, 0x70,0x78,0x7c,0x6e,0x67,0x63,0x61,0x30) : return
Num_2H: hi2cout (SETSTARTLINE, 0x1c,0x1e,0x06,0x06,0x06,0xce,0xfc,0xf8) : return
Num_3B: hi2cout (SETSTARTLINE, 0x30,0x70,0x60,0x60,0x60,0x71,0x3f,0x1f) : return
Num_3H: hi2cout (SETSTARTLINE, 0x0c,0x0e,0x06,0x86,0xc6,0xce,0xfc,0xf8) : return
Num_4B: hi2cout (SETSTARTLINE, 0x07,0x0f,0x0c,0x0c,0x0c,0x7f,0x7f,0x0c) : return
Num_4H: hi2cout (SETSTARTLINE, 0x80,0xe0,0xfc,0x1e,0x06,0x00,0x00,0x00) : return
Num_5B: hi2cout (SETSTARTLINE, 0x61,0x61,0x61,0x61,0x61,0x33,0x3f,0x1e) : return
Num_5H: hi2cout (SETSTARTLINE, 0xfe,0xfe,0x86,0x86,0x86,0x86,0x06,0x06) : return
Num_6B: hi2cout (SETSTARTLINE, 0x1f,0x3f,0x73,0x61,0x61,0x73,0x3f,0x1e) : return
Num_6H: hi2cout (SETSTARTLINE, 0xF8,0xFC,0x8E,0x86,0x86,0x8E,0x1C,0x18) : return
Num_7B: hi2cout (SETSTARTLINE, 0x60,0x70,0x38,0x1f,0x07,0x01,0x00,0x00) : return
Num_7H: hi2cout (SETSTARTLINE, 0x0e,0x06,0x06,0x06,0xc6,0xfe,0x7e,0x06) : return
Num_8B: hi2cout (SETSTARTLINE, 0x1e,0x3f,0x73,0x61,0x61,0x73,0x3f,0x1e) : return
Num_8H: hi2cout (SETSTARTLINE, 0x78,0xfc,0xce,0x86,0x86,0xce,0xfc,0x78) : return
Num_9B: hi2cout (SETSTARTLINE, 0x40,0x60,0x61,0x61,0x71,0x71,0x1f,0x0f) : return
Num_9H: hi2cout (SETSTARTLINE, 0x78,0xfc,0xce,0x86,0x86,0xce,0xfc,0xf8) : return
 
Last edited:
Hello,
I just saw that it's possible to replace:

Code:
     if ClearPixel <> 0 then
     if ClearY = 0 then : bit8  = 0 endif
     if ClearY = 1 then : bit9  = 0 endif
     if ClearY = 2 then : bit10 = 0 endif
     if ClearY = 3 then : bit11 = 0 endif
     if ClearY = 4 then : bit12 = 0 endif
     if ClearY = 5 then : bit13 = 0 endif
     if ClearY = 6 then : bit14 = 0 endif
     if ClearY = 7 then : bit15 = 0 endif
     endif

by

Code:
if ClearPixel  <> 0 then : clearbit R_graphique,ClearY endif

Because it works the same way. I tested it.
Warning. Valid only on the X1 and X2 series.
 
Last edited:
Hi,

The CLEARBIT instructions are not particularly efficient (each uses about 10 bytes of program memory). I believe the above section is preceded by:
Code:
DrawsPixel:
 .......   : R_graphique = Y // 8
     ClearY = R_graphique                                                ; ** Move this line to after the next line **
     lookup R_graphique,(0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80),R_graphique
If you move the ClearY = R_graphique line to after the Lookup..... , then I believe the following code does the same thing, even if b1 were not bit-addressable. Note that the ANDNOT instruction is not the same as a NAND, but is available on all current PICaxes.

Code:
if ClearPixel <> 0 then 
      b1 = b1 ANDNOT ClearY
endif

Also, if you need to use the same LOOKUP data more than once, then a READTABLE or READ R_graphique {+ offset} , R_graphique into TABLE / DATA ( 1 , 2 , 4 , 8 , 0x10 , 0x20 , 0x40 , 0x80 ) is more compact and faster.

Cheers, Alan.
 
Hello,
The proposed code does not work. (tested)
Code:
if ClearPixel = 0 then :  b1 = b1 ANDNOT ClearY endif

Here is what the (Draw Pixel) function currently contains:
Code:
DrawsPixel:
      Y = Y Max 127
      Page = Y / 8 + 176 : R_graphique = Y // 8
      ClearY = R_graphique
      lookup R_graphique,(0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80),R_graphique
      gosub SetPosition : hi2cout (0,Page) : hi2cin SETSTARTLINE,(BitsTempon,L_graphique)
      R_graphique = R_graphique or L_graphique
 
      if ClearPixel  <> 0 then : clearbit R_graphique,ClearY endif
      if L_graphique <> R_graphique then : gosub SetPosition : hi2cout (0,Page) : hi2cout (SETSTARTLINE,R_graphique) endif
return
 
Last edited:
Hi,

Your program code looks more complex now, is R_graphique still in b1 (which my code assumes) ? Also, I don't understand using R_graphique in both the "source" instruction and the "target" (which always produces a result of zero).

I was only trying to demonstrate that the CLEARBIT instruction can be replaced by ANDNOT sometimes with advantage. My test code:
Code:
#picaxe 20x2
symbol R_graphique  = b0
symbol R_graphique0 = b1
symbol R_graphique1 = b2
symbol R_graphique2 = b3
symbol ClearY    = b4
symbol ClearY2   = b5
R_graphique1 = 255
R_graphique2 = 255 
   for R_graphique = 0 to 7
      ClearY = R_graphique
      lookup R_graphique,(0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80),R_graphique0   ; 13 bytes
      ClearY2 = R_graphique0  
      clearbit R_graphique0,ClearY
      clearbit R_graphique1,ClearY                   ; 10 bytes
      R_graphique2 = R_graphique2 ANDNOT ClearY2    ; 5 bytes
      sertxd(#R_graphique0,"  ",#R_graphique1,"  ",#R_graphique2,cr)
   next
#rem Output :
0  254  254
0  252  252
0  248  248
0  240  240
0  224  224
0  192  192
0  128  128
0  0  0
#endrem

Cheers, Alan.
 
Hi,
Here is the complete code with all the modifications that have been made (.Lib_SH1107_PICAXE_20X2_24LC512 Compresser.bas)
Here is also a piece of code to add to (StarCode)to test the ClearPixel (ClearY) function.

Do you have an SH1107 or an SH1106 to test the program?
In my program, I'm trying to reduce the number of variables because I'll need them later to create an interface similar to the module (led042 128 x 64) that used to be sold by PICAXE, but in an improved version.

Code:
StarCode:
 
    AngleX1 = 45'AngleDuRectangleEndegres ( mini 0 maxi 90)
    x1      = 63'x_DimensionDuRectangle
    y1      = 63'y_DimensionDuRectangle
    x0      = 63'x_CordonnerDepartEnPixel
    y0      = 20'y_CordonnerDepartEnPixel
    stipple =  0 : ClearPixel = 0 : gosub DrawsRectangle'dessine un rectangle
 
    AngleX1 = 0'AngleDuRectangleEndegres ( mini 0 maxi 90)
    x1      = 63'x_DimensionDuRectangle
    y1      = 63'y_DimensionDuRectangle
    x0      = 32'x_CordonnerDepartEnPixel
    y0      = 32'y_CordonnerDepartEnPixel
    stipple =  0 : ClearPixel = 0 : gosub DrawsRectangle'dessine un rectangle
 
    for Val = 0 to 359
              AngleX1 = Val'AngleDeLaLigneEndegres ( mini 0 maxi 359)
              x1      = 24'x_LongeurDeLaLigneEnPixel
              x0      = 63'x_CordonnerDepartEnPixel
              y0      = 63'y_CordonnerDepartEnPixel
              stipple = 0 : ClearPixel = 0 : gosub DrawsLine  'dessine une ligne
      next

      val = 0
      pause 50000
 
      for Val = 0 to 359
              AngleX1 = Val'AngleDeLaLigneEndegres ( mini 0 maxi 359)
              x1      = 12'x_LongeurDeLaEnPixel
              x0      = 63'x_CordonnerDepartEnPixel
              y0      = 63'y_CordonnerDepartEnPixel
              stipple = 0 : ClearPixel = 1 : gosub DrawsLine  'Efface une ligne
    next
 
    val = 0
    pause 50000
      
 
goto StarCode
 

Attachments

Last edited:
Hi,
I found out why (andnot) wasn't working. I had forgotten to change the (ClearY) variable.
(ClearY) is now passed to b2.
Here's an excerpt of the code with the working modification and the complete code (Lib_SH1107_PICAXE_20X2_24LC512 Compressed) to download.

Code:
DrawsPixel:
      Y = Y Max 127
      Page = Y / 8 + 176 : R_graphique = Y // 8
      lookup R_graphique,(0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80),R_graphique
      gosub SetPosition : hi2cout (0,Page) : hi2cin SETSTARTLINE,(BitsTempon,L_graphique)
      ClearY = R_graphique
      R_graphique = R_graphique or L_graphique
       
      if ClearPixel  <> 0 then : R_graphique = R_graphique andnot ClearY endif
      if L_graphique <> R_graphique then : gosub SetPosition : hi2cout (0,Page) : hi2cout (SETSTARTLINE,R_graphique) endif
return
 

Attachments

Last edited:
Hi,

Thanks for the update (I suspected that might be the issue). Personally, I always try to write my programs to be compatible with the M2 family (and to use instructions that don't need you to refer to the User Manual every time :) ). However, if you are using an X2 and want to save even more program space:

That particular LOOKUP Instruction and Data has its own dedicated instruction in the X2 chips : DCD - The "Decode" function, see Manual 2, page 29 ;) . Thus I believe the instructions ClearY = DCD R_graphique : R_graphique = R_graphique ANDNOT ClearY and CLEARBIT R_graphique , ClearY should give the same result. Interestingly, both lines use about 10 bytes of program memory, probably because the compiler is generating very similar code for the two alternatives !

Cheers, Alan.
 
Hi,
That's very interesting. I just saw that it can be replaced:
Code:
lookup R_graphique,(0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80),R_graphique
with:
Code:
let R_graphique = dcd R_graphique
We get this and it works very well :
Code:
DrawsPixel:
      Y = Y Max 127
      Page = Y / 8 + 176 : R_graphique = Y // 8
      let R_graphique = dcd R_graphique
      gosub SetPosition : hi2cout (0,Page) : hi2cin SETSTARTLINE,(BitsTempon,L_graphique)
      ClearY = R_graphique
      R_graphique = R_graphique or L_graphique
        
      if ClearPixel  <> 0 then : R_graphique = R_graphique andnot ClearY endif
      if L_graphique <> R_graphique then : gosub SetPosition : hi2cout (0,Page) : hi2cout (SETSTARTLINE,R_graphique) endif
return

However, I also notice that the M2 series only has 28 variables (b0 to b27).
Therefore, I'm not sure if my program is suitable for these models.
 
Last edited:
Hi,

Indeed there may need to be some compromises to use the M2 chips, although I believe the 14/20M2 chips actually have more User memory than the 20X2 (if you don't mind program-bank / slot switching) ! Additionally, the M2s (including the 08M2) have the six (or 7-8) "System Words" (s_w1 to s_w6) available for the User/Programmer, if required (but the constituent bytes and bits do not have corresponding "overlaid" addresses). The X2s do have some additional instructions (as we're discussing here), most of which can be emulated by Basic instructions, although probably with some speed penalty. It's amazing how few variables need to be used, with some creativity, for example with @bptr ;) .

Cheers, Alan.
 
Hi,
The project is evolving.
The program is now able to differentiate between an SH1106 and an SH1107 and configures itself accordingly.
See attached photos

Here is a code to put in (Star Code) to test all the new functions.
Code:
StarCode:
      
    x = 44 : gosub SetPosition
    Print = 0
    for Print = 0 to 6
         if SH1107 = 0 then : lookup Print,(0,"SH1106"), NumASCII else : lookup Print,(0,"SH1107"), NumASCII endif
         gosub Numero_8
    next
        
    x1 = 54'x_DimensionDuCercle
    y1 = 26'y_DimensionDuCercle
    x0 = 63'x_CordonnerDepart
    y0 = 63'y_CordonnerDepart
    stipple = 0 : ClearPixel = 0 : gosub DrawsCircle'dessine un cercle
                                       
    AngleX1 = 45'AngleDuRectangleEndegres ( mini 0 maxi 90)
    x1      = 63'x_DimensionDuRectangle
    y1      = 63'y_DimensionDuRectangle
    x0      = 63'x_CordonnerDepartEnPixel
    y0      = 20'y_CordonnerDepartEnPixel
    stipple =  0 : ClearPixel = 0 : gosub DrawsRectangle'dessine un rectangle
 
    AngleX1 = 0'AngleDuRectangleEndegres ( mini 0 maxi 90)
    x1      = 63'x_DimensionDuRectangle
    y1      = 63'y_DimensionDuRectangle
    x0      = 32'x_CordonnerDepartEnPixel
    y0      = 32'y_CordonnerDepartEnPixel
    stipple =  0 : ClearPixel = 0 : gosub DrawsRectangle'dessine un rectangle
         
    for Val = 0 to 359 
              AngleX1 = Val'AngleDeLaLigneEndegres ( mini 0 maxi 359)
              x1      = 24'x_LongeurDeLaLigneEnPixel
              x0      = 63'x_CordonnerDepartEnPixel
              y0      = 63'y_CordonnerDepartEnPixel
              stipple = 1 : ClearPixel = 0 : gosub DrawsLine  'dessine une ligne
    next

    val = 0
    pause 50000
  
    for Val = 0 to 359 
              AngleX1 = Val'AngleDeLaLigneEndegres ( mini 0 maxi 359)
              x1      = 24'x_LongeurDeLaLigneEnPixel
              x0      = 63'x_CordonnerDepartEnPixel
              y0      = 63'y_CordonnerDepartEnPixel
              stipple = 0 : ClearPixel = 0 : gosub DrawsLine  'dessine une ligne
    next
 
    x = 63 : y = 63 : ClearPixel = 1 : gosub DrawsPixel 'Efface un pixel
 
    val = 0
    pause 50000
 
    for Val = 0 to 359 
              AngleX1 = Val'AngleDeLaLigneEndegres ( mini 0 maxi 359)
              x1      = 24'x_LongeurDeLaLigneEnPixel
              x0      = 63'x_CordonnerDepartEnPixel
              y0      = 63'y_CordonnerDepartEnPixel
              stipple = 1 : ClearPixel = 1 : gosub DrawsLine  'Efface une ligne
    next

    val = 0
    pause 50000
 
    for Val = 0 to 359 
              AngleX1 = Val'AngleDeLaLigneEndegres ( mini 0 maxi 359)
              x1      = 24'x_LongeurDeLaLigneEnPixel
              x0      = 63'x_CordonnerDepartEnPixel
              y0      = 63'y_CordonnerDepartEnPixel
              stipple = 0 : ClearPixel = 1 : gosub DrawsLine  'Efface une ligne
    next

    val = 0
    pause 50000  
  
goto StarCode
 

Attachments

Last edited:
Hi,
I have just finished creating a program for a PICAXE 20X2 that allows it to draw geometric shapes and display text in two fonts by receiving instructions (N9600_64).The program distinguishes between SH1107 and SH1106 controllers and configures itself automatically. Instructions are sent to it from a second PICAXE 20X2.
The photo shows the module with the display in operation.The five LEDs indicate the display's operating status.There are also four microswitches for quick configuration.Additionally, there is a tilt switch to determine display orientation at power-up, and a potentiometer that can be set to adjust either the offset or the contrast.The green LED was removed for the photo.
Is it possible to further compress the code running on the 20X2 that controls the display?
 

Attachments

Last edited:
Hi,
Is it possible to further compress the code running on the 20X2 that controls the display?
Programming often has to be a compromise between size and speed of the program and the "Readability" or clarity of operation. But yes, there are certainly ways to reduce the size further.

If sections of the program look similar (particularly if "Cut-and-Paste" was used) then a subroutine or another method may be appropriate. Where there are slight differences, they might be handled with a "Lookup Table" (most PICaxes have typically 256 bytes of EPROM and 256 bytes of TABLE Memory), but here, the "Byte Pointer" (BPTR) is even more powerful because the byte can be embedded directly within Mathematical expressions as "@BPTR". Thus, I believe there is one section which can be reduced by almost 300 program bytes, and also "releases" 11 named variable bytes (b38 to b48) ! Often, INCrementing the BPTR is most efficient, particularly the direct "Post Increment" (@BPTRINC), but here I have just used a FOR .. NEXT loop for clarity.

Code:
#DEFINE USE_BPTR
#IFDEF USE_BPTR
LoadNumero:
; REM    If bptr = 38 : NumASCII_0  = b38 = @bptr      ; etc.
   high Status
   serin ComIn, N9600_64,NumASCII
   for bptr = 38 to 48
      serin [6,Num],ComIn, N9600_64, @bptr    ; NumASCII_0 to NumASCII_10
   next
Num:
   low Status
   if BigNunero = 0  then : gosub Numero_8   else : gosub Numero_16 endif
   for bptr = 38 to 48
      NumASCII = @bptr : if BigNunero = 0 then : gosub Numero_8 else : gosub Numero_16 endif : @bptr  = 255
   next
   return

#ELSE   ; Original code
LoadNumero:
      high Status
    serin ComIn, N9600_64,NumASCII
    serin [6,Num],ComIn, N9600_64,NumASCII_0
    serin [6,Num],ComIn, N9600_64,NumASCII_1
    serin [6,Num],ComIn, N9600_64,NumASCII_2
    serin [6,Num],ComIn, N9600_64,NumASCII_3
    serin [6,Num],ComIn, N9600_64,NumASCII_4
    serin [6,Num],ComIn, N9600_64,NumASCII_5
    serin [6,Num],ComIn, N9600_64,NumASCII_6
    serin [6,Num],ComIn, N9600_64,NumASCII_7
    serin [6,Num],ComIn, N9600_64,NumASCII_8
    serin [6,Num],ComIn, N9600_64,NumASCII_9
    serin [6,Num],ComIn, N9600_64,NumASCII_10
Num:
   low Status
   if BigNunero = 0  then : gosub Numero_8   else : gosub Numero_16 endif
      NumASCII = NumASCII_0  : if BigNunero = 0 then : gosub Numero_8 else : gosub Numero_16 endif : NumASCII_0  = 255
      NumASCII = NumASCII_1  : if BigNunero = 0 then : gosub Numero_8 else : gosub Numero_16 endif : NumASCII_1  = 255
      NumASCII = NumASCII_2  : if BigNunero = 0 then : gosub Numero_8 else : gosub Numero_16 endif : NumASCII_2  = 255
      NumASCII = NumASCII_3  : if BigNunero = 0 then : gosub Numero_8 else : gosub Numero_16 endif : NumASCII_3  = 255
      NumASCII = NumASCII_4  : if BigNunero = 0 then : gosub Numero_8 else : gosub Numero_16 endif : NumASCII_4  = 255
      NumASCII = NumASCII_5  : if BigNunero = 0 then : gosub Numero_8 else : gosub Numero_16 endif : NumASCII_5  = 255
      NumASCII = NumASCII_6  : if BigNunero = 0 then : gosub Numero_8 else : gosub Numero_16 endif : NumASCII_6  = 255
      NumASCII = NumASCII_7  : if BigNunero = 0 then : gosub Numero_8 else : gosub Numero_16 endif : NumASCII_7  = 255
    NumASCII = NumASCII_8  : if BigNunero = 0 then : gosub Numero_8 else : gosub Numero_16 endif : NumASCII_8  = 255
    NumASCII = NumASCII_9  : if BigNunero = 0 then : gosub Numero_8 else : gosub Numero_16 endif : NumASCII_9  = 255
    NumASCII = NumASCII_10 : if BigNunero = 0 then : gosub Numero_8 else : gosub Numero_16 endif : NumASCII_10 = 255
return

#ENDIF

The SELECT ... CASE structure can be very powerful, but simpler structures which can use basic mathematical formulae may be smaller and/or faster with an ON ... GOTO. A GOSUB is also avaiIable, but I don't think it is any more efficient, and here I preferred the "trick" of using a "Fall-through" to catch a range of values (0 to 15) with no additional calcuations. I can't test the following but it might give an indication of how almost another 150 bytes could be saved.

Code:
#DEFINE USE_ONGOTO
#IFDEF USE_ONGOTO
If Codeselect => 200 Then : Codeselect = Codeselect - 148 / 2 : Endif              ; Convert 200 to 26, 202 to 27, etc. 
   CodeSelect = CodeSelect - 20                ; <20 produces an underflow, i.e. a Large number that will cause a "Fall through"
   GOSUB sel_case              ; ON .. GOSUB  will not work with a Fallthrough
done_sel: GOTO end_sel        ; Destination after the RETURNs  (So now jump over the "Case" processing)
sel_case: ON Codeselect GOTO case20, case21, SensPixelConfigInitial, SensPixelConfigMiroir, NormalDisplay,_
    InversDisplay, ClearDisplay, case202, DrawsRectangle, DrawsCircle, DrawsLine             ; Or fallthrough to case0to15
case0to15:
    x = x0 : gosub SetPosition
    if SH1107 = 0 then : NumASCII = CodeSelect Max 7 else : NumASCII = CodeSelect : endif
    if BigNunero = 0 then : gosub Numero_8 else : gosub Numero_16 : endif
    goTo LoadNumero        ; Then Returns to "done_sel"
case20:  gosub DISPLAY_OFF : goTo DisplayID    ; Returns to "done_sel"
case21:  gosub DISPLAY_ON  : goTo DisplayID
case202: x = x0 : y = y0 : goTo DrawsPixel
end_sel:
#ELSE     ; Original code
    select case CodeSelect
   case 0 to 15 : x = x0 : gosub SetPosition
   if SH1107 = 0 then : NumASCII = CodeSelect Max 7 else : NumASCII = CodeSelect : endif
   if BigNunero = 0 then : gosub Numero_8 else : gosub Numero_16 endif : gosub LoadNumero
    case 20  : gosub DISPLAY_OFF : gosub DisplayID
    case 21  : gosub DISPLAY_ON  : gosub DisplayID
    case 22  : gosub SensPixelConfigInitial  'Ecri a l'Endroi
    case 23  : gosub SensPixelConfigMiroir   'Ecri a l'Enver
    case 24  : gosub NormalDisplay           'Ecriture blanche sur fond noir
    case 25  : gosub InversDisplay           'Ecriture Noir sur fond blanc
    case 200 : gosub ClearDisplay            'Efface l'ecrant
    case 202 : x = x0 : y = y0 : gosub DrawsPixel
    case 204 : gosub DrawsRectangle
    case 206 : gosub DrawsCircle
    case 208 : gosub DrawsLine
    endselect
#ENDIF

Cheers, Alan.
 
Hi,
I applied the suggested tips to reduce the code, and they worked well.
I also fixed some bugs in the program.
The character limit is 16 for small numbers.
The character limit for large numbers is set to 11.
Thanks for the help.
Here are the updated files.

Code:
LoadNumero:
      if BigNunero = 0  then : Print = 55 : else : Print = 50 endif
      high Status
      serin ComIn, N9600_64,NumASCII
      for bptr = 41 to Print
      serin [6,Num],ComIn, N9600_64,@bptr
      next
      Num:
      low Status
      if BigNunero = 0  then : gosub Numero_8 else : gosub Numero_16 endif
      for bptr = 41 to Print
      NumASCII = @bptr : if BigNunero = 0 then : gosub Numero_8 else : gosub Numero_16 endif : @bptr = 255
      next
      Print = 0
return
 

Attachments

Last edited:
Back
Top