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