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