There are many benefits to inline functions over preprocessor macros:
- Type checking. The compiler will complain if the arguments are of incorrect type.
No risk of multiple evaluation: macros can be dangerous when the argument is an expression, consider:
#include <stdio.h>
#define square(n) ((n)*(n))
int main(void)
{
int x, i = 0;
while (i < 10) {
x = square(i++);
printf("%d\r\n", x);
}
return 0;
}
- Can include multiple lines of code without trailing backlashes.
- Inline functions have their own scope for variables and they can return a value.
Macros can be useful e.g. when concatenating tokens into code, so as to simplify some complex declarations.
Consider macros as just a dumb text replacement functionality. A good rule of thumb is to avoid function-like macros, unless there is no other viable solution.
It is a common misconception that inlining always equals faster code. Keep in mind, that every inlined call from more than one place adds up to code size. Premature optimization is not a virtue, even when working with the constrained resources of a microcontroller.
GCC interprets the inline
keyword as a mere hint. There is no guarantee thet the function will actually be inlined. To have GCC inline your function regardless of optimization level, declare the function with the always_inline
attribute:
void function() __attribute__((always_inline));
first, the Read-Modify-Write issue (from http://www.voti.nl/DB038/DB038-1-1.pdf, p51):
PICs use a funny IO architecture where a reading of a pin always returns the current external level of the pin, which can be different from the last value written to a pin, even when the pin is set as output, for two reasons:
• PIC instructions execute in a 2-stage pipeline, and for IO pins the read part of the next instruction takes place before the writing of the previous instruction.
• The load on the pin can be too high for it to reach its 'desired' level. This can easily happen (for a short time) when the load is capacitive, but it can also happen with a static load that is well within the normal operating specifications.
All PIC instructions that modify some bits in a byte are read-modify-write instructions, so when for instance two consecutive BCF (bit clear) instructions are executed on the same IO port the second instruction can ruin the effect of the first because of the first of the two reasons. Actually a BCF instruction (and a lot of other instructions, like INCF) on a port can ruin any previous setting of that port because of the second reason! It should be noted that the first reason occurs far more often, and can be avoided by placing a NOP or another instruction between any two read-modify-write instructions on the same IO port. But to really avoid all problems it is advised to allocate a separate register, do manipulations on that register, and copy it to the port register after each change. This technique is called a “using a shadow register”.
=====================================================
Next: RC delay. When you change an I/O pin connected to a non-trivial amount of wire always wait a reasonable amount of time before expecting the result. In the absense of another source of 'reasonable': find out what seems to work, then double the time.
=====================================================
Last: debouncing. AFAIK 50 ms is a reasonable upper limit for the bounce time of a switch. When your sample interval is longer than this time (you use 100 ms), you have no bounce problem. The proof is left as an excercise to the reader :)
Best Answer
In an abstract manner the code does this:
KEYPAD_PORT|= 0X0F;
should preset the output register of the port with '0's for PA6 to PA4. The other pins (bit 7 and bits 3 to 0) are not relevant. But here is an error because of the operator|=
: If any of the bits 7 to 4 is already 1, it will keep this value. The correct statement isKEYPAD_PORT &= 0x8F;
.The
KEYPAD_DDR
register selects the direction of the pins of your keypad port. Each bit corresponds to a pin. Setting a bit to 1 make the pin an output, 0 an input.The
KEYPAD_PIN
register is used to read the pins of your keypad port.Now to the shifting operations:
KEYPAD_DDR|=(0X40>>c);
: The hex value0x40
is shifted to the right by the value ofc
. This results in values of0x40
(0b01000000
),0x20
(0b00100000
), and0x10
(0b00010000
). This value is then ORed toKEYPAD_DDR
which was ANDed before with the complement of0x7F
=0x80
(0b10000000
). The results are0xC0
(0b11000000
),0xA0
(0b10100000
), and0x90
(0b10010000
), resp.!(KEYPAD_PIN & (0X08>>r))
: The hex value0x08
is shifted to the right by the value ofr
. This results in values of0x08
(0b00001000
),0x04
(0b00000100
),0x02
(0b00000010
), and0x01
(0b00000001
). The value read fromKEYPAD_PIN
is ANDed with this value, giving zero if the "masked" pin is '0' and non-zero otherwise. By the unary operator!
a zero is converted totrue
and a non-zero tofalse
. So the statement of theif
will be executed if the masked pin is '0'.Note: I like a lowercase 'X'/'B' better than the uppercase for hex and binary constants. But this is a bit of personal taste.
Only you can tell about the pin PA7. That's why I ignored it.