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.17 $ 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 UART to the external device. See UART_BAUD for the baud rate setting. 29 The external device is responsible for preparing the proper responses to these events. It sends the response back to the handler via UART. 30 Handler then responses with the response prepared by the external device back to the IQRF network. 31 32 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. 33 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): 34 #IQRF-BB-01 Arduino 35 GND GND 36 +3V3/+5V 3.3V 37 MISO (C8=RX) TX -> Digital 1 38 SS (C5=TX) RX <- Digital 0 39 40 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. 41 Please see https://www.iqrf.org/DpaTechGuide/#2.3.2%20UART for details. 42 43 The handler and the external device pack a different set of information to the packet depending on the event. 44 45 [1] DpaEvent_DpaRequest 46 The packet (data part) sent to the device has the following structure: 47 #Byte Content Notes 48 0 0 = DpaEvent_DpaRequest 49 1 _DpaDataLength 50 2 NADR 51 3 NADRhigh 52 4 PNUM 53 5 PCMD 54 6 HWPID.low8 55 7 HWPID.high8 56 8... PDATA Optional DPA Request data. 57 58 Please note that _DpaDataLength does not have to equal (in case of enumeration DPA Requests) length of PDATA. 59 The handler waits maximum RESPONSE_TIMEOUT ms for the response from the external device otherwise ERROR_FAIL is returned. 60 61 The device responses with a packet of the following structure: 62 #Byte Content Notes 63 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] 64 1 _DpaDataLength 65 2... PDATA Optional DPA Response data. 66 67 [2] DpaEvent_FrcValue 68 The 32 bytes long packet (data part) sent to the device has the following structure: 69 #Byte Content Notes 70 0 10 = DpaEvent_FrcValue 71 1 RFC Command 72 2...31 FRC user data 73 74 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. 75 76 The device responses with a 5 bytes long packet of the following structure: 77 #Byte Content Notes 78 0 10 DpaEvent_DpaRequest 79 1...4 FRC value Content will be written to the responseFRCvalue, responseFRCvalue2B, responseFRCvalue4B variables. Coded using Little Endian. 80 */ 81 //############################################################################################ 82 83 // UART baud rate 84 #define UART_BAUD 115200 85 86 // DPA response timeout from external device [ms] 87 #define RESPONSE_TIMEOUT ( 30 / 10 ) 88 // FRC value response timeout from external device [ms] 89 #define FRC_RESPONSE_TIMEOUT ( 20 / 10 ) 90 // DPA reported fixed FRC response time 91 #define _FRC_RESPONSE_TIME_FIXED _FRC_RESPONSE_TIME_40_MS 92 93 //############################################################################################ 94 95 // Division macro with rounding 96 #define DIV(Dividend,Divisor) (((Dividend+((Divisor)>>1))/(Divisor))) 97 // PIC baud register computation 98 #define UART_SPBRG_VALUE( Baud ) ( DIV( F_OSC, ( ( ( uns24 )4 ) * ( Baud ) ) ) - 1 ) 99 100 // HDLC byte stuffing bytes 101 // Flag Sequence 102 #define HDLC_FRM_FLAG_SEQUENCE 0x7e 103 // Asynchronous Control Escape 104 #define HDLC_FRM_CONTROL_ESCAPE 0x7d 105 // Asynchronous transparency modifier 106 #define HDLC_FRM_ESCAPE_BIT 0x20 107 108 // Flag to DpaEvent_DpaRequest event value to indicate return TRUE not FALSE 109 #define EVENT_RETURN_TRUE 0x80 110 // Flag to DpaEvent_DpaRequest event value to report error, error value is the 1st data byte 111 #define EVENT_RESPONSE_ERROR 0x40 112 113 //############################################################################################ 114 115 // Buffer used for exchange data with external device 116 #define RxBuffer bufferCOM 117 // Sends byte to the external device 118 void TxByte( uns8 data ); 119 // Sends HDLC byte to the external device 120 void TxHdlcByte( uns8 data ); 121 // Receives data from external device, returns length, 0 if timeout occurred 122 uns8 RxPacket( uns8 timeout ); 123 // Initialization 124 void Init(); 125 126 //############################################################################################ 127 bit CustomDpaHandler() 128 //############################################################################################ 129 { 130 // Handler presence mark 131 clrwdt(); 132 133 // Detect DPA event to handle (unused event handlers can be commented out or even deleted) 134 switch ( GetDpaEvent() ) 135 { 136 // ------------------------------------------------- 137 // Handler these unused events as fast as possible 138 case DpaEvent_Idle: 139 Init(); 140 141 case DpaEvent_Interrupt: 142 return Carry; 143 144 // ------------------------------------------------- 145 case DpaEvent_FrcValue: 146 // Called to get FRC value 147 148 // Initialize HW if needed 149 Init(); 150 // Start measuring timeout for RxPacket() function 151 startCapture(); 152 153 // Start packet 154 TxByte( HDLC_FRM_FLAG_SEQUENCE ); 155 // Send event value 156 TxHdlcByte( DpaEvent_FrcValue ); 157 // Send FRC command 158 TxHdlcByte( _PCMD ); 159 // Now send all FRC user data 160 uns8 loop = sizeof( DataOutBeforeResponseFRC ); 161 FSR0 = &DataOutBeforeResponseFRC[0]; 162 do 163 { 164 TxHdlcByte( *FSR0++ ); 165 } while ( --loop != 0 ); 166 // Stop packet 167 TxByte( HDLC_FRM_FLAG_SEQUENCE ); 168 169 // Receive the FRC value from the device via UART, length must equal to the event value + 4 FRC value bytes 170 if ( RxPacket( FRC_RESPONSE_TIMEOUT ) == ( sizeof( uns8 ) + sizeof( uns32 ) ) && RxBuffer[0] == DpaEvent_FrcValue ) 171 { 172 // Return FRC values to DPA 173 #if IQRFOS >= 403 174 responseFRCvalue4B.low8 = RxBuffer[1]; 175 responseFRCvalue4B.midL8 = RxBuffer[2]; 176 responseFRCvalue4B.midH8 = RxBuffer[3]; 177 responseFRCvalue4B.high8 = RxBuffer[4]; 178 #else 179 responseFRCvalue = RxBuffer[1]; 180 responseFRCvalue2B.low8 = RxBuffer[1]; 181 responseFRCvalue2B.high8 = RxBuffer[2]; 182 #endif 183 } 184 185 break; 186 187 // ------------------------------------------------- 188 case DpaEvent_FrcResponseTime: 189 // Called to get FRC response time 190 // ToDo - Improve, make value dynamic? 191 responseFRCvalue = _FRC_RESPONSE_TIME_FIXED; 192 break; 193 194 // ------------------------------------------------- 195 case DpaEvent_DpaRequest: 196 // Called to interpret DPA request for peripherals 197 198 // Initialize HW if needed 199 Init(); 200 // Pass the request to the connected device via UART (must not modify FSR0 till it is used) 201 // Start packet 202 TxByte( HDLC_FRM_FLAG_SEQUENCE ); 203 // Send event value 204 TxHdlcByte( DpaEvent_DpaRequest ); 205 // Send DPA variable data length (must not equal to the actual data length sent) 206 TxHdlcByte( _DpaDataLength ); 207 // Send DPA message fields 208 TxHdlcByte( _NADR ); 209 TxHdlcByte( _NADRhigh ); 210 TxHdlcByte( _PNUM ); 211 TxHdlcByte( _PCMD ); 212 TxHdlcByte( _HWPID.low8 ); 213 TxHdlcByte( _HWPID.high8 ); 214 215 // How much data to pass to the device? 216 uns8 dataLength; 217 if ( IsDpaEnumPeripheralsRequest() ) 218 dataLength = sizeof( _DpaMessage.EnumPeripheralsAnswer ); 219 else if ( IsDpaPeripheralInfoRequest() ) 220 dataLength = sizeof( _DpaMessage.PeripheralInfoAnswer ); 221 else 222 // Same amount as available 223 dataLength = _DpaDataLength; 224 225 // FSRx might have been destroyed by Init() 226 setFSR01( _FSR_RF, _FSR_RF ); 227 // Now send the data byte by byte 228 for ( ; dataLength != 0; dataLength-- ) 229 TxHdlcByte( *FSR0++ ); 230 // Stop packet 231 TxByte( HDLC_FRM_FLAG_SEQUENCE ); 232 233 // Start measuring timeout for RxPacket() function 234 startCapture(); 235 236 // Receive the response from the device via UART 237 dataLength = RxPacket( RESPONSE_TIMEOUT ); 238 // Check for timeout and correct event 239 if ( dataLength == 0 || ( RxBuffer[0] & ~( EVENT_RETURN_TRUE | EVENT_RESPONSE_ERROR ) ) != DpaEvent_DpaRequest ) 240 DpaApiReturnPeripheralError( ERROR_FAIL ); 241 242 // Report DPA error? 243 if ( ( RxBuffer[0] & EVENT_RESPONSE_ERROR ) != 0 ) 244 DpaApiReturnPeripheralError( RxBuffer[2] ); 245 246 // Get DPA data length field 247 _DpaDataLength = RxBuffer[1]; 248 // Copy DPA response data (all data minus event value and _DpaDataLength value) 249 copyMemoryBlock( &RxBuffer[2], &_DpaMessage.Response.PData[0], dataLength - 2 ); 250 251 // Return TRUE or FALSE 252 #if EVENT_RETURN_TRUE != 0x80 253 #error Cannot optimize 254 #endif 255 // Carry = TRUE of FALSE to return, got from EVENT_RETURN_TRUE part of the 1st byte which is header 256 W = rl( RxBuffer[0] ); 257 return Carry; 258 259 // ------------------------------------------------- 260 case DpaEvent_DisableInterrupts: 261 // Called when device needs all hardware interrupts to be disabled (before Reset, Restart, LoadCode, Remove bond and run RFPGM) 262 263 // Disable UART so on the next boot it will be open again 264 SPEN = FALSE; 265 break; 266 } 267 268 return FALSE; 269 } 270 271 //############################################################################################ 272 uns8 RxByteValue; 273 // RxByteValue = read byte, Carry = result 274 bit RxByte() 275 //############################################################################################ 276 { 277 #pragma updateBank exit = UserBank_01 278 279 if ( FERR || OERR ) 280 { 281 W = RCREG; 282 283 if ( OERR ) 284 { 285 CREN = 0; 286 CREN = 1; 287 } 288 289 _returnFALSE: 290 return FALSE; 291 } 292 293 if ( !RCIF ) 294 goto _returnFALSE; 295 296 RxByteValue = RCREG; 297 return TRUE; 298 } 299 300 //############################################################################################ 301 uns8 RxPacket( uns8 timeout ) 302 //############################################################################################ 303 { 304 #define MIN_RX_PACKET_DATA_LENGTH 1 305 #define MAX_RX_PACKET_DATA_LENGTH sizeof( RxBuffer ) 306 307 typedef enum { RXstateWaitHead, RXstatePacket, RXstateEscape } TState; 308 309 // Make sure buffered UART RX is empty 310 RxByte(); 311 RxByte(); 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 UART 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 state++; 358 continue; 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 TxByte( uns8 data ) 395 //############################################################################################ 396 { 397 while ( !TXIF ); 398 TXREG = data; 399 } 400 401 //############################################################################################ 402 void TxHdlcByte( uns8 data ) 403 //############################################################################################ 404 { 405 switch ( data ) 406 { 407 default: 408 TxByte( data ); 409 return; 410 411 case HDLC_FRM_FLAG_SEQUENCE: 412 case HDLC_FRM_CONTROL_ESCAPE: 413 { 414 TxByte( HDLC_FRM_CONTROL_ESCAPE ); 415 TxByte( data ^ HDLC_FRM_ESCAPE_BIT ); 416 return; 417 } 418 } 419 } 420 421 //############################################################################################ 422 void Init() 423 //############################################################################################ 424 { 425 // Initialize UART 426 if ( !SPEN ) 427 { 428 // Connected TR pins (e.g. TR72D)? 429 moduleInfo(); 430 if ( !bufferINFO[5].7 ) 431 { 432 // Set them as inputs 433 TRISC.5 = 1; 434 TRISA.5 = 1; 435 TRISB.4 = 1; 436 } 437 438 // RX input 439 TRISC.7 = 1; 440 // TX output 441 TRISC.6 = 0; 442 443 // Set baud rate 444 SPBRGL = UART_SPBRG_VALUE( UART_BAUD ) & 0xff; 445 SPBRGH = UART_SPBRG_VALUE( UART_BAUD ) >> 8; 446 // baud rate control setup: BRG16 = 1 447 BAUDCON = 0b0000.1.000; 448 449 // CSRC TX9 TXEN SYNC SENDB BRGH TRMT TX9D 450 // TXEN = 1 451 // BRGH = 1 452 // async UART, high speed, 8 bit, TX enabled 453 TXSTA = 0b0010.0100; 454 455 // SPEN RX9 SREN CREN ADDEN FERR OERR RX9D 456 // SPEN = 1 457 // CREN = 1 458 // Continuous receive, enable port, 8 bit 459 RCSTA = 0b1001.0000; 460 } 461 } 462 463 //############################################################################################ 464 // 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) 465 #include "DPAcustomHandler.h" 466 //############################################################################################