PIC 中的去抖动按钮
Debounce button in PIC
我是PIC mcu的新手。我使用 pic12f675 MPLAB 和 XC8 制作 LED 多次闪烁模式。
我有按钮的问题(在审查它调用弹跳和去抖动之后)。
有时当我按下按钮时,它会依次执行。 1->2->3->4->5 但有时它会跳转。 1->3->4->6 等
请教我如何在pic mcu中去抖或其他方法来解决我的问题。
谢谢。大家
(PS.I 将按钮与 10K 电阻连接)
我的代码在下面
#include <xc.h>
#pragma config FOSC=INTRCIO,WDTE=OFF,MCLRE=OFF,BOREN=OFF
#define _XTAL_FREQ 4000000
int cnt = 0;
int k = 0;
void __interrupt() MyISR(void){
if(INTCONbits.INTF) //If External Edge INT Interrupt
{
cnt++;
INTCONbits.GIE = 0;
INTCONbits.INTF = 0; // Clear the interrupt
INTCONbits.GPIF = 0;
if( cnt > 6 ){
cnt = 1;
}
}
INTCONbits.GIE = 1;
}
void main(void) {
ANSEL = 0;
CMCON = 0b00000111; //turns comparators off
TRISIO = 0b00000100;
GPIO = 0;
TRISIO2 = 1; // Make GP2 pin as input
INTCONbits.GIE = 1;
INTCONbits.INTE = 1;
INTCONbits.GPIF = 1;
INTCONbits.INTF = 0;
OPTION_REG = 0b01000000;
while(1){
if( cnt == 1 ){
GP0 = 1;
GP5 = 1;
}else if( cnt == 2 ){
for(k=0;k<30;k++){
GP5 = 1;
GP0 = 1;
}
k=0;
while(k<3){
GP5 = ~GP5;
__delay_ms(70);
GP0 = ~GP0;
__delay_ms(70);
k++;
}
}else if( cnt == 3 ){
for(k=0;k<5;k++){
GP5 = 1;
GP0 = 1;
__delay_ms(70);
GP5 = 0;
GP0 = 0;
__delay_ms(70);
}
GP5 = 0;
GP0 = 0;
__delay_ms(1200);
}else if( cnt == 4 ){
for(k=0;k<3;k++){
GP0 = 1;
__delay_ms(50);
GP0 = 0;
__delay_ms(50);
}
for(k=0;k<3;k++){
GP5 = 1;
__delay_ms(50);
GP5 = 0;
__delay_ms(50);
}
}else if( cnt == 5 ){
GP0 = 1;
GP5 = 1;
for(k=0;k<3;k++){
GP5 = 1;
__delay_ms(50);
GP5 = 0;
__delay_ms(50);
}
GP0 = 1;
GP5 = 1;
for(k=0;k<3;k++){
GP0 = 1;
__delay_ms(50);
GP0 = 0;
__delay_ms(50);
}
}else if( cnt == 6 ){
GP0 = 1;
GP5 = 1;
__delay_ms(20);
GP0 = 0;
GP5 = 0;
__delay_ms(3000);
}
}
return;
}
我重写了您的代码并在 MPLAB 仿真中对其进行了测试。它按预期工作。它按升序更改模式,然后在所选模式中 运行s 直到再次按下更改按钮,然后它更改为下一个模式。如果需要,您可以添加更多工作模式,也可以修改 GPIO 闪烁的方式。没有 __delay_ms(),这就是延迟 运行 而没有消耗 CPU 的原因。请在实际电路中测试并反馈给我。
/*
* File: main.c
* Author: kozmotronik
*
*/
#define _XTAL_FREQ 4000000
#include <xc.h>
#include <stdint.h>
#include <stdbool.h>
#pragma config FOSC=INTRCIO,WDTE=OFF,MCLRE=OFF,BOREN=OFF
// Work mode definitions
#define MODE_IDLE 0
#define MODE_OFF 1
#define MODE_ON 2
#define MODE_SLOW 3
#define MODE_FAST 4
#define MODE_CANCEL 5
#define LAST_MODE MODE_FAST
// Button states
#define BUTTON_IDLE 0
#define BUTTON_PRESS_DETECTED 1
#define BUTTON_DEBOUNCING 2
#define BUTTON_PRESS_CONFIRMED 3
#define SYSTEM_CLOCK_MS 1
#define SYSTEM_CLOCK_uS (SYSTEM_CLOCK_MS * 1000)
#define _XTAL_FREQ_MHZ (_XTAL_FREQ / 1000000) // Oscillator freq in MHz
#define TMR0_RELOAD_VALUE 256 - ( (SYSTEM_CLOCK_uS * _XTAL_FREQ_MHZ) / (8 * 4) ) // Result must be 131
#define MS_TO_TICKS(msTime) (msTime / SYSTEM_CLOCK_MS)
typedef struct{
unsigned int start;
unsigned int ticks;
} time_t;
char mode = MODE_IDLE;
char lastMode = MODE_OFF;
char buttonState = BUTTON_IDLE;
char k = 0;
unsigned int systemTick = 0; // Time value count by Timer0
void __interrupt() MyISR(void){
if(INTCONbits.INTF) //If External Edge INT Interrupt
{
INTCONbits.INTF = 0; // Clear the interrupt
buttonState = BUTTON_PRESS_DETECTED; // Signal the detected press
}
// Check for 1 ms periodic interrupt for system clock
else if(INTCONbits.T0IF){
INTCONbits.T0IF = 0; // clear flag
TMR0 = TMR0_RELOAD_VALUE; // Reload the calculated value for 1 ms
systemTick++;
}
}
// Setup Timer0 for 1ms interrupt
void setupTimer0(){
#define PRESCALER_VALUE 2
#define PRESCALER_MASK ~7
OPTION_REG &= PRESCALER_MASK; // Clear prescaler bits
OPTION_REG |= PRESCALER_VALUE; // Set prescaler value for 1:8
OPTION_REGbits.PSA = 0; // Assign prescaler to Tim0
OPTION_REGbits.T0CS = 0; // Set internal oscillator as clock source
TMR0 = TMR0_RELOAD_VALUE;
INTCONbits.T0IF = 0;
INTCONbits.T0IE = 1; // Enable Timer0 interrupt
}
// Get count atomically
unsigned int getTickCount(){
unsigned int count;
di(); // disable interrupts
count = systemTick;
ei(); // enable interrupts again
return count;
}
void performMode(){
static time_t modeDelay;
static char slowModeState = 1;
static char fastModeState = 1;
switch(mode){
case MODE_OFF:
// Always must save the current mode before put it into the IDLE
lastMode = mode; // We have to save the last mode first then put it into the IDLE state
mode = MODE_IDLE; // The rollover bug caused by here since we haven't save the last mode before put it into the IDLE state
GP0 = 0; GP5 = 0;
break;
case MODE_ON:
GP0 = 1; GP5 = 1;
break;
case MODE_SLOW:
if(slowModeState == 1){
GP0 = 1; GP5 = 1;
modeDelay.ticks = MS_TO_TICKS(100);
modeDelay.start = getTickCount();
slowModeState = 2; // Proceed the next step
}
else if(slowModeState == 2){
if( !((getTickCount() - modeDelay.start) >= modeDelay.ticks) ){
// Delay not expired yet
return;
}
GP0 = ~GP0; GP5 = ~GP5; // Toggle
// Reload the start time
modeDelay.start = getTickCount();
}
break;
case MODE_FAST:
if(fastModeState == 1){
GP0 = 1; GP5 = 1;
modeDelay.ticks = MS_TO_TICKS(50);
modeDelay.start = getTickCount();
fastModeState = 2; // Proceed the next step
}
else if(fastModeState == 2){
if( !((getTickCount() - modeDelay.start) >= modeDelay.ticks) ){
// Delay not expired yet
return;
}
// Delay time expired, proceed toggle
GP0 = ~GP0; GP5 = ~GP5; // Toggle
// Reload the start time
modeDelay.start = getTickCount();
}
break;
case MODE_CANCEL:
// Cancel the current running mode, reset everything
modeDelay.start = 0;
modeDelay.ticks = 0;
slowModeState = 1;
fastModeState = 1;
// Also reset the outputs
GP0 = 0; GP5 = 0;
break;
default:
mode = MODE_IDLE;
}
}
void checkButton(){
#define DEBOUNCE_DELAY_MS 100u // Debounce delay is 100 ms
static time_t debounceTimer;
switch(buttonState){
case BUTTON_IDLE:
break;
case BUTTON_PRESS_DETECTED:
debounceTimer.ticks = MS_TO_TICKS(DEBOUNCE_DELAY_MS);
debounceTimer.start = getTickCount();
buttonState = BUTTON_DEBOUNCING;
break;
case BUTTON_DEBOUNCING:
if( !((getTickCount() - debounceTimer.start) >= debounceTimer.ticks) ){
// Debounce time has not expired yet
return;
}
// Debounce time has expired so check the button last time to confirm if it is still pressed
if(GPIObits.GP2 != 1){
// Not stable yet, debounce again
buttonState = BUTTON_PRESS_DETECTED;
}
// Button press is stable, confirm it
buttonState = BUTTON_PRESS_CONFIRMED;
break;
case BUTTON_PRESS_CONFIRMED:
buttonState = BUTTON_IDLE; // Change state so that it can process a new button press
if(mode != MODE_IDLE && mode != MODE_OFF){
// Cancel the running mode first
lastMode = mode; // save the last mode
mode = MODE_CANCEL; // purge the current one
performMode();
}
mode = lastMode + 1; // Switch to next mode
if(mode > LAST_MODE){
// Rewind mode to the beginning which is MODE_OFF
mode = MODE_OFF;
}
break;
default:
buttonState = BUTTON_IDLE;
}
}
void main(void) {
ANSEL = 0;
CMCON = 0b00000111; //turns comparators off
TRISIO = 0b00000100;
GPIO = 0;
TRISIO2 = 1; // Make GP2 pin as input
INTCONbits.INTF = 0;
INTCONbits.INTE = 1;
INTCONbits.GIE = 1;
OPTION_REG = 0b01000000; // Rising edge interrupt
setupTimer0();
// Super loop
while(1){
// Task 1: Button check
if(buttonState != BUTTON_IDLE){
checkButton();
}
// Task 2: Mode check
else if(mode != MODE_IDLE){
performMode();
}
}
return;
}
我是PIC mcu的新手。我使用 pic12f675 MPLAB 和 XC8 制作 LED 多次闪烁模式。 我有按钮的问题(在审查它调用弹跳和去抖动之后)。 有时当我按下按钮时,它会依次执行。 1->2->3->4->5 但有时它会跳转。 1->3->4->6 等
请教我如何在pic mcu中去抖或其他方法来解决我的问题。
谢谢。大家
(PS.I 将按钮与 10K 电阻连接)
我的代码在下面
#include <xc.h>
#pragma config FOSC=INTRCIO,WDTE=OFF,MCLRE=OFF,BOREN=OFF
#define _XTAL_FREQ 4000000
int cnt = 0;
int k = 0;
void __interrupt() MyISR(void){
if(INTCONbits.INTF) //If External Edge INT Interrupt
{
cnt++;
INTCONbits.GIE = 0;
INTCONbits.INTF = 0; // Clear the interrupt
INTCONbits.GPIF = 0;
if( cnt > 6 ){
cnt = 1;
}
}
INTCONbits.GIE = 1;
}
void main(void) {
ANSEL = 0;
CMCON = 0b00000111; //turns comparators off
TRISIO = 0b00000100;
GPIO = 0;
TRISIO2 = 1; // Make GP2 pin as input
INTCONbits.GIE = 1;
INTCONbits.INTE = 1;
INTCONbits.GPIF = 1;
INTCONbits.INTF = 0;
OPTION_REG = 0b01000000;
while(1){
if( cnt == 1 ){
GP0 = 1;
GP5 = 1;
}else if( cnt == 2 ){
for(k=0;k<30;k++){
GP5 = 1;
GP0 = 1;
}
k=0;
while(k<3){
GP5 = ~GP5;
__delay_ms(70);
GP0 = ~GP0;
__delay_ms(70);
k++;
}
}else if( cnt == 3 ){
for(k=0;k<5;k++){
GP5 = 1;
GP0 = 1;
__delay_ms(70);
GP5 = 0;
GP0 = 0;
__delay_ms(70);
}
GP5 = 0;
GP0 = 0;
__delay_ms(1200);
}else if( cnt == 4 ){
for(k=0;k<3;k++){
GP0 = 1;
__delay_ms(50);
GP0 = 0;
__delay_ms(50);
}
for(k=0;k<3;k++){
GP5 = 1;
__delay_ms(50);
GP5 = 0;
__delay_ms(50);
}
}else if( cnt == 5 ){
GP0 = 1;
GP5 = 1;
for(k=0;k<3;k++){
GP5 = 1;
__delay_ms(50);
GP5 = 0;
__delay_ms(50);
}
GP0 = 1;
GP5 = 1;
for(k=0;k<3;k++){
GP0 = 1;
__delay_ms(50);
GP0 = 0;
__delay_ms(50);
}
}else if( cnt == 6 ){
GP0 = 1;
GP5 = 1;
__delay_ms(20);
GP0 = 0;
GP5 = 0;
__delay_ms(3000);
}
}
return;
}
我重写了您的代码并在 MPLAB 仿真中对其进行了测试。它按预期工作。它按升序更改模式,然后在所选模式中 运行s 直到再次按下更改按钮,然后它更改为下一个模式。如果需要,您可以添加更多工作模式,也可以修改 GPIO 闪烁的方式。没有 __delay_ms(),这就是延迟 运行 而没有消耗 CPU 的原因。请在实际电路中测试并反馈给我。
/*
* File: main.c
* Author: kozmotronik
*
*/
#define _XTAL_FREQ 4000000
#include <xc.h>
#include <stdint.h>
#include <stdbool.h>
#pragma config FOSC=INTRCIO,WDTE=OFF,MCLRE=OFF,BOREN=OFF
// Work mode definitions
#define MODE_IDLE 0
#define MODE_OFF 1
#define MODE_ON 2
#define MODE_SLOW 3
#define MODE_FAST 4
#define MODE_CANCEL 5
#define LAST_MODE MODE_FAST
// Button states
#define BUTTON_IDLE 0
#define BUTTON_PRESS_DETECTED 1
#define BUTTON_DEBOUNCING 2
#define BUTTON_PRESS_CONFIRMED 3
#define SYSTEM_CLOCK_MS 1
#define SYSTEM_CLOCK_uS (SYSTEM_CLOCK_MS * 1000)
#define _XTAL_FREQ_MHZ (_XTAL_FREQ / 1000000) // Oscillator freq in MHz
#define TMR0_RELOAD_VALUE 256 - ( (SYSTEM_CLOCK_uS * _XTAL_FREQ_MHZ) / (8 * 4) ) // Result must be 131
#define MS_TO_TICKS(msTime) (msTime / SYSTEM_CLOCK_MS)
typedef struct{
unsigned int start;
unsigned int ticks;
} time_t;
char mode = MODE_IDLE;
char lastMode = MODE_OFF;
char buttonState = BUTTON_IDLE;
char k = 0;
unsigned int systemTick = 0; // Time value count by Timer0
void __interrupt() MyISR(void){
if(INTCONbits.INTF) //If External Edge INT Interrupt
{
INTCONbits.INTF = 0; // Clear the interrupt
buttonState = BUTTON_PRESS_DETECTED; // Signal the detected press
}
// Check for 1 ms periodic interrupt for system clock
else if(INTCONbits.T0IF){
INTCONbits.T0IF = 0; // clear flag
TMR0 = TMR0_RELOAD_VALUE; // Reload the calculated value for 1 ms
systemTick++;
}
}
// Setup Timer0 for 1ms interrupt
void setupTimer0(){
#define PRESCALER_VALUE 2
#define PRESCALER_MASK ~7
OPTION_REG &= PRESCALER_MASK; // Clear prescaler bits
OPTION_REG |= PRESCALER_VALUE; // Set prescaler value for 1:8
OPTION_REGbits.PSA = 0; // Assign prescaler to Tim0
OPTION_REGbits.T0CS = 0; // Set internal oscillator as clock source
TMR0 = TMR0_RELOAD_VALUE;
INTCONbits.T0IF = 0;
INTCONbits.T0IE = 1; // Enable Timer0 interrupt
}
// Get count atomically
unsigned int getTickCount(){
unsigned int count;
di(); // disable interrupts
count = systemTick;
ei(); // enable interrupts again
return count;
}
void performMode(){
static time_t modeDelay;
static char slowModeState = 1;
static char fastModeState = 1;
switch(mode){
case MODE_OFF:
// Always must save the current mode before put it into the IDLE
lastMode = mode; // We have to save the last mode first then put it into the IDLE state
mode = MODE_IDLE; // The rollover bug caused by here since we haven't save the last mode before put it into the IDLE state
GP0 = 0; GP5 = 0;
break;
case MODE_ON:
GP0 = 1; GP5 = 1;
break;
case MODE_SLOW:
if(slowModeState == 1){
GP0 = 1; GP5 = 1;
modeDelay.ticks = MS_TO_TICKS(100);
modeDelay.start = getTickCount();
slowModeState = 2; // Proceed the next step
}
else if(slowModeState == 2){
if( !((getTickCount() - modeDelay.start) >= modeDelay.ticks) ){
// Delay not expired yet
return;
}
GP0 = ~GP0; GP5 = ~GP5; // Toggle
// Reload the start time
modeDelay.start = getTickCount();
}
break;
case MODE_FAST:
if(fastModeState == 1){
GP0 = 1; GP5 = 1;
modeDelay.ticks = MS_TO_TICKS(50);
modeDelay.start = getTickCount();
fastModeState = 2; // Proceed the next step
}
else if(fastModeState == 2){
if( !((getTickCount() - modeDelay.start) >= modeDelay.ticks) ){
// Delay not expired yet
return;
}
// Delay time expired, proceed toggle
GP0 = ~GP0; GP5 = ~GP5; // Toggle
// Reload the start time
modeDelay.start = getTickCount();
}
break;
case MODE_CANCEL:
// Cancel the current running mode, reset everything
modeDelay.start = 0;
modeDelay.ticks = 0;
slowModeState = 1;
fastModeState = 1;
// Also reset the outputs
GP0 = 0; GP5 = 0;
break;
default:
mode = MODE_IDLE;
}
}
void checkButton(){
#define DEBOUNCE_DELAY_MS 100u // Debounce delay is 100 ms
static time_t debounceTimer;
switch(buttonState){
case BUTTON_IDLE:
break;
case BUTTON_PRESS_DETECTED:
debounceTimer.ticks = MS_TO_TICKS(DEBOUNCE_DELAY_MS);
debounceTimer.start = getTickCount();
buttonState = BUTTON_DEBOUNCING;
break;
case BUTTON_DEBOUNCING:
if( !((getTickCount() - debounceTimer.start) >= debounceTimer.ticks) ){
// Debounce time has not expired yet
return;
}
// Debounce time has expired so check the button last time to confirm if it is still pressed
if(GPIObits.GP2 != 1){
// Not stable yet, debounce again
buttonState = BUTTON_PRESS_DETECTED;
}
// Button press is stable, confirm it
buttonState = BUTTON_PRESS_CONFIRMED;
break;
case BUTTON_PRESS_CONFIRMED:
buttonState = BUTTON_IDLE; // Change state so that it can process a new button press
if(mode != MODE_IDLE && mode != MODE_OFF){
// Cancel the running mode first
lastMode = mode; // save the last mode
mode = MODE_CANCEL; // purge the current one
performMode();
}
mode = lastMode + 1; // Switch to next mode
if(mode > LAST_MODE){
// Rewind mode to the beginning which is MODE_OFF
mode = MODE_OFF;
}
break;
default:
buttonState = BUTTON_IDLE;
}
}
void main(void) {
ANSEL = 0;
CMCON = 0b00000111; //turns comparators off
TRISIO = 0b00000100;
GPIO = 0;
TRISIO2 = 1; // Make GP2 pin as input
INTCONbits.INTF = 0;
INTCONbits.INTE = 1;
INTCONbits.GIE = 1;
OPTION_REG = 0b01000000; // Rising edge interrupt
setupTimer0();
// Super loop
while(1){
// Task 1: Button check
if(buttonState != BUTTON_IDLE){
checkButton();
}
// Task 2: Mode check
else if(mode != MODE_IDLE){
performMode();
}
}
return;
}