B4R Question rSPI32 lib - Send One Command Byte and Receive Two Bytes Back As Int

KiloBravo

Active Member
Licensed User
Can I use SPI.Transfer_Byte_Array in the rSPI32 lib to accomplish this ?
The syntax to do it is currently eluding me! :)

SPI Send One Command Byte then Receive Two Bytes Back (Int):
        ' y1 
           Dim yi As Int = 0
           Dim Y_COMMAND_BYTE As Byte = 0x90  ' Send One Byte Y Command
           Dim MSB, LSB As Byte = 0 ' Receive Back Two Bytes
          
           SPI.Transfer_Byte(Y_COMMAND_BYTE) ' send command byte
           MSB = SPI.Transfer_Byte(0)
           LSB = SPI.Transfer_Byte(0)
           yi = Bit.ShiftLeft(Bit.And(MSB,MSB_BIT_MASK),8)
           yi = yi + LSB
           yi = Bit.ShiftRight(yi,3)
 

KiloBravo

Active Member
Licensed User
I rewrote the first sub and tested both. So, I will answer my own question. :)
I broke my only touch screen, but these two spi subs seem to work the same.
First Sub uses One Byte Xfer and Rcv. Second one uses the Transfer Byte Array Xfer and Rcv.

Xfer One Byte and Rcv Two Bytes back:
Sub xpt2046_spi(cmd As Byte) As Int
    Dim MSB, LSB As Byte = 0
    Dim xpt_data As Int
    SPI.Transfer_Byte(cmd) ' send one byte command
    MSB = SPI.Transfer_Byte(0)
    LSB = SPI.Transfer_Byte(0)
'      xpt_data = (((MSB & MSB_BIT_MASK) << 8)+ LSB) >> 3;
       xpt_data =  Bit.ShiftLeft(Bit.And(MSB,MSB_BIT_MASK),8)
       xpt_data = xpt_data + LSB
       xpt_data = Bit.ShiftRight(xpt_data,3)
    Return xpt_data
End Sub

Send One Command Byte Rcv Two Data Bytes back:
Sub xpt2046_spi(cmd As Byte) As Int
    Dim MSB, LSB As Byte = 0
    Dim xpt_data As Int
    Dim cmd_Array() As Byte = Array As Byte (0,0,0)
    Dim rcv_Array() As Byte = Array As Byte (0,0,0)
    cmd_Array(0) = cmd
    SPI.Transfer_Byte_Array(cmd_Array, rcv_Array) ' Send One Command Byte Rcv Two Data Bytes back
    MSB = rcv_Array(1)
    LSB = rcv_Array(2)
'      xpt_data = (((MSB & MSB_BIT_MASK) << 8)+ LSB) >> 3;
       xpt_data =  Bit.ShiftLeft(Bit.And(MSB,MSB_BIT_MASK),8)
       xpt_data = xpt_data + LSB
       xpt_data = Bit.ShiftRight(xpt_data,3)
    Return xpt_data
End Sub
 
Upvote 0

Gerardo Tenreiro

Active Member
Licensed User
Hello
The rSPI32 library can send a BYTE or an ARRAY of BYTEs depending on whether "SPI.Transfer_Byte" or "SPI.Transfer_Byte_Array" is used.
Depending on the device you are using, it will be more comfortable for you to send the BYTEs one at a time or at other times you can send the array with a single instruction.
The library works the same in both ways, in fact internally sending an ARRAY uses sending the BYTEs one by one.
But what is your question?
 
Upvote 0

KiloBravo

Active Member
Licensed User
In the Arduino Libs I see a lot of this " data[0] = _pspi->transfer16(0xD1 /* Y */) >> 3 " (Data(0) and 0XD1 are both Int)
in the rSPI32 lib it does not have transfer16.
So I rewrote that in B4X using the code below. cmd, MSB, LSB are defined as Bytes. xpt_data as an Int.
I was asking if it I should be using the transfer_byte (like I did) or should I be using transfer_byte_array
... or if the lib was going to be updated with transfer16 or transfer32 :)


My Code:
    Dim MSB, LSB As Byte = 0
    Dim xpt_data As Int
    SPI.Transfer_Byte(cmd) ' send one byte command
    MSB = SPI.Transfer_Byte(0)
    LSB = SPI.Transfer_Byte(0)
     xpt_data = xpt_data + LSB
     xpt_data = Bit.ShiftRight(xpt_data,3)




void XPT2046_Touchscreen::update():
    int16_t data[6];
    int z;
    if (!isrWake) return;
    uint32_t now = millis();
    if (now - msraw < MSEC_THRESHOLD) return;
    if (_pspi) {
        _pspi->beginTransaction(SPI_SETTING);
        digitalWrite(csPin, LOW);
        _pspi->transfer(0xB1 /* Z1 */);
        int16_t z1 = _pspi->transfer16(0xC1 /* Z2 */) >> 3;
        z = z1 + 4095;
        int16_t z2 = _pspi->transfer16(0x91 /* X */) >> 3;
        z -= z2;
        if (z >= Z_THRESHOLD) {
            _pspi->transfer16(0x91 /* X */);  // dummy X measure, 1st is always noisy
            data[0] = _pspi->transfer16(0xD1 /* Y */) >> 3;
            data[1] = _pspi->transfer16(0x91 /* X */) >> 3; // make 3 x-y measurements
            data[2] = _pspi->transfer16(0xD1 /* Y */) >> 3;
            data[3] = _pspi->transfer16(0x91 /* X */) >> 3;
        }
        else data[0] = data[1] = data[2] = data[3] = 0;    // Compiler warns these values may be used unset on early exit.
        data[4] = _pspi->transfer16(0xD0 /* Y */) >> 3;    // Last Y touch power down
        data[5] = _pspi->transfer16(0) >> 3;
        digitalWrite(csPin, HIGH);
        _pspi->endTransaction();
    }  
    // If we do not have either _pspi or _pflexspi than bail.
    else return;
 
Upvote 0

KiloBravo

Active Member
Licensed User
FYI, in reading some old posts, I realized there are two different rSPI32 libraries.
I was using the rSPI32 b4xlib that hatzisn provided on 12/4/21. It only has the tranfer_byte and transfer_byte_array functions
The one wrapped by candide and provided on 10/25/21 has the transfer16, etc.. commands.
 
Upvote 0

Gerardo Tenreiro

Active Member
Licensed User
The rSPI32 library is a modification of the rSPI library, the difference is that the rSPI32 library works with the two free ports of the ESP32 modules while the rSPI library did not work
Regarding the sending of 16 BITs through the SPI bus, the library does not manage it, so with sending two 8-bit packets the task is fulfilled, in fact in the tests carried out with different devices I found no difference between sending a BYTE or an array, of echo and with the means I have it is faster to send two BYTEs than a two-byte array, but I don't really think that is important unless you are very tight on time.
 
Upvote 0

hatzisn

Well-Known Member
Licensed User
Longtime User
It is exactly as @Gerardo Tenreiro said at post #3. The thing that you gain with SPI.Transfer_Byte_Array is that you will get the reply all together but internally as said it works the same.
 
Upvote 0

KiloBravo

Active Member
Licensed User
I agree. :)

It slipped my mind that SPI is synchronous. I.E. you are always receiving a byte when sending one.
It is up to you whether you do anything with that byte. Thanks!
 
Upvote 0

Gerardo Tenreiro

Active Member
Licensed User
tigrot
If you don't want to wait, delegate SPI access to an external micro . I did 20 years ago, to keep on testing an external device for allarm condition. When this happened it was able to interrupt the main micro.
It depends on the SPI device used, for example the ADS12xx has a pin that indicates when the conversion is done and thus proceed to read the result.
Many SPI devices have mechanisms to generate an INT to the mic as soon as it requires your attention. It is a much more effective method than to be continuously consulting and occupying process capacity.
In the designs you have to try to be energy efficient, I know it seems silly but a few years ago in a review carried out on some digital input cards the greatest consumption was of the signaling LEDs, with only having a button that will enable the LEDs in the moment the operator wanted to see their status and while they were off was enough.
I do not think that having a microprocessor consulting a device without more is the most efficient.
.
 
Last edited:
Upvote 0

hatzisn

Well-Known Member
Licensed User
Longtime User
I rewrote the first sub and tested both. So, I will answer my own question. :)
I broke my only touch screen, but these two spi subs seem to work the same.
First Sub uses One Byte Xfer and Rcv. Second one uses the Transfer Byte Array Xfer and Rcv.

Xfer One Byte and Rcv Two Bytes back:
Sub xpt2046_spi(cmd As Byte) As Int
    Dim MSB, LSB As Byte = 0
    Dim xpt_data As Int
    SPI.Transfer_Byte(cmd) ' send one byte command
    MSB = SPI.Transfer_Byte(0)
    LSB = SPI.Transfer_Byte(0)
'      xpt_data = (((MSB & MSB_BIT_MASK) << 8)+ LSB) >> 3;
       xpt_data =  Bit.ShiftLeft(Bit.And(MSB,MSB_BIT_MASK),8)
       xpt_data = xpt_data + LSB
       xpt_data = Bit.ShiftRight(xpt_data,3)
    Return xpt_data
End Sub

Send One Command Byte Rcv Two Data Bytes back:
Sub xpt2046_spi(cmd As Byte) As Int
    Dim MSB, LSB As Byte = 0
    Dim xpt_data As Int
    Dim cmd_Array() As Byte = Array As Byte (0,0,0)
    Dim rcv_Array() As Byte = Array As Byte (0,0,0)
    cmd_Array(0) = cmd
    SPI.Transfer_Byte_Array(cmd_Array, rcv_Array) ' Send One Command Byte Rcv Two Data Bytes back
    MSB = rcv_Array(1)
    LSB = rcv_Array(2)
'      xpt_data = (((MSB & MSB_BIT_MASK) << 8)+ LSB) >> 3;
       xpt_data =  Bit.ShiftLeft(Bit.And(MSB,MSB_BIT_MASK),8)
       xpt_data = xpt_data + LSB
       xpt_data = Bit.ShiftRight(xpt_data,3)
    Return xpt_data
End Sub

Please also note that there are also other functions in rSPI32 as well as in the rSPI library as you can see in the thread of rSPI thread. You can download the attached project in the following link to see all the other functions available also in rSPI32.


f.e.
B4X:
    Dim b as Byte
    b = SPI.Transfer_Byte(SPI.GetByteFromString("01100111"))

    'We are interested only in the last 4 bits
    b = SPI.ApplyMask(b, SPI.GetByteFromString("00001111"))
    Log(b)

    'We want to know the value of the 2nd bit from right in the byte
    Dim iBitInSecondPositionFromRight As UInt = SPI.GetBitAt(b, 2)
    Log(iBitInSecondPositionFromRight)
  
    'This command will write to the logs "--" if the board is not supported
    'and "-You can use this library with your board-" if your board is supported
    SPI.CheckIfThisBoardIsSupported
  
    'Disable Inerrupts
    SPI.DisableInterrupts

    'Enable Inerrupts
    SPI.EnableInterrupts
 
Upvote 0

KiloBravo

Active Member
Licensed User
Hatzsin, I followed the thread in your post to "test_spi32" is that the project you are referring to ?

I was aware of the other functions in the library thanks to the B4X Help Viewer.

I do not understand how to use SPI.EnableInterrupts and SPI.DisableInterrupts

The device I am using is the XPT2046 which is the same as the TSC2046.
There is No Pin Defined in SPI.BAS (SPI.ESP32_SPI_Set_Pins(14,12,13,15)) for an Interrupt.

Those devices do have an interrupt pin. I defined the Interrupt Pin (T_IRQ = 0) outside of the SPI INIT.
I am just reading T_IRQ in a timer loop to see when the display is touched and then I set a read flag.

MY SPI Code works fine.
Also, Hardware Interrupts are not supported in B4R, correct ?

My question is should I be defining T_IRQ in the SPI INIT and then use SPI.EnableInterrupts and SPI.DisableInterrupts to read the status of the T_IRQ pin when the Touch Screen is "Touched" ? Hopefully that makes sense. :)

I am using this code to INIT the Touch interface for SPI ...
B4X:
    SPI.CheckIfThisBoardIsSupported
    'CHANGE HERE THE PIN NUMBERS (SCK, MISO, MOSI, CS)
    'SS/CS =15 MISO=12 MOSI=13 SCK =14 T_IRQ = 0 <-- Pins for XPT2046 SPI
    SPI.ESP32_SPI_Set_Pins(14,12,13,15)
    SPI.CSPinDeActivate(SPI.CS__SPI)
 
Upvote 0

hatzisn

Well-Known Member
Licensed User
Longtime User
..............
I am just reading T_IRQ in a timer loop to see when the display is touched and then I set a read flag.

MY SPI Code works fine.
Also, Hardware Interrupts are not supported in B4R, correct ?

My question is should I be defining T_IRQ in the SPI INIT and then use SPI.EnableInterrupts and SPI.DisableInterrupts to read the status of the T_IRQ pin when the Touch Screen is "Touched" ? Hopefully that makes sense. :)
..............

B4R generates Arduino code which is then compiled by Arduino. Since the Arduino code supports interrupts I cannot see why isn't it possible to set interrupts and set the program to run voids in case of an interrupt with InLine C. My intention though was the oposite. For uninterrupted communication you just follow this scheme: SPI.DisableInterrupts > {Communicate through SPI} > SPI.EnableInterrupts

As @Erel has mentioned here some times Interrupting can get complicated:
 
Upvote 0

Gerardo Tenreiro

Active Member
Licensed User
Hello
Since the "PENIRQN" pin is available in the "XPT2046" what you can do is define it in the list of pins to notify of any change.
B4R has the "AddListener" Instruction that allows defining a pin so that it generates an interruption of the program and that request is attended. It would have to carry out the management of edges in the interruption call and manage what to do or how to proceed in it, this way of acting frees the ESP32 from being attentive to the change of the pin in question.
Check this option in case it could be valid for you.
 
Upvote 0

KiloBravo

Active Member
Licensed User
B4R generates Arduino code which is then compiled by Arduino. Since the Arduino code supports interrupts I cannot see why isn't it possible to set interrupts and set the program to run voids in case of an interrupt with InLine C. My intention though was the oposite. For uninterrupted communication you just follow this scheme: SPI.DisableInterrupts > {Communicate through SPI} > SPI.EnableInterrupts

As @Erel has mentioned here some times Interrupting can get complicated:

"For uninterrupted communication you just follow this scheme: SPI.DisableInterrupts > {Communicate through SPI} > SPI.EnableInterrupts"
OK, that makes sense. Thanks for the explanantion, Hatzisn.
 
Upvote 0

KiloBravo

Active Member
Licensed User
Hello
Since the "PENIRQN" pin is available in the "XPT2046" what you can do is define it in the list of pins to notify of any change.
B4R has the "AddListener" Instruction that allows defining a pin so that it generates an interruption of the program and that request is attended. It would have to carry out the management of edges in the interruption call and manage what to do or how to proceed in it, this way of acting frees the ESP32 from being attentive to the change of the pin in question.
Check this option in case it could be valid for you.

Hi Gerardo, In my testing I was just using a Timer to check the status of the PENIRQN/T_irq pin. But, as you suggest "AddListener" is probably a better way to do it.
I don't have any critical timing requirements to read the PENIRQ. I was using the display to show the values of some variables. I might add some menu items or buttons that could be used to change some settings. Thanks!
 
Upvote 0

KiloBravo

Active Member
Licensed User
Just as an FYI, this was part of a larger project to control a two motor mobile robotic platform. The display and touch screen interrupt was a nice to have for the mobile base to display messages and interact with the robot base platform outside of the normal control program. I am using an ESP32 to read a left and right encoder and adjust the PWM using a PID algorithm to keep the motors in sync. I.E. keep it going in a straight line. I have two "addlisteners" reading the left/right encoder data. The left geared motor is a little faster than the right geared motor. Full speed on both geared motors is 127 rpm. I 3d printed a "wheel encoder" that has 8 slots connected to the actual motor before the gearing. The RPM of the un-geared motors is 4050 rpm. The B4R code sees 8 "slots" for one motor revolution. This translates to 540 slots per second or a slot every 1.85 mili-seconds.

Running my program with an "addlistener" for the left and right motor encoder and adjusting the PWM for each motor based on the PID algorithm I am able to keep the raw motor counters within a one or two count deviation per revolution.

What does all that mean ? B4R rocks !!!

The main B4J program sends and receives UDP messages. The main B4R program sends and receives UDP messages to control the Base robotic platform, the Base reads and adjusts the PWM (based on the PID) to keep the motor rpm's insync, and is able to display data on the TFT and take inputs from the touch screen to adjust parameters as needed. The Base code is all done in the BASE ESP32 Main B4R program without using interrupts. It uses several AddListeners, one for each Encoder and one for the PENQIRQ on the touch screen.
 
Upvote 0

Gerardo Tenreiro

Active Member
Licensed User
The "AddListeners" are very effective as long as the ESP32 is not at 100% use, to see two encoders and an INT of the TFT I think that the ESP32 is quite released so you would not have any problem.
I am using an ESP32 to read 8 analog inputs of 24 bit and carry out the calculations of alternating current of voltage and intensity, at the moment I am at about 500 samples per second but I am very at the limit to carry out the calculations of RMS and frequency of the measurements, in this case I also have an OLED and a rotary encoder as an input element and it is very fair to me to read everything in a synchronized way and see the result in real time.

Comment that I have just used DSPIC and PIC24 and now I am starting with the ESP32 and B4R. The ESP32 have power but I think the libraries are not well optimized internally and the performance could be better. But little by little it will improve if we all support a little.
 
Upvote 0

KiloBravo

Active Member
Licensed User
Yes, originally I was using a PIC18F chip to read the encoders but decided I could just use the ESP32.
If I am not using an ESP32 or ESP8266 my go to device is a PIC chip. I liked CCS -C for the PIC chips but it was a little expensive for hobby use.
I also really like the IDE Erel has created for B4X. In my projects I usually use B4R, B4J, and occasionally B4A.
 
Upvote 0

Gerardo Tenreiro

Active Member
Licensed User
Hello
I really like "PROSITRON" for PICs, it is a great compiler but it falters in the IDE, now they are starting to use VSCODE and I think it will be a great advance. The compiler is very highly optimized and creates very compact and fast code. But I think that with the ESP32 costers and the power that the PIC days have, they are coming to an end, that's why I'm initiating the change and I think that B4R has a lot of future and a lot to optimize.
 
Upvote 0
Top