# Struggling to understand LTM-8522HR data transmission

#### JohnInTX

Joined Jun 26, 2012
4,629
Not sure if this deserves an entire thread of its own so I will ask here, CKP sets the clock idle state high or low and CKE sets the data transmission from clk idle->active or active->idle right?
Right.
In this case, CKP=0 so SCK is normally 0 (idle).
CKE = 1 which makes the 'transmission of data' when SCK goes active->idle' i.e. SDO changes on the falling edge of SCL. That means that the data is stable on the rising edge of SCL which is what the display wants.
In Fig 24-6 of the datasheet, the 3rd line of clock pulses is CKP=0, CKE=1. Draw a line down from each edge of SCL and you'll see that SDO is stable on SCL rising edge and changes at the falling edge with that setup.

#### odm4286

Joined Sep 20, 2009
265
Thanks, I finally got it. I just bit banged it like JohnlnTX. SPI was giving me more trouble than it was worth, one thing I still don't quite understand is what Data Enable does. When I bought this display I thought it was an SPI part with an inverted chip select, I have the feeling that data enable works a bit differently. Can anyone elaborate?

https://photos.app.goo.gl/vzawW1KZJDw7XDQw6

#### JohnInTX

Joined Jun 26, 2012
4,629
DATA ENABLE just forces the shift register data to ‘0’ when it’s high. That’s it. The shift reg is cleared when the previous data is latched. Raising DE after that just prevents any ones being shifted in if you pulse the clock. (The first ‘1’ is the start bit, yes?). Deselecting DATA ENABLE Is different than CE/ in an SPI interface as it does not re-sync any logic or prevent shifting - it just keeps you from shifting any ‘1’s in. The reason that’s important is that after shifting in the first ‘1’ (making it the so called START bit), 35 clocks later, that 1 will arrive at the end of the SR and latch the 34 data bits following it onto the display, regardless of what DATA ENABLE does after that first ‘1’. Note that DE needs to be low to shift any ‘1’s in but once you get that first one in you’re committed to an update. There’s no other way to reset the shifter than to propagate that first ‘1’ to the end of the SR by clocking.

Did you try the code in #19?

Last edited:

#### odm4286

Joined Sep 20, 2009
265
DATA ENABLE just forces the shift register data to ‘0’ when it’s high. That’s it. The shift reg is cleared when the previous data is latched. Raising DE after that just prevents any ones being shifted in if you pulse the clock. (The first ‘1’ is the start bit, yes?). Deselecting DATA ENABLE Is different than CE/ in an SPI interface as it does not re-sync any logic or prevent shifting - it just keeps you from shifting any ‘1’s in. The reason that’s important is that after shifting in the first ‘1’ (making it the so called START bit), 35 clocks later, that 1 will arrive at the end of the SR and latch the 34 data bits following it onto the display, regardless of what DATA ENABLE does after that first ‘1’. Note that DE needs to be low to shift any ‘1’s in but once you get that first one in you’re committed to an update. There’s no other way to reset the shifter than to propagate that first ‘1’ to the end of the SR by clocking.

Did you try the code in #19?
Sorry for the late reply. I did try that code, didn't work as expected. Bitbanging worked and gave me more control too.
Code:
void update_display(unsigned char data[])
{
start_bit();                          // clock out the start bit
clock_byte(data[0]);           // Third Digit
clock_byte(data[1]);           // Second Digit
clock_byte(data[2]);           // First Digit
DATA_1 = 0;                     // I don't care about the display I/O pins yet
for (int i = 0; i < 10; i++)         // just clocking out the rest of the bits
clock_it();
DATA_1 = 1;                    // stop bit
clock_it();                         // clock in the stop bit
//DATA_ENABLE_1 = 1;
}

void clock_byte(unsigned char data)
{
for (int i = 0; i < 8; i++)
{
#ifdef REAL_DATA
DATA_1 = 1 & data;      // AND against data
data = data >> 1;           // shift over 1
#endif
#ifndef REAL_DATA
DATA_1 = 1;
#endif
clock_it();
}
}

void clock_it(void)
{
CLK_1 = 1;
__delay_us(2);
CLK_1 = 0;
}

void start_bit(void)
{
DATA_ENABLE_1 = 1;              // enable comms
DATA_1 = 0;
CLK_1 = 0;
DATA_ENABLE_1 = 0;
DATA_1 = 1;                     // start bit 1 of 36
clock_it();
}

#### JohnInTX

Joined Jun 26, 2012
4,629
@odm4286
I managed to bag a couple of the displays at Jameco while traveling to the Bay Area. When I get back, I’ll check out the the SPI code and see what’s wrong. It will be nice to know.

Last edited:

#### JohnInTX

Joined Jun 26, 2012
4,629
@odm4286
The code I posted works fine using SPI. I did notice that when ENABLE was floating, the display picked up noise. Make sure that your port is configured as a digital output and be sure to write to LATx, not PORTx. I added a section to blink the display on and off to verify that it was working with repeated data transmissions.

BTW, I can't tell from the video clip but be sure that VLED is no greater than 3.5V. I made that mistake when I used these way back then.
Hope that helps.
C:
/**
Generated Main Source File
File Name:
main.c
Blinks 12.3 at 1 sec intervals
*/

#include "mcc_generated_files/mcc.h"

// Data format for display:
// Byte 0: Sabcdefg Start and segments for digit 1 (MSDig)
// Byte 1: Dabcdefg DP for digit1, segs for digit 2
// Byte 2: Dabcdefg DP for digit 2, segs for digit 3
// Byte 4: Dooooooo DP for digit 3, output pins 4-10
// Byte 5: ooo00000 output pins 11-13, remainder of bits MUST be 0

// Segment patterns that show 12.3 on display and PIN13 high (hopefully)
uint8_t segs[5] = {0b10110000, 0b01101101, 0b11111001, 0x00,0b00100000};
uint8_t Zsegs[5]= {0x80,0,0,0,0}; // start bit and all segs off

// Sends byte to SPI, waits for BF indicating byte has been transmitted.
// Can pre-check BF if desired and/or read SSP1BUF to prevent WCOL.
void sendSPI(uint8_t c){
SSP1BUF = c;
while(SSP1STATbits.BF == 0);
//SSP1BUF;  // opt. read SSP1BUF to avoid WCOL
}

void main(void)
{
uint8_t k;
// initialize the device
SYSTEM_Initialize();  // MCC init IO

// Manual SPI init:
RC7PPS = 0b10001;  // using RC7 as SDO.
RB6PPS = 0b10000;  // using RB6 as CLK
SSP1CONbits.SSPM = 0b0010;  // SPI master mode with a clock of Fosc/64
SSP1CONbits.CKP = 0;  // clock idle is 0
SSP1STATbits.CKE = 1;  // clocks data on rising edge of clock
SSP1CON1bits.SSPEN = 1;
SSPOV = 0;

while (1)
{
// show 12.3
ENABLE__SetLow();  // my ENABLE is RC4
for (k=0; k < 5; k++){  // send segment image
sendSPI(segs[k]);
}
ENABLE__SetHigh();
__delay_ms(1000);

// All off

ENABLE__SetLow();
for (k=0; k < 5; k++){  // send segment image
sendSPI(Zsegs[k]);
}
ENABLE__SetHigh();
__delay_ms(1000);

}// while
}// main

#### odm4286

Joined Sep 20, 2009
265
@odm4286
The code I posted works fine using SPI. I did notice that when ENABLE was floating, the display picked up noise. Make sure that your port is configured as a digital output and be sure to write to LATx, not PORTx. I added a section to blink the display on and off to verify that it was working with repeated data transmissions.

BTW, I can't tell from the video clip but be sure that VLED is no greater than 3.5V. I made that mistake when I used these way back then.
Hope that helps.
Thanks for checking in again, I think I did the exact opposite when dealing with digital I/O. Reading from LATx and writing to PORTx. I could be wrong, still learning, but I remember hearing you never write to LATx and always to PORTx. Also, my VLED was about 2.5 but the brightness was turned all the way up. Still not too sure how that part of the display works, the datasheet circuit shows what looks like a poor attempt at a voltage divider. Can you clear that up for me?

https://photos.app.goo.gl/n3H3aUoWoWN9Z5Bq9

#### JohnInTX

Joined Jun 26, 2012
4,629
Thanks for checking in again, I think I did the exact opposite when dealing with digital I/O. Reading from LATx and writing to PORTx. I could be wrong, still learning, but I remember hearing you never write to LATx and always to PORTx. Also, my VLED was about 2.5 but the brightness was turned all the way up. Still not too sure how that part of the display works, the datasheet circuit shows what looks like a poor attempt at a voltage divider. Can you clear that up for me?

https://photos.app.goo.gl/n3H3aUoWoWN9Z5Bq9
You always write to LATx and read from PORTx. LATx accesses the output flip-flops and PORTx accesses the pin logic level for inputs. Likewise, you always do a bsf,bcf etc to LATx, never to PORTx. Note that you can specify PORTx for both reads and writes but that can lead to problems. LATx for writes. PORTx for reads - always. That will keep you out of trouble.

Brightness and VLED are different things:

VLED supplies the LED current drivers. It is unfortunate that it it is not internally derived from VDD but that's the way it is - this is an old part. I used an LM317T with R1=240 and R2=390 for about 3.3VLED. The segments are driven by constant-current generators whose current is a function of the current injected into pin 18, the 'BRIGHTNESS' pin. They get their power from VLED, not VDD, so VLED has to be able to supply enough current.

The 'BRIGHTNESS' control shown in the datasheet is a variable resistor, not a voltage divider. It varies the current injected into pin 18. The LED current is approx 25 times this current and there is an internal series resistance of 400 ohms. I used a 9.1K resistor from Vdd (+5V) to pin 18. My LED segment current is thus:
25 * (5v/(9100+400)) ~= 13ma/segment.

On yours, connecting directly to +5V gives:
25 * (5V/400) = 31.25ma/segment.

Hope that helps!

Last edited:

#### odm4286

Joined Sep 20, 2009
265
You always write to LATx and read from PORTx. LATx accesses the output flip-flops and PORTx accesses the pin logic level for inputs. Likewise, you always do a bsf,bcf etc to LATx, never to PORTx. Note that you can specify PORTx for both reads and writes but that can lead to problems. LATx for writes. PORTx for reads - always. That will keep you out of trouble.

Brightness and VLED are different things:

VLED supplies the LED current drivers. It is unfortunate that it it is not internally derived from VDD but that's the way it is - this is an old part. I used an LM317T with R1=240 and R2=390 for about 3.3VLED. The segments are driven by constant-current generators whose current is a function of the current injected into pin 18, the 'BRIGHTNESS' pin. They get their power from VLED, not VDD, so VLED has to be able to supply enough current.

The 'BRIGHTNESS' control shown in the datasheet is a variable resistor, not a voltage divider. It varies the current injected into pin 18. The LED current is approx 25 times this current and there is an internal series resistance of 400 ohms. I used a 9.1K resistor from Vdd (+5V) to pin 18. My LED segment current is thus:
25 * (5v/(9100+400)) ~= 13ma/segment.

On yours, connecting directly to +5V gives:
25 * (5V/400) = 31.25ma/segment.

Hope that helps!
Thanks a ton. Now that I know that pin 18 relies on current input and not voltage input things are much clearer.

#### JohnInTX

Joined Jun 26, 2012
4,629
Happy to help!
As an old favorite math teacher used to say "These things are well known to those who know them well."