SPIIN / SHIFTIN vs. BIT-BANG EXAMPLE

techElder

Well-known member
You must not have anything else to do if you are anticipating explaining this program problem! :D

There's really no way to test the program without having the particular hardware that I have, but suffice it to say that I've exercised and created quite a few macros and subroutines using the SPI style interface on the module to display numbers (8-digits) and light LEDs (8). SPIOUT statements worked fine with my 20X2. Then it came time to read the switches (8).

tm1638.jpg

Code:
; "C:/Users/Paul/Documents/SensorNetwork/spiin_bit_bang_test.bas"

#no_data
#no_table

; ~~~~~ SYMBOL DEFINITIONS ~~~~~
; Required for all routines. Change pin numbers/bits as required.
; Uses variables b7-b13 (i.e. b7,w4,w5,w6). If only using 8 bits
; all the word variables can be safely changed to byte variables.

;***** Sample symbol definitions *****
symbol sclk = B.3				; clock (output pin)
'symbol sdata = 7			; data (output pin for shiftout)
symbol serdata = pinB.4			; data (input pin for shiftin, note input7)
symbol counter = b7			; variable used during loop
symbol counter2 = b8
'symbol mask = w4			; bit masking variable
'symbol var_in = w0			; data variable used durig shiftin
'symbol var_out = w6			; data variable used during shiftout
symbol var_in = w6			; data variable used durig shiftin
symbol var_out = w0			; data variable used during shiftout
symbol bits = 8				; number of bits
symbol MSBvalue = 128			; MSBvalue =128 for 8 bits, 512 for 10 bits, 2048 for 12 bits)

'Pin assignments
symbol d_strobe               = B.2          ; strobe
'symbol d_clk                  = B.3          ; clock (output pin)
symbol d_data                 = B.4          ; data (output pin for shiftout)
symbol READ_IN                = 0x42         ; read incoming byte

'input serdata
output d_data
	let var_in = 0
   let var_out = 0

do
   gosub shiftin_LSB_Pre ; works like a champ!
   'gosub spi_LSB_Pre
   'gosub shiftin_MSB_Pre ; works but bits are backwards
   'gosub shiftin_LSB_Post ; does not work
loop
end

shiftin_LSB_Pre:
   low d_strobe                              ; activate the strobe
   spiout sclk, d_data, LSBFirst_L,(READ_IN) ; set the module for reading the switches
   input d_data                              ; change the data pin to INPUT
	let var_in = 0                            ; clear the bit variable
	let var_out = 0                           ; clear the byte variable
   for counter2 = 0 to 3                     ; read 4 bytes total
   	for counter = 1 to bits		            ; number of bits
   	  var_in = var_in / 2		            ; shift right as LSB first
   	  if serdata <> 0 then 
   	    var_in = var_in + MSBvalue		   ; set MSB if serdata = 1
   	  end if
   	  pulsout sclk,1		                  ; pulse clock to get next data bit
   	next counter                           ; test next bit
      var_out = var_in << counter2 OR var_out; summarize the bits into the byte
   next counter2                             ; next byte
   ' debug output
   sertxd("var_out: ",#bit7,#bit6,#bit5,#bit4,#bit3,#bit2,#bit1,#bit0,cr,lf)
   high d_strobe                             ; inactivate the strobe
   output d_data                             ; change the data pin back to OUTPUT
	return                                    ; the 8 switch status in bits of var_out

spi_LSB_Pre:
   low d_strobe                              ; activate the strobe
   spiout sclk, d_data, LSBFirst_L,(READ_IN) ; set the module for reading the switches
   input d_data                              ; change the data pin to INPUT
	let var_in = 0                            ; clear the bit variable
	let var_out = 0                           ; clear the byte variable
   for counter2 = 0 to 3                     ; read 4 bytes total
      spiin sclk, d_data, LSBPre_L,(var_in)
;   	for counter = 1 to bits		            ; number of bits
;   	  var_in = var_in / 2		            ; shift right as LSB first
;   	  if serdata <> 0 then 
;   	    var_in = var_in + MSBvalue		   ; set MSB if serdata = 1
;   	  end if
;   	  pulsout sclk,1		                  ; pulse clock to get next data bit
;   	next counter                           ; test next bit
      var_out = var_in << counter2 OR var_out; summarize the bits into the byte
   next counter2                             ; next byte
   ' debug output
   sertxd("var_out: ",#bit7,#bit6,#bit5,#bit4,#bit3,#bit2,#bit1,#bit0,cr,lf)
   high d_strobe                             ; inactivate the strobe
   output d_data                             ; change the data pin back to OUTPUT
	return                                    ; the 8 switch status in bits of var_out
The SPIIN statement in the second subroutine would not read the data from the module. So, I went to modifying the bit-bang example code from "BASIC Commands" for SHIFTIN/SPIIN because I have to read four bytes to get the status of the eight switches. Once I got the code right and OR'd the bits correctly into the output byte, I'm done because it works great.

However, its a mystery to me (because I'm not privy to the code involved with SPIIN) why the example code works, but the SPIIN code does not. One thing I couldn't do is try the "idle high" option, because it wouldn't compile with my 20X2 version according to the error message.

What happened to SPIIN?
 

westaust55

Moderator
Hello Tex,

A bit confused by your statements.

In the code you have the comment:
'gosub shiftin_LSB_Post ; does not work
With: spiin sclk, d_data, LSBPost_L,(var_in)
It compiles okay for me in PE5 and PE6.0.9.3 for the 20X2

Wondering if problem this is related to timing. What is the chip on the display/switch board ?
From my past experience the X1/X2 inbuilt ShiftOut/In commands are quite a bit faster than the bit-bang routines.

And
One thing I couldn't do is try the "idle high" option, because it wouldn't compile with my 20X2 version according to the error message.
With: spiin sclk, d_data, LSBPost_H,(var_in)

Again tried and it compiles okay in PE5 and PE6.0.9.3 for the 20X2 without error for me.
 

techElder

Well-known member
Thanks for responding, Westi.

EDIT: display shift register type is TM1638.

My note on the "Post" routine was meant to remind me that the bits from the hardware did not come back correctly, if at all. "Pre" has always worked. I've not looked at timing.

As far as compiling, syntax checks ok, but there's a hardware revision check that fails. I must be using a 20X2 from the Dark Age.

Faster? Yep, that's why I was planning on using spiin, but it won't bring the data in from the pin where the bit-bang version works great.
 
Last edited:

hippy

Technical Support
Staff member
Do you have a link to a readable and usable datasheet for the TM1368 ? The only ones I could find all displayed as blank except for the page headings or crashed Adobe Reader.

If you are having to end with a "High d_strobe" then it may be that, because your chip firmware is not supporting the "idle high" option, that's why you can't get it to work using SPIIN.

If the TM1368 is anything like the TM1367 which I am familiar with it uses a pseudo I2C protocol but isn't compatible with HI2C commands, or compatible with HSPI commands or any firmware bit-banged equivalents.

The TM1367 has to be software bit-banged, and it wouldn't surprise me if that's also the case for the TM1368.
 

hippy

Technical Support
Staff member
Found a datasheet, and it does seem to be standard SPI, both CLK and DIO idle high, with an additional strobe -

https://www.futurashop.it/image/catalog/data/Download/TM1638_V1.3_EN.pdf

Data bit order is reversed to standard hardware SPI, lsb first rather than msb, but can't see why it wouldn't work with firmware SPI commands, or hardware SPI with software bit reversal applied before writing, after reading.

What I often do for SPI interfacing is get the bit-banged version working first, put a logic analyser on the signals lines, then try the hardware or firmware commands I think it should be and check how they compare.

Though if one works it's probably the right mode to use so just a case of trial and error, working through which option it is until one or more does work.
 

techElder

Well-known member
Though if one works it's probably the right mode to use so just a case of trial and error, working through which option it is until one or more does work.
Yah, like I said before, that's what I did with the SPIOUT statement using "LSBFirst_L". It was easy to do once I got the right sequence and command (with help from some previous 'C' fellows.) So, I assumed that SPIIN would also work with "LSBPre_L", but doesn't.

I have it working with the bit-banged version, but SPIIN will only read zero in the bits. Its as if there is no clock shifting bits out. Bit reversal problems would be a relief, because it would mean that I have some bits to reverse! :D

The two subroutines above show that simply exchanging the bit-banged code for the SPIIN version goes from working fine (all 8 bits are shifted) to not working (bits are always zero.)

The hardware doesn't care what idle state the clock is in for the pulsout statement with the bit-bang code. It works starting at either level.

I was wondering if I was specifying the data input correctly for the SPIIN statement. The SPIIN statement won't allow a PINx.X version of the input. The bit-banged uses PINx.X.
 

techElder

Well-known member
If you are having to end with a "High d_strobe" then it may be that, because your chip firmware is not supporting the "idle high" option, that's why you can't get it to work using SPIIN.
The strobe doesn't really have anything to do with SPI. Its a hardware signal level equivalent to chip select (CS).
 

westaust55

Moderator
For bit reversal with an X1 or X2 part you could try the MSBfirst and then use the REV command/function.
Albeit a &#8220;band-aid&#8221; at least that would be faster than Big Bang methods.

B1= B1 REV 8
 

techElder

Well-known member
Good reading material.

I've got a pullup on the data line since it is an open driver, but can't remember on the strobe. However, the strobe line is always driven by the PICAXE. Shouldn't need any further pullup/down.

Still flummoxed about the SPIIN not cooperating.
 

hippy

Technical Support
Staff member
Still flummoxed about the SPIIN not cooperating.
I suspect it's because you are using SBPre_L when you should be using one of the _H variants of the mode, perhaps Post_H looking at the datasheet. Outputting may work with _L but it doesn't necessarily follow that inputting will.

While your bit-banged version of Pre_L input appears to work this may be more good fortune and an issue of timing as westaust55 suggests. SPIIN with Pre-L may not get the same results.

Its as if there is no clock shifting bits out.
You can check that the clock line is activating by putting SPIIIN within a DO-LOOP, disconnecting your hardware and looking at the signals on a scope or with a LED+R between CLK and V+.

Different data values should be obtained by connecting DIN via an R to 0V and V+.
 

techElder

Well-known member
Thanks for the confirmation. Looks like I'll be looking in the storage for my new 20X2 PICAXEs. The one I have is version "C.0". I'll need to find a "C.3".

Previously resolved issues:

ISSUE - CORRECTED ISSUE WITH SHIFTIN AND SHIFTOUT IDLE HIGH MODES
The xxxx_H 'idle high' modes of shiftin and shiftout do not operate as expected.
To workaround this issue use the bit-busting routines described in manual part 2.
Fixed in vC.3
 

techElder

Well-known member
Success!

Following up on this problem that I had, with a few quirks that I don't understand (since the code is not visible) I have managed to get SPIIN to work on my gadget.

If you read above you'll find that SPIIN didn't work in my program as easily as SPIOUT, but the PICAXE BASIC example code (with appropriate mods) did work just fine. The SPIIN mode parameter for "idles high" was not available to me because my 20X2 was not of the latest version.

So, after obtaining a 20X2 with the "C.3" revision level, I was set to go with the mode "LSBPre_H", except for one thing. It didn't work the way I expected. :(

At first it was confusing, because the byte received by SPIIN was inverted from what the example code was returning. That made my program logic wonky at best. That's the part that I don't understand; the inversion.

Code:
[color=Blue]spiin d_clk[/color][color=Black], [/color][color=Blue]d_data[/color][color=Black], [/color][color=Blue]LSBPre_H[/color][color=Black],[/color][color=Blue]([/color][color=Purple]spi_byte[/color][color=Blue])[/color]
[color=Purple]spi_byte [/color][color=DarkCyan]= inv [/color][color=Purple]spi_byte[/color]
So, I tried the INV (bit-by-bit inversion) statement on the returned byte, and all was back to correct operation. My bits were bit and my lamps were lit. :D

Seems strange to me that the SPIIN reads inverted compared to the example code (and the hardware's description.)
 

techElder

Well-known member
I forgot to mention this in my last post.

There is something else not mentioned in the documents involving SPIIN.

You don't have to change an OUTPUT pin to an INPUT pin when using it.

It doesn't seem to matter that I used the DIO pin on the TM1638 module (connected to an OUTPUT pin d_data through a symbol definition.) In my code, I was changing it to an INPUT (input d_data) and then back to an OUTPUT (output d_data).

However, in testing this I can comment out the "input d_data" statement, and the results don't change.

Apparently, SPIIN toggles the pin direction on the PICAXE? I don't know, but it works without the "input d_data" statement on a 20X2.
 
Top