NHD-0216CW module using SPI - Question

Comments

9 comments

  • Paul_B

    Morning Fred,

    Below is some example code written for the Arduino  

    https://github.com/NewhavenDisplay/NHD_US2066

    Next, (IS, RE and SD) will need to be configured correctly in order for the controller to correctly identify which command the user wishes to execute. Take a look at the initialization sequence (File: NHD_US2066.cpp). The code is heavily commented and should clear up any questions.

    Please keep us posted on your progress!

    0
  • atlantaswl

    Hi Paul,
    Thanks for the assistance.  Unfortunately, no joy yet.  The code I wrote is below.  I did change a few of the commands, as I have a 2x16 display, and the code on the US2066 directory I think is for a 4-line display.  Also, I am using 5V digital logic, so I changed that command.  I think all else is the same.  The SPI is definitely sending least significant bit first, and the /CS line is going low before the data is transmitted.  I am using a board with 2n7000-based level shifters for clock, SDI, and /CS. /RES is tied to +5V.

    I also tried the initialization code at a much lower baud rate, without success.

    The following pins are tied to +5V:
    2,3,16
    The following lines are tied to 0V:
    1,4,5,6,10,11,12,13,14,17,18,19,20
    The following pins are connected to the SPI port on the Atmel processor, first passing through a level-shifter board
    7 - SPI clock
    8 - SPI data out from
    9 - Floating
    15 - /CS

    Code follows:

    display_command:
             push   temp ; save command
             cbi   VPORT2_OUT, oled_enable ; clear the bit so the display will listen
             ldi   temp, $1f ; LSB transmits first, so reverse command preamble byte
             rcall   sout ; output the command preamble
             pop   temp ; retrieve command
             mov   r17, temp ; save a copy
             ; command byte needs to be transmitted as
             ; D0 D1 D2 D3 0 0 0 0
             ; D4 D5 D6 D7 0 0 0 0
             ; the SPI UART is already configured to transmit
             ; LSB first, so all we need to do is to mask off
             ; the D0-D3, transmit, then shift left D4-D7 to
             ; D0-D3 and transmit.
             andi   temp, $0F
             rcall   sout
             mov   temp, r17   ; get the original byte back
             swap   temp      ; swap nibbles
             andi   temp, $0F
             rcall   sout
             sbi   VPORT2_OUT, oled_enable ; set bit so display will go deaf
             ret

    display_data:
             push   temp ; save data
             cbi   VPORT2_OUT, oled_enable ; clear the bit so the display will listen
             ldi   temp, $5f ; LSB transmits first, so reverse data preamble byte
             rcall   sout ; output the command preamble
             pop   temp ; retrieve data
             mov   r17, temp ; save a copy
             ; data byte needs to be transmitted as
             ; D0 D1 D2 D3 0 0 0 0
             ; D4 D5 D6 D7 0 0 0 0
             ; the SPI UART is already configured to transmit
             ; LSB first, so all we need to do is to mask off
             ; the D0-D3, transmit, then shirt left D4-D7 to
             ; D0-D3 and transmit.
             andi   temp, $0F
             rcall   sout
             mov   temp, r17   ; get the original byte back
             swap   temp
             andi   temp, $0F
             rcall   sout
             sbi   VPORT2_OUT, oled_enable ; set bit so display will go deaf
             ret

    init_display:
             ldi   temp, $2A ; //function set (extended command set)
             rcall    display_command
             ldi   temp, $71; //function selection A
                 rcall    display_command
             ldi   temp, $5c; // (0x5C) = enable regulator (5V I/O)
             rcall    display_data
             ldi   temp, $28; //function set (fundamental command set)
             rcall    display_command
             ldi   temp, $08; //display off, cursor off, blink off
             rcall    display_command
             ldi   temp, $2A; //function set (extended command set)
             rcall    display_command
             ldi   temp, $79; //OLED command set enabled
             rcall    display_command
             ldi   temp, $D5; //set display clock divide ratio/oscillator frequency
             rcall    display_command
             ldi   temp, $70; //set display clock divide ratio/oscillator frequency
             rcall    display_command
             ldi   temp, $78; //OLED command set disabled
             rcall    display_command
             ldi   temp, $08; //extended function set (2-lines)
             rcall    display_command
             ldi   temp, $06; //COM SEG direction
             rcall    display_command
             ldi   temp, $72; //function selection B
             rcall    display_command
             ldi   temp, $00; //ROM CGRAM selection
             rcall    display_data
             ldi   temp, $2A; //function set (extended command set)
             rcall    display_command
             ldi   temp, $79; //OLED command set enabled
             rcall    display_command
             ldi   temp, $DA; //set SEG pins hardware configuration
             rcall    display_command
             ldi   temp, $10; //set SEG pins hardware configuration
             rcall    display_command
             ldi   temp, $DC; //function selection C
             rcall    display_command
             ldi   temp, $10; //function selection C
             rcall    display_command
             ldi   temp, $81; //set contrast control
             rcall    display_command
             ldi   temp, $7F; //set contrast control
             rcall    display_command
             ldi   temp, $D9; //set phase length
             rcall    display_command
             ldi   temp, $F1; //set phase length
             rcall    display_command
             ldi   temp, $DB; //set VCOMH deselect level
             rcall    display_command
             ldi   temp, $40; //set VCOMH deselect level
             rcall    display_command
             ldi   temp, $78; //OLED command set disabled
             rcall    display_command
             ldi   temp, $28; //function set (fundamental command set)
             rcall    display_command
              ldi   temp, $01; //clear display
             rcall    display_command
             ldi   temp, $80; // SET dram ADDRESS TO 0X00
             rcall    display_command
             ldi   temp, $0F; //display ON
             rcall    display_command
             ldi   temp, $01
             rcall    display_command
             ldi   temp, $45
             rcall    display_data
             ret
    ;
    Would you please check the initialization code and let me know if this looks OK?  Again, your assistance is greatly appreciated.  This is the last build step for my new receiver.

    Regards,
    Fred
     
    « Last Edit: June 25, 2015, 10:13:44 AM by atlantaswl »
    0
  • atlantaswl

    Hi Paul,
    Thinking there might be an issue using the SPI on the Atmel processor, i rewrote the c code in the example program in AVR assembler, and would love to share the code with the forum, and the display is now working - almost.  The issue seems to be now the required delay for the various commands. Do you or someone on the forum have any guidelines on required delays for a few of the commands requiring such?  It seems as if a delay will be required if the /RES line is brought low and high, and the display on command seems to require quite a long delay (.1 seconds?)

    Thanks all for looking and sharing.

    Regards, Fred

    0
  • atlantaswl

    Here is the AVR assembler code.  The argument (command or data) is loaded into the temp register (r16), and either display_command or display_data is called.  VPORT2 is mapped to Port C and VPORT3 is mapped to Port A.  For those interested, once a port is mapped to a virtual port, individual bits within a port can be turned on (SBI) or turned off (CBI), without regard to what the other pins on that port are set to.

    ; registers used:
    ;   R16 will have the command or data byte already set by the caller
    ;   R17 is used to store the initial preamble (command or data)
    ;   R18 is used as a loop counter
    display_command:
             cbi      VPORT2_OUT, oled_enable ; clear bit so display will listen
             ldi      r17, $f8 ; command preamble
             ldi      r18, 8 ; times through
    dcommand1:   cbi      VPORT3_OUT, oled_clock ; clock goes low
             lsl      r17 ; high order bit shifted into Cary Condition status bit
             brcc           dcommand2 ; if Cary clear, bit is zero, output a zero
             sbi      VPORT3_OUT, oled_data ; data goes high
             rjmp           dcommand3
    dcommand2:   cbi      VPORT3_OUT, oled_data ; data goes low
    dcommand3:   sbi      VPORT3_OUT, oled_clock ; clock goes high
             dec      r18
             brne           dcommand1
    ; Preamble sent, now to send the command
             ldi      r18, 4 ; send only 4 bits at a time
    dcommand4:   cbi      VPORT3_OUT, oled_clock ; clock goes low
             lsr      temp ; LSB goes into CC
             brcc           dcommand5 ; if Carry bit clear, data bit is a 0
             sbi      VPORT3_OUT, oled_data ; data goes high
             rjmp           dcommand6
    dcommand5:   cbi      VPORT3_OUT, oled_data ; data gles low
    dcommand6:   sbi      VPORT3_OUT, oled_clock ; clock goes high
             dec      r18
             brne           dcommand4
             ; four bits have gone, need to send four 0's
             ldi      r18, 4
    dcommand7:   cbi      VPORT3_OUT, oled_clock ; clock goes low
             cbi      VPORT3_OUT, oled_data ; data goes low
             sbi      VPORT3_OUT, oled_clock ; clock goes high
             dec      r18
             brne          dcommand7
             ; now handle the upper 4 bits of the command
             ldi      r18, 4
    dcommand8:   cbi      VPORT3_OUT, oled_clock ; clock goes low
             lsr      temp
             brcc           dcommand9
             sbi      VPORT3_OUT, oled_data ; data goes high
             rjmp           dcommand10
    dcommand9:   cbi      VPORT3_OUT, oled_data ; data goes low
    dcommand10:   sbi      VPORT3_OUT, oled_clock ; clock goes high
             dec      r18
             brne           dcommand8
             ; last four bits have gone, need to send four 0's
             ldi      r18, 4
    dcommand11:   cbi      VPORT3_OUT, oled_clock ; clock goes low
             cbi      VPORT3_OUT, oled_data ; data goes low
             sbi      VPORT3_OUT, oled_clock ; clock goes high
             dec      r18
             brne           dcommand11
             sbi      VPORT2_OUT, oled_enable ; set bit so display will go deaf
             ret ; whew, done with the command!!!


    display_data:   cbi      VPORT2_OUT, oled_enable ; clear bit so display will listen
             ldi      r17, $fa ; command preamble
             ldi      r18, 8 ; times through
    ddata1:      cbi      VPORT3_OUT, oled_clock ; clock goes low
             lsl      r17 ; set CC based on highest remaining bit
             brcc           ddata2 ; bit is zero, output a zero
             sbi      VPORT3_OUT, oled_data ; data goes high
             rjmp           ddata3
    ddata2:      cbi      VPORT3_OUT, oled_data ; data goes low
    ddata3:      sbi      VPORT3_OUT, oled_clock ; clock goes high
             dec      r18
             brne           ddata1
    ; Preamble sent, now to send the command
             ldi      r18, 4 ; last 4 nibbles first
    ddata4:      cbi      VPORT3_OUT, oled_clock ; clock goes low
             lsr      temp ; LSB goes into CC
             brcc           ddata5 ; if CC clear, data goes low
             sbi      VPORT3_OUT, oled_data ; data goes high
             rjmp           ddata6
    ddata5:      cbi      VPORT3_OUT, oled_data ; data goes low
    ddata6:      sbi      VPORT3_OUT, oled_clock ; clock goes high
             dec      r18
             brne           ddata4
             ; four bits have gone, need to send four 0's
             ldi      r18, 4
    ddata7:      cbi      VPORT3_OUT, oled_clock ; clock goes low
             cbi      VPORT3_OUT, oled_data ; data goes low
             sbi      VPORT3_OUT, oled_clock ; clock goes high
             dec      r18
             brne           ddata7
             ; now handle the upper 4 bits of the data
             ldi      r18, 4
    ddata8:      cbi      VPORT3_OUT, oled_clock ; clock goes low
             lsr      temp
             brcc           ddata9
             sbi      VPORT3_OUT, oled_data ; data goes high
             rjmp           ddata10
    ddata9:      cbi      VPORT3_OUT, oled_data ; data goes low
    ddata10:           sbi      VPORT3_OUT, oled_clock ; clock goes high
             dec      r18
             brne           ddata8
             ; last four bits have gone, need to send four 0's
             ldi      r18, 4
    ddata11:           cbi      VPORT3_OUT, oled_clock ; clock goes low
             cbi      VPORT3_OUT, oled_data ; data goes low
             sbi      VPORT3_OUT, oled_clock ; clock goes high
             dec      r18
             brne           ddata11
             sbi      VPORT2_OUT, oled_enable ; set bit so display will go deaf
             rcall           display_delay ; 5 ms delay
             ret ; whew, done with the data!!!
    « Last Edit: June 27, 2015, 07:45:40 PM by atlantaswl »
    0
  • Paul_B

    Afternoon Fred,

    Thank you for sharing your code :)

    Regarding the delay for the /RES line, you will need to follow the sequence below:

    Power on
    Wait at least 1ms
    Bring reset low
    Wait at least 3us
    Bring reset high
    Wait at least 200ms before sending commands

    Finally, the .1 second delay listed in the example code was used during mock-up and is in no way the min value. For specific delay times please refer to the US2066 datasheet:

    https://newhavendisplay.com/content/app_notes/US2066.pdf 

    Hope this helps!

    0
  • neiljhs

    Hello Fred,

    I am currently in a situation very similar to the one you were in a couple of weeks back. I likewise am working with a screen that has a US2066 driver and am looking to use SPI for communication. The two differences between our setups is that my screen is 2x20 (instead of your 2x16) and I am running it in 'Low Voltage' mode (instead of your 5v mode).

    I've cross compared my setup code with yours and have identified two points of deviation. One pertains to 3v versus 5v mode and thus is to be expected. The other difference I couldn't resolve/figure out what you were setting. I've highlighted the one in question below.

    Quote
    init_display:
             ldi   temp, $2A ; //function set (extended command set)
             rcall    display_command
             ldi   temp, $71; //function selection A
                 rcall    display_command
             ldi   temp, $5c; // (0x5C) = enable regulator (5V I/O)
             rcall    display_data
             ldi   temp, $28; //function set (fundamental command set)
             rcall    display_command
             ldi   temp, $08; //display off, cursor off, blink off
             rcall    display_command
             ldi   temp, $2A; //function set (extended command set)
             rcall    display_command
             ldi   temp, $79; //OLED command set enabled
             rcall    display_command
             ldi   temp, $D5; //set display clock divide ratio/oscillator frequency
             rcall    display_command
             ldi   temp, $70; //set display clock divide ratio/oscillator frequency
             rcall    display_command
             ldi   temp, $78; //OLED command set disabled
             rcall    display_command
             ldi   temp, $08; //extended function set (2-lines)
             rcall    display_command
             ldi   temp, $06; //COM SEG direction
             rcall    display_command
             ldi   temp, $72; //function selection B
             rcall    display_command
             ldi   temp, $00; //ROM CGRAM selection
             rcall    display_data
             ldi   temp, $2A; //function set (extended command set)
             rcall    display_command
             ldi   temp, $79; //OLED command set enabled
             rcall    display_command
             ldi   temp, $DA; //set SEG pins hardware configuration
             rcall    display_command
             ldi   temp, $10; //set SEG pins hardware configuration
             rcall    display_command
             ldi   temp, $DC; //function selection C
             rcall    display_command
             ldi   temp, $10; //function selection C
             rcall    display_command
             ldi   temp, $81; //set contrast control
             rcall    display_command
             ldi   temp, $7F; //set contrast control
             rcall    display_command
             ldi   temp, $D9; //set phase length
             rcall    display_command
             ldi   temp, $F1; //set phase length
             rcall    display_command
             ldi   temp, $DB; //set VCOMH deselect level
             rcall    display_command
             ldi   temp, $40; //set VCOMH deselect level
             rcall    display_command
             ldi   temp, $78; //OLED command set disabled
             rcall    display_command
             ldi   temp, $28; //function set (fundamental command set)
             rcall    display_command
              ldi   temp, $01; //clear display
             rcall    display_command
             ldi   temp, $80; // SET dram ADDRESS TO 0X00
             rcall    display_command
             ldi   temp, $0F; //display ON
             rcall    display_command
             ldi   temp, $01
             rcall    display_command
             ldi   temp, $45
             rcall    display_data
             ret
    ;
     


    Looking at the driver datasheet I could not resolve what the commands was (it seemed to deviate from the table). For reference I'm looking at the second line in the row dedicated to the 'command' named 'Function Selection C' on page 32 of the driver's datasheet

    https://newhavendisplay.com/content/app_notes/US2066.pdf 


    I am wondering if you changed this - or any other lines in your initialization - in between your posting it and your getting the OLED screen to work?

    Secondly, it sounds as though you ended up placing delays at certain points in your code to help it function. In addition to the delays related to 'reset', did you also decide to place a blanket delay after each command/data byte is sent?

    Thank you very much Fred, I greatly appreciate any insight you can provide!

    0
  • atlantaswl

    Hi,
    I found it expeditious to change the interface to parallel.  Uses many more pins, but the display works perfectly now.  One thing I will say, is that by verifying the command has completed by reading the BF before sending the next command certainly removed any issues from the display initialization.  If I had implemented the logic to check the BF flag in serial mode, I believe I would not have had a problem initializing the display.  But, can't be certain, since I did not implement that code in SPI mode. Since I got this working using the 8080 8-bit interface, I will not be revisiting initializing and using this chip using SPI.  The controller is the Atmel XMEGA 256A3BU developer kit.  Port C (J1) is used for the parallel data, and using SPI on port E (J4) for communication to two DDS modules and a 16 port relay board.

    Good luck,
    Fred

    0
  • atlantaswl

    Thought I share the assembler code using the 8080 8-bit interface.

    init_display:
             ldi      temp, RES
             sts      PORTA_OUTCLR, temp
             rcall   display_delay
             sts      PORTA_OUTSET, temp

             ldi      temp, $2A ; function set (extended command set)
             rcall   display_command
             ldi      temp, $71; //function selection A
              rcall   display_command
             ldi      temp, $00; // data($00) = disable internal VDD regulator (2.8V I/O). data(0x5C) = enable regulator (5V I/O)
             rcall   display_data
             ldi      temp, $28; //function set (fundamental command set)
             rcall   display_command
             ldi      temp, $08; //display off, cursor off, blink off
             rcall   display_command
             ldi      temp, $2A; //function set (extended command set)
             rcall   display_command
             ldi      temp, $79; //OLED command set enabled
             rcall   display_command
             ldi      temp, $D5; //set display clock divide ratio/oscillator frequency
             rcall   display_command
             ldi      temp, $70; //set display clock divide ratio/oscillator frequency
             rcall   display_command
             ldi      temp, $78; //OLED command set disabled
             rcall   display_command
             ldi      temp, $08; //extended function set (2-lines)
             rcall   display_command
             ldi      temp, $06; //COM SEG direction
             rcall   display_command
             ldi      temp, $72; //function selection B
             rcall   display_command
             ldi      temp, $00; //ROM CGRAM selection
             rcall   display_data
             ldi      temp, $2A; //function set (extended command set)
             rcall   display_command
             ldi      temp, $79; //OLED command set enabled
             rcall   display_command
             ldi      temp, $DA; //set SEG pins hardware configuration
             rcall   display_command
             ldi      temp, $10; //set SEG pins hardware configuration
             rcall   display_command
             ldi      temp, $DC; //function selection C
             rcall   display_command
             ldi      temp, $00; //function selection C
             rcall   display_command
             ldi      temp, $81; //set contrast control
             rcall   display_command
             ldi      temp, $08; //set contrast control
             rcall   display_command
             ldi      temp, $D9; //set phase length
             rcall   display_command
             ldi      temp, $F1; //set phase length
             rcall   display_command
             ldi      temp, $DB; //set VCOMH deselect level
             rcall   display_command
             ldi      temp, $40; //set VCOMH deselect level
             rcall   display_command
             ldi      temp, $78; //OLED command set disabled
             rcall   display_command
             ldi      temp, $28; //function set (fundamental command set)
             rcall   display_command
              ldi      temp, $01; //clear display
             rcall   display_command
             ldi      temp, $80 ; set DDRAM ADDR to 0x00
             rcall   display_command
             ldi      temp, $0C; //display ON
             rcall   display_command
             sbi      GPIO_GPIOR1, display_ok   ; let the world know we are now initialized
             ret


    display_delay:  ; a counter/timer interrupts every 6 ms and increments dlycnt.
             clr      dlycnt
    display_delay1:
             cpi      dlycnt,1 ; approx 6 ms
             brne           display_delay1
             ret

    check_ready:   ; We need to ensure the last command has completed
             push    temp ; save command on stack
             ldi      temp, DC
             sts      PORTA_OUTCLR, temp ; This is a command
             ldi      temp, CS
             sts      PORTA_OUTCLR, temp ; Select the display
             ldi      temp, RD
             sts      PORTA_OUTCLR, temp ; command to read data to get the BUSY_FLAG
             lds      temp, PORTA_IN ; This is the first (dummy) byte to synchronize the display processor with this one
    tst_again:   ldi      temp, RD
             sts      PORTA_OUTSET, temp ; bring RD high
             sts      PORTA_OUTCLR, temp ; bring RD line low to read the busy flag
             lds      temp, PORTA_IN ; get the data byte
             sbrc         temp, BUSY
             rjmp           tst_again
             ldi      temp, RD
             sts      PORTA_OUTSET, temp ; reset RD line to high, we are done reading
             ldi      temp, CS
             sts      PORTA_OUTSET, temp ; reset CS line to high
             pop      temp ; restore command, we are done
             ret
             
    display_command:
             rcall           check_ready ; see if we are not busy for the next command
             ; first check to make sure last command is finished

             push    temp ; save the command on the stack
             ldi      temp, DC
             sts      PORTA_OUTCLR, temp ; Bring D/C line to low for a command byte
             ldi      temp, CS
             sts      PORTA_OUTCLR, temp ; Bring /CS line to low
             ldi      temp, WR
             sts      PORTA_OUTCLR, temp ; Bring /WR line to low to write
             pop      temp ; retrieve command from stack
             sts      PORTC_OUT, temp ; set all 8 bits of PORTC
             ldi      temp, WR
             sts      PORTA_OUTSET, temp ; Bring WR line to high
             ;      let DC remain where it is, but end command by resetting /CS
             ldi      temp, CS
             sts      PORTA_OUTSET, temp
             ; we are now done
             ret

    display_data:
             push           temp ; save the command on the stack
             ldi      temp, DC
             sts      PORTA_OUTSET, temp ; Bring D/C line to high for a data byte
             ldi      temp, CS
             sts      PORTA_OUTCLR, temp ; Bring /CS line to low
             ldi      temp, WR
             sts      PORTA_OUTCLR, temp ; Bring /WR line to low to write
             pop      temp ; retrieve command from stack
             sts      PORTC_OUT, temp ; set all 8 bits of PORTC
             ldi      temp, WR
             sts      PORTA_OUTSET, temp ; Bring WR line to high
             ;      let DC remain where it is, but end command by resetting /CS
             ldi      temp, CS
             sts      PORTA_OUTSET, temp
             ; we are now done
             ret
    0
  • Paul_B

    Hi Fred,

    Thank you for sharing! :)

    0

Please sign in to leave a comment.