1 // ********************************************************************* 2 // Custom DPA Handler - Bridge using SPI * 3 // ********************************************************************* 4 // Copyright (c) MICRORISC s.r.o. 5 // 6 // File: $RCSfile: CustomDpaHandler-Bridge-SPI.c,v $ 7 // Version: $Revision: 1.19 $ 8 // Date: $Date: 2023/03/07 08:03:13 $ 9 // 10 // Revision history: 11 // 2023/03/07 Release for DPA 4.30 12 // 2022/10/05 Release for DPA 4.18 13 // 2019/03/07 Release for DPA 4.01 14 // 2018/10/25 Release for DPA 3.03 15 // 16 // ********************************************************************* 17 18 // Online DPA documentation https://doc.iqrf.org/DpaTechGuide/ 19 20 // Default IQRF include (modify the path according to your setup) 21 #include "IQRF.h" 22 23 // Default DPA header (modify the path according to your setup) 24 #include "DPA.h" 25 // Default Custom DPA Handler header (modify the path according to your setup) 26 #include "DPAcustomHandler.h" 27 28 #if !defined( TR7xD ) 29 #error This example is intended for TR7xD 30 #endif 31 32 //############################################################################################ 33 /* 34 This handler forwards [1] DpaEvent_DpaRequest and [2] DpaEvent_FrcValue events via SPI to the external device. 35 The handler behaves as the SPI Slave while the external device is SPI Master. 36 The external device is responsible for preparing the proper responses to these events. It sends the response back to the handler via SPI. 37 Handler then responses with the response prepared by the external device back to the IQRF network. 38 39 There is an example CustomDpaHandler-Bridge-SPI.ino for Arduino that shows how the external device prepares the response. In this case the device behaves as Standard IQRF Sensor. 40 Use an IQRF Breakout board IQRF-BB-01 or IQRF-SHIELD-02 containing the level shifters to connect TR module (3.3 V logic) to the Arduino board (5 V logic): 41 #IQRF-BB-01 Arduino (Uno) 42 GND GND 43 +3V3/+5V 3.3V 44 SS Digital 7 45 MOSI Digital 11 (MOSI) 46 MISO Digital 12 (MISO) 47 SCK Digital 13 (SCK) 48 49 The Handler (SPI Slave) sets SS low to signal SPI Master (external device) that the SPI transaction starts. 50 It keep SS low during the whole transaction. SS signal can be used to wake up the external SPI master device from sleep state. 51 52 Please restart the TR module every time after the Arduino sketch is uploaded. 53 54 This handler and the external device exchange data (packets) using the HDLC protocol the same way the DPA UART Interface except the CRC is not used. 55 Please see https://www.iqrf.org/DpaTechGuide/#2.3.2%20UART for details. SPI master recognizes the end of the packet sent from SPI slave by detecting the 56 closing HDLC_FRM_FLAG_SEQUENCE 0x7e byte. The it prepares the response and sends it back to the SPI slave using the same HDLC coding. 57 58 The handler and the external device pack a different set of information to the packet depending on the event. 59 60 [1] DpaEvent_DpaRequest 61 The packet (data part) sent to the device has the following structure: 62 #Byte Content Notes 63 0 0 = DpaEvent_DpaRequest 64 1 _DpaDataLength 65 2 NADR 66 3 NADRhigh 67 4 PNUM 68 5 PCMD 69 6 HWPID.low8 70 7 HWPID.high8 71 8... PDATA Optional DPA Request data. 72 73 Please note that _DpaDataLength does not have to equal (in case of enumeration DPA Requests) length of PDATA. 74 The handler waits maximum RESPONSE_TIMEOUT ms for the response from the external device otherwise ERROR_FAIL is returned. 75 76 The device responses with a packet of the following structure: 77 #Byte Content Notes 78 0 see Notes = DpaEvent_DpaRequest with ORed optional flags. When bit7 set, the handler returns TRUE otherwise returns FALSE. When bit6 is set, the handler return DPA error specified at byte #2 i.e. PDATA[0] 79 1 _DpaDataLength 80 2... PDATA Optional DPA Response data. 81 82 [2] DpaEvent_FrcValue 83 The 32 bytes long packet (data part) sent to the device has the following structure: 84 #Byte Content Notes 85 0 10 = DpaEvent_FrcValue 86 1 RFC Command 87 2...31 FRC user data 88 89 The handler waits maximum FRC_RESPONSE_TIMEOUT ms for the response from the external device otherwise the FRC is not handled and the default DPA value (bit0=1) is returned. 90 91 The device responses with a 5 bytes long packet of the following structure: 92 #Byte Content Notes 93 0 10 DpaEvent_DpaRequest 94 1...4 FRC value Content will be written to the responseFRCvalue, responseFRCvalue2B, responseFRCvalue4B variables. Coded using Little Endian. 95 */ 96 //############################################################################################ 97 98 99 // _SS_ pin assignment (C5 = PORTA.5) 100 // When SS is set low by this Slave, then SPI master wakes up and know that SPI transaction is active while SS is low 101 #define SS_PIN LATA.5 102 103 // DPA response timeout from external device 104 #define RESPONSE_TIMEOUT ( 30 / 10 ) 105 // FRC value response timeout from external device 106 #define FRC_RESPONSE_TIMEOUT ( 20 / 10 ) 107 // DPA reported fixed FRC response time 108 #define _FRC_RESPONSE_TIME_FIXED _FRC_RESPONSE_TIME_40_MS 109 110 //############################################################################################ 111 112 // Division macro with rounding 113 #define DIV(Dividend,Divisor) (((Dividend+((Divisor)>>1))/(Divisor))) 114 // PIC baud register computation 115 #define UART_SPBRG_VALUE( Baud ) ( DIV( F_OSC, ( ( ( uns24 )4 ) * ( Baud ) ) ) - 1 ) 116 117 // HDLC byte stuffing bytes 118 // Flag Sequence 119 #define HDLC_FRM_FLAG_SEQUENCE 0x7e 120 // Asynchronous Control Escape 121 #define HDLC_FRM_CONTROL_ESCAPE 0x7d 122 // Asynchronous transparency modifier 123 #define HDLC_FRM_ESCAPE_BIT 0x20 124 125 // Flag to DpaEvent_DpaRequest event value to indicate return TRUE not FALSE 126 #define EVENT_RETURN_TRUE 0x80 127 // Flag to DpaEvent_DpaRequest event value to report error, error value is the 1st data byte 128 #define EVENT_RESPONSE_ERROR 0x40 129 130 //############################################################################################ 131 132 // Buffer used for exchange data with external device 133 #define RxBuffer bufferCOM 134 // Sends byte to the external device 135 uns8 TxByte( uns8 data ); 136 // Sends HDLC byte to the external device 137 void TxHdlcByte( uns8 data ); 138 // Receives data from external device, returns length, 0 if timeout occurred 139 uns8 RxPacket( uns8 timeout ); 140 // Indicates start/stop of the SPI transaction to the master 141 void StartSpiTransaction(); 142 void StopSpiTransaction(); 143 // Initialization 144 void Init(); 145 146 //############################################################################################ 147 bit CustomDpaHandler() 148 //############################################################################################ 149 { 150 // Handler presence mark 151 clrwdt(); 152 153 // Detect DPA event to handle (unused event handlers can be commented out or even deleted) 154 switch ( GetDpaEvent() ) 155 { 156 // ------------------------------------------------- 157 // Handler these unused events as fast as possible 158 case DpaEvent_Idle: 159 Init(); 160 161 case DpaEvent_Interrupt: 162 return Carry; 163 164 // ------------------------------------------------- 165 case DpaEvent_FrcValue: 166 // Called to get FRC value 167 168 // Initialize HW if needed 169 Init(); 170 // Start measuring timeout for RxPacket() function 171 startCapture(); 172 173 // Start packet 174 StartSpiTransaction(); 175 TxByte( HDLC_FRM_FLAG_SEQUENCE ); 176 // Send event value 177 TxHdlcByte( DpaEvent_FrcValue ); 178 // Send FRC command 179 TxHdlcByte( _PCMD ); 180 // Now send all FRC user data 181 uns8 loop = sizeof( DataOutBeforeResponseFRC ); 182 FSR0 = &DataOutBeforeResponseFRC[0]; 183 do 184 { 185 TxHdlcByte( *FSR0++ ); 186 } while ( --loop != 0 ); 187 // Stop packet 188 TxByte( HDLC_FRM_FLAG_SEQUENCE ); 189 190 // Receive the FRC value from the device via UART, length must equal to the event value + 4 FRC value bytes 191 if ( RxPacket( FRC_RESPONSE_TIMEOUT ) == ( sizeof( uns8 ) + sizeof( uns32 ) ) && RxBuffer[0] == DpaEvent_FrcValue ) 192 { 193 // Return FRC values to DPA 194 #if IQRFOS >= 403 195 responseFRCvalue4B.low8 = RxBuffer[1]; 196 responseFRCvalue4B.midL8 = RxBuffer[2]; 197 responseFRCvalue4B.midH8 = RxBuffer[3]; 198 responseFRCvalue4B.high8 = RxBuffer[4]; 199 #else 200 responseFRCvalue = RxBuffer[1]; 201 responseFRCvalue2B.low8 = RxBuffer[1]; 202 responseFRCvalue2B.high8 = RxBuffer[2]; 203 #endif 204 } 205 206 StopSpiTransaction(); 207 break; 208 209 // ------------------------------------------------- 210 case DpaEvent_FrcResponseTime: 211 // Called to get FRC response time 212 // ToDo - Improve, make value dynamic? 213 responseFRCvalue = _FRC_RESPONSE_TIME_FIXED; 214 break; 215 216 // ------------------------------------------------- 217 case DpaEvent_DpaRequest: 218 // Called to interpret DPA request for peripherals 219 220 // Initialize HW if needed 221 Init(); 222 // Pass the request to the connected device via UART (must not modify FSR0 till it is used) 223 // Start packet 224 StartSpiTransaction(); 225 TxByte( HDLC_FRM_FLAG_SEQUENCE ); 226 // Send event value 227 TxHdlcByte( DpaEvent_DpaRequest ); 228 // Send DPA variable data length (must not equal to the actual data length sent) 229 TxHdlcByte( _DpaDataLength ); 230 // Send DPA message fields 231 TxHdlcByte( _NADR ); 232 TxHdlcByte( _NADRhigh ); 233 TxHdlcByte( _PNUM ); 234 TxHdlcByte( _PCMD ); 235 TxHdlcByte( _HWPID.low8 ); 236 TxHdlcByte( _HWPID.high8 ); 237 238 // How much data to pass to the device? 239 uns8 dataLength; 240 if ( IsDpaEnumPeripheralsRequest() ) 241 dataLength = sizeof( _DpaMessage.EnumPeripheralsAnswer ); 242 else if ( IsDpaPeripheralInfoRequest() ) 243 dataLength = sizeof( _DpaMessage.PeripheralInfoAnswer ); 244 else 245 // Same amount as available 246 dataLength = _DpaDataLength; 247 248 // FSRx might have been destroyed by Init() 249 setFSR01( _FSR_RF, _FSR_RF ); 250 // Now send the data byte by byte 251 for ( ; dataLength != 0; dataLength-- ) 252 TxHdlcByte( *FSR0++ ); 253 // Stop packet 254 TxByte( HDLC_FRM_FLAG_SEQUENCE ); 255 256 // Start measuring timeout for RxPacket() function 257 startCapture(); 258 259 // Receive the response from the device via UART 260 dataLength = RxPacket( RESPONSE_TIMEOUT ); 261 262 StopSpiTransaction(); 263 264 // Check for timeout and correct event 265 if ( dataLength == 0 || ( RxBuffer[0] & ~( EVENT_RETURN_TRUE | EVENT_RESPONSE_ERROR ) ) != DpaEvent_DpaRequest ) 266 DpaApiReturnPeripheralError( ERROR_FAIL ); 267 268 // Report DPA error? 269 if ( ( RxBuffer[0] & EVENT_RESPONSE_ERROR ) != 0 ) 270 DpaApiReturnPeripheralError( RxBuffer[2] ); 271 272 // Get DPA data length field 273 _DpaDataLength = RxBuffer[1]; 274 // Copy DPA response data (all data minus event value and _DpaDataLength value) 275 copyMemoryBlock( &RxBuffer[2], &_DpaMessage.Response.PData[0], dataLength - 2 ); 276 277 // Return TRUE or FALSE 278 #if EVENT_RETURN_TRUE != 0x80 279 #error Cannot optimize 280 #endif 281 // Carry = TRUE of FALSE to return, got from EVENT_RETURN_TRUE part of the 1st byte which is header 282 W = rl( RxBuffer[0] ); 283 return Carry; 284 } 285 286 return FALSE; 287 } 288 289 //############################################################################################ 290 uns8 RxByteValue; 291 bit RxByte() 292 //############################################################################################ 293 { 294 // SPI byte ready? 295 if ( !BF ) 296 return FALSE; 297 // Get the byte 298 RxByteValue = SSPBUF; 299 // This value make sure HDLC machine will reset 300 SSPBUF = HDLC_FRM_FLAG_SEQUENCE; 301 return TRUE; 302 } 303 304 //############################################################################################ 305 uns8 RxPacket( uns8 timeout ) 306 //############################################################################################ 307 { 308 #define MIN_RX_PACKET_DATA_LENGTH 1 309 #define MAX_RX_PACKET_DATA_LENGTH sizeof( RxBuffer ) 310 311 typedef enum { RXstateWaitHead, RXstatePacket, RXstateEscape } TState; 312 313 TState state = RXstateWaitHead; 314 uns8 rxLength; 315 for ( ; ; ) 316 { 317 clrwdt(); 318 // Timeout? 319 captureTicks(); // Note: must not modify FSR0 320 if ( param3 > timeout ) 321 return 0; 322 323 // If anything received via SPI 324 if ( RxByte() ) 325 { 326 // HDLC machine 327 skip( (uns8)state ); 328 #pragma computedGoto 1 329 goto _RXstateWaitHead; 330 goto _RXstatePacket; 331 goto _RXstateEscape; 332 #pragma computedGoto 0 333 ; 334 // --------------------------- 335 _RXstateEscape: 336 switch ( RxByteValue ) 337 { 338 case HDLC_FRM_FLAG_SEQUENCE: 339 goto _SetRXstatePacket; 340 341 case HDLC_FRM_CONTROL_ESCAPE: 342 _SetRXstateWaitHead: 343 state = RXstateWaitHead; 344 continue; 345 } 346 347 state--; // RXstatePacket 348 RxByteValue ^= HDLC_FRM_ESCAPE_BIT; 349 goto _StoreByte; 350 351 // --------------------------- 352 _RXstatePacket: 353 switch ( RxByteValue ) 354 { 355 case HDLC_FRM_CONTROL_ESCAPE: 356 // RXstateEscape 357 _NextState: 358 state++; 359 360 case HDLC_FRM_FLAG_SEQUENCE: 361 { 362 if ( rxLength >= MIN_RX_PACKET_DATA_LENGTH ) 363 return rxLength; 364 365 goto _SetRXstatePacket; 366 } 367 } 368 369 _StoreByte: 370 if ( rxLength == ( MAX_RX_PACKET_DATA_LENGTH + 2 ) ) 371 goto _SetRXstateWaitHead; 372 373 setINDF0( RxByteValue ); 374 FSR0++; 375 rxLength++; 376 continue; 377 378 // --------------------------- 379 _RXstateWaitHead: 380 if ( RxByteValue == HDLC_FRM_FLAG_SEQUENCE ) 381 { 382 _SetRXstatePacket: 383 rxLength = 0; 384 FSR0 = RxBuffer; 385 state = RXstatePacket; 386 } 387 388 continue; 389 } 390 } 391 } 392 393 //############################################################################################ 394 void StartSpiTransaction() 395 //############################################################################################ 396 { 397 // This value make sure HDLC machine will reset 398 SSPBUF = HDLC_FRM_FLAG_SEQUENCE; 399 // SPI transaction starts 400 SS_PIN = 0; 401 // Some delay to let master wake up (might be removed) 402 waitMS( 1 ); 403 } 404 405 //############################################################################################ 406 void StopSpiTransaction() 407 //############################################################################################ 408 { 409 // SPI transaction is over 410 SS_PIN = 1; 411 // This value make sure HDLC machine will reset 412 SSPBUF = HDLC_FRM_FLAG_SEQUENCE; 413 } 414 415 //############################################################################################ 416 uns8 TxByte( uns8 data ) 417 //############################################################################################ 418 { 419 SSPBUF = data; 420 // WDT reset will handle the timeout 421 while ( !BF ); 422 return SSPBUF; 423 } 424 425 //############################################################################################ 426 void TxHdlcByte( uns8 data ) 427 //############################################################################################ 428 { 429 switch ( data ) 430 { 431 default: 432 TxByte( data ); 433 return; 434 435 case HDLC_FRM_FLAG_SEQUENCE: 436 case HDLC_FRM_CONTROL_ESCAPE: 437 { 438 TxByte( HDLC_FRM_CONTROL_ESCAPE ); 439 TxByte( data ^ HDLC_FRM_ESCAPE_BIT ); 440 return; 441 } 442 } 443 } 444 445 //############################################################################################ 446 void Init() 447 //############################################################################################ 448 { 449 // Initialize SPI 450 if ( !SSPEN ) 451 { 452 // Initialize PINs to inputs and outputs 453 TRISC.5 = 0; // RC5 as output SDO (C8) 454 TRISC.4 = 1; // RC4 as input SDI (C7) 455 TRISC.3 = 1; // RC3 as input SCK (C6) 456 TRISA.5 = 0; // RA5 as output SS (C5) 457 458 // Set idle level of SS 459 SS_PIN = 1; 460 461 // TR module with connected pins? 462 moduleInfo(); 463 if ( bufferINFO[5].7 == 0 ) 464 { 465 // Yes 466 TRISC.6 = 1; // RC6 as input (connected to RA5 in parallel) 467 TRISB.4 = 1; // RB4 as input (connected to RA5 in parallel) 468 TRISC.7 = 1; // RC7 as input (connected to RC5 in parallel) 469 } 470 471 // Setup SPI 472 // SSPSTAT: Transmit occurs on transition from Idle to active clock state 473 SSPSTAT = 0; 474 // No SPI interrupts 475 SSPIE = 0; 476 // SSPCON1: (SSPCON1 cannot be accessed directly due to OS restriction) 477 // SPI Slave mode, clock = SCK pin, SS pin control disabled, SS can be used as I/O pin 478 // Idle state for clock is a low level 479 writeToRAM( &SSPCON1, 0b0.0.1.0.0101 ); 480 } 481 } 482 483 //############################################################################################ 484 // Default Custom DPA Handler header; 2nd include implementing a Code bumper to detect too long code of the Custom DPA Handler (modify the path according to your setup) 485 #include "DPAcustomHandler.h" 486 //############################################################################################