How to check if a button is pressed twice within 500 ms (PIC16)? - timer

I have a switch button that is connected to RB1. This button acts as a reset but it needs to be pressed twice for it to work. If the second press happens after 500 ms (relative to the initial press), it will do nothing.
I think I should make use of timers or ccp module but I am not sure how to implement those. What do you suggest?
Edit (My ISR):
void interrupt ISR(void){
GIE = 0;
if(INTF){
INTF = 0;
if(reset_press == 0){
TMR1 = 0x0BDC; // counter starts counting at 0x0BDC (3036)
TMR1ON = 1; // Turns on Timer1 (T1CON reg)
}
reset_press++;
}
else if(TMR1IF==1){ // checks Timer1 interrupt flag
TMR1IF = 0; // clears interrupt flag
if(reset_press == 2){
reset_count(); // reset function
}
TMR1ON = 0; // Turns off Timer1 (T1CON reg)
reset_press = 0;
}
GIE = 1;
}

Setup the timer or CCP for 500ms once in initialisation, but don't fire it. (You may have to use an additional byte to achive to hold a value that corresponds to 500ms depending on the osc freq).
After you enter main's superloop, detect the button press; either use a flag or a counter to count the button press. As soon as you detect the first press,fire the 500ms timer.
If the 500ms time has relapsed (you can detect it in the interrupts), that means that you will do nothing, so reset everything; button press counter, fired timer etc.
Meanwhile before the 500ms has not been relapsed, if you detect a second press; that is the press counter value must be 2, then you execute what you wish and then reset everything for the next twice button press detection.

Related

Problem reading rotary encoder with atmega8

I am trying to read a rotary encoder as part of a larger project. I am using a Atmega8L and MPLab X with the XC8 compiler.
The issue I am having, is that the encoder does not work reliable and only seems to be able to count up and not down.
Due to some other aspects of the circuit the encoder pins could not be connected to interrupt-capable pins of the atmega8.
Here is my code, that reads the encoder.
#include <xc.h>
#include "pinmapping.h"
#include "main.h"
static inputEvent lastEvent;
static int aLastState;
static int aState;
//get the last Events data for use elsewhere
inputEvent getLastInputEvent(){
return lastEvent;
}
void initEncoder(){
//get our current button state and set rotation change to 0
if((PINB & ENCODER_SW) == 0){
//also save that to our Event
lastEvent.buttonPressed = 1;
}
else{
lastEvent.buttonPressed = 0;
}
lastEvent.rotationChange = 0;
//also save the current state of the A pin for the first read
aLastState = (PINB & ENCODER_A);
if(aLastState >= 1){
aLastState = 1;
}
}
//this should run regularly eg. on a timer interrupt
void readEncoder(){
//first get the state of both pins
aState = (PINB & ENCODER_A);
int bState = (PINB & ENCODER_B);
if(aState >= 1){
aState = 1;
}
if(bState >= 1){
bState = 1;
}
//check if the button is pressed
if((PINB & ENCODER_SW) == 0){
//save that to our Event
lastEvent.buttonPressed = 1;
}
else{
lastEvent.buttonPressed = 0;
}
//check if the state has changed since last time
if(aState != aLastState){
//check if A and B have the same state -> positive rotation
if(bState != aState){
//save the rotation change to our Event
lastEvent.rotationChange = 1;
}
else{
lastEvent.rotationChange = -1;
}
//save the current state of the A pin for next time
aLastState = aState;
}
else{
//if no rotation change happend, save that to our Event
lastEvent.rotationChange = 0;
}
}
The button press is working properly, but the rotation change is never -1 and always 1 or 0.
The ENCODER_SW, ENCODER_A and ENCODER_B are bitmasks, that are all 0s and a single 1 at the bit corresponding to the correct IO-pin.
In the main, first the initEncoder is called (after IO-setup) and then in the infinite loop a function is called, which in turn calls readEncoder() and then getLastInputEvent() and then does some more stuff before returning to the main.
I have also tried running the readEncoder() function on a timer interrupt roughly every ms. This however results in it not working at all.
What is the problem? Why isn't it working?
Edit: Updated the code after I made some changes.
I am using a ALPS EC12E with pushbutton.
The rotationChange is used to increment/decrement a number that is printed to an LCD.
Edit 2: I now think, it is a timing problem, since I tried running basically the same code, just without anything else on an Arduino and printed the values to the serial console and it worked basically perfectly.
I checked what else is done and realized that in my current code the readEncoder() function only runs about once every 98 or so ms.
When I run the function on a timer interrupt every 1 or 2 ms, it just doesn't work at all. I don't even get a reaction to the button push anymore, which is something I do not understand at all.
In fact the timer does not seem to run at all, or at least the interrupt service routine is never executed. (I tried turning on an LED in the setup and turning it off in the isr, but it stays on indefinitely.)
So I guess theres a new problem now...
Here is the timer initialization:
ASSR = 0x00; //make sure we are not in asynchronous mode
OCR2 = 3; //Output compare register
TCNT2 = 0; //Reset counter to 0
TCCR2 = 0b00001111; //Timer 2 CTC mode, clkio/1024
This is done inside of initDecoder(). The OCIE2 bit is already set earlier in the main setup, enabling the timer 2 interrupt.
The isr than just looks like this:
void __interrupt (TIMER2_COMP_vect_num) timer2_isr(){
//Just read the encoder
readEncoder();
}
The interrupt flag gets cleared by hardware when the isr is executed.
The TIMER2_COMP_vect_num is definitely correct as well, as MPLab recognizes it and I can even look at its definition.

MSP430 LaunchPad | Assign double click on button

I am using MSP-EXP430F5529LP. I can already blink the red led using
#pragma vector=TIMER2_A0_VECTOR
__interrupt void Timer_A2(void)
{
P1OUT ^= 0x01;
}
and
TA0CCR0 = 25000;
TA0CCTL0 |= CCIE;
TA0CTL |= TASSEL_2 | ID_3 | MC_1 | TACLR;
I would really like some assistance in making a double click check on
#pragma vector=PORT1_VECTOR
so i can distinguish it from single click. I just want to use simple if's in there and do some staff based on single or double click.
Your code to blink the LED through the use of a timer hardware is already a good starting point towards detecting the double-press to a button. I suggest using a timer to implement the basics of a debounce function, which are simply up/down counters that process some event such as button input.
The thing about buttons is they are noisy. If you sample the input pin of the MSP430 from the button fast enough you will find it will toggle more than once for a simple press-and-hold event. Releasing the button produces noise too. So you can expect a lot of noise for the double-press event. It is possible to create external hardware circuits to filter out the noise part of the press, but software is cheaper so most implementations go this route.
The idea behind a debounce function is the timer is used to measure events from the button input to deal with the noise. Here is a starting point for detecting single button press. This can be modified easily for the double (or tripple) press!
// Define button status and the button variable
typedef enum {BUTTON_STATUS_NOTPRESSED, BUTTON_STATUS_HOLDING, BUTTON_STATUS_RELEASED} button_status_t;
button_status_t button = BUTTON_STATUS_NOTPRESSED;
// Define button state values (if inverted then swap the values, assume input at bit 1)
typedef enum {BUTTON_STATE_NOTPRESSED = 0, BUTTON_STATE_PRESSED = BIT1} button_state_t;
// Setup timer hardware for 1 ms ticks
TA0CCR0 = 125;
TA0CCTL0 = CCIE;
TA0CTL = TASSEL_2 | ID_3 | MC_1 | TACLR;
// Process the button events for every timer tick
#pragma vector=TIMER2_A0_VECTOR
__interrupt void Timer_A2(void) // Better name would be Timer_A0_2_ISR
{
// Local variable for counting ticks when button is pressed
// Assume nobody will ever hold the button for 2^32 ticks long!
static uint32_t counter = 0;
// Constant for measuring how long (ms) the button must be held
// Adjust this value to feel right for a single button press
const uint32_t BUTTON_PRESSED_THRES = 50;
// Check status of button, assume P2.1 input for the button
// Note once the button enters release status, other code must change back to not pressed
switch (button)
{
case BUTTON_STATUS_NOTPRESSED:
// Increment if pressed, else reset the count
// This will filter out the noise when starting to press
if ((P2IN & BIT1) == BUTTON_STATE_PRESSED) counter += 1;
else counter = 0;
if (counter > BUTTON_PRESSED_THRES) button = BUTTON_STATUS_HOLDING;
break;
case BUTTON_STATUS_HOLDING:
// Decrement if not pressed, else set the count to threshold
// This will filter out the noise when starting to release
if ((P2IN & BIT1) == BUTTON_STATE_NOTPRESSED) counter -= 1;
else counter = BUTTON_PRESSED_THRES;
if (counter == 0) button = BUTTON_STATUS_RELEASED;
break;
}
}
// Your other code to detect when the button has been pressed and release
if (button == BUTTON_STATUS_RELEASED)
{
// do something
// Must reset after detecting button has been release
button = BUTTON_STATUS_NOTPRESSED;
}

How to wake Pic32 from sleep mode?

I am trying to keep PIC32 in sleep mode on boot up. And when the power button is pressed the Pic32 should exit the sleep mode and wake up.
I am able to put PIC32 in sleep mode but I am not sure how I can trigger it to wake up on button press.
Is it possible if I can trigger an interrupt whenever the user presses the power button and thus wake the PIC32?
I am using microchip harmony framework and am quite new to this can someone please suggest how I can achieve this?
To put PIC32 I am using the PowerEnterMode function of harmony. To wake up PIC32 I have tried using the watch dog timer following the sample project provided with microchip harmony but couldn't get it to work.
I also read that I can use external interrupt to set it up but I don't know how to set it up.
I have added my code below.
void APP_Initialize ( void )
{
DRV_ADC_Open();
DRV_ADC_Start();
PLIB_ADC_Enable(ADC_ID_1);
SPIHandle = DRV_SPI_Open(DRV_SPI_INDEX_0, DRV_IO_INTENT_READWRITE );
DelayMs(100);
SYS_DEVCON_PowerModeEnter(SYS_POWER_MODE_IDLE);
appData.state = APP_STATE_POWER_UP;
}
void APP_Tasks (void)
{
switch ( appData.state )
{
case APP_STATE_POWER_UP:
{
uint8_t pwrButton;
pwrButton = PWR_BTNStateGet() ;
if (npwrButton == 0) // If button is pressed
{
PwrButtonDebounce += 1; // Increment the pressed timer
DelayMs(10);
} else
{
PwrButtonDebounce = 0; // Clear the Debounce Timer
}
if (PwrButtonDebounce == MAX_DEBOUNCE) // Debounced....
{
// Here Wake up routine on button press
appData.state = APP_STATE_INIT;
}
}
}
I expect that whenever I press the power button the Pic32 should come in the APP_STATE_POWER_UP state and on debounce go in the initialization state but It never comes in this state.
Can someone suggest how I can set this up?
You should use Change Notification interrupt.
Enable the CN interrupt on the pin that you have your button and it will wake your device when pressed.

Why does this state machine not maintain its state?

Hey there StackOverflow!
In the following code I have a simple state machine that changes the operation of some external lighting device (as the comments imply). The state is changed via the pressing of the button connected to GP1. The circuit connected to GP1 is a comparator debouncing circuit that compares VDD to 0.6VDD (I've also tried an RC/diode/schmitt trigger circuit), which then forces the signal LO. On a scope, we see a clean square wave when the button is actuated rapidly.
The current (and undesirable) behavior of the PIC10F200 is as follows:
Switch is pressed (state = 0)
State machine variable increments (state = 1)
Lighting goes to case 1, and turns on
Lighting remains on for at least a second
Lighting turns off
System remains in this state until button is actuated again or
powered off
The question is: Why does it behave like this? And how if possible, do I fix it such that a single press of the button equates to a single state increment, which the PIC then maintains for as long as the system is powered and the button is not actuated again?
#define SYS_FREQ 8000000L
#define FCY SYS_FREQ/4
#define _XTAL_FREQ 4000000
/******************************************************************************/
/* User Global Variable Declaration */
/******************************************************************************/
/******************************************************************************/
/* Main Program */
/******************************************************************************/
__CONFIG(MCLRE_ON & CP_OFF & OSC_IntRC);
void main(void)
{
TRIS = 0b111110;
unsigned char state = 0;
while(1)
{
switch (state)
{
case 0: // IDLE/OFF
if (GPIObits.GP0) GPIObits.GP0 = 0;
break;
case 1: // ON
if (!GPIObits.GP0) GPIObits.GP0 = 1;
break;
case 2: // BLINK (slow)
GPIObits.GP0 = !GPIObits.GP0;
__delay_ms(100);
break;
case 3: // BLINK (fast)
GPIObits.GP0 = !GPIObits.GP0;
__delay_ms(50);
break;
case 4: // BEAT DETECT
GPIObits.GP0 = GPIObits.GP2;
break;
default:
state = 0;
break;
}
if (!GPIObits.GP1)
{
__delay_ms(250);
state++;
}
}
}
UPDATE: Since there seems to be a little confusion as to what I am trying to accomplish with this code/system, lets provide the full context. This microcontroller, the PIC10F200 is part of an overall board design for an electroluminescent (EL) wire driver. The miconcontroller simply controls whether or not the driver circuit is enabled by connecting GP0 to the EN port of the driver IC. The system has four modes of operation, the wire is constantly on, the wire is blinking, the wire is blinking faster, and the wire blinks whenever a low-frequency beat is detected (another circuit in the system). The transition from these modes of operation is governed by a pushbutton (on momentarily) switch to be mounted on the PCB. This necessitates that state in the code above remains stable between button actuations. It currently does not do this and behaves as described in the original part of this post. As the question title states, why isn't state stable currently, and how do I make it so?
UPDATE (2014-03-08): Solution
The following settings need to be set assuming GP0 is the output, GP2 is your T0CKI and you have a switch that drives the signal to LO when actuated.
TRIS = 0b111110;
OPTION = 0b11101111;
Whether or not bits 0-3 for OPTION really matter is a judgement call and whether or not you choose to use the WDT module.
Additionally, the implementation for the button release detection is a simple counter mechanism that resets upon GP2 being LO at any point during the count.
if (TMR0 > 0)
{
while (count < 20)
{
if (!GPIObits.GP2) count = 0;
__delay_ms(10);
count++;
}
TMR0 = 0;
state++;
}
You have a hardware/software design problem!
When your program is in delay loop than your key button is not
checked!
You are checking only on key press event, but you must also on key
relase.
My purpose is that you can use GP2 (T0CKI) pin instead GP1 for key buttom. This pin has schmitt trigger input if is used as counter TMR0 input. After that configure your MCPU TMR0 as counter with external clock on GP2 (T0CKI) pin. You must also set the T0SE bit to configure counter that will increment on high-to-low transition on the T0CKI pin.
In program after any loop check the TMR0 content if biger than 0 the key was pressed.
Wait some ms and check if key was relased if relased than increase the state variable and clear TMR0 content.
move your
if (!GPIObits.GP1){
if(isPressed == false){
//__delay_ms(250); //you dont need the delay
state++;
if(state == 5){state = 0;}
isPressed = true;
}
}
else{isPressed = false;}
before the switch statement. char isPressed = false; before the while loop
valter
__delay_ms(250); <-- too small delay
While you are pressing the button slowly, the loop may rotate several times. Try increasing it to 1000ms.
Update
You should run the PIC with WDT (watch dog timer) disabled, otherwise it will reset the pic in few seconds. That is similar to your observation. You can blink a LED at the beginning of main function to check if this happen or you can initialization unsigned char state = 1; and see the behavior then.
Try this __CONFIG(MCLRE_ON & CP_OFF & OSC_IntRC & WDT_OFF);

How to set Port as Input for Push Button in C?

I'm using MikroC to try and program my PIC16f62 Microcontroller. I've managed to get my outputs working (I can have LEDs turn on, etc) but I can't seem to get the inputs work.
Here is my current code:
void main() {
TRISB.RB0 = 0; //set Port RB0 as output
PORTB.RB0 = 1; //set Port RB0 to high (turn on LED)
TRISA = 1; //Set PORTA as inputs
for(;;){ //endless loop
if(PORTA.RA0 == 1){ //if push button is pressed
PORTB.RB0 = !PORTB.RB0; \\toggle LED
}
}
}
I don't know if the problem is that I'm not configuring the PORT correctly or if I'm checking whether or not the button is pressed incorrectly.
Any help is appreciated. Thanks.
This change may help you.
for(;;){ //endless loop
if(PORTA.RA0 == 1){ //if push button is pressed
PORTB.RB0 = !PORTB.RB0; \\toggle LED
while(PORTA.RA0 == 1);
/*wait till button released as press of a buttons take time and processor is too fast */
}
You are probably reading the port pin correctly, however because you're toggling the LED on and off when you detect a press, your eye can't see the result.
For example, a clock rate of 1Mhz will have the on/off toggle approximately 150,000 times per second (1,000,000 cycles / ~3 ASM instructions per loop / 2 loops to turn on then off).
I would suggest taking the approach of having the LED match the state of the input pin.
for(;;)
{
if(PORTA.RA0 == 1) //if button is pressed
{
PORTB.RB0 = 1; //turn on LED
}
else
{
PORTB.RB0 = 0; //turn off LED
}
}
This technique is similar to what Rajesh suggested, but provides a bit more direct feedback on whether the input pin is set or not.
If that doesn't work, then something with your setup of the TRISA is not correct. You may want to try this:
TRISA.RB0 = 1;

Resources