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