# Struggling to understand LTM-8522HR data transmission

#### odm4286

Joined Sep 20, 2009
265
Hello all,

I'm trying to wrap my head around the data transmission routine for this display. Its a 36 bit register, so I'm assuming I need to bit bang this? I had hoped I could use SPI for more than just the clock. Any guidance would help tremendously, currently, I'm using a PIC16F1619 and the SDO pin. I can get the display to change, but there isn't any rhyme or reason to it just yet. Hopefully, this is a coherent post its getting pretty late.

#### Papabravo

Joined Feb 24, 2006
17,583
Hello all,

I'm trying to wrap my head around the data transmission routine for this display. Its a 36 bit register, so I'm assuming I need to bit bang this? I had hoped I could use SPI for more than just the clock. Any guidance would help tremendously, currently, I'm using a PIC16F1619 and the SDO pin. I can get the display to change, but there isn't any rhyme or reason to it just yet. Hopefully, this is a coherent post its getting pretty late.
So you only have to transfer data in one direction and there is no requirement to read data back. It is about as simple as it can can get. The only way to use the SPI hardware would be to control the data enable in the middle of a byte. I really don't think you want to do that.

#### odm4286

Joined Sep 20, 2009
265
Thanks PB, I've never written code to do this before so it seems a little intimidating. Here is some pseudocode, can you tell me if I'm on the right track?
Code:
If !i
Data enable high
Delay 1us
Enable change of state interrupt
Data enable low
Interrupt on clock pin falling edge
Set transmit flag high
If transmit flag && i < 35
SDO = data & 1
Data = data >> 1
Transmit flag low
++i
Else
Disable change of state interrupt
i = 0
Almost seems easier to just use SPI, count the number of falling edges, and pull data enable high after the 36 bit. Not sure how the chip would respond to that.

#### JohnInTX

Joined Jun 26, 2012
4,628
Here's the datasheet for the display. It wants you to shift 35 bits of data plus one extra bit that causes the internal shift register to latch. That will be a problem with SPI since SPI always sends 8 bit bytes - you'll get out of sync.
Just bit-bang it. You don't need interrupts.
Maintain your data into 5 bytes (SegsOut)
Code:
Send the first clock (START)
For 35 bits:
Set the data out to the current bit in SegsOut
Toggle the clock output
Shift the  SegsOut register once
Toggle the clock output once more to latch the display.
Note that the current data bit can be the LSbit or bit35 since the PIC can extract bits easily:
Code:
btfsc SegsOut, data_bit ;echo current data bit to output
bsf    LATx, data_out
btfss SegsOut, data_bit
bcf   LATx,data_out

bsf  LATx,clock_out ; clock it into the display
; shift the register while the clock is high for some clock time
bcf LATx,clock_out
Each segment maps to one bit in the 5 byte SegsOut register. Once you have your internal data mapped to those segments, the shift direction will become apparent.

EDIT: looking at Table 1 in the datasheet, each digit is nicely mapped to 8 bit bytes with the rest of the bits mapped to extra output pins on the display - nice. So if your 5 byte SegsOut register is right-justified, each byte will be mapped bit7=DP -> bit0 = segmentA. SegsOut will look like this MSbyte to LSbyte:
xxxxxxOO OOOOOO Pgfedcba Pgfedcba Pgfedcba
where O is an output, P is a decimal point, a-g are digit segments in digits 3->1

The LSbit in red is what you sample each time to set the data output bit. Right-shift the whole 5 bytes for each subsequent data bit.

Note that in line 6 above, shifting the register between clock=1 and clock=0 is a cheap way to get the clock width necessary assuming that the time it takes to shift is longer than the min. clock time.

Finally, note that SegsOut is a buffer register that gets clobbered when you update the display necessitating that you regenerate it each time, including the extra output bits. Another, perhaps better, way to do it is to maintain the 5 bytes as your data then copy one at a time to a one byte output register (WREG would do). It would take a little more code but less RAM. I like that better now that I think about it but too lazy to update the post. Hopefully you get the general idea.

Have fun!

#### Attachments

• 90.9 KB Views: 9
Last edited:

#### odm4286

Joined Sep 20, 2009
265
Thanks a lot. I didn't even think to manually drive the clock too!

#### nsaspook

Joined Aug 27, 2009
9,081
Can't you just send 5 bytes with the first byte being 00001xxx where xxx will be 3 of the 35 bits with 32 remaining?
My read of the datasheets says you need the start bit AND 36 clocks after the start bit to load the register.

#### odm4286

Joined Sep 20, 2009
265
Can't you just send 5 bytes with the first byte being 00001xxx where xxx will be 3 of the 35 bits with 32 remaining?
My read of the datasheets says you need the start bit AND 36 clocks after the start bit to load the register.
Hmmm I hope so, right now I'm struggling to get the timing right. It is tough to always fall in sync with the clock per the datasheet

#### odm4286

Joined Sep 20, 2009
265
I'm still stuck. I'm wondering about data enable now. I toggle that high for about 1µS and then pull it low, is that correct? Also, do I need to do this for each bit or just once per 36 bits.

Last edited:

#### nsaspook

Joined Aug 27, 2009
9,081
I'm still stuck. I'm wondering about data enable now. I toggle that high for about 1µS and then pull it low, is that correct? Also, do I need to do this for each bit or just once per 36 bits.
The data enable line (CS) typically stays low for the entire (all bytes 5/bits 40 for 8 bit hardware transfers with null bits before the start bit) SPI data transfer to a device. This device needs at least 100ns delay after the enable line is low before data starts.

A typical device that uses 8 bit transfers with a data Start bit for a non-byte sized data format.

Last edited:

#### odm4286

Joined Sep 20, 2009
265
Thank you for all the help. nsaspook, I don't think this is an SPI chip. SPI doesn't seem to work with it, the data enable isn't a normal chip select bar pin. I took some pictures of what I have so far and some code. I'm hoping someone can PLEASE point me in the right direction, this is driving me crazy. The only difference I notice between the datasheet and what I get on my scope is the clock duty cycle, I don't think that matters but I could be wrong. Any tips?

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

Here is my routine...right now I'm just trying to turn on every segment

Code:
// Future bit-bang routine
while(1)
{
DATA_ENABLE_1 = 1;
__delay_us(1);
DATA_ENABLE_1 = 0;
for(int i = 0; i < 36; ++i)
{
CLK_1 = 0;
__delay_us(2);
DATA_1 = 1;
CLK_1 = 1;
__delay_us(2);
CLK_1 = 0;
DATA_1 = 0;
}
}
Here is a simpler routine that gets better results but still...doesn't drive the display correctly.

Code:
// Future bit-bang routine
while(1)
{
DATA_ENABLE_1 = 1;
DATA_ENABLE_1 = 0;
__delay_us(1);
for(int i = 0; i < 36; ++i)
{
DATA_1 = 1;
CLK_1 = 1;
__delay_us(1);
CLK_1 = 0;
DATA_1 = 0;

}
}

Last edited:

#### odm4286

Joined Sep 20, 2009
265
Another try with SPI, still no good. Excuse the sloppy code, just trying to get this thing to work

C:
// Setting up SPI.
SSP1CONbits.SSPM = 0b0010;     // SPI master mode with a clock of Fosc/64 in this case 125Khz
SSP1CONbits.CKP = 1;
SSP1STATbits.CKE = 0;
RC7PPS = 0b10001;               // using RC7 as SDO.
RB6PPS = 0b10000;                 // using RB6 as CLK
SSP1CON1bits.SSPEN = 1;         // enable SPI
SSPOV = 0;
PORTCbits.RC6 = 0;                 // set ss bar low
SSP1STATbits.SMP = 1;           // i don't think i need this, leaving it for now

while(1){
DATA_ENABLE_1 = 1;
__delay_us(1);
DATA_ENABLE_1 = 0;
while(1)
{
SSP1BUF = 0b00001111;
SSP1BUF = 255;
SSP1BUF = 255;
SSP1BUF = 255;
SSP1BUF = 255;
break;
}
}

Last edited by a moderator:

#### JohnInTX

Joined Jun 26, 2012
4,628
I've used this display before and I think the extra clocks in SPI are problematic, but I bit banged it in assembler. I would bit bang it in C too. YMMV.
Try this:
C:
// Turns on all segments.
// Note: IO functions are macros generated by MCC.
#include <xc.h>

void clockIt(void){  // issue one clock pulse
CLOCK_SetHigh();
__delay_us(1);
CLOCK_SetLow();
}

void main(void)
{
uint8_t   k;
initIO();   // init system etc. Make sure that initial values are valid during initIO:
ENABLE__SetHigh();  // initial values for display
CLOCK_SetLow();
DATAO_SetLow();

while(1){
ENABLE__SetLow();
DATAO_SetHigh();  // send start bit
clockIt();
__delay_us(5);  // space for scope

for (k = 0; k < 34; k++){ // send 34 data bits 3*8bit digits + 10 Output Bits
DATAO_SetHigh();
clockIt();
__delay_us(2);
}

__delay_us(5);  // space for scope
DATAO_SetLow();  // drop data to see
clockIt();  // send latch clock

ENABLE__SetHigh();  // disable
__delay_ms(20);   // wait for repeat

}// while
}// main
YELOW : ENABLE/
GREEN: CLOCK
BLUE: DATA

Good luck!

Last edited:

#### nsaspook

Joined Aug 27, 2009
9,081
SPI is just a shift register interface with adjustable modes to match device data and clock edge requirements.
Another try with SPI, still no good. Excuse the sloppy code, just trying to get this thing to work

Code:
// Setting up SPI.
SSP1CONbits.SSPM = 0b0010;     // SPI master mode with a clock of Fosc/64 in this case 125Khz
SSP1CONbits.CKP = 1;
SSP1STATbits.CKE = 0;
RC7PPS = 0b10001;               // using RC7 as SDO.
RB6PPS = 0b10000;                 // using RB6 as CLK
SSP1CON1bits.SSPEN = 1;         // enable SPI
SSPOV = 0;
PORTCbits.RC6 = 0;                 // set ss bar low
SSP1STATbits.SMP = 1;           // i don't think i need this, leaving it for now

while(1){
DATA_ENABLE_1 = 1;
__delay_us(1);
DATA_ENABLE_1 = 0;
while(1)
{
SSP1BUF = 0b00001111;
SSP1BUF = 255;
SSP1BUF = 255;
SSP1BUF = 255;
SSP1BUF = 255;
break;
}
}
Can you sync the 1st channel scope on the rising edge of the first data byte with the second channel on the clock for a picture to show us. It looks like you are using mode 3, have you tried mode 0.
0 (or 0,0) CKP 0, CKE 1

#### odm4286

Joined Sep 20, 2009
265
SPI is just a shift register interface with adjustable modes to match device data and clock edge requirements.

Can you sync the 1st channel scope on the rising edge of the first data byte with the second channel on the clock for a picture to show us. It looks like you are using mode 3, have you tried mode 0.
0 (or 0,0) CKP 0, CKE 1
I can check that tomorrow, SPI would be MUCHHHH easier to code for if the display works with it. For now JohnlnTX your method got all the segments to turn on. Enough progress for me tonight. Thanks again!

#### JohnInTX

Joined Jun 26, 2012
4,628
Thinkin' more on @nsaspook 's SPI implementation. I guess I don't see why it wouldn't work with these caveats:

You'd want to make the start bit part of the byte-oriented SPI transmission. That would push the DP for digit n into digit n+1. Not a big deal.
You'd have to make sure all bits after the data were 0. The shift register / latch /clear works by starting with all zeros. The action happens when the '1' start bit gets shifted all the way through the shift reg and it sees that first '1'.
The ENABLE doesn't init the shift register, it just locks the data to 0 so that the first 'enabled' bit is the START bit IIRC. That means that all bits past the latch bit must be 0 until the START of the next data image.

But yeah, SPI maybe..

Last edited:

#### JohnInTX

Joined Jun 26, 2012
4,628
@odm4286
One thing wrong with your SPI code above is that you are not waiting for a byte transmit to be completed before writing the next byte. You need to poll SSPxSTAT,BF or SPIxIF to see when the previous byte has been shifted out and you can load another. If you poll SSPSTAT,BF (received a character in response to a master TX) I think you need to read and discard the data read in SSPxBUF to clear that flag (IIRC).

#### odm4286

Joined Sep 20, 2009
265
Thinkin' more on @nsaspook 's SPI implementation. I guess I don't see why it wouldn't work with these caveats:

You'd want to make the start bit part of the byte-oriented SPI transmission. That would push the DP for digit n into digit n+1. Not a big deal.
You'd have to make sure all bits after the data were 0. The shift register / latch /clear works by starting with all zeros. The action happens when the '1' start bit gets shifted all the way through the shift reg and it sees that first '1'.
The ENABLE doesn't init the shift register, it just locks the data to 0 so that the first 'enabled' bit is the START bit IIRC. That means that all bits past the latch bit must be 0 until the START of the next data image.

But yeah! SPI maybe..
Ahhh so data enable clears the bits in the shift register?. I'll experiment with the SPI flag tonight, I did see something in the datasheet about that. Also, I wish they just made this thing 40 bits wide...oh well good learning exercise.

Thanks again everyone.

#### JohnInTX

Joined Jun 26, 2012
4,628
No, the shift reg is cleared (and the shifted-in bits latched) when it encounters the first ‘1’ bit shifted out the end of the SR. (the start bit). After the latching and clearing, the next ‘1’ that is shifted out would have to be the start bit for the next data packet.

The ENABLE forces the data shifted in to be ‘0’ so you can have other stuff on the bus, it doesn’t re-initialize the logic (as I remember and how I interpret the datasheet). It looks like you can clock extra bits after the data bits if you are sure they are all ‘0’. That implies that you could indeed use SPI as long as you observe the notes in #15.

Props to @nsaspook , too. I'm a little slow sometimes!

Last edited:

#### JohnInTX

Joined Jun 26, 2012
4,628
@odm4286
I wanted to mess with MCC w/SPI on the 16F1619 so came up with this. It looks like it should work. I think your CKP/CKE was wrong. How you manage the checking of BF is up to you, this one's kind of a kludge.
I justified the segment array so that the segments in each digit are in one byte, the DP for that digit is in the next byte to make room for the start bit. That should make it easier to look up the digits and convet to segments.

C:
/*
Generated Main Source File
PIC16F1619 on Curiosity Board
Fosc = 32MHz
*/

#include "mcc_generated_files/mcc.h"

// Data format for display segs array:
// Byte 0: 1abcdefg 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};

// 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);  Wait for it to go
}

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)
{
ENABLE__SetLow();

for (k=0; k < 5; k++){  // send segment image
sendSPI(segs[k]);
}

ENABLE__SetHigh();
__delay_ms(20);

}// while
}// main

Last edited: