6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Sun Jun 30, 2024 4:22 pm

All times are UTC




Post new topic Reply to topic  [ 9 posts ] 
Author Message
PostPosted: Wed Mar 10, 2021 5:35 am 
Offline

Joined: Thu Apr 14, 2016 2:13 pm
Posts: 9
Hey everyone. I recently built a 6502 computer using ben eater's videos. I got everything working on the breadboard with ram, rom, 6522 and 16x2 LCD so I figured I'd try to do something on my own. I bought a 128x64 LCD that uses an ST7920 chip and due to the fact that I've only got 5 pins left on the VIA chip I figured I'd try out bit banging SPI to communicate with it. I found this post from a while back viewtopic.php?p=45555 where Dr Jefyll shows some methods for bit banging SPI using the VIA and I've been trying to use some of that code. I'm pretty new to assembly and electronics so I decided I would try to just get something working first then try to optimize from there.

From what I can tell from reading the data sheet (https://www.lcd-module.de/eng/pdf/zubeh ... hinese.pdf) each data frame consists of 3 bytes:
1.) Sync bit - 5 1's followed by RW, RS and an additional zero to complete the byte (last 3 are 0's in my case)
2.) Most significant nibble followed by 4 0's
3.) Least significant nibble followed by 4 0's

I have my LCD hooked up like this:
VCC -> Connect to 5V
GND -> Connect to GND
RS -> Connect to 5V
RW (MOSI) -> VIA PA0
E (CLOCK) -> VIA PA1
PSB -> Connect to GND
RST -> Connect to 5V
A (or BLA) -> Connect to 5V
K (or BLK) -> Connect to GND

Here is the code I'm using to send a SPI byte:
Code:
SYNC_START = %00000010

lcd_spi_send:
  stz PORTA

  ;==========================
  ; Step 1.) send 5 1s
  ;==========================
  ldx #5
  ldy #SYNC_START
  sty PORTA
@startsync:
  ; pulse clock 5 times
  inc PORTA
  dec PORTA
  dex
  bne @startsync
 
  ;==========================
  ; Step 2.) send 3 0s
  ; RW, RS, and extra 0
  ; to round out byte
  ;==========================
  stz PORTA
  ldx #3
@endbits:
  ; pulse clock 3 times
  inc PORTA
  dec PORTA
  dex
  bne @endbits


  ;==========================
  ; Step 3.) first nibble
  ;==========================
  ldy #00000010 ; constant for mosi high
  ldx #4
@nibble_1:
  asl A         ; put bit into carry
  bcs @mosi_1_1
  stz PORTA     ; clock = 0, mosi = 0
  inc PORTA     ; pulse clock
  dec PORTA
  jmp @dec_1
@mosi_1_1:
  sty PORTA     ; clock = 0, mosi = 1
  inc PORTA     ; pulse clock
  dec PORTA
@dec_1:
  dex
  bne @nibble_1

  ;==========================
  ; Step 4.) 4 sync bits
  ;==========================
  stz PORTA
  ldx #4
@sync_bits_1:
  inc PORTA
  dec PORTA
  dex
  bne @sync_bits_1
 

  ;==========================
  ; Step 5.) second nibble
  ;==========================
  ldy #00000010 ; constant for mosi high
  ldx #4
@nibble_2:
  asl A         ; put bit into carry
  bcs @mosi_1_2
  stz PORTA     ; clock = 0, mosi = 0
  inc PORTA     ; pulse clock
  dec PORTA
  jmp @dec_2
@mosi_1_2:
  sty PORTA     ; clock = 0, mosi = 1
  inc PORTA     ; pulse clock
  dec PORTA
@dec_2:
  dex
  bne @nibble_2

  ;==========================
  ; Step 6.) 4 sync bits
  ;==========================
  stz PORTA
  ldx #4
@sync_bits_2:
  inc PORTA
  dec PORTA
  dex
  bne @sync_bits_2
  rts


and then I'm using it like this:

Code:
    lda #%11111111 ; PORT A to output
  sta DDRA

  ; Basic function mode
  lda #%00110000
  jsr lcd_spi_send
  jsr delay

  ; clear screen
  lda #%00000001
  jsr lcd_spi_send
  jsr delay

  ; entry mode set
  lda #%00000110
  jsr lcd_spi_send
  jsr delay

  ; display control
  lda #%00001100
  jsr lcd_spi_send
  jsr delay

  lda #%00000010
  jsr lcd_spi_send
  jsr delay


The data sheet said to wait 72 microseconds between commands, so I've added this delay code. Most likely overkill but it is what it is.

Code:
delay:
  ldy #$ff
  ldx #$ff
@loop:
  dex
  bne @loop
  dey
  bne @loop
  rts


I'm expecting to see a cursor or something, but there is no change in the display at all. I'm not really sure what to do to debug this. Do you guys see anything wrong in my code?


Top
 Profile  
Reply with quote  
PostPosted: Wed Mar 10, 2021 6:36 am 
Offline

Joined: Sat Mar 06, 2021 6:50 am
Posts: 5
Hi,

I think you are confusing some things.

When using SPI you need to know the following:
MOSI (Master Out Slave In) is the bit output from CPU to the LCD, MISO (Master IN Slave Out) is the bit input from the LCD to the CPU. CLK is the shift clock output to the display, CS is the chipselect for the LCD SPI port.
Bitbanging SPI isn't so difficult, here a code example (it's 6809 assembly, but must easy be portable to 6502 assembly).
This code is for sending 16 bit to a 7 segment display array controlled by SPI. PPORT is the I/O port I use (A simple 74574 latch). PORTBUF is a variable where the last status of pport is stored. D is a double accumulator containing A and B, in 6502 assembly you probably use psha and pshb for saving both accumulators.
Code:
write_word

        pshs    d                       ;Save registers A,B and X.
        pshs    x
        ldx     #16
wbloop1 lslb                            ;lsld.
        rola
        bcs     clkbit1
        bra     clkbit0
clkbit1 pshs    a
        lda     portbuf
        ora     #%00000001
        sta     pport
        sta     portbuf                 ;Bit is 1.
        puls    a
        bra     toggleclk
clkbit0 pshs    a
        lda     portbuf
        anda    #%11111110
        sta     pport
        sta     portbuf                 ;Bit is 0.
        puls    a

toggleclk

        pshs    a
        lda     portbuf
        ora     #%00000010              ;Toggle SPI clock.
        sta     pport
        anda    #%11111101
        sta     pport
        sta     portbuf
        puls    a
        leax    -1,x
        bne     wbloop1                 ;Send next bit of word.

        lda     portbuf                 ;Toggle CS line to clock.
        anda    #%11111011              ;the data into display latch.
        sta     pport
        ora     #%00000100
        sta     pport
        sta     portbuf
        puls    x
        puls    d
        rts


I hope this will help you. Unfortunataly I am not so in 6502 assembly yet. For the LCD probably you need to set CS to zero while sending the bits and rise it to one when all bits are send.

_________________
Greetings,

Nick


Top
 Profile  
Reply with quote  
PostPosted: Wed Mar 10, 2021 1:54 pm 
Offline

Joined: Thu Apr 14, 2016 2:13 pm
Posts: 9
nbrok wrote:
Hi,

I think you are confusing some things.

When using SPI you need to know the following:
MOSI (Master Out Slave In) is the bit output from CPU to the LCD, MISO (Master IN Slave Out) is the bit input from the LCD to the CPU. CLK is the shift clock output to the display, CS is the chipselect for the LCD SPI port.
Bitbanging SPI isn't so difficult, here a code example (it's 6809 assembly, but must easy be portable to 6502 assembly).
This code is for sending 16 bit to a 7 segment display array controlled by SPI. PPORT is the I/O port I use (A simple 74574 latch). PORTBUF is a variable where the last status of pport is stored. D is a double accumulator containing A and B, in 6502 assembly you probably use psha and pshb for saving both accumulators.

I hope this will help you. Unfortunataly I am not so in 6502 assembly yet. For the LCD probably you need to set CS to zero while sending the bits and rise it to one when all bits are send.

[/quote]

Thanks for the response. Could you possibly clarify on what I seem to be missing. Reading through your post, I feel like that was already my understanding. Is there something that I have wired incorrectly? This particular LCD only takes input in SPI which is why I’ve only hooked up MOSI and the clock. Did you see something wrong with the logic in my code?

Thanks.


Top
 Profile  
Reply with quote  
PostPosted: Wed Mar 10, 2021 2:53 pm 
Offline

Joined: Tue Sep 03, 2002 12:58 pm
Posts: 306
instantaphex wrote:
RW (MOSI) -> VIA PA0
E (CLOCK) -> VIA PA1


Is this correct? The code is expecting the clock on bit 0, not bit 1.

Clear needs a 1.6ms delay. Normally you'd read the status to see when it's ready for another command, but it looks like serial is write-only. You'll have to assume the worst case delays.

The flowcharts include a much longer (40ms) power-on delay, and different delays after each of the initialisation commands. This isn't an HD44780, but it looks like the interface is compatible with it. When I've had to deal with HD44780's I've always followed the flowcharts to the letter, and never had any trouble. Trying to take short-cuts has usually gone wrong.

Quote:
; display control
lda #%00001100
jsr lcd_spi_send


This will disable the cursor, so you shouldn't be expecting to see one. Change it to 00001110 (or 00001111 for blinking).


Top
 Profile  
Reply with quote  
PostPosted: Wed Mar 10, 2021 5:50 pm 
Offline

Joined: Thu Apr 14, 2016 2:13 pm
Posts: 9
Good catch on the wiring. I actually do have the clock connected to bit 0 on port a and MOSI connected to bit 1 on port A. That was a typo on my part. I suppose I should look closer into the delay. I'm not really sure what is the best way to delay for exactly 40ms, 1.6ms and 72 microseconds. I guess I'll need to do some calculations on my clock speed and cycle counts and come up with some amount of work that takes that amount of time. Is that how that is normally done?

Looks like you're correct about disabling the cursor. I'll try the byte you've provided tonight and see if there is any change.


Top
 Profile  
Reply with quote  
PostPosted: Wed Mar 10, 2021 6:02 pm 
Offline

Joined: Sat Mar 06, 2021 6:50 am
Posts: 5
My SPI display has in SPI the following lines: CS, CLK and MOSI. Without CS the display won't work. I assumed that it is the same as on your display. The delays in write only mode are needed as John West told already.
I hope after changing the instruction given by him and using the given delays it will work.

_________________
Greetings,

Nick


Top
 Profile  
Reply with quote  
PostPosted: Wed Mar 10, 2021 8:42 pm 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10837
Location: England
Welcome, instantaphex. Yes, normally for a delay you use a loop which counts down, or two nested loops can be better for a longer delay. Maybe this thread will help. (There's a wealth of experience in previous threads, so be sure to search.)


Top
 Profile  
Reply with quote  
PostPosted: Thu Mar 11, 2021 3:40 am 
Offline

Joined: Thu Apr 14, 2016 2:13 pm
Posts: 9
John West wrote:
This will disable the cursor, so you shouldn't be expecting to see one. Change it to 00001110 (or 00001111 for blinking).


Just tried this and saw a blinking cursor... thank you!! I've literally been at this like an idiot for 4 nights like an idiot writing the first bit of code for this machine that wasn't modified from a tutorial, desoldering headers from random things I had lying around, poking around with a multimeter and pulling my hair out that it didn't work. Looks like my delay code is long enough for the worst case scenario for the commands I've used so far. I really appreciate your help.


Top
 Profile  
Reply with quote  
PostPosted: Thu Mar 11, 2021 9:18 am 
Offline

Joined: Tue Sep 03, 2002 12:58 pm
Posts: 306
Glad it's working! And yes, your delay will be more than comfortably adequate, to the point of being far too slow for displaying graphics (somewhere over 300,000 cycles if I'm counting them right, so depending on the clock speed it'll be tens or hundreds of milliseconds). But that's OK. It's better to be slow but working than fast but not. It's a lot easier to make improvements when you can test each change.


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 9 posts ] 

All times are UTC


Who is online

Users browsing this forum: No registered users and 3 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to: