XPT2046 SPI Smartpin driver
cheezus
Posts: 298
in Propeller 2
I wanted to share this bit of code I've been working on for reading X-Y from the touch panel ADC. It could be generalized into an SPI driver but I think I'm going the other route and creating a SPI cog for my platform. I have 3 spi devices +1 spare external, with muxed cs creating an object to handle all of it makes sense. I have some crazy ideas for later..
Maybe someone will find it useful as is, (and I need to check what the RPI display I have uses...)
I'm thinking of pre-encoding the x,y config registers since these don't change... I'm thinking about keeping the bit-time conversion though, since I could be changing the clock dynamically in the future. It might make sense to pre-compute as much of the bit-time calc as possible, but haven't really looked at that yet. I also am inverting the clock pin using the out register. This is a residual from testing, as is the removal of the GetMask and hard-coding of the b-pin.
It works for now and I hope to not have to revisit this until I'm writing a driver for the SPI flash. Two down (the important 2), one to go! Now I just need to refine the touch panel calibration program and it's almost demo time!
I'm thinking of pre-encoding the x,y config registers since these don't change... I'm thinking about keeping the bit-time conversion though, since I could be changing the clock dynamically in the future. It might make sense to pre-compute as much of the bit-time calc as possible, but haven't really looked at that yet. I also am inverting the clock pin using the out register. This is a residual from testing, as is the removal of the GetMask and hard-coding of the b-pin.
It works for now and I hope to not have to revisit this until I'm writing a driver for the SPI flash. Two down (the important 2), one to go! Now I just need to refine the touch panel calibration program and it's almost demo time!
CON
{{ =============================================================
Basic X-Y driver for XPT 2046 Touch Screen Controller
Inline Smartpin SPI v0.5 - 11-26-19
Cheezus Slice - Cheezusslicedj@gmail.com
This driver uses hard-coded pins but should be easy to
make dynamic if you wish. Only reads X-Y
=============================================================
}}
_CS = 15 '+ 32 ' chip select
_CLK = 13 '+ 32 ' Pins A, uncomment +32 for Pins B
_DI = 14 '+ 32 ' MOSI
_DO = 12 '+ 32 ' MISO
'' config register
'' S A2__A1__A0 MODE SER PD1__PDO
y_cfg = %1__1___0___1___0____0____0____0 ' X DIFF
x_cfg = %1__0___0___1___0____0____0____0 ' Y DIFF
{
z1_cfg = %1__0___1___1___0____0____0____0 ' Z1 DIFF
z2_cfg = %1__1___0___0___0____0____0____0 ' Z2 DIFF
t_cfg = %1__0___0___0___0____1____0____0 ' temperature
b_cfg = %1__0___1___0___0____1____0____0 ' battery
a_cfg = %1__1___1___0___0____1____0____0 ' battery
}
'' spi stuff
spi_clk_max = 2_000_000
'%AAAA_BBBB_FFF_PPPP_PPP_PPP_PPP_TT_MMMMM_0
'ppp =___xxxx_CIO_HHH_LLL
sp_inv_pin = %0000_001_000_000 << 8
'smartpin modes TT_MMMMM_0
sp_stx = %01_11100_0 + (%1111 << 24) ' -1 b pin - clk
sp_srx = %00_11101_0 + (%0001 << 24) ' +1 b pin - clk
sp_clk = %01_00101_0 '+ sp_inv_pin
'sp configs
stx_c = (%1 << 5) + (numBits - 1) ' start-stop mode
srx_c = (%0 << 5) + (numBits - 1) ' pre-clock sample
clks = numBits * 2
numBits = 24 ' 24 bits conversion
PUB TouchXY| x, y, xo, yo, b
b := GetBitTime(spi_clk_max)
asm
'' prepare data out x ?should pre-encode?
mov xo, #x_cfg ' type is conversion
SHL xo, #32-8 ' Set MSB First
REV xo ' Set MSB First
'' prepare data out y
mov yo, #y_cfg
SHL yo, #32-8
REV yo
'' clk - transition
dirl #_CLK
outh #_CLK ' mode 1,1 - clock starts high
wrpin #sp_clk, #_CLK
wxpin b, #_CLK ' set base period (*2)
dirh #_CLK
'' do - srx
dirl #_DO
wrpin #sp_srx, #_DO ' sync rx
wxpin #srx_c, #_DO ' stop/start mode
dirh #_DO
'' di - stx
dirl #_DI
wrpin #sp_stx, #_DI ' sync tx
wxpin #stx_c, #_DI ' stop/start mode
wypin #$FF, #_DI
dirh #_DI
drvl #_CS
'' prepare to read
rdpin pa, #_DO ' clear RX buffer
wypin xo, #_DI ' data !!
'' start clock
rdpin pa, #_CLK
wypin #clks, #_CLK ' start clock
.busy testp #_CLK wc
if_nc jmp #.busy
rdpin x, #_DO ' get data
'' prepare to read
rdpin pa, #_DO ' clear RX buffer
wypin yo, #_DI ' data !!
'' start clock
rdpin pa, #_CLK
wypin #clks, #_CLK ' start clock
.busy2 testp #_CLK wc
if_nc jmp #.busy2
rdpin y, #_DO ' get data
drvh #_CS ' drive CS high
' now handle data
shl x, #3 ' throw away msb
rev x
and x, #$ffff ' isolate 12b
' now handle data
shl y, #3 ' throw away msb
rev y
and y, #$ffff ' isolate 12b
wypin #0, #_DI ' clear buffer
wrpin #0, #_CLK
wrpin #0, #_DO
wrpin #0, #_DI
dirl #_CLK
dirl #_DO
dirl #_DI
endasm
result := x + (y << 16)
PRI getmask(clk, data) : t ' get mask to add to smartpin mode
t := clk - data
if ( || t ) <> t
t := ( || t ) + %0100
PRI GetBitTime(max_clkfreq) : bt
bt :=((clkfreq / max_clkfreq) /2 )+1 ' SPI bit time
if bt < 2 ' make sure at least sysclock /2
bt := 2


Comments
Thanks loads for this! I've been able to get most of the way converting this to general-purpose usage. Are the number of clock pulses calculated by numBits and clks meant to cover both the register address write as well as the 16b read in your original code? I ask because initially, my knee-jerk reaction was to change it to 8 (well, change numBits to 8) for my device but this yielded no data with my device. Setting it back to 24 gets the expected value back from the reg, but along with some other junk before and after.
Cheers
Yes, numBits covered the 8 bit write, as well as the 16 bit read to make a 24 bit transfer. I'm not seeing anything that should prevent you from just changing numBits to 8, although I do have that nagging feeling there might be a gotcha somewhere. If i can think of anything I'll update this post when i do!
After a few days of working on bits and pieces, I was able to get a general-purpose smart pin SPI engine going, based on your code. (com.spi.spin2 at https://github.com/avsa242/p2-spin-standard-library/tree/testing/library). Limitations are it only does mode 0...couldn't figure out how to get CPOL 1 working...I was able to invert the clock pin output, but the number of bits shifted out ended up being incorrect...will have to revisit this at some point. Also only MSB-first data. It's translated to FastSpin spin2 also, so it looks much different than your original code, but I felt it should be credited in the header, anyway.
Cheers,
Jesse
MSB/LSB should be fairly easy, although i'm not sure I have any LSB devices to test with.
In READ, remove result := (result rev 31) & $FF ' Reverse bit order and discard
In WRITE, remove val <<= (32-8) ' Move byte to MSB position
val := val rev 31 ' Reverse bit order
Polarity as I said is trickier, but i think change sampling from pre to post?
A comment for anyone that wants to use this code:
If your DI pin is not immediately before the CLK pin, you will need to adjust the clock pin offset (%1111) below: Likewise, if your DO pin is not immediately after the CLK pin, you will need to adjust the clock pin offset (%0001) below: