Shop OBEX P1 Docs P2 Docs Learn Events
XPT2046 SPI Smartpin driver — Parallax Forums

XPT2046 SPI Smartpin driver

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.. :D 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!
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

  • avsa242avsa242 Posts: 424
    edited 2020-04-17 23:20
    @cheezus ,
    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
  • avsa242 wrote: »
    @cheezus ,
    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!
  • Thanks and long time no see!

    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

  • Since I had looked at converting it to a general SPI object, i'm pretty sure all 4 modes could be done. To handle LSB, simply remove the shift/reverse of the data. I had played around with clock polarity and IIRC the mode(s) must be changed as well. I can't remember all the details off the top of my head but When I get a chance I'll see what i can come up with.

    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?
  • Thanks for this code, @cheezus!
    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:
    sp_stx      =   %01_11100_0 + (%1111 << 24)     ' -1    b pin - clk
    
    Likewise, if your DO pin is not immediately after the CLK pin, you will need to adjust the clock pin offset (%0001) below:
    sp_srx      =   %00_11101_0 + (%0001 << 24)     ' +1    b pin - clk
    

Sign In or Register to comment.