1 // ********************************************************************************* 2 // Custom DPA Handler code example - User peripheral implementation - SPI master * 3 // ********************************************************************************* 4 // Copyright (c) IQRF Tech s.r.o. 5 // 6 // File: $RCSfile: CustomDpaHandler-UserPeripheral-SPImaster.c,v $ 7 // Version: $Revision: 1.25 $ 8 // Date: $Date: 2019/01/04 17:14:37 $ 9 // 10 // Revision history: 11 // 2017/03/13 Release for DPA 3.00 12 // 2015/08/05 Release for DPA 2.20 13 // 2015/01/16 Release for DPA 2.12 14 // 15 // ********************************************************************* 16 17 // Online DPA documentation http://www.iqrf.org/DpaTechGuide/ 18 19 // This example implements Master SPI as a user peripheral 20 21 // Default IQRF include (modify the path according to your setup) 22 #include "IQRF.h" 23 24 // Default DPA header (modify the path according to your setup) 25 #include "DPA.h" 26 // Default Custom DPA Handler header (modify the path according to your setup) 27 #include "DPAcustomHandler.h" 28 29 // ********************************************************************* 30 31 // SPI master is controlled by data sent to the user peripheral PNUM=0x20, PCMD=0x00 32 // Data consists of subcommands. Each four available subcommand consists of one SCMD byte and optional data. 33 // If the subcommand reads data from SPI slave, then the read data is appended to the DPA response data 34 35 // # Subcommand: SCMD data Read data 36 // 1. WriteRead: 00LL.LLLL _byte_1_ ... _byte_n_ _byte_1_ ... _byte_n_ 37 // 2. Write: 10LL.LLLL _byte_1_ ... _byte_n_ 38 // 3. Read: 01LL.LLLL _byte_1_ ... _byte_n_ 39 // 4. Delay: 11DD.DDDD dddddddd 40 // 41 // Notes: 42 // 1.-3. LLLLLL specifies length of written and/or read bytes to/from SPI slave 43 // 1.-3. When LLLLLL is zero (SCMD values 0x00, 0x40, 0x80), then no data is written and/or read but next command 1.-3. will not deactivate _SS_ signal 44 // 3. SDO is 0 during reading from SPI 45 // 4. DDDDDD.dddddddd specifies 14 bit delay in 1 ms units to be performed 46 47 // Example: 48 // The following example shows how to control serial 32 kB SRAM memory 23K256 (http://www.microchip.com/23K256) connected as SPI slave to the SPI master peripheral 49 // 23K256 uses "SCK low when inactive" and "SDO/SDI valid on SCK rising edge" 50 // 51 // The example executes 3 actions using 5 subcommands: 52 // 1. Sets serial RAM to "Sequential mode" 53 // Write to serial RAM SPI: 0x01{WRSR}, 0x40{status value} 54 // Subcommand #1: 0x82{#1:write 2 bytes to SPI}, 0x01{WRSR}, 0x40{status value} 55 // 56 // 2. Writes 2 bytes 0xAA and 0xBB starting from address 2 to the RAM 57 // Write to serial RAM SPI: 0x02{WRITE}, 0x00{address high8}, 0x02{address low8}, 0xAA{1st byte@2}, 0xBB{2nd byte@3} 58 // Subcommand #2: 0x85{#2:write 5 bytes}, 0x02{WRITE}, 0x00{address high8}, 0x02{address low8}, 0xAA{1st byte@2}, 0xBB{2nd byte@3} 59 // 60 // 3. Reads 4 bytes from address 1 from the RAM 61 // Write to serial RAM SPI: 0x03{READ}, 0x00{address high8}, 0x01{address low8} (keep _SS_ active after this SPI write) 62 // Read from serial RAM SPI: 0x??{unknown byte@1}, 0xAA{previously written byte@2}, 0xBB{previously written byte@3}, 0x??{unknown byte@4} 63 // Subcommands #3-5: 0x00{#3:keep _SS_ after subcommand #4}, 0x83{#4:write 3 bytes}, 0x03{READ}, 0x00{address high8}, 0x01{address low8}, 0x44{#5:read 4 bytes} 64 // 65 // DPA request: PNUM=0x20, PCMD=0x00, Data[0x0F]=0x82, 0x01, 0x40, 0x85, 0x02, 0x00, 0x02, 0xAA, 0xBB, 0x00, 0x83, 0x03, 0x00, 0x01, 0x44 66 // DPA response: PNUM=0x20, PCMD=0x80, Data[0x04]=0x??, 0xAA, 0xBB, 0x?? 67 68 // ********************************************************************* 69 70 // SPI signals to PIN assignment: 71 // TR module pin DK-EVAL-04x pin SPI 72 // --------------------------------------- 73 // C8 1 SDO 74 // C7 2 SDI 75 // C6 3 SCK 76 // C5 4 _SS_ 77 // C4 7 GND 78 79 // _SS_ pin assignment (C5 = PORTA.5) 80 #define SS_PIN LATA.5 81 82 // Custom peripheral request internal subcommands 83 // Write and read from SPI slave 84 #define CMD_WRITE_READ 0x00 85 // Write to SPI slave 86 #define CMD_WRITE ((uns8)0x80) 87 // Read from SPI slave 88 #define CMD_READ 0x40 89 // Execute delay 90 #define CMD_DELAY ((uns8)0xC0) 91 92 // Mask to get the data from the subcommand 93 #define CMD_MASK ((uns8)0xC0) 94 95 // Must be the 1st defined function in the source code in order to be placed at the correct FLASH location! 96 //############################################################################################ 97 bit CustomDpaHandler() 98 //############################################################################################ 99 { 100 // Handler presence mark 101 clrwdt(); 102 103 // Detect DPA event to handle 104 switch ( GetDpaEvent() ) 105 { 106 // ------------------------------------------------- 107 case DpaEvent_Interrupt: 108 // Do an extra quick background interrupt work 109 // ! 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. 110 // ! 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. 111 // ! 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. 112 // ! Only global variables or local ones marked by static keyword can be used to allow reentrancy. 113 // ! Make sure race condition does not occur when accessing those variables at other places. 114 // ! 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. 115 // ! Do not call any OS functions except setINDFx(). 116 // ! Do not use any OS variables especially for writing access. 117 // ! 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. 118 119 DpaHandleReturnTRUE: 120 return TRUE; 121 122 // ------------------------------------------------- 123 case DpaEvent_Init: 124 // Do a one time initialization work before main loop starts 125 126 // Initialize PINs to inputs and outputs 127 TRISC.5 = 0; // RC5 as output SDO (C8) 128 TRISC.4 = 1; // RC4 as input SDI (C7) 129 TRISC.3 = 0; // RC3 as output SCK (C6) 130 TRISA.5 = 0; // RA5 as output SS (C5) 131 132 // TR module with connected pins? 133 moduleInfo(); 134 if ( bufferINFO[5].7 == 0 ) 135 { 136 // Yes 137 TRISC.6 = 1; // RC6 as input (connected to RA5 in parallel) 138 TRISB.4 = 1; // RB4 as input (connected to RA5 in parallel) 139 TRISC.7 = 1; // RC7 as input (connected to RC5 in parallel) 140 } 141 142 // Set idle level of SS 143 SS_PIN = 1; 144 145 // Setup SPI 146 // SSPSTAT 147 // Transmit occurs on SCK rising edge 148 CKE = 1; 149 // SSPCON1: (SSPCON1 cannot be accessed directly due to OS restriction) 150 // SPI enabled 151 // Idle state for clock is a low level 152 // CLK = 250kHz @ 16 MHz 153 writeToRAM( &SSPCON1, 0b0.0.1.0.0010 ); 154 155 break; 156 157 // ------------------------------------------------- 158 case DpaEvent_DpaRequest: 159 // Called to interpret DPA request for peripherals 160 // ------------------------------------------------- 161 // Peripheral enumeration 162 if ( IsDpaEnumPeripheralsRequest() ) 163 { 164 // We implement 1 user peripheral 165 _DpaMessage.EnumPeripheralsAnswer.UserPerNr = 1; 166 FlagUserPer( _DpaMessage.EnumPeripheralsAnswer.UserPer, PNUM_USER + 0 ); 167 _DpaMessage.EnumPeripheralsAnswer.HWPID = 0x000F; 168 _DpaMessage.EnumPeripheralsAnswer.HWPIDver = 0x6789; 169 170 goto DpaHandleReturnTRUE; 171 } 172 // ------------------------------------------------- 173 // Get information about peripheral 174 else if ( IsDpaPeripheralInfoRequest() ) 175 { 176 if ( _PNUM == PNUM_USER + 0 ) 177 { 178 _DpaMessage.PeripheralInfoAnswer.PerT = PERIPHERAL_TYPE_USER_AREA; 179 _DpaMessage.PeripheralInfoAnswer.PerTE = PERIPHERAL_TYPE_EXTENDED_READ_WRITE; 180 goto DpaHandleReturnTRUE; 181 } 182 183 break; 184 } 185 // ------------------------------------------------- 186 else 187 { 188 // Handle peripheral command 189 if ( _PNUM == PNUM_USER + 0 ) 190 { 191 // Check DPA command value 192 if ( _PCMD != 0 ) 193 DpaApiReturnPeripheralError( ERROR_PCMD ); 194 195 // Check total data length 196 if ( _DpaDataLength == 0 ) 197 DpaApiReturnPeripheralError( ERROR_DATA_LEN ); 198 199 // FSR0 points to the next byte from DPA request 200 FSR0 = _DpaMessage.Request.PData; 201 // FSR1 points to the place for the next read byte 202 FSR1 = bufferINFO; 203 // Flag for keeping SS 204 bit keepSS = FALSE; 205 do 206 { 207 // Get subcommand type from the 1st byte 208 uns8 cmd @ userReg0; 209 cmd = *FSR0 & CMD_MASK; 210 // Get data part from the 1st byte 211 uns8 dataFromCmd @ userReg1; 212 dataFromCmd = *FSR0++ & ~CMD_MASK; 213 214 // Write and/or read subcommand? 215 if ( cmd != CMD_DELAY ) 216 { 217 if ( dataFromCmd == 0 ) 218 // Keep SS after next SPI command 219 keepSS = TRUE; 220 else 221 { 222 // Yes! 223 // SS - active 224 SS_PIN = 0; 225 226 // Loop all the bytes 227 do 228 { 229 // Writing? 230 if ( cmd == CMD_WRITE || cmd == CMD_WRITE_READ ) 231 // Yes, get the byte from request 232 W = *FSR0++; 233 else 234 // No data to write, write 0 instead 235 W = 0; 236 237 // Reset interrupt flag 238 SSPIF = 0; 239 // Write the byte 240 SSPBUF = W; 241 // Wait until the byte is transmitted/received 242 while ( !SSPIF ); 243 244 // Reading? 245 if ( cmd == CMD_WRITE_READ || cmd == CMD_READ ) 246 { 247 // Yes! 248 setINDF1( SSPBUF ); 249 FSR1++; 250 } 251 // Next byte to write and/or read? 252 } while ( --dataFromCmd != 0 ); 253 254 if ( !keepSS ) 255 // SS - deactivate 256 SS_PIN = 1; 257 keepSS = FALSE; 258 } 259 } 260 else 261 { 262 // Delay subcommand 263 // Wait low8 264 // WaitMS must not destroy FSRx registers (works well with current OS version)! 265 waitMS( *FSR0++ ); 266 // Wait high6 * 256 ms 267 for ( ; dataFromCmd != 0; dataFromCmd-- ) 268 { 269 waitMS( 256 / 2 ); 270 clrwdt(); 271 waitMS( 256 / 2 ); 272 } 273 } 274 // Another subcommand? 275 } while ( FSR0.low8 - (uns8)_DpaMessage.Request.PData != _DpaDataLength ); 276 277 // Compute data length 278 _DpaDataLength = FSR1.low8 - (uns8)bufferINFO; 279 // Copy read bytes to the response 280 copyMemoryBlock( bufferINFO, _DpaMessage.Response.PData, _DpaDataLength ); 281 // The previous statement can be replaced by shorter copyBufferINFO2RF(), because _DpaMessage.Response.PData == bufferRF 282 goto DpaHandleReturnTRUE; 283 } 284 } 285 } 286 287 return FALSE; 288 } 289 290 //############################################################################################ 291 // 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) 292 #include "DPAcustomHandler.h" 293 //############################################################################################