Hello evreyone ,
I am trying to integrate NBP8s pressure sensor with vector system. VT2710 sensor module has SPI interface.
Has anyone tried this integration before ? Would love to know your suggestions. I am not able to get proper and valid response from the sensor.
CAPL script i wrote :
/*@!Encoding:1252*/
variables
{
/* IO buffers, timers */
byte txBuffer[2];
msTimer tMain, tSpi, tDelay;
/* Main state machine */
int mainState = 0;
word curFrame;
int intAsserted, b_INTF;
int s4, s3, s2, s1, s0;
/* Micro-FSM for SPI transaction */
enum Spiop {OP_NONE = 0, OP_RD=1, OP_WR=2};
enum Spisub {IDLE=0, TX1=10, RX1=11, TX2=12, RX2=13};
int tr_op , spiSub;
word tr_addr = 0;
byte tr_wdata = 0;
byte tr_rdata = 0;
int tr_retry = 0; // bounded retry count (faults)
int needDummy = 1; // after WAKE/CS glitch or any detected fault
/* SPI response */
byte misoHi = 0, misoLo = 0;
word spiResp = 0;
/* Cached registers (live mirror) */
byte regSPIOPS = 0;
byte spiops_base = 0;
int spiops_base_valid = 0;
/* INT edge auto-detect */
int lastIntLevel = -1;
int Idle;
int active_IntPol = 0; // bit 5 of INTTRIG
int intLevel = 0;
int USE_READY_GATE = 1; // 0 - off (default) , 1 - on (real HW)
/* device asserts READY when it s safe to transact. */
int startupWaitDone = 0;
/* Optional: last decoded flags */
byte status, data;
word msb, lsb, msb_addr, lsb_addr;
/* Register Addresses */
word ADDR_SPIOPS = 0x0038; // [2]=CORE_TR_HOLD, [1:0]=FLASH_RANGE ; [7:3] reserved
/* Firmware/HW derivative/version windows (via SPIOPS) */
word ADDR_FW_DERIV = 0x0805; // valid when SPIOPS FLASH_RANGE == 00 (0x04)
word ADDR_FW_VER = 0x0804;
word ADDR_HW_DERIV = 0x1542; // valid when SPIOPS FLASH_RANGE == 11 (0x07)
word ADDR_HW_VER = 0x1543;
}
/* SPI frame builder
p1 covers bits b15..b9, p0 covers b8..b2, both even parity. */
/* Build a 16-bit SPI frame per NBP8S protocol:
- bit15: R/W (1=write, 0=read)
- bits14..2: address / data field
- bits1..0: parity bits (even parity on bits15..9 and bits8..2) */
word makeNbp8sFrame(int rw, word addr_or_data)
{
word f = 0;
int group1, group2, p1, p0;
if (spiSub == TX1) {
/* TX1: Address frame 13-bit address */
f = ((rw & 1) << 15) | ((addr_or_data & 0x1FFF) << 2);
} else {
/* TX2: Data frame or dummy 8-bit data in bits9..2 */
f = ((rw & 1) << 15) | ((addr_or_data & 0xFF) << 2);
}
write("[makeFrame] RAW Frame (no parity) = 0x%04X", f);
/* --- Inline parity logic (same as computeParity, no countBits) --- */
group1 = (f >> 9) & 0x7F; // bits 15..9
group2 = (f >> 2) & 0x7F; // bits 8..2
// count ones manually
p1 = 0; p0 = 0;
while (group1) { if (group1 & 1) p1++; group1 >>= 1; }
while (group2) { if (group2 & 1) p0++; group2 >>= 1; }
p1 = (p1 & 1) ; // even parity for group1
p0 = (p0 & 1) ; // even parity for group2
f &= 0xFFFC; // clear bits1..0
f |= ((p1 << 1) | p0); // set parity bits
write("[makeFrame] Final frame = 0x%04X", f);
return f;
}
/* Status & fault logging */
void logSpiStatus(word r)
{
s4 = (r >> 14) & 1;
s3 = (r >> 13) & 1;
s2 = (r >> 12) & 1;
s1 = (r >> 11) & 1;
s0 = (r >> 10) & 1;
status = (r >> 10) & 0x1F;
data = (r >> 2) & 0xFF;
write("[SPI Status Bits] s4=%d s3=%d s2=%d s1=%d s0=%d | Full Response=0x%04X", s4, s3, s2, s1, s0, r);
write("[SPI Decoded] Status=0x%02X Data=0x%02X", status, data);
if (s4) write("Fault: Reserved fault (undefined)");
if (s3) write("Fault: Ignored command or invalid previous read");
if (s2) write("Fault: Clock error");
if (s1) write("Fault: Parity error");
if (s0) write("Fault: Bus contention or illegal access");
}
/* (s4..s0 any non-zero indicates an issue). */
int hasFault(word r)
{
return (((r >> 14) & 1) || ((r >> 13) & 1) || ((r >> 12) & 1) || ((r >> 11) & 1) || ((r >> 10) & 1));
}
/* Queue a 16-bit frame on MOSI (CS low is driven here) */
void sendFrame(word frame16)
{
txBuffer[0] = (byte)(frame16 >> 8);
txBuffer[1] = (byte)(frame16 & 0xFF);
sensorQueueMosiData("SENSOR::SPI::Master_Port::NBP_Sensor", txBuffer, 16);
}
/* High-level: schedule a READ/WRITE op */
void spiReadByte(word addr)
{
tr_addr = addr;
tr_op = OP_RD;
tr_retry= 0;
spiSub = TX1;
setTimer(tSpi, 1);
}
void spiWriteByte(word addr, byte data)
{
tr_addr = addr;
tr_wdata = data;
tr_op = OP_WR;
tr_retry= 0;
spiSub = TX1;
setTimer(tSpi, 1);
}
/* SPI Micro-FSM */
on timer tSpi
{
switch (spiSub)
{
/* TX1 */
case TX1:
@VTS::VT2710_1_DIO_Ch2::DigitalOutput6 = 0 ; // CS low
if (USE_READY_GATE && (tr_addr != ADDR_SPIOPS))
{
intAsserted = (active_IntPol ? (intLevel == 1) : (intLevel == 0));
if( b_INTF && !intAsserted)
{
write("[SPI] Waiting for READY before first TX1 - Sensor initiated");
setTimer(tSpi, 1);
break;
}
}
curFrame = needDummy ? makeNbp8sFrame(0, 0x0000): makeNbp8sFrame((tr_op == OP_WR) ? 1 : 0, tr_addr);
write("[TX1] Master --> Sensor: Frame = 0x%04X (R/W=%d Addr=0x%04X)",curFrame, (curFrame >> 15) & 1, (curFrame >> 2) & 0x1FFF);
sendFrame(curFrame);
spiSub = RX1;
setTimer(tSpi, 1);
break;
/* RX1 (R0 after TX1) */
case RX1:
misoHi = @sensor::SPI::Master_Port::NBP_Sensor::Frame1.MISO_Signals.MISO_High_byte;
misoLo = @sensor::SPI::Master_Port::NBP_Sensor::Frame1.MISO_Signals.MISO_Low_byte;
spiResp = ((word)misoHi << | misoLo;
write("[RX1] Sensor --> Master: Response = 0x%04X (Status=0x%02X Data=0x%02X)",spiResp, (spiResp >> 10) & 0x1F, (spiResp >> 2) & 0xFF);
if (needDummy) {
needDummy = 0;
spiSub = TX1;
setTimer(tSpi, 1);
break;
}
// correct fault handling
logSpiStatus(spiResp);
if(tr_addr != ADDR_SPIOPS)
{
s4 = (spiResp >> 14) & 1;
s3 = (spiResp >> 13) & 1;
s2 = (spiResp >> 12) & 1;
s1 = (spiResp >> 11) & 1;
s0 = (spiResp >> 10) & 1;
if (s2) { // Clock fault
if (tr_retry < 4) {
write("[SPI] Clock fault: dummy+retry");
needDummy = 1; tr_retry++;
spiSub = TX1; setTimer(tSpi, 5);
break;
}
tr_op = OP_NONE;
spiSub = IDLE;
break;
}
if (s3 || s4) {
write("[SPI] Fatal fault (Reserved/Previous read/write) error, aborting transfer");
tr_op = OP_NONE;
spiSub = IDLE;
break;
}
if (s1 || s0) {
if (tr_retry < 4) {
write("[SPI] SPI fault (s1/s0) : dummy+retry");
needDummy = 1;
tr_retry++;
spiSub = TX1;
setTimer(tSpi, 1);
break;
}
tr_op = OP_NONE;
spiSub = IDLE;
break;
}
}
if (tr_op == OP_WR) {
curFrame = makeNbp8sFrame(1, tr_wdata);
write("[TX2] Master --> Sensor: Data Frame = 0x%04X", curFrame);
sendFrame(curFrame);
spiSub = RX2;
setTimer(tSpi, 5);
break;
}
/* For READs send dummy frame */
else if (tr_op == OP_RD) {
curFrame = makeNbp8sFrame(0, 0x0000);
write("[TX2] Master --> Sensor: Dummy Frame = 0x%04X", curFrame);
sendFrame(curFrame);
spiSub = RX2;
setTimer(tSpi, 1);
break;
}
/* TX2 */
case TX2:
spiSub = RX2;
setTimer(tSpi, 1);
break;
/* RX2 (R1: DATA for READ, STATUS for WRITE) */
case RX2:
misoHi = @sensor::SPI::Master_Port::NBP_Sensor::Frame1.MISO_Signals.MISO_High_byte;
misoLo = @sensor::SPI::Master_Port::NBP_Sensor::Frame1.MISO_Signals.MISO_Low_byte;
spiResp = ((word)misoHi << | misoLo;
@VTS::VT2710_1_DIO_Ch2::DigitalOutput6 = 1 ; // CS high
setTimer(tDelay, 10);
write("[RX2] Sensor --> Master: Response = 0x%04X (Status=0x%02X Data=0x%02X)",spiResp, (spiResp >> 10) & 0x1F, (spiResp >> 2) & 0xFF);
logSpiStatus(spiResp); // correct fault handling */
if(tr_addr != ADDR_SPIOPS)
{
s4 = (spiResp >> 14) & 1;
s3 = (spiResp >> 13) & 1;
s2 = (spiResp >> 12) & 1;
s1 = (spiResp >> 11) & 1;
s0 = (spiResp >> 10) & 1;
if (s2) { // Clock fault
if (tr_retry < 4) {
write("[SPI] Clock fault: dummy+retry");
needDummy = 1;
tr_retry++;
spiSub = TX1;
setTimer(tSpi, 1);
break;
}
tr_op = OP_NONE;
spiSub = IDLE;
break;
}
if (s3 || s4) {
write("[SPI] Fatal fault (parity/contend), aborting transfer");
tr_op = OP_NONE;
spiSub = IDLE;
break;
}
if (s1 || s0) {
if (tr_retry < 4) {
write("[SPI] SPI fault (s1/s0) : dummy+retry");
needDummy = 1;
tr_retry++;
spiSub = TX1;
setTimer(tSpi, 1);
break;
}
tr_op = OP_NONE;
spiSub = IDLE;
break;
}
}
if (tr_op == OP_RD)
tr_rdata = (spiResp >> 2) & 0xFF;
tr_op = OP_NONE;
spiSub = IDLE;
setTimer(tDelay, 1);
break;
}
}
/* Utility */
int spiBusy() { return (tr_op != OP_NONE) || (spiSub != IDLE); }
/* Start */
on start
{
write("NBP8s Evaluation");
setTimer(tDelay,38);
needDummy = 1; /* first transfer dummy after WAKE/CS glitch */
mainState = 0;
tr_op = OP_NONE;
spiSub = IDLE;
startupWaitDone = 1;
setTimer(tMain, 35);
}
/* tDelay yields back to main */
on timer tDelay
{
setTimer(tMain, 5);
}
/* Main FSM */
on timer tMain
{
setTimer(tMain, 1);
/* State machine */
switch (mainState)
{
/* SPIOPS handshake */
case 0:
if (spiBusy()) break;
// If a dummy is pending (e.g. after reset/fault), consume it
write("SPIOPS - Dummy frame to clear start up");
// @VTS::VT2710_1_DIO_Ch2::DigitalOutput6 = 0; // CS low
// setTimer(tDelay, 2);
// @VTS::VT2710_1_DIO_Ch2::DigitalOutput6 = 1; // CS high
// setTimer(tDelay, 5);
spiReadByte(0x0000); // dummy frame (ignored by device)
setTimer(tDelay,5);
needDummy = 0;
setTimer(tDelay,5);
mainState = 1;
break;
case 1:
if (spiBusy()) break;
write("SPIOPS - SENSOR Ready , proceeding to write");
spiWriteByte(ADDR_SPIOPS, 0x04); // write SPIOPS = 0x04
setTimer(tDelay,5);
mainState = 2;
break;
case 2:
if (spiBusy()) break;
spiReadByte(ADDR_SPIOPS); // read back SPIOPS
setTimer(tDelay, 5);
mainState = 3;
break;
case 3:
if (spiBusy()) break;
mainState = 4;
break;
case 4:
if (spiBusy()) break;
// Use tr_rdata (captured byte) instead of spiResp bits
regSPIOPS = (byte)(tr_rdata & 0x07);
// If HOLD active and range 00 -> SPIOPS must be 0x04
if (regSPIOPS == 0x04) {
write("SPIOPS: SET OK (0x%02X)", regSPIOPS);
mainState = 10; // go to FW/HW read
}
else {
write("SPIOPS NOT READY (SPIOPS = 0x%02X) --> retrying", regSPIOPS);
needDummy = 1;
setTimer(tDelay,5);
mainState = 1 ;
}
break;
/* FW / HW READ SEQUENCE */
case 10:
if (spiBusy()) break;
write("SPIOPS already set to 0x04 --> Firmware window active");
write("Reading FW_DERIV (0x0805)...");
spiReadByte(ADDR_FW_DERIV);
setTimer(tDelay, 5);
mainState = 11;
break;
case 11:
if (spiBusy()) break;
write("FW_DERIV = 0x%02X", tr_rdata);
write("Reading FW_VER (0x0804)...");
spiReadByte(ADDR_FW_VER);
setTimer(tDelay, 5);
mainState = 12;
break;
case 12:
if (spiBusy()) break;
write("FW_VER = 0x%02X", tr_rdata);
setTimer(tDelay, 5);
mainState = 13;
break;
case 13:
if (spiBusy()) break;
write("Clearing SPIOPS (0x0038) to release NBP8s CPU");
spiWriteByte(ADDR_SPIOPS, 0x00);
setTimer(tDelay, 5);
mainState = 14;
break;
case 14:
if (spiBusy()) break;
write("FW read complete");
mainState = 99;
break;
case 99:
// Idle / stop
break;
}}
any suggestions and feedback would be helpful for further progress.
Best regards