1 // ************************************************************************** 2 // Custom DPA Handler code example - User peripheral implementation - PWM * 3 // ************************************************************************** 4 // Copyright (c) MICRORISC s.r.o. 5 // 6 // File: $RCSfile: CustomDpaHandler-UserPeripheral-PWMandTimer.c,v $ 7 // Version: $Revision: 1.23 $ 8 // Date: $Date: 2023/03/07 08:03:13 $ 9 // 10 // Revision history: 11 // 2023/03/07 Release for DPA 4.30 12 // 2022/10/05 Release for DPA 4.18 13 // 2017/03/13 Release for DPA 3.00 14 // 2015/09/03 Release for DPA 2.21 15 // 16 // ********************************************************************* 17 18 // Online DPA documentation https://doc.iqrf.org/DpaTechGuide/ 19 20 // This example demonstrates usage of PWM and Timer at same time 21 // It implements one user peripheral PNUM=0x20, PCMD=0x00 22 // Data contains list of commands at the following format 23 // 0x00, PWM 24 // Sets 8bit PWM duty cycle to value "PWM" 25 // 0x01, loopLow8, loopHigh8, pwmAddlow8, pwmAddhigh8 26 // Adds 16bit "pwmAdd" value divided by 256 "loop" times. Time unit is approx 16 ms 27 // 0x02 28 // Starts executing command from the beginning 29 // 0x03 30 // Stops execution of commands 31 32 // This example works only at STD mode, not at LP mode 33 34 // Example data for PNUM=0x20, PCMD=0x00 35 // 0x00, 0x00, 36 // - Sets PWM duty cycle to 0 37 // 0x01, 0x00, 0x01, 0x40, 0x00, 38 // - Adds 0x0100 = 256 times every 16 ms value 0x0040 / 256 = 0.25 to the duty cycle 39 // 0x01, 0x00, 0x01, 0xC0, 0xFF, 40 // - Adds 0x0100 = 256 times every 16 ms value 0xFFC0 / 256 = -0x0040 / 256 = -0.25 to the duty cycle 41 // 0x01, 0x80, 0x00, 0x00, 0x00, 42 // - Adds 0x0800 = 128 times every 16 ms value 0 to the duty cycle (wait delay for approx 2 s) 43 // 0x02 44 // - Starts again 45 46 // Default IQRF include (modify the path according to your setup) 47 #include "IQRF.h" 48 49 // Default DPA header (modify the path according to your setup) 50 #include "DPA.h" 51 // Default Custom DPA Handler header (modify the path according to your setup) 52 #include "DPAcustomHandler.h" 53 54 #if !defined( TR7xD ) 55 #error This example is intended for TR7xD 56 #endif 57 58 // PWM commands 59 typedef enum 60 { 61 CMD_SET_PWM = 0, 62 CMD_CHANGE_PWM = 1, 63 CMD_LOOP = 2, 64 CMD_STOP = 3 65 }; 66 67 //############################################################################################ 68 69 // Must be the 1st defined function in the source code in order to be placed at the correct FLASH location! 70 //############################################################################################ 71 bit CustomDpaHandler() 72 //############################################################################################ 73 { 74 // Handler presence mark 75 clrwdt(); 76 77 // List of commands 78 static uns8 Cmds[40]; 79 // Index of the next command 80 static uns8 CmdsIndex; 81 // Loop for changing PWM 82 static uns16 ChangePWMloop; 83 // Value to add to PWM during change 84 static uns16 ChangePWMadd; 85 // Full 16b PWM duty cycle value, only higher part is used at MCU 86 static uns16 CCPR3Lshadow; 87 88 // Detect DPA event to handle 89 switch ( GetDpaEvent() ) 90 { 91 // ------------------------------------------------- 92 case DpaEvent_Interrupt: 93 // Do an extra quick background interrupt work 94 // ! 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. 95 // ! 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. 96 // ! 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. 97 // ! Only global variables or local ones marked by static keyword can be used to allow reentrancy. 98 // ! Make sure race condition does not occur when accessing those variables at other places. 99 // ! 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. 100 // ! Do not call any OS functions except setINDFx(). 101 // ! Do not use any OS variables especially for writing access. 102 // ! 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. 103 104 // If TMR6 interrupt occurred 105 if ( TMR6IF ) 106 { 107 // Unmask interrupt 108 TMR6IF = 0; 109 110 // Changing PWM? 111 if ( ChangePWMloop != 0 ) 112 { 113 // Decrease loop 114 ChangePWMloop--; 115 // Add PWM 116 CCPR3Lshadow += ChangePWMadd; 117 // Copy to MCU 118 CCPR3L = CCPR3Lshadow.high8; 119 } 120 else 121 { 122 // Get command pointer 123 FSR0 = Cmds + CmdsIndex; 124 switch ( FSR0[0] ) 125 { 126 // Set PWM 127 case CMD_SET_PWM: 128 // Next cmd 129 CmdsIndex += 1 + 1; 130 // Get value 131 CCPR3Lshadow.high8 = CCPR3L = FSR0[1]; 132 CCPR3Lshadow.low8 = 0; 133 break; 134 135 // Change PWM 136 case CMD_CHANGE_PWM: 137 // Next cmd 138 CmdsIndex += 1 + 4; 139 // Get loop count 140 ChangePWMloop.low8 = FSR0[1]; 141 ChangePWMloop.high8 = FSR0[2]; 142 // Get added value 143 ChangePWMadd.low8 = FSR0[3]; 144 ChangePWMadd.high8 = FSR0[4]; 145 break; 146 147 // Loop commands 148 case CMD_LOOP: 149 // Start from the 1st command 150 CmdsIndex = 0; 151 break; 152 153 // Stop commands 154 case CMD_STOP: 155 default: 156 break; 157 } 158 } 159 } 160 return Carry; 161 162 // ------------------------------------------------- 163 case DpaEvent_Init: 164 // Do a one time initialization before main loop starts 165 166 // Start with no commands 167 Cmds[0] = CMD_STOP; 168 // CmdsIndex = 0 // By C definition 169 170 // Definitions used for TR72 having connected pins 171 #define _OUT_A5 TRISA.5 172 #define _OUT_B4 TRISB.4 173 #define _OUT_C6 TRISC.6 174 #define _PIN_C6 LATC.6 175 176 // Read module info into bufferINFO 177 moduleInfo(); 178 // TR module with connected pins? 179 if ( bufferINFO[5].7 == 0 ) 180 { 181 _OUT_A5 = 1; 182 _OUT_B4 = 1; 183 } 184 185 // Single output; PxA modulated; PxB, PxC, PxD assigned as port pins 186 // PWM mode: PxA, PxC active-high; PxB, PxD active-high 187 CCP3CON = 0b00.00.1100; 188 // PWM duty cycle 189 CCPR3L = 0; 190 // Period 191 PR6 = 0xff; 192 // CCP3 is based off Timer6 in PWM mode 193 CCPTMRS0 = 0b00100000; 194 // CCP3/P3A function is CCP3 195 CCP3SEL = 0; 196 #if F_OSC == 16000000 197 // Prescaler 16, Postscaler 8, 16 * 16 * 256 = 65536 = 61 Hz = 16 ms @16MHz 198 // TMR6 on 199 T6CON = 0b0.1111.1.10; 200 #else 201 #error Unsupported oscillator frequency 202 #endif 203 204 // TMR6 interrupt 205 TMR6IE = 1; 206 // Set output 207 _OUT_C6 = 0; 208 209 break; 210 211 // ------------------------------------------------- 212 case DpaEvent_AfterSleep: 213 // Called on wake-up from sleep 214 TMR6IE = TRUE; 215 TMR6ON = TRUE; 216 break; 217 218 // ------------------------------------------------- 219 case DpaEvent_BeforeSleep: 220 // Called before going to sleep (the same handling as DpaEvent_DisableInterrupts event) 221 222 // ------------------------------------------------- 223 case DpaEvent_DisableInterrupts: 224 // Called when device needs all hardware interrupts to be disabled (before Reset, Restart, LoadCode, Remove bond, and Run RFPGM) 225 // Must not use TMR6 any more 226 TMR6ON = FALSE; 227 TMR6IE = FALSE; 228 _PIN_C6 = FALSE; 229 break; 230 231 // ------------------------------------------------- 232 case DpaEvent_DpaRequest: 233 // Called to interpret DPA request for peripherals 234 // ------------------------------------------------- 235 // Peripheral enumeration 236 if ( IsDpaEnumPeripheralsRequest() ) 237 { 238 // We implement 1 user peripheral 239 _DpaMessage.EnumPeripheralsAnswer.UserPerNr |= 1; 240 FlagUserPer( _DpaMessage.EnumPeripheralsAnswer.UserPer, PNUM_USER + 0 ); 241 _DpaMessage.EnumPeripheralsAnswer.HWPID |= 0x000F; 242 _DpaMessage.EnumPeripheralsAnswer.HWPIDver |= 0xFeFe; 243 244 DpaHandleReturnTRUE: 245 return TRUE; 246 } 247 // ------------------------------------------------- 248 // Get information about peripheral 249 else if ( IsDpaPeripheralInfoRequest() ) 250 { 251 if ( _PNUM == PNUM_USER + 0 ) 252 { 253 _DpaMessage.PeripheralInfoAnswer.PerT = PERIPHERAL_TYPE_USER_AREA; 254 _DpaMessage.PeripheralInfoAnswer.PerTE = PERIPHERAL_TYPE_EXTENDED_WRITE; 255 goto DpaHandleReturnTRUE; 256 } 257 258 break; 259 } 260 // ------------------------------------------------- 261 else 262 { 263 // Handle peripheral command 264 if ( _PNUM == PNUM_USER + 0 ) 265 { 266 // Check command 267 if ( _PCMD != 0 ) 268 DpaApiReturnPeripheralError( ERROR_PCMD ); 269 270 // Check data length 271 if ( _DpaDataLength > ( sizeof( Cmds ) - 1 ) ) 272 DpaApiReturnPeripheralError( ERROR_DATA_LEN ); 273 274 // Copy commands and always append CMD_STOP for sure 275 GIE = FALSE; 276 copyMemoryBlock( _DpaMessage.Request.PData, Cmds, sizeof( Cmds ) - 1 ); 277 writeToRAM( Cmds + _DpaDataLength, CMD_STOP ); 278 // Start new commands 279 CmdsIndex = 0; 280 ChangePWMloop = 0; 281 GIE = TRUE; 282 283 // Write no error 284 _DpaDataLength = 0; 285 goto DpaHandleReturnTRUE; 286 } 287 } 288 } 289 290 return FALSE; 291 } 292 293 //############################################################################################ 294 // 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) 295 #include "DPAcustomHandler.h" 296 //############################################################################################