Reputation: 145
I want to write a function for my AVR ATmega328 that debounces switches using state space to confirm a switch press. After finishing it I wanted to generalize my function so that I may reuse it in the future with little work, but that involves passing the pin I want to use as a function parameter, and I just can't get that to work.
This is what I have now:
int debounceSwitch(unsigned char *port, uint8_t mask)
{
int n = 0;
while (1)
{
switch (n)
{
case 0: //NoPush State
_delay_ms(30);
if(!(*port & (1<<mask))){n = n + 1;}
else {return 0;}
break;
case 1: //MaybePush State
_delay_ms(30);
if(!(*port & (1<<mask))){n = n + 1;}
else {n = n - 1;}
break;
case 2: //YesPush State
_delay_ms(30);
if(!(*port & (1<<mask))){return 1;}
else {n = n - 1;}
break;
}
}
}
I have a hunch my issue is with the data type I'm using as the parameter, and I seem to have gotten different answers online.
Any help would be appreciated!
Upvotes: 1
Views: 819
Reputation: 1496
What I dislike on your implementation is the pure dependency on PORT/IO handling and the actual filter/debouncing logic. What are you doing then, when the switch input comes over a signal e.g. from CAN?
Also, it can be handled much easier, if you think in configurable/parameterizable filters. You implement the logic once, and then just create proper configs and pass separate state variables into the filter.
// Structure to keep state
typedef struct {
boolean state;
uint8 cnt;
} deb_state_t;
// Structure to configure the filters debounce values
typedef struct {
uint8 cnt[2]; // [0] = H->L transition, [1] = L->H transition
} deb_config_t;
boolean debounce(boolean in, deb_state_t *state, const deb_config_t *cfg)
{
if (state->state != in) {
state->cnt++;
if (state->cnt >= cfg->cnt[in]) {
state->state = in;
state->cnt = 0;
}
} else {
state->cnt = 0;
}
return state->state;
}
static const deb_config_t debcfg_pin = { {3,4} };
static const deb_config_t debcfg_can = { {2,1} };
int main(void)
{
boolean in1, in2, out1, out2;
deb_state_t debstate_pin = {0, 0};
deb_state_t debstate_can = {0, 0};
while(1) {
// read pin and convert to 0/1
in1 = READ_PORT(PORTx, PINxy); // however this is defined on this architecture
out1 = debounce(in1, &debstate_pin, &debcfg_pin);
// same handling, but input from CAN
in2 = READ_CAN(MSGx, SIGxy); // however this is defined on this architecture
out2 = debounce(in2, &debstate_can, &debcfg_can);
// out1 & out2 are now debounced
}
Upvotes: 0
Reputation: 213940
Various issues:
void debounceSwitch(volatile uint8_t* port, uint8_t pin)
. Pointers to hardware registers must always be volatile
. It doesn't make sense to return anything.1
signed int
literals when bit-shifting. Should be 1u << n
or your program will bug out when n
is larger than 8.There are many ways to debounce buttons. The simplest professional form is probably to have a periodic timer running with interrupt every 10ms (should be enough, if in doubt measure debounce spikes of your button with a scope). It will look something like the following pseudo code:
volatile bool button_pressed = false;
void timer_interrupt (void)
{
uint8_t button = port & mask;
button_pressed = button && prev;
prev = button;
}
This assuming that buttons use active high logic.
Upvotes: 0
Reputation: 923
Well in AVR ports are special IO registers and they are accessed using IN and OUT instructions. Not like memory using LDR etc.
From the port definition you can see that you need to make the port pointer volatile. which the compiler would have also told you as a warning when you would had tried to pass PORT to the function.
#define PORTB _SFR_IO8(0x05)
which maps to
#define _SFR_IO8(io_addr) _MMIO_BYTE((io_addr) + __SFR_OFFSET)
#define _MMIO_BYTE(mem_addr) (*(volatile uint8_t *)(mem_addr))
Upvotes: 0