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