I am getting started learning more about AVR ATMEGA programming.
Reading an article about the inner workings of Arduinos, I am learning how the shiftOut
method is structured. I am kind of familiar with bitwise operations so far, but I have an expression I do not understand yet:
void shiftOut(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder, uint8_t val)
{
uint8_t i;
for (i = 0; i < 8; i++) {
if (bitOrder == LSBFIRST) {
PORTD |= !!(val & (HIGH << i));
} else {
PORTD |= !!(val & (HIGH << (7 - i)));
}
PORTB |= (HIGH << clockPin);
PORTB ^= (HIGH << clockPin);
}
}
The line PORTD |= !!(val & (HIGH << i));
is not 100% clear to me. I understand that I set the i-th bit High on PORTD but what does the !!
mean and val&(HIGH <<i))
I know this might sound basic, but can you please help me out?
Just try it:
#include <stdio.h>
#define HIGH 0
int main ( void )
{
unsigned char i;
unsigned char PORTD;
unsigned char val;
PORTD=0;
for(val=3;val;val<<=1)
{
printf("0x%02X\n",val);
for(i=0;i<8;i++)
{
PORTD |= !!(val & (HIGH << i));
printf("PORTD 0x%02X\n",PORTD);
}
}
return(0);
}
I dont know what your definition of HIGH is but since they are walking it 8 time across a byte i assume it is one bit, it is perhaps allowing for positive or negative logic on the port, so could be a 0 or 1 I am thinking.
I get all 0x01 if HIGH is 1 and all 0x00 if HIGH is 0. so it doesnt seem to be doing anyting. When you have && for example that is a boolean expression not a bitwise logical, so perhaps this is a typo?
the val&(HIGH<<i)
though is pretty simple, again you should have posted what HIGH was defined as, lets assume it is a 1 since that makes the most sense relative to the rest of the code. There is a loop i that goes from 0 to 7. so that means that you are anding (bitwise) with 1<<0
and then 1<<1
and then 1<<2
and so on. 1<<0
is just 1 right? so val&(1<<0)
is the same as val&0x01. 1<<1 = 0x02
so val&0x02. this code is one bit at a time isolating the individual bits in val. you understand that if for example val is 0x07 then 0x07&0x02 = 0x02? you line each bit up 0x07 = 0b00000111, 0x02 = 0b00000010
00000111
&00000010
=========
00000010
you look at this vertically isolate one column at a time and use the AND truth table which basically says the result is 0 unless both operands are a 1, there is one column where both operands are a 1 so the result for that column is a 1, the rest of the columns one or the other operand or both is a zero so the result is zero for that column.
00000111
&00000100
=========
00000100
00000111
&000010000
=========
00000000
Increment i two more times and evaluate against a val of 0x07 and you see what is happening at least with the val&(HIGH<<i)
assuming that HIGH is a 1, if HIGH is a 0 then you will always get zeros come out of this code.
If you are wanting to and with a walking one why dont you just do val&HIGH, so again this doesnt make sense unless that peripheral or what is on the other end of that port desires this one bit at a time thing.
The double bang (!!) looks like a logical operation to me and has no business here IMO.
A single bang is a logical operation
#include <stdio.h>
#define HIGH 1
int main ( void )
{
unsigned char i;
unsigned char PORTD;
unsigned char val;
PORTD=0;
val=3;
{
printf("0x%02X\n",val);
for(i=0;i<8;i++)
{
PORTD |= !(val & (HIGH << i));
printf("PORTD 0x%02X\n",PORTD);
}
}
return(0);
}
The hope here would be that we know the compiler will generate 0x00 for a false, but what does it generate for a true? 0x01, 0xFF, the C language probably has a definition. So the above code is genrating a bitstream based on our value
PORTD 0x00
PORTD 0x00
PORTD 0x01
PORTD 0x01
PORTD 0x01
PORTD 0x01
PORTD 0x01
PORTD 0x01
In the lsbit position, the the loop tickles a clock.
My personal preference would not be to play games with the C language spec, but be explicit to what you want to do:
#include <stdio.h>
#define HIGH 1
int main ( void )
{
unsigned char i;
unsigned char PORTD;
unsigned char val;
PORTD=0;
val=3;
{
printf("0x%02X\n",val);
for(i=0;i<8;i++)
{
PORTD |= (~(val>>i))&1;
printf("PORTD 0x%02X\n",PORTD);
}
}
return(0);
}
PORTD 0x00
PORTD 0x00
PORTD 0x01
PORTD 0x01
PORTD 0x01
PORTD 0x01
PORTD 0x01
PORTD 0x01
using all bitwise operations, no boolean true/false operations.
Wondering if they were trying to do two boolean nots in a row to shift the not-zero bit to the zero bit position or something...instead for lsbit first or 7-i for msbit first.
#include <stdio.h>
#define HIGH 1
int main ( void )
{
unsigned char i;
unsigned char PORTD;
unsigned char val;
PORTD=0;
val=3;
{
printf("0x%02X\n",val);
for(i=0;i<8;i++)
{
PORTD |= (val>>i)&1;
printf("PORTD 0x%02X\n",PORTD);
}
}
return(0);
}