This code was posted 2 or 3 years ago from AVR freaks and the user is probably moved on. Therefore, I have been trying to understand what is going on line by line. I know that the user is pulsing the TX pin high and low for specific delay times but, I am having trouble understanding the lines that look like bit shifting and compounding. Basically, anywhere there are things that look like "bitwise" code, I get confused.
If someone would have time to do something like...///"this line he is compound x and y to get x(y)"
Or other wise say TX_PORT &= ~_BV(TX_BIT); ///"this line means this".
I would be grateful for any help or direction to help me understand what this code is doing.
Thanks,
Mark
Sure, here is the init function. I just send address 0x33 and
my Accord's ECU responded correctly. I didn't bother using the UART
for such a slow baud, just software bit toggling.
Code:
void send_address(uint8_t addr) {
uint8_t i;
uint8_t temp;
/* idle high */
TX_PORT |= _BV(TX_BIT);
TX_DDR |= _BV(TX_BIT);
/* required idle delay w0 (2ms), be safe with 4ms */
_delay_ms(4);
/* start bit */
TX_PORT &= ~_BV(TX_BIT);
/* this is at 5bps */
_delay_ms(200);
/* send byte */
for (i = 0; i < 8; i++) {
temp = (addr >> i) & 0x01;
if (((TX_PORT & _BV(TX_BIT)) >> TX_BIT) == temp) {
/* already at the correct level, do nothing */
} else {
if (0 != temp) {
TX_PORT |= _BV(TX_BIT);
} else {
TX_PORT &= ~_BV(TX_BIT);
}
}
_delay_ms(200);
}
TX_PORT |= _BV(TX_BIT);
_delay_ms(200);
/* already idling high on TX */
return;
}
11.6 What is all this _BV() stuff about?
When performing low-level output work, which is a very central point in microcontroller
programming, it is quite common that a particular bit needs to be set or cleared
in some IO register. While the device documentation provides mnemonic names for
the various bits in the IO registers, and the AVR device-specific IO definitions reflect
these names in definitions for numerical constants, a way is needed to convert a bit
number (usually within a byte register) into a byte value that can be assigned directly
to the register. However, sometimes the direct bit numbers are needed as well (e. g. in
an SBI() instruction), so the definitions cannot usefully be made as byte values in the
first place.
So in order to access a particular bit number as a byte value, use the _BV() macro.
Of course, the implementation of this macro is just the usual bit shift (which is done
by the compiler anyway, thus doesn’t impose any run-time penalty), so the following
applies:
_BV(3) => 1 << 3 => 0x08
However, using the macro often makes the program better readable.
"BV" stands for "bit value", in case someone might ask you.
Example: clock timer 2 with full IO clock (CS2x = 0b001), toggle OC2 output on
compare match (COM2x = 0b01), and clear timer on compare match (CTC2 = 1). Make
OC2 (PD7) an output.
TCCR2 = _BV(COM20)|_BV(CTC2)|_BV(CS20);
DDRD = _BV(PD7);
So, the following statement clears the TX_BIT in the TX_PORT register. Refer to the datasheet to understand the register and bit mnemonics. Not sure which datasheet to refer you to, as TX_PORT and TX_BIT aren't in the few AVR datasheets that I'm familiar with.
Thank you Jack Christensen! You have solved the BV mystery that I was hung up in! I followed your link for the manual and found the quote for BV on page 77. I am that much wiser now.
@Awol, I think you are correct about the TX_PORT being a general define for a port register. I wasn't sure about it but, when it would not compile I suspected it as a variable that needed defined somewhere.
Thanks you guys have gotten me closer to understanding how to put this to use.
Thank you WizenedEE for taking the time to comment every part of that code! I looked up shiftOut() and I can see the similarity.
I am going to take parts of the code and do some tests with an led and see if I can see it in action.
@Awol thanks for pointing out the "without the clock" part, I found what you mean in the shift() tutorial.
I will try to make good use of this new knowledge, thanks again!