1 // ********************************************************************* 2 // Custom DPA Handler - Bridge using UART * 3 // ********************************************************************* 4 // Copyright (c) MICRORISC s.r.o. 5 // 6 // File: $RCSfile: CustomDpaHandler-Bridge-UART.c,v $ 7 // Version: $Revision: 1.25 $ 8 // Date: $Date: 2022/02/25 09:41:25 $ 9 // 10 // Revision history: 11 // 2022/02/24 Release for DPA 4.17 12 // 2019/03/07 Release for DPA 4.01 13 // 2018/10/25 Release for DPA 3.03 14 // 15 // ********************************************************************* 16 17 // Online DPA documentation https://doc.iqrf.org/DpaTechGuide/ 18 19 // Default IQRF include (modify the path according to your setup) 20 #include "IQRF.h" 21 22 // Default DPA header (modify the path according to your setup) 23 #include "DPA.h" 24 // Default Custom DPA Handler header (modify the path according to your setup) 25 #include "DPAcustomHandler.h" 26 27 // If defined then the initialization message with various useful data for the arduino part is sent at DpaEvent_Init, see code below 28 #define INIT_MESSAGE 29 30 //############################################################################################ 31 /* 32 This handler forwards [1] DpaEvent_DpaRequest and [2] DpaEvent_FrcValue events via UART to the external device. See UART_BAUD for the baud rate setting. 33 The external device is responsible for preparing the proper responses to these events. It sends the response back to the handler via UART. 34 Handler then responses with the response prepared by the external device back to the IQRF network. 35 36 There is an example CustomDpaHandler-Bridge-UART.ino for Arduino that shows how the external device prepares the response. In this case the device behaves as Standard IQRF Sensor. 37 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): 38 #IQRF-BB-01 Arduino 39 GND GND 40 +3V3/+5V 3.3V 41 MISO (C8=RX) TX -> Digital 1 42 SS (C5=TX) RX <- Digital 0 43 44 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. 45 Please see https://www.iqrf.org/DpaTechGuide/#2.3.2%20UART for details. 46 47 The handler and the external device pack a different set of information to the packet depending on the event. 48 49 [1] DpaEvent_DpaRequest 50 The packet (data part) sent to the device has the following structure: 51 #Byte Content Notes 52 0 0 = DpaEvent_DpaRequest 53 1 _DpaDataLength 54 2 NADR 55 3 NADRhigh 56 4 PNUM 57 5 PCMD 58 6 HWPID.low8 59 7 HWPID.high8 60 8... PDATA Optional DPA Request data. 61 62 Please note that _DpaDataLength does not have to equal (in case of enumeration DPA Requests) length of PDATA. 63 The handler waits maximum RESPONSE_TIMEOUT ms for the response from the external device otherwise ERROR_FAIL is returned. 64 65 The device responses with a packet of the following structure: 66 #Byte Content Notes 67 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 returns DPA error specified at byte #2 i.e. PDATA[0] 68 1 _DpaDataLength 69 2... PDATA Optional DPA Response data. 70 71 [2] DpaEvent_FrcValue 72 The 32 bytes long packet (data part) sent to the device has the following structure: 73 #Byte Content Notes 74 0 10 = DpaEvent_FrcValue 75 1 RFC Command 76 2...31 FRC user data 77 78 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. 79 80 The device responses with a 5 bytes long packet of the following structure: 81 #Byte Content Notes 82 0 10 DpaEvent_DpaRequest 83 1...4 FRC value Content will be written to the responseFRCvalue, responseFRCvalue2B, responseFRCvalue4B variables. Coded using Little Endian. 84 */ 85 //############################################################################################ 86 87 // UART baud rate 88 #define UART_BAUD 115200 89 90 // DPA response timeout from external device [ms] 91 #define RESPONSE_TIMEOUT ( 30 / 10 ) 92 // FRC value response timeout from external device [ms] 93 #define FRC_RESPONSE_TIMEOUT ( 20 / 10 ) 94 // DPA reported fixed FRC response time 95 #define _FRC_RESPONSE_TIME_FIXED _FRC_RESPONSE_TIME_40_MS 96 97 //############################################################################################ 98 99 // Division macro with rounding 100 #define DIV(Dividend,Divisor) (((Dividend+((Divisor)>>1))/(Divisor))) 101 // PIC baud register computation 102 #define UART_SPBRG_VALUE( Baud ) ( DIV( F_OSC, ( ( ( uns24 )4 ) * ( Baud ) ) ) - 1 ) 103 104 // HDLC byte stuffing bytes 105 // Flag Sequence 106 #define HDLC_FRM_FLAG_SEQUENCE 0x7e 107 // Asynchronous Control Escape 108 #define HDLC_FRM_CONTROL_ESCAPE 0x7d 109 // Asynchronous transparency modifier 110 #define HDLC_FRM_ESCAPE_BIT 0x20 111 112 // Flag to DpaEvent_DpaRequest event value to indicate return TRUE not FALSE 113 #define EVENT_RETURN_TRUE 0x80 114 // Flag to DpaEvent_DpaRequest event value to report error, error value is the 1st data byte 115 #define EVENT_RESPONSE_ERROR 0x40 116 117 //############################################################################################ 118 119 // Buffer used for exchange data with external device 120 #define RxBuffer bufferCOM 121 // Sends byte to the external device 122 void TxByte( uns8 data ); 123 // Sends HDLC byte to the external device 124 void TxHdlcByte( uns8 data ); 125 // Receives data from external device, returns length, 0 if timeout occurred 126 uns8 RxPacket( uns8 timeout ); 127 // Initialization 128 void Init(); 129 130 //############################################################################################ 131 bit CustomDpaHandler() 132 //############################################################################################ 133 { 134 // Handler presence mark 135 clrwdt(); 136 137 // Detect DPA event to handle (unused event handlers can be commented out or even deleted) 138 switch ( GetDpaEvent() ) 139 { 140 // ------------------------------------------------- 141 // Handler these unused events as fast as possible 142 case DpaEvent_Idle: 143 Init(); 144 145 case DpaEvent_Interrupt: 146 return Carry; 147 148 // ------------------------------------------------- 149 #ifdef INIT_MESSAGE 150 case DpaEvent_Init: 151 152 Init(); 153 // Start packet 154 TxByte( HDLC_FRM_FLAG_SEQUENCE ); 155 // Send event value 156 TxHdlcByte( DpaEvent_Init ); 157 158 getNetworkParams(); 159 // Address of the device in network 160 TxHdlcByte( ntwADDR ); 161 // VRN of the device 162 TxHdlcByte( ntwVRN ); 163 164 moduleInfo(); 165 // MID 166 TxHdlcByte( ModuleInfo.MID[0] ); 167 TxHdlcByte( ModuleInfo.MID[1] ); 168 TxHdlcByte( ModuleInfo.MID[2] ); 169 TxHdlcByte( ModuleInfo.MID[3] ); 170 171 // Stop packet 172 TxByte( HDLC_FRM_FLAG_SEQUENCE ); 173 return Carry; 174 #endif 175 176 // ------------------------------------------------- 177 case DpaEvent_FrcValue: 178 // Called to get FRC value 179 180 // Initialize HW if needed 181 Init(); 182 // Start measuring timeout for RxPacket() function 183 startCapture(); 184 185 // Start packet 186 TxByte( HDLC_FRM_FLAG_SEQUENCE ); 187 // Send event value 188 TxHdlcByte( DpaEvent_FrcValue ); 189 // Send FRC command 190 TxHdlcByte( _PCMD ); 191 // Now send all FRC user data 192 uns8 loop = sizeof( DataOutBeforeResponseFRC ); 193 FSR0 = &DataOutBeforeResponseFRC[0]; 194 do 195 { 196 TxHdlcByte( *FSR0++ ); 197 } while ( --loop != 0 ); 198 // Stop packet 199 TxByte( HDLC_FRM_FLAG_SEQUENCE ); 200 201 // Receive the FRC value from the device via UART, length must equal to the event value + 4 FRC value bytes 202 if ( RxPacket( FRC_RESPONSE_TIMEOUT ) == ( sizeof( uns8 ) + sizeof( uns32 ) ) && RxBuffer[0] == DpaEvent_FrcValue ) 203 { 204 // Return FRC values to DPA 205 #if IQRFOS >= 403 206 responseFRCvalue4B.low8 = RxBuffer[1]; 207 responseFRCvalue4B.midL8 = RxBuffer[2]; 208 responseFRCvalue4B.midH8 = RxBuffer[3]; 209 responseFRCvalue4B.high8 = RxBuffer[4]; 210 #else 211 responseFRCvalue = RxBuffer[1]; 212 responseFRCvalue2B.low8 = RxBuffer[1]; 213 responseFRCvalue2B.high8 = RxBuffer[2]; 214 #endif 215 } 216 217 break; 218 219 // ------------------------------------------------- 220 case DpaEvent_FrcResponseTime: 221 // Called to get FRC response time 222 // ToDo - Improve, make value dynamic? 223 responseFRCvalue = _FRC_RESPONSE_TIME_FIXED; 224 break; 225 226 // ------------------------------------------------- 227 case DpaEvent_DpaRequest: 228 // Called to interpret DPA request for peripherals 229 230 // Initialize HW if needed 231 Init(); 232 // Pass the request to the connected device via UART (must not modify FSR0 till it is used) 233 // Start packet 234 TxByte( HDLC_FRM_FLAG_SEQUENCE ); 235 // Send event value 236 TxHdlcByte( DpaEvent_DpaRequest ); 237 // Send DPA variable data length (must not equal to the actual data length sent) 238 TxHdlcByte( _DpaDataLength ); 239 // Send DPA message fields 240 TxHdlcByte( _NADR ); 241 TxHdlcByte( _NADRhigh ); 242 TxHdlcByte( _PNUM ); 243 TxHdlcByte( _PCMD ); 244 TxHdlcByte( _HWPID.low8 ); 245 TxHdlcByte( _HWPID.high8 ); 246 247 // How much data to pass to the device? 248 uns8 dataLength; 249 if ( IsDpaEnumPeripheralsRequest() ) 250 dataLength = sizeof( _DpaMessage.EnumPeripheralsAnswer ); 251 else if ( IsDpaPeripheralInfoRequest() ) 252 dataLength = sizeof( _DpaMessage.PeripheralInfoAnswer ); 253 else 254 // Same amount as available 255 dataLength = _DpaDataLength; 256 257 // FSRx might have been destroyed by Init() 258 setFSR01( _FSR_RF, _FSR_RF ); 259 // Now send the data byte by byte 260 for ( ; dataLength != 0; dataLength-- ) 261 TxHdlcByte( *FSR0++ ); 262 // Stop packet 263 TxByte( HDLC_FRM_FLAG_SEQUENCE ); 264 265 // Start measuring timeout for RxPacket() function 266 startCapture(); 267 268 // Receive the response from the device via UART 269 dataLength = RxPacket( RESPONSE_TIMEOUT ); 270 // Check for timeout and correct event 271 if ( dataLength == 0 || ( RxBuffer[0] & ~( EVENT_RETURN_TRUE | EVENT_RESPONSE_ERROR ) ) != DpaEvent_DpaRequest ) 272 DpaApiReturnPeripheralError( ERROR_FAIL ); 273 274 // Report DPA error? 275 if ( ( RxBuffer[0] & EVENT_RESPONSE_ERROR ) != 0 ) 276 DpaApiReturnPeripheralError( RxBuffer[2] ); 277 278 // Get DPA data length field 279 _DpaDataLength = RxBuffer[1]; 280 // Copy DPA response data (all data minus event value and _DpaDataLength value) 281 copyMemoryBlock( &RxBuffer[2], &_DpaMessage.Response.PData[0], dataLength - 2 ); 282 283 // Return TRUE or FALSE 284 #if EVENT_RETURN_TRUE != 0x80 285 #error Cannot optimize 286 #endif 287 // Carry = TRUE of FALSE to return, got from EVENT_RETURN_TRUE part of the 1st byte which is header 288 W = rl( RxBuffer[0] ); 289 return Carry; 290 291 // ------------------------------------------------- 292 case DpaEvent_DisableInterrupts: 293 // Called when device needs all hardware interrupts to be disabled (before Reset, Restart, LoadCode, Remove bond and run RFPGM) 294 295 // Disable UART so on the next boot it will be open again 296 SPEN = FALSE; 297 break; 298 } 299 300 return FALSE; 301 } 302 303 //############################################################################################ 304 uns8 RxByteValue; 305 // RxByteValue = read byte, Carry = result 306 bit RxByte() 307 //############################################################################################ 308 { 309 #pragma updateBank exit = UserBank_01 310 311 if ( FERR || OERR ) 312 { 313 W = _RCREG; 314 315 if ( OERR ) 316 { 317 CREN = 0; 318 CREN = 1; 319 } 320 321 _returnFALSE: 322 return FALSE; 323 } 324 325 if ( !RCIF ) 326 goto _returnFALSE; 327 328 RxByteValue = _RCREG; 329 return TRUE; 330 } 331 332 //############################################################################################ 333 uns8 RxPacket( uns8 timeout ) 334 //############################################################################################ 335 { 336 #define MIN_RX_PACKET_DATA_LENGTH 1 337 #define MAX_RX_PACKET_DATA_LENGTH sizeof( RxBuffer ) 338 339 typedef enum { RXstateWaitHead, RXstatePacket, RXstateEscape } TState; 340 341 // Make sure buffered UART RX is empty 342 RxByte(); 343 RxByte(); 344 345 TState state = RXstateWaitHead; 346 uns8 rxLength; 347 for ( ; ; ) 348 { 349 clrwdt(); 350 // Timeout? 351 captureTicks(); // Note: must not modify FSR0 352 if ( param3 > timeout ) 353 return 0; 354 355 // If anything received via UART 356 if ( RxByte() ) 357 { 358 // HDLC machine 359 skip( (uns8)state ); 360 #pragma computedGoto 1 361 goto _RXstateWaitHead; 362 goto _RXstatePacket; 363 goto _RXstateEscape; 364 #pragma computedGoto 0 365 ; 366 // --------------------------- 367 _RXstateEscape: 368 switch ( RxByteValue ) 369 { 370 case HDLC_FRM_FLAG_SEQUENCE: 371 goto _SetRXstatePacket; 372 373 case HDLC_FRM_CONTROL_ESCAPE: 374 _SetRXstateWaitHead: 375 state = RXstateWaitHead; 376 continue; 377 } 378 379 state--; // RXstatePacket 380 RxByteValue ^= HDLC_FRM_ESCAPE_BIT; 381 goto _StoreByte; 382 383 // --------------------------- 384 _RXstatePacket: 385 switch ( RxByteValue ) 386 { 387 case HDLC_FRM_CONTROL_ESCAPE: 388 // RXstateEscape 389 state++; 390 continue; 391 392 case HDLC_FRM_FLAG_SEQUENCE: 393 { 394 if ( rxLength >= MIN_RX_PACKET_DATA_LENGTH ) 395 return rxLength; 396 397 goto _SetRXstatePacket; 398 } 399 } 400 401 _StoreByte: 402 if ( rxLength == ( MAX_RX_PACKET_DATA_LENGTH + 2 ) ) 403 goto _SetRXstateWaitHead; 404 405 setINDF0( RxByteValue ); 406 FSR0++; 407 rxLength++; 408 continue; 409 410 // --------------------------- 411 _RXstateWaitHead: 412 if ( RxByteValue == HDLC_FRM_FLAG_SEQUENCE ) 413 { 414 _SetRXstatePacket: 415 rxLength = 0; 416 FSR0 = RxBuffer; 417 state = RXstatePacket; 418 } 419 420 continue; 421 } 422 } 423 } 424 425 //############################################################################################ 426 void TxByte( uns8 data ) 427 //############################################################################################ 428 { 429 while ( !TXIF ); 430 _TXREG = data; 431 } 432 433 //############################################################################################ 434 void TxHdlcByte( uns8 data ) 435 //############################################################################################ 436 { 437 switch ( data ) 438 { 439 default: 440 TxByte( data ); 441 return; 442 443 case HDLC_FRM_FLAG_SEQUENCE: 444 case HDLC_FRM_CONTROL_ESCAPE: 445 { 446 TxByte( HDLC_FRM_CONTROL_ESCAPE ); 447 TxByte( data ^ HDLC_FRM_ESCAPE_BIT ); 448 return; 449 } 450 } 451 } 452 453 //############################################################################################ 454 void Init() 455 //############################################################################################ 456 { 457 // Initialize UART 458 if ( !SPEN ) 459 { 460 // Connected TR pins (e.g. TR72D)? 461 moduleInfo(); 462 if ( !bufferINFO[5].7 ) 463 { 464 // Set them as inputs 465 TRISC.5 = 1; 466 TRISA.5 = 1; 467 TRISB.4 = 1; 468 } 469 470 // RX input 471 TRISC.7 = 1; 472 // TX output 473 TRISC.6 = 0; 474 475 #if defined( TR7xG ) 476 UART1MD = 0; 477 unlockPPS(); 478 RC6PPS = 0x10; // TX (Note: RX does not have to be set, use default) 479 lockPPS(); 480 #endif 481 482 // Set baud rate 483 _SPBRGL = UART_SPBRG_VALUE( UART_BAUD ) & 0xff; 484 _SPBRGH = UART_SPBRG_VALUE( UART_BAUD ) >> 8; 485 // baud rate control setup: BRG16 = 1 486 #if defined( TR7xG ) 487 setBAUD1CON( 0b0000.1.000 ); 488 #else 489 BAUDCON = 0b0000.1.000; 490 #endif 491 492 // CSRC TX9 TXEN SYNC SENDB BRGH TRMT TX9D 493 // TXEN = 1 494 // BRGH = 1 495 // async UART, high speed, 8 bit, TX enabled 496 #if defined( TR7xG ) 497 setTX1STA( 0b0010.0100 ); 498 #else 499 TXSTA = 0b0010.0100; 500 #endif 501 502 // SPEN RX9 SREN CREN ADDEN FERR OERR RX9D 503 // SPEN = 1 504 // CREN = 1 505 // Continuous receive, enable port, 8 bit 506 _RCSTA = 0b1001.0000; 507 } 508 } 509 510 //############################################################################################ 511 // 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) 512 #include "DPAcustomHandler.h" 513 //############################################################################################