PIC micro very weird behaviour - Page 1
Forum section: electronics / general

author: Veketti
author_rank: Regular Contributor
timestamp: September 17, 2023, 07:10:17 am
content: Dear All,Every now and then these things seem to beat me and leave me stumbled. Now I'm stumbling with this very easy part of code that I really can't figure out why it is behaving like it does. I'm using this down and dirty method of delay_ms here and not timers. Basically what it does is once the button press interrupt sets flag "FlagforBtnOpen", it first call "LockOpen()" procedure to set the PWM frequency for RC servo to 1.2ms ON then turns on the power supply for the servo and keep that for 2 seconds. Then change the PWM frequency to 2.2ms ON and after one second delay shuts off the power supply to the servo.But the problem is that it does all this twice. Each and every time, and I don't understand why. Maybe I'm missing something very stupid here? I'm using mikroC pro for pic and PIC16F1847. Thank you very much for helping.Code:void LockLocked(){// PWM settings 275 2,2 ms onCCPR1L = 0b01000100; // MSBCCP1CON = 0b00111100; // LSBT6CON = 0b00000111;}void LockOpen(){// PWM settings 150 1,2ms onCCPR1L = 0b00100101; // MSBCCP1CON = 0b00101100; // LSB = bit4 & 5T6CON = 0b00000111;}// this is in main loopif (FlagforBtnOpen) // Manual open button pressed{FlagforBtnOpen = 0;LockOpen();SOLENOID = 1;delay_ms(2000);LockLocked();delay_ms(1000);SOLENOID = 0;}

author: Ian.M
author_rank: Super Contributor
timestamp: September 17, 2023, 07:51:16 am
content: The snippets above can only repeat if FlagforBtnOpen is getting set repeatedly. Show *all* your code, and as you are using MikroC, the PIC's CONFIG (fuse) settings, so we can determine if the PIC could be resetting itself or dropping through the main loop!

author: bingo600
author_rank: Super Contributor
timestamp: September 17, 2023, 08:35:15 am
content: If there's no button debouncing , wouldn't if(button.....) entry , be a "random lottery"/Bingo

author: Ian.M
author_rank: Super Contributor
timestamp: September 17, 2023, 08:58:07 am
content: I don't know, as my crystal ball is down for maintenance!We haven't been shown the button press ISR, so have no evidence for or against it handling debouncing.

author: jpanhalt
author_rank: Super Contributor
timestamp: September 17, 2023, 09:06:20 am
content: I agree on button bounce as the potential culprit. If the ISR clears the flag before implementing the rest of the code, it could happen that the flag gets reset by bounce. That wouldn't be noticed until GIE is reset (i.e., retfie).To test, insert a 40-ms delay before clearing the flag, or for example, clear, do something, and clear again before exiting the ISR. Those suggested sequences are made without know the details of the ISR. Same problem if using polling exists.Why 40 ms? Albeit, that is longer than the often cited value of 10 ms, but it is the value often used by hardware debouncers.

author: Veketti
author_rank: Regular Contributor
timestamp: September 17, 2023, 09:31:23 am
content: Indeed it certainly could be some reset happening. I had before very weird issue (oscillating) with RA4 FET control until I set the Power-up timer to Enable in configs. I don't completely understand all these config options so I might have something stupid there. Settings attached. I'm preventing the button debounce by hardware with 100K resistor and 0.1uF capacitor and I've checked it many times with scope that there is zero bounce. There is also other button which initiates TX510 face recognizing module and that works perfectly. Just this very dummy part where manually should operate the servo does it twice which is driving me crazy.Here's the code:Code:#define SOLENOID LATA4_bit#define TX510POWER LATB4_bit#define BUZZER LATA0_bitvolatile char FlagforRcgBtnPress = 0, FlagForUARTReceive = 0, FlagforDisplayOff = 0, FlagforBacklightOff = 0, FlagforBtnOpen = 0, FlagforOneSecond = 0;volatile char Data;volatile char rData;volatile unsigned int TimerRounds = 0, OneSecondRound = 0, HalfSecondRound = 0, Timer4Rounds = 0;volatile char FlagForSolenoid = 0, FlagForTX510Sleep = 0;void InitMain();void FaceRecRcvRecData();void FaceRecProcessPacket();void FaceRecSendCommand(char command, char param1, char param2);void TurnOnTX510();void LockOpen();void LockLocked();void InitMain(void) {DACCON0.b7 = 0; // DAC disabledSRCON0.b7 = 0; // SR latch disabledCM1CON0.b7 = 0; // Disable comparator 1CM2CON0.b7 = 0; // Disable comparator 2TRISA = 0b00000000; // Set all outputs, 0 = output 1 = inputTRISB = 0b00100111; // Set RB3, 4, 6 7 as outputs others inputs. RB0 recq initiate btn, RB1 = RX, RB2 = TX, RB5 = manual open buttonANSELA = 0b00000000; // Set all as digitalANSELB = 0b00000000; // Set all B ports as digitalSCKP_bit = 0; // polarity selection. 1 = idle low. 0 idle highOPTION_REG = 0b10000000;// Timer 2 = 0.124928 ~ 0.125 seconds eg. 1/8 of secT2CON |= 120; // bits 6-3 Post scaler 1:1 thru 1:16T2CON.T2CKPS1 = 1; // bits 1-0 Prescaler Rate Select bitsT2CON.T2CKPS0 = 1;PR2 = 244; // PR2 (Timer2 Match value)PIR1.TMR2IF = 0; // clear timer2 interupt flag TMR2IF//Timer4 Registers 0.124928 ~ 0.125 secondsT4CON |= 120; // bits 6-3 Post scaler 1:1 thru 1:16T4CON.TMR4ON = 1; // bit 2 turn timer4 on;T4CON.T4CKPS1 = 1; // bits 1-0 Prescaler Rate Select bitsT4CON.T4CKPS0 = 1;PR4 = 244; // PR4 (Timer4 Match value)PIR3.TMR4IF = 0; // clear timer4 interupt flag TMR1IFPIE3.TMR4IE = 1; // enable Timer4 interrupts// Timer6 registersTRISB3_bit = 1;PR6 = 250; // 125 HzCCP1CON = 0b00111100;CCPR1L = 0b01000100; // set dutycycle. 150 = 1,2ms. 275 = 2,2msCCPTMRS = 0b00000010; // User timer 6 for PWM modeTMR6IF_bit = 0;T6CON = 0b00000111; // 111 for prescaler 64TRISB3_bit = 0;IOCBN0_bit = 1; // Set Interrupt on change negative edge register, initiate recognitionIOCBN5_bit = 1; // Set Interrupt on change negative edge register, manual open buttonINTCON = 0b11001000; // Interrupt on change bit enabled 16.3. lisätty IOCE bit 3 = 1TX510POWER = 0;}void Interrupt(void){if (IOCIF_bit) { // Pin Interrupt happenedif (IOCBF0_bit) // Initiate recognition btn pressed{IOCBF0_bit = 0;FlagforRcgBtnPress = 1;} if (IOCBF5_bit) // Manual open button pressed{IOCBF5_bit = 0;FlagforBtnOpen = 1;}IOCBF = 0b00000000;}if (TMR2IF_bit){ // Timer2 Interrupt happenedTMR2IF_bit = 0;if (TimerRounds > 40 ) { // 40 rounds = 5 seconds. 1 tick = 0,125 sec.TimerRounds = 0;FlagForSolenoid = 0;} else TimerRounds ++;if (OneSecondRound > 7){OneSecondRound = 0;FlagforOneSecond = 1;} else OneSecondRound ++;} // Timer2 Interrupt endif (TMR4IF_bit){ // Timer4 Interrupt happenedTMR4IF_bit = 0;if (Timer4Rounds > 480 ) { // 480 rounds = 60 seconds. 1 tick = 0,125 sec.Timer4Rounds = 0;FlagForTX510Sleep = 1;} else Timer4Rounds ++;} // Timer4 Interrupt end// UART receive buffer interruptedif (RCIF_bit){FlagForUARTReceive = 1;RCIE_bit = 0;}} // Interruptvoid TurnOnTX510(){char trash;Uart1_init(115200);delay_ms(100);TX510POWER = 0;delay_ms(2000);FaceRecSendCommand(DISPL, TURNOFF, 0x00); // Display offdelay_ms(500);while (Uart1_Data_Ready()) trash = Uart1_Read(); // Clear the uart bufferT4CON.TMR4ON = 1;PIE3.TMR4IE = 1;}void LockLocked(){// 275 2,2 ms onCCPR1L = 0b01000100; // MSBCCP1CON = 0b00111100; // LSBT6CON = 0b00000111;}void LockOpen(){// 150 1,2ms onCCPR1L = 0b00100101; // MSBCCP1CON = 0b00101100; // LSB = bit4 & 5T6CON = 0b00000111;}void main() {int e;char trash;InitMain(void);Uart1_init(115200);delay_ms(2000);FaceRecSendCommand(DISPL, TURNOFF, 0x00); // Display offdelay_ms(500);// FaceRecSendCommand(BACKLIT, TURNOFF, 0x00); // Backlight off// delay_ms(500);while (Uart1_Data_Ready()) trash = Uart1_Read(); // Clear the uart bufferSOLENOID = 0;BUZZER = 0;while (1){if (FlagforRcgBtnPress){if (RB4_bit) TurnOnTX510();FaceRecSendCommand(IDENT,0x00,0x00);FlagforRcgBtnPress = 0;RCIE_bit = 1;Timer4Rounds = 0;}if (FlagForUARTReceive){FlagForUARTReceive = 0;FaceRecRcvRecData();RCIE_bit = 1;}// Solenoidif (!FlagForSolenoid){TMR2IE_bit = 0; // Turn off the timerTMR2ON_bit = 0;FlagForSolenoid = 1;LockLocked(); // turn servo to locked}if (FlagForTX510Sleep){FlagForTX510Sleep = 0;SOLENOID = 0; // Shut down servo powerTX510POWER = 1; // Shut down TX510TXSTA = 0;RCSTA = 0;T4CON.TMR4ON = 0;PIE3.TMR4IE = 0;}if (FlagforBtnOpen){FlagforBtnOpen = 0;LockOpen();SOLENOID = 1;delay_ms(2000);LockLocked();delay_ms(1000);SOLENOID = 0;}if (FlagforOneSecond && RA0_bit){FlagforOneSecond = 0;BUZZER = 0;}}}

author: jpanhalt
author_rank: Super Contributor
timestamp: September 17, 2023, 10:06:40 am
content: I am very much an empiricist. Theory has its place, but finding button bounce can be tricky.* Try a 1 uF cap and see if that helps. Even Microchip suggests checking flags before exiting an ISR; although, I usually don't do that.*I often use hardware debounce, such as the MAX6817 that gives about a 40 ms delay.

author: Veketti
author_rank: Regular Contributor
timestamp: September 17, 2023, 11:37:03 am
content: You're a wise man jpanhalt. I added one more flag clearance at the end of that procedure, so there is now like three seconds of debounce time and it works. I can't believe that the 10ms from the RC circuitry was not enough and I couldn't see any debounce on scope. Next projects I'll use 0.47uf cap instead of 0.1uf to get 47ms of debounce. Except if some of those earlier commands somehow trigger the interrupt as well? Is my interrupt routine correct for this button by the way? I was also in impression that when "delay_ms()" running the MCU disregards everything else. But it seems I was wrong and interrupts still work?Code:if (FlagforBtnOpen){FlagforBtnOpen = 0;LockOpen();SOLENOID = 1;delay_ms(2000);LockLocked();delay_ms(1000);SOLENOID = 0;FlagforBtnOpen = 0; // this one added. ~three seconds of debounce.. :)}

author: Ian.M
author_rank: Super Contributor
timestamp: September 17, 2023, 12:09:33 pm
content: RC debouncing *REQUIRES* a Schmitt trigger input characteristic, to convert a slowly rising/falling signal into valid logic levels without glitching at the transition due to noise pickup. Only some PIC pins have Schmitt trigger inputs, and only when feeding a peripheral module, not when read as a GPIO. e.g. T1G, T1CKI.I therefore conclude your decision to use RC debouncing is fundamentally broken, and increasing the debounce time constant is likely to make things worse as the input will spend longer in the transition zone where it can toggle on noise.As a bandaid, you have now implemented 'overkill' software debouncing. I strongly recommend readinghttp://www.ganssle.com/debouncing.htm(both parts), then reviewing your design to remove ineffectual RC debouncing (though some RC filtering may still be desirable for ESD protection), and rework your code to properly implement software debouncing, (though simply ignoring the button once triggered till its action is complete so only the first press or bounce 'counts' is likely to be sufficient).

author: jpanhalt
author_rank: Super Contributor
timestamp: September 17, 2023, 01:26:13 pm
content: Based on the Veketti's latest post, it appears the ISR is quite lengthy. (I am not here to praise or condemn that practice. It works.) In that case, the interrupt flag only needs to be cleared once, and he probably doesn't need any switch debouncing at all. That is, he could clear the interrupt flag just before exiting. Clearing it twice won't create any problems, but it may waste a microsecond or so.

author: Veketti
author_rank: Regular Contributor
timestamp: September 17, 2023, 03:59:21 pm
content: Thank you all for help.Looked at that MAX6817 and it looks very interesting component. But darn, that's expensive for such a simple component.I ended up disabling the IOCBNx_bit after setting the flag boolean in interrupt and enabling back later on when expecting new button presses... This way it only enters once to the interrupt no matter how much it bounces. Is there any caveats on doing it like this?

author: jpanhalt
author_rank: Super Contributor
timestamp: September 17, 2023, 04:05:54 pm
content: Sounds good to me if it works.Thanks for the update. Many posters forget to do that, and its something everyone who contributes likes to see.John

author: DavidAlfa
author_rank: Super Contributor
timestamp: September 17, 2023, 04:16:37 pm
content: Use the search function, there're lots of threads with decent code for button debouncing without wasting time in delays.

author: slugrustle
author_rank: Frequent Contributor
timestamp: September 17, 2023, 04:30:17 pm
content: I always debounce in software if I have an MCU. You can get nice deterministic behavior.My favorite polling debounce method is to check the switch I/O pin input every 10ms and require it to have the same state for four consecutive samples in a row before proceeding.The typical interrupt method (as far as I know) is to interrupt on the appropriate edge, then start a timer to fire another interrupt after waiting longer than you expect the switch to bounce and see if the switch is still in the active state in that interrupt.If you want to do hardware debounce without expensive external ICs, you can use resistors, capacitors, and transistors to get a pretty nice debounce circuit. The idea is to put a lot of capacitance at the I/O pin,slowlycharge(discharge) the capacitance when the switch is in the active position, andquicklydischarge(charge) the capacitance with the transistor when the switch is in the inactive position. That way, your switch has to stay continuously in the active state to get an active switch state logic level voltage at the I/O pin, and any bounce will quickly reset the I/O pin voltage close to the inactive switch state logic level voltage.I've attached a schematic that debounces a normally open switch for both transitions: opening and closing. It only uses discrete components. The whole schematic is a little contraption to trigger a scope or other test equipment from a pedal switch. It's got switch debounce, a latch, a one shot, and a line driver.You can also use an SR latch to debounce a switch if you want to go the hardware route. Multiple online articles are available. Some show an SPDT switch, but I've seen others that use a pushbutton with a pullup resistor.This post is just to give you ideas. There's all sorts of ways to make this happen.

author: Ian.M
author_rank: Super Contributor
timestamp: September 17, 2023, 04:31:55 pm
content: I seem to remember some (possibly undocumented) IOC errata for some of the older 8 bit PICs. IIRC a 'stale' IOC pin state mismatch could cause an immediate interrupt when enabling IOC on that pin. The fix was to disable global interrupts, enable the pin interrupt, read the port to clear any mismatch, clear the IOC interrupt flag, then reenable global interrupts.Fortunately the PIC16(L)F1847 Errata make no mention of any IOC, port or peripheral interrupt related issues, and I cant find anything significant on the Microchip forums (in the sea of IOC PICNIC errors you will find there), so I agree with jpanhalt, subject to testing, you are good to go.If you port to a different PIC, for %DEITY's% sake check the errata for known documented issues and the Microchip forum for potential undocumented issues!