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