PIC 18F BMP to KS0108 LCD conversion

by Joe Colquitt

Copied with permission from http://home.clear.net.nz/pages/joecolquitt/bmp2lcd.html

This board is also used for Text to graphic LCD using PIC 18F





A .BMP image can be converted fairly easily for display on a graphic LCD. The
example here is for a 1-bit colour depth (ie black/white) 128 x 64 image

Here are a couple of pages describing the BMP format

fileformat.com

fortunecity.com

The important things to remember for this conversion are that the BMP is stored in
memory 'upside-down'and that there are 1024 data bytes. For this known size and
colour depth, the header information can be ignored

The LCD screen used is the Agena AG1286401 ag1286401 which has two KS0108 controllers ks0108

The example above was drawn with Paint Shop Pro, 2 colours (1-bit), 128 x 64, black
background, which is actually an inverse image on the PC monitor

Circuit for this s/w



This particular screen needs -10V for Vo, and pins 3 and 18 are connected on the PCB. It
has pads for a voltage inverter (Epson SCI7661) but that's not installed, so Vo has to be made,
in this case with an MC34063 SMPS IC. Not the only or cheapest way, but parts were handy.
PCB artwork (I use 600dpi laser toner transfer) and overlay at the bottom of this page.

I should have transposed the PortC and DB0-7 connections, but instead added a s/w mirror.

The screen is drawn in the schematic to show that there are 16 distinct areas on it. The
KS0108 for each half controls 8 pages. Each page is 8 bits high by 64 bits wide

Layout - BMP vs LCD





The difference is that a BMP line is bytes stacked end-to-end, ie sequentially in memory. The
same line on an LCD is made of the same bit numbers (ie 0 - 7) of a column of bytes

So, the method to convert BMP data to LCD data is to rotate an array of 8 x 8 pixels

Consider this pattern in a 16 x 8 (two 8x8 blocks) section of a BMP



Line 0 starts FF, 7F and then 14 bytes to the end of the line at byte015
Line 1 starts 83, 01 and then etc
Line 2 starts A1, 81 and then etc

What's needed to be done is 'vertically slice' that picture, effectively rotating the data
and converting it to the LCD format.

Taking it column by column, these are data values that will be stored in 'array'. The s/w
does this on each 8x8 pixel block, working across the BMP data

bit7 of byte000 becomes bit0 of byte00, bit6 of byte001 becomes bit0 of byte01 etc
bit7 of byte016 becomes bit1 of byte00, bit6 of byte016 becomes bit1 of byte01 etc



My data is sourced from a USB drive through the Data/Clock lines (you could of course bit-
bang or upload via the UART or type data directly into a look-up table), and stored from
0x0100 (bmp_base) onwards. Note that because the BMP data comes starting with line
63 and ends with line 0 that it's 'upside-down' in memory. The s/w therefore works
backwards, starting with the block of 128 bytes (= 128 * 8 pixels = 2 pages, 1 left, 1 right)
beginning at 0x0480 and ending at 0x04FF

Left page 0       Right page 0
0x0480 - 0x0487 0x0488 - 0x048f 0x0490 - 0x0497 0x0498 - 0x049f 0x04a0 - 0x04a7 0x04a8 - 0x04af 0x04b0 - 0x04b7 0x04b8 - 0x04bf 0x04c0 - 0x04c7 0x04c8 - 0x04cf 0x04d0 - 0x04d7 0x04d8 - 0x04df 0x04e0 - 0x04e7 0x04e8 - 0x04ef 0x04f0 - 0x04f7 0x04f8 - 0x04ff

cblock 0x0000 array:64 ;64 RAM bytes = 1 LCD page endc ; lata,0 ;spare ; lata,1 ;spare ; lata,2 ;spare #define cs1 lata,3 ;CS1 #define cs2 lata,4 ;CS2 #define lcd_rst lata,5 ;/Reset ;LCD pins 0V V+ VO RS R/W En DB0-DB7 CS1 CS2 /Reset Vee A K #define en latb,0 ;LCD #define rw latb,1 ;LCD #define rs latb,2 ;LCD #define led latb,3 ;LED #define bbclk portb,4 ;input clock #define bbdata portb,5 ;input data ; latb,6 ;spare ; latb,7 ;spare ;PortC 0-7 - LCD data, mirrored #define busy portc,3 ;per KS0108 datasheet page_base = 0xb8 ; + 0 - 7 y_base = 0x40 ; + address 0 - 63, pixel position on line line_base = 0xc0 ; + line 0 - 63, not needed for this bmp_base = 0x0100 ;data storage RAM address ;================================================ ; Extract data and display ;================================================ ;One page of pixels is 8 high x 64 wide = 64 bytes ;BMP in PIC memory is in reverse line scan order, ie picture is inverted ;in original BMP file format ; M L ; S S ; b b ;line 63 (a63) 76543210 (b63) 76543210 (c63) 76543210 etc, 16 bytes wide @ 0100 ;line 62 (a62) 76543210 (a62) 76543210 (c62) 76543210 etc, 16 bytes wide @ 0120 ;...... ;line 01 (a01) 76543210 (b01) 76543210 (c01) 76543210 etc, 16 bytes wide @ 04e0 ;line 00 (a00) 76543210 (b00) 76543210 (c00) 76543210 etc, 16 bytes wide @ 04f0 ;On KS0108 screen, one page is 8 vertical bytes * 64 columns ;8 bytes sent to LCD are made of the bit7's, bit6's, bit5's etc ; b b b b b b b b b b ; y y y y y y y y y y ; t t t t t t t t t t ; e e e e e e e e e e ; 01 02 03 04 05 06 07 08 09 10 ;a56 7 a56 6 a56 5 a56 4 a57 3 a56 2 a56 1 a56 0 b56 7 b56 6 etc ;a57 7 a57 6 a57 5 a57 4 a58 3 a57 2 a57 1 a57 0 b57 7 b57 6 etc ;a58 7 a58 6 a58 5 a58 4 a59 3 a58 2 a58 1 a58 0 b58 7 b58 6 etc ;a59 7 a59 6 a59 5 a59 4 a60 3 a59 2 a59 1 a59 0 b59 7 b59 6 etc ;a60 7 a60 6 a60 5 a60 4 a61 3 a60 2 a60 1 a60 0 b60 7 b60 6 etc ;a61 7 a61 6 a61 5 a61 4 a62 3 a61 2 a61 1 a61 0 b61 7 b61 6 etc ;a62 7 a62 6 a62 5 a62 4 a62 3 a62 2 a62 1 a62 0 b62 7 b62 6 etc ;a63 7 a63 6 a63 5 a63 4 a63 3 a63 2 a63 1 a63 0 b63 7 b63 6 etc ;128 pixels per line = 16 bytes a - p ;line 00 (a00) 76543210 (b00) 76543210 (c00) 76543210 ... (p00) etc, 16 bytes wide @ 04f0 ;line 01 (a01) 76543210 (b01) 76543210 (c01) 76543210 ... (p01) etc, 16 bytes wide @ 04e0 ;line 02 (a02) 76543210 (b02) 76543210 (c02) 76543210 ... (p02) etc, 16 bytes wide @ 04d0 ;line 03 (a03) 76543210 (b03) 76543210 (c03) 76543210 ... (p03) etc, 16 bytes wide @ 04c0 ;line 04 (a04) 76543210 (b04) 76543210 (c04) 76543210 ... (p04) etc, 16 bytes wide @ 04b0 ;line 05 (a05) 76543210 (b05) 76543210 (c05) 76543210 ... (p05) etc, 16 bytes wide @ 04a0 ;line 06 (a06) 76543210 (b06) 76543210 (c06) 76543210 ... (p06) etc, 16 bytes wide @ 0490 ;line 07 (a07) 76543210 (b07) 76543210 (c07) 76543210 ... (p07) etc, 16 bytes wide @ 0480 ;..... ;line 62 (a62) 76543210 (b62) 76543210 (c62) 76543210 ... (p62) etc, 16 bytes wide @ 0110 ;line 63 (a63) 76543210 (b63) 76543210 (c63) 76543210 ... (p63) etc, 16 bytes wide @ 0100 ;To make 64 Page0 bytes ; ; LSb MSb ; ;byte1 = a00,7 + a01,7 + a02,7 + a03,7 + a04,7 + a05,7 + a06,7 + a07,7 ;byte2 = a00,6 + a01,6 + a02,6 + a03,6 + a04,6 + a05,6 + a06,6 + a07,6 ;... ;byte8 = a00,0 + a01,0 + a02,0 + a03,0 + a04,0 + a05,0 + a06,0 + a07,0 process bsf cs1 ;do left-hand side usec ;1us delay bcf cs2 usec movlw line_base call write_c call convert bcf cs1 ;do right-hand side usec bsf cs2 usec movlw line_base call write_c movlw 0x88 ;half-way across screen call right return ;start retrieval from end of RAM (0x0480 block) convert movlw 0x80 ;add to source RAM base address, 0x0100 right movwf block_lo movlw 0x03 movwf block_hi clrf page_no ;LCD page, 0 - 7 extract call clrpage ;erase page array, 0x0000 - 0x003f movlw page_base ;0xb8 clrc addwf page_no,w ;0 - 7 call write_c ;set page number to work on clrf column ;position of BMP data on the screen line clrf byte movlw .8 ;bit counter movwf loop_cnt next_col lfsr fsr0,bmp_base ;RAM base address movfw block_lo ;make actual source address clrc addwf fsr0l movfw block_hi clrc addwf fsr0h movfw column ;byte position along line clrc addwf fsr0l ;in current 8 lines of data ;in that block of data - ; ;480 - 8th line of BMP, all the bit0s array bytes ; ;490 - all the bit1s " ; ;4a0 - all the bit2s " ; ;4b0 - all the bit3s " ; ;4c0 - all the bit4s " ; ;4d0 - all the bit5s " ; ;4e0 - all the bit6s " ; ;4f0 - top line of BMP all the bit7s " loop8 lfsr fsr1,array ;64 bytes starting at 0x0000 movfw byte ;set to start of page array clrc addwf fsr1l movff fsr1h,temp1 ;save FSR1 movff fsr1l,temp0 movfw indf0 ;fetch BMP data byte btfsc wreg,7 ;set its bits in corresponding page array bytes bsf indf1,0 incf fsr1l btfsc wreg,6 bsf indf1,0 incf fsr1l btfsc wreg,5 bsf indf1,0 incf fsr1l btfsc wreg,4 bsf indf1,0 incf fsr1l btfsc wreg,3 bsf indf1,0 incf fsr1l btfsc wreg,2 bsf indf1,0 incf fsr1l btfsc wreg,1 bsf indf1,0 incf fsr1l btfsc wreg,0 bsf indf1,0 movff temp1,fsr1h ;restore FSR1 movff temp0,fsr1l dcfsnz loop_cnt bra rot_done ;do only 7 rotates rlncf postinc1 ;rotate the bits through the 8 array bytes rlncf postinc1 rlncf postinc1 rlncf postinc1 rlncf postinc1 rlncf postinc1 rlncf postinc1 rlncf postinc1 movlw .16 ;next BMP line clrc addwf fsr0l bra loop8 rot_done incf column ;next column of BMP bytes movlw .8 ;reset counter movwf loop_cnt movlw .8 ;storage to be in next 8 array bytes clrc addwf byte btfss byte,6 ;8 bytes on 8 lines processed bra next_col ;no, next column of 8 BMP bytes ;--------------------- display movlw y_base ;left-most call write_c movlw page_base ;set LCD page that data is for clrc addwf page_no,w call write_c lfsr fsr0,array do64 movfw postinc0 ;write 64 bytes to screen call write_d btfss fsr0l,6 bra do64 ;--------------------- movlw 0x80 ;move backwards through RAM by 1 block subwf block_lo skpc decf block_hi incf page_no btfss page_no,3 ;8 pages done bra extract return ;--------------------- ;clear page buffer clrpage lfsr fsr0,array lp64 clrf postinc0 btfss fsr0l,6 ;64 bytes cleared bra lp64 return ;================================================ ; Write data ;================================================ write_c bcf rs ;write command bra d_out write_d bsf rs ;write data d_out movwf swap_d clrf temp2 bcf rw movlw .8 ;mirror data for port movwf dcnt1 swap btfsc swap_d,0 bsf temp2,7 rlncf swap_d rrncf temp2 decfsz dcnt1 bra swap d0 movff temp2,latc ;write to port usec bcf en usec bsf en usec bcf rs usec bsf rw usec mov b'00001000',trisc btfsc busy bra $-2 mov b'00000000',trisc bcf rw usec return ;================================================ ; Clear all PIC RAM ;================================================ clr_ram lfsr fsr0,0x000 lfsr fsr1,0x100 lfsr fsr2,0x200 clear1 clrf postinc0 clrf postinc1 clrf postinc2 btfss fsr0h,0 bra clear1 lfsr fsr0,0x300 lfsr fsr1,0x400 lfsr fsr2,0x500 clear2 clrf postinc0 clrf postinc1 clrf postinc2 btfss fsr0h,2 bra clear2 return ;================================================ ; Clear all screen RAM ;================================================ clearall movlw page_base call write_c movlw y_base call write_c movlw line_base call write_c reload movlw .64 movwf cnt1 show0 movlw 0x00 dispw decfsz cnt1 bra show0 incf page_no btfsc page_no,3 bra clr_done movfw page_no addlw page_base call write_c bra reload clr_done return