MSP430 LaunchPad | Assign double click on button - c

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;
}

Related

Avoid Input Latency while reading input from keyboard loop

I'm just starting win STM32 and am trying to write some code which tries to accept input from user keypress, and vary the blink-rate of 2 LEDs or blink them alternatively, or at the same time.
So, I have 3 Keys(2 keys each for inc/dec, and one key for mode), and 2 LEDs.
The loop section looks something like this:
/* USER CODE BEGIN WHILE */
const uint32_t step = 50u;
const uint32_t max_interval = 500u;
uint32_t interval = max_interval;
short mode = 0;
while (1)
{
volatile GPIO_PinState wakeup = HAL_GPIO_ReadPin(WAKEUP_GPIO_Port, WAKEUP_Pin);
mode = (wakeup == GPIO_PIN_RESET? 0: 1);
if(mode == 1) {
HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_RESET);
}
HAL_GPIO_TogglePin(LED2_GPIO_Port, LED2_Pin);
HAL_GPIO_TogglePin(LED2_GPIO_Port, LED3_Pin);
volatile GPIO_PinState key0 = HAL_GPIO_ReadPin(KEY0_GPIO_Port, KEY0_Pin);
volatile GPIO_PinState key1 = HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin);
if(key0 == GPIO_PIN_RESET)
interval -= (interval==step? 0: step);
if(key1 == GPIO_PIN_RESET)
interval += (interval==max_interval? 0: step);
HAL_Delay(interval);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
Now, depending on the HAL_Delay(interval), the loop would get a chance to check for key-input, whose purpose is to control the blink rate. Is there some way, I can untie this latency for key-input? The microcontroller in question is STM32F407VET6, and I'm using CubeIDE. It would be nice to have a single-threaded solution.
Some delay for key input is unavoidable, unless you have some hardware-debounced keys. Normally, when hitting a key, the transition is not a single edge, but some burst of level changes until the mechanics settle. One way to do debouncing would be to have a periodic interval timer (e.g. at 1khz rate) and you check each time the level of a key. If it is high, you count a counter up. If it is low, you count a counter down and then you have 2 thresholds (hysteresis) in the count values, when you consider it a button down or button up transition. Since all that works in the interrupt, you could then push the key event into a FIFO (queue) and in your "main thread", you can pull the events at convenient occasions.
If there is also a programmable timer on your hardware, you could use that to toggle the output pins for the LED and your main loop would then simply be along the lines:
void mainloop() {
while (true) {
KeyEvent_t key;
nextKeyEvent(&key);
switch (key) {
case BUTTON1_DOWN:
reduceBlinkRate();
break;
case BUTTON2_DOWN:
increaseBlinkRate();
break;
default:
// skillful doing nothing (e.g. to save power)
break;
}
}
}

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

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.

MSP430 issue with button not switching off

I'm making a program for the msp430.
The incrementation runs away on first button click. It doesn't stop when the button is released.
How can incrementation be limited to one incrementation for each button click?
#include <msp430.h>
int main(void)
{
int i; //delay variable
int dimeRead=0;
int desired=1000;
volatile int total=0;
P1OUT=0; //Supposed to get rid of it hanging at the top
WDTCTL = WDTPW | WDTHOLD; // Stop watchdog timer
while(total<desired)
{
if((P1IN&0x16)!=0x16) // check if switch is pressed or not
{
dimeRead=dimeRead+1;
total=total + 10;
}
//Goal is to flip an out put on to turn on light when desired number is hit.
}
return 0;
}
At first write your button pins by mask like this
#define MASK PIN1 | PIN2 (1 and 2 change to your pins) it's better for visual error control.
At second statement for check all pressed buttons if ((P1IN&MASK)==MASK).
Now your statement if((P1IN&0x16)!=0x16) check that 3 pins (PIN1, PIN2, PIN4) are in Hi state and when it's false make code
{
dimeRead=dimeRead+1;
total=total + 10;
}
If you want increment when one or two buttons are pushed statement must be like this if((P1IN&MASK)!=0)
All this is true for buttons that pushed up (HI state) when pressed, for pulled down (LOW state) is if((P1IN&MASK)!=MASK).
Add some delay after increment for debounse button.
If your buttons are connected by PIN and ground, you must enable pull-up for this pins/
if((P1IN&0x16)!=0x16) ,
when buttons arent pushed this statement is true. You should change it with equal sign.
Also im not sure about where 0x16 came from, i think you should also take a second look at it.
I am not authorized comment so I'm writing as an answer. If am not mistaken, you are trying to increment in the if, every time you pressed the button. That's why it should be like this.
if((P1IN&0x16) == 0x16)
But I would like to mention the following also:
if((P1IN&0x16) == 0x16)
by writing this statement you are expecting P1.1, P1.2 and P1.4 to be high.

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