1 // ************************************************************************** 2 // Custom DPA Handler code example - User peripheral supporting UART HW * 3 // ************************************************************************** 4 // Copyright (c) MICRORISC s.r.o. 5 // 6 // File: $RCSfile: CustomDpaHandler-UserPeripheral-HW-UART.c,v $ 7 // Version: $Revision: 1.31 $ 8 // Date: $Date: 2025/09/22 15:45:41 $ 9 // 10 // Revision history: 11 // 2023/03/07 Release for DPA 4.30 12 // 2022/10/05 Release for DPA 4.18 13 // 2022/02/24 Release for DPA 4.17 14 // 2017/08/14 Release for DPA 3.01 15 // 16 // ********************************************************************* 17 18 // Online DPA documentation https://doc.iqrf.org/DpaTechGuide/ 19 20 // This example implements the user peripheral supporting HW UART of the PIC 21 // This example works only at STD mode, not at LP mode 22 // RX = RC7 23 // TX = RC6 24 25 // Default IQRF include (modify the path according to your setup) 26 #include "IQRF.h" 27 28 // Default DPA header (modify the path according to your setup) 29 #include "DPA.h" 30 // Default Custom DPA Handler header (modify the path according to your setup) 31 #include "DPAcustomHandler.h" 32 33 //############################################################################################ 34 35 // TX byte to UART 36 void TxUART( uns8 data ); 37 // RX byte from UART; W = read byte, Carry = byte was read 38 bit RxUART(); 39 40 //############################################################################################ 41 42 // UART baud rate 43 #define UART_BAUD 19200 44 45 // Number of UART received bytes 46 uns8 RxDataLengthUART; 47 48 // Length of RX and TX buffers, must be power of 2 49 #define UART_BUFFER_LENGTH 16 50 51 #if 0 != ( UART_BUFFER_LENGTH & ( UART_BUFFER_LENGTH - 1 ) ) 52 #error UART_BUFFER_LENGTH is not power of 2 53 #endif 54 55 // Rx 56 // Circular RX UART buffer pointers 57 uns8 RxBufferPointerStartUART; 58 uns8 RxBufferPointerEndUART; 59 uns8 RxBufferUART[UART_BUFFER_LENGTH] /*@ PeripheralRam[0]*/; 60 61 // TX 62 // Circular TX UART buffer pointers 63 uns8 TxBufferPointerStartUART; 64 uns8 TxBufferPointerEndUART; 65 uns8 TxBufferUART[UART_BUFFER_LENGTH] /*@ PeripheralRam[UART_BUFFER_LENGTH]*/; 66 67 // Division macro with rounding 68 #define DIV(Dividend,Divisor) (((Dividend+((Divisor)>>1))/(Divisor))) 69 // PIC baud register computation 70 #define UART_SPBRG_VALUE( Baud ) ( DIV( F_OSC, ( ( ( uns24 )4 ) * ( Baud ) ) ) - 1 ) 71 72 // Must be the 1st defined function in the source code in order to be placed at the correct FLASH location! 73 //############################################################################################ 74 bit CustomDpaHandler() 75 //############################################################################################ 76 { 77 // Handler presence mark 78 clrwdt(); 79 80 // TXIE state before sleep 81 static bit wasTXIE; 82 83 // Detect DPA event to handle 84 switch ( GetDpaEvent() ) 85 { 86 // ------------------------------------------------- 87 case DpaEvent_Interrupt: 88 // Do an extra quick background interrupt work 89 // ! The time spent handling this event is critical.If there is no interrupt to handle return immediately otherwise keep the code as fast as possible. 90 // ! Make sure the event is the 1st case in the main switch statement at the handler routine.This ensures that the event is handled as the 1st one. 91 // ! It is desirable that this event is handled with immediate return even if it is not used by the custom handler because the Interrupt event is raised on every MCU interrupt and the “empty” return handler ensures the shortest possible interrupt routine response time. 92 // ! Only global variables or local ones marked by static keyword can be used to allow reentrancy. 93 // ! Make sure race condition does not occur when accessing those variables at other places. 94 // ! Make sure( inspect.lst file generated by C compiler ) compiler does not create any hidden temporary local variable( occurs when using division, multiplication or bit shifts ) at the event handler code.The name of such variable is usually Cnumbercnt. 95 // ! Do not call any OS functions except setINDFx(). 96 // ! Do not use any OS variables especially for writing access. 97 // ! All above rules apply also to any other function being called from the event handler code, although calling any function from Interrupt event is not recommended because of additional MCU stack usage. 98 99 // ----------------------------------------------------------------------------------------- 100 // UART Receive 101 // 102 if ( RCIF ) 103 { 104 // We ignore FERR 105 FSR1L = _RCREG; 106 // Put the received byte in circular buffer 107 if ( RxDataLengthUART < UART_BUFFER_LENGTH ) 108 { 109 // One more byte received 110 RxDataLengthUART++; 111 // Prepare pointer 112 FSR0 = RxBufferUART + RxBufferPointerEndUART; 113 // Recalculate tail pointer 114 // Optimization: Same as (UART_BUFFER_LENGTH is power of 2) : RxBufferPointerEndUART = ( RxBufferPointerEndUART + 1 ) % UART_BUFFER_LENGTH; 115 RxBufferPointerEndUART++; 116 RxBufferPointerEndUART &= ~UART_BUFFER_LENGTH; 117 // Store byte 118 setINDF0( FSR1L ); 119 } 120 } 121 122 // Overrun recovery (we do it after receiving UART byte in order to receive it as soon as possible) 123 if ( OERR ) 124 CREN = 0; 125 126 // Seems excess, but at the end it is shorted and faster than having this statement at else branch 127 CREN = 1; 128 129 // ----------------------------------------------------------------------------------------- 130 // UART Transmit 131 // 132 if ( TXIF && TXIE ) 133 { 134 // Send byte from circular buffer to the UART 135 FSR0 = TxBufferUART + TxBufferPointerStartUART; 136 TxBufferPointerStartUART = ( TxBufferPointerStartUART + 1 ) % UART_BUFFER_LENGTH; 137 // Buffer empty? 138 if ( TxBufferPointerStartUART == TxBufferPointerEndUART ) 139 TXIE = FALSE; 140 // TX the byte 141 _TXREG = *FSR0; 142 } 143 144 // Return value does not matter 145 return Carry; 146 147 148 // ------------------------------------------------- 149 case DpaEvent_DpaRequest: 150 // Called to interpret DPA request for peripherals 151 // ------------------------------------------------- 152 // Peripheral enumeration 153 if ( IsDpaEnumPeripheralsRequest() ) 154 { 155 // We implement 1 user peripheral 156 _DpaMessage.EnumPeripheralsAnswer.UserPerNr |= 1; 157 FlagUserPer( _DpaMessage.EnumPeripheralsAnswer.UserPer, PNUM_USER + 0 ); 158 _DpaMessage.EnumPeripheralsAnswer.HWPID |= 0x000F; 159 _DpaMessage.EnumPeripheralsAnswer.HWPIDver |= 0x0000; 160 161 DpaHandleReturnTRUE: 162 return TRUE; 163 } 164 // ------------------------------------------------- 165 // Get information about peripheral 166 else if ( IsDpaPeripheralInfoRequest() ) 167 { 168 if ( _PNUM == PNUM_USER + 0 ) 169 { 170 _DpaMessage.PeripheralInfoAnswer.PerT = PERIPHERAL_TYPE_USER_AREA; 171 _DpaMessage.PeripheralInfoAnswer.PerTE = PERIPHERAL_TYPE_EXTENDED_READ_WRITE; 172 goto DpaHandleReturnTRUE; 173 } 174 175 break; 176 } 177 // ------------------------------------------------- 178 else 179 { 180 // Handle peripheral command 181 if ( _PNUM == PNUM_USER + 0 ) 182 { 183 // Check command 184 switch ( _PCMD ) 185 { 186 // ------------------------------ 187 // Write to UART 188 case 0: 189 // There must be some data to write 190 if ( _DpaDataLength == 0 ) 191 { 192 _ERROR_DATA_LEN: 193 DpaApiReturnPeripheralError( ERROR_DATA_LEN ); 194 } 195 196 // Pointer to data (setFSR1( _FSR_RF ) can be used too to save code) 197 FSR1 = &_DpaMessage.Request.PData[0]; 198 do 199 { 200 // Send one byte 201 TxUART( *FSR1++ ); 202 // Loop all bytes 203 } while ( --_DpaDataLength != 0 ); 204 205 // _DpaDataLength = 0 => no data returned 206 goto DpaHandleReturnTRUE; 207 208 // ------------------------------ 209 // Read from UART 210 case 1: 211 // No data must be sent 212 if ( _DpaDataLength != 0 ) 213 goto _ERROR_DATA_LEN; 214 215 // Pointer to the output buffer (setFSR1( _FSR_RF ) can be used too to save code) 216 FSR1 = &_DpaMessage.Response.PData[0]; 217 // Initial count of received bytes 218 _DpaDataLength = 0; 219 // Loop while there is enough space and some byte to read 220 while ( _DpaDataLength < sizeof( _DpaMessage.Response.PData ) && RxUART() ) 221 { 222 // Store read byte 223 setINDF1( W ); 224 // Move pointer 225 FSR1++; 226 // Count next byte 227 _DpaDataLength++; 228 } 229 230 // Return number of read bytes 231 goto DpaHandleReturnTRUE; 232 } 233 } 234 } 235 break; 236 237 // ------------------------------------------------- 238 case DpaEvent_Init: 239 // Do a one time initialization before main loop starts 240 241 // Connected TR pins (e.g. TR72D)? 242 moduleInfo(); 243 if ( !bufferINFO[5].7 ) 244 { 245 // Set them as inputs 246 TRISC.5 = 1; 247 TRISA.5 = 1; 248 TRISB.4 = 1; 249 } 250 251 // RX input 252 TRISC.7 = 1; 253 // TX output 254 TRISC.6 = 0; 255 256 #if !defined( TR7xD ) 257 UART1MD = 0; 258 unlockPPS(); 259 RC6PPS = 0x10; // TX (Note: RX does not have to be set, use default) 260 lockPPS(); 261 #endif 262 263 // Set baud rate 264 _SPBRGL = UART_SPBRG_VALUE( UART_BAUD ) & 0xff; 265 _SPBRGH = UART_SPBRG_VALUE( UART_BAUD ) >> 8; 266 // baud rate control setup: BRG16 = 1 267 #if !defined( TR7xD ) 268 setBAUD1CON( 0b0000.1.000 ); 269 #else 270 BAUDCON = 0b0000.1.000; 271 #endif 272 273 // CSRC TX9 TXEN SYNC SENDB BRGH TRMT TX9D 274 // TXEN = 1 275 // BRGH = 1 276 // async UART, high speed, 8 bit, TX enabled 277 #if !defined( TR7xD ) 278 setTX1STA( 0b0010.0100 ); 279 #else 280 TXSTA = 0b0010.0100; 281 #endif 282 283 // SPEN RX9 SREN CREN ADDEN FERR OERR RX9D 284 // SPEN = 1 285 // CREN = 1 286 // Continuous receive, enable port, 8 bit 287 _RCSTA = 0b1001.0000; 288 289 // Enable UART RX interrupt 290 RCIE = TRUE; 291 292 break; 293 294 // ------------------------------------------------- 295 case DpaEvent_AfterSleep: 296 // Called after woken up after sleep 297 298 TXIE = wasTXIE; 299 RCIE = TRUE; 300 break; 301 302 // ------------------------------------------------- 303 case DpaEvent_BeforeSleep: 304 // Called before going to sleep 305 306 // ------------------------------------------------- 307 case DpaEvent_DisableInterrupts: 308 // Called when device needs all hardware interrupts to be disabled (before Reset, Restart, LoadCode, Remove bond, and Run RFPGM) 309 310 wasTXIE = TXIE; 311 TXIE = FALSE; 312 RCIE = FALSE; 313 break; 314 } 315 316 return FALSE; 317 } 318 319 //############################################################################################ 320 // Note: make sure the parameter does not overlap another variable as the function is ready to be called from (timer) interrupt too 321 static uns8 _data; 322 void TxUART( uns8 data @ _data ) 323 //############################################################################################ 324 { 325 // Wait for a space in the buffer 326 while ( TXIE && TxBufferPointerStartUART == TxBufferPointerEndUART ); 327 328 // Disable TX interrupt 329 TXIE = FALSE; 330 // Compute pointer 331 FSR0 = TxBufferUART + TxBufferPointerEndUART; 332 // Optimization: TxBufferPointerEndUART = ( TxBufferPointerEndUART + 1 ) % UART_BUFFER_LENGTH; 333 TxBufferPointerEndUART++; 334 TxBufferPointerEndUART &= ~UART_BUFFER_LENGTH; 335 // Store byte 336 setINDF0( data ); 337 // Start transmitting 338 TXIE = TRUE; 339 } 340 341 //############################################################################################ 342 // W = read byte, Carry = result 343 bit RxUART() 344 //############################################################################################ 345 { 346 // Buffer empty? 347 if ( RxDataLengthUART == 0 ) 348 return FALSE; 349 350 // Disable RX interrupt 351 RCIE = FALSE; 352 // Get byte from the circular buffer 353 FSR0 = RxBufferUART + RxBufferPointerStartUART; 354 // Optimization: RxBufferPointerStartUART = ( RxBufferPointerStartUART + 1 ) % UART_BUFFER_LENGTH; 355 RxBufferPointerStartUART++; 356 RxBufferPointerStartUART &= ~UART_BUFFER_LENGTH; 357 // One byte less 358 RxDataLengthUART--; 359 // Returned byte 360 W = *FSR0; 361 // Enable RX interrupt 362 RCIE = TRUE; 363 // TRUE => byte was read 364 return TRUE; 365 } 366 367 //############################################################################################ 368 // 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) 369 #include "DPAcustomHandler.h" 370 //############################################################################################