14

I am working on school project and need to learn the basics of C with a AVR atmega controller.

I don't understand how everything is set up. For example PORTB, PORTD, DDRB; DDRD, PINB, PIND and stuff like that. And I don't know how everything works with if statements, while loops, etc.

Can someone give me a short explanation please?

I have a few code lines...

DDRB = 0b00000011; // I know that here DDRB is set to input/output

And an if statement:

if (PINB & (1 << PINB0)){
    A = true;
}

Can someone explain me how this 'if statement' works? Why PINB & (1<< PINB0))?

Thanks

Brett Holman
  • 743
  • 6
  • 18
Mike_NotGuilty
  • 2,253
  • 5
  • 32
  • 64

4 Answers4

14

Microprocessors use a memory map to interface their hardware functionality with the software.

Basically, there are static addresses in memory that the hardware will use to determine its functionality. These are specific to the manufacturer, the part, and sometimes even how the part is configured.

The datasheet for the part will tell you the exact memory location to control different functionality. However, this is normally very tedious. As a result, the datasheet will also (almost) always give a name to that particular location in memory that describes its functionality. Once again, these names are manufacturer and part specific.

To make this interface easier for programmers, people (the manufacturer or the community) will often create macros to these memory locations. For example, you could find

// Clock Prescalar Register for ATMega328p
#define CLKPR *0x61

in a header file associated with the part (such as AVR libc).

Now, by writing OSCCAL = 0b10000000 (or whatever else I want to write that is allowable by the specifications in the datasheet) I can directly access and change the clock prescalar module for this part.

But, often we are interested in the value of a single bit, not the entire byte. As a result, we use bitwise operators (such as &, |, ~, << >>) to "mask" off bits that we are interested in manipulating.

This offers the simultaneous advantage of allowing us to only read the value from the bit of interest, while not inadvertently changing any bits that we do not mean to toggle.

Many bit positions are also given by macros as well. For instance, bit 7 in OSCCAL is named CLKPCE (again, from the datasheet).

CLKPCE is most likely defined (at least in AVR libc - the standards vary) by:

#define CLKPCE 7

because it defines the bit shift needed to get to the desired bit inside OSCCAL.

In order to talk about the bit here, I can do several things.

Set the bit

To set the bit, we want to make it a 1, without affecting any of the other bits. To do this, we use an OR mask, as follows:

OSCCAL = (OSCCAL | (1 << CLKPCE));

I'll leave it to you to review bit operators and see how this works.

Clear the bit

Here, we want to make it a 0, without affecting the other bits. It will look something like this:

OSCCAL = (OSCCAL & ~(1 << CLKPCE));

Query the bit

When querying, we want an expression that returns nonzero if the bit was set (1), and zero if the bit was cleared (0). It'll look like this:

(OSCCAL & (1 << CLKPCE));

The Takeaway

By using these different bitwise operations with predefined macros, we can directly control and query the state of the hardware by using this static memory map.

However, to make sense of all of these macros, you will need to consult (and read, and re-read, and re-re-read) the datasheet for your part. Luckily, searchable PDFs are available for free from Atmel on your part's page!

Glenn
  • 1,059
  • 6
  • 14
12

Do you means what is if-condition PINB & (1<< PINB0))?

It checks whether PINB0 + 1 number bit( from rhs) is ON (1) in PINB or OFF (0).

For example. (a & (1 << 2)) checks whether 3rd bit is ON in a or OFF. In the expression two operators are used << bitwise left shift and & bitwise and below I have explained for one byte example:

  1. 1 is 0000 0001
  2. 1 << 2 after left shift gives 0000 0100
  3. a bitwise and with 0000 0100 gives either all zeros 0000 0000 or 0000 0100

    3a. If all zeros then if condition is false (when third bit in a is zero).
    3b. If result of bitwise and is 0000 0100 then if condition evaluates as true (when third bit in a is one).

Grijesh Chauhan
  • 57,103
  • 20
  • 141
  • 208
5

For the meaning of the registers, it is adviseable to consult

  • the data sheet of the device you are using and
  • the header file which is shipped with the C compiler you are using.

In short, the last letter (B, D) means the port you are accessing: the GPIO pins are grouped together 8-wise so that each port has 8 pins.

DDRx is a means to set the direction of each port pin.

PORTx and PINx are used for input and output, but as I am used to use PORTA.IN, PORTB.DDR, PORTD.OUT etc., I cannot tell by the heart which of them does what.

For the basics of the language, there are books and tutorials which enable you to learn this language.

glglgl
  • 89,107
  • 13
  • 149
  • 217
4
if (PINB & (1 << PINB0)){
        A = true;
    }

This code checks whether PIN 0 in PORTB is HIGH or LOW. If it is high then assigns A = true; Here, PINB --> Reads data from PORTB, (1<<PINB0) --> Make 0th bit as 1 and AND's both values to know whether PIN 0 in PORTB is high or not.

Chinna
  • 3,930
  • 4
  • 25
  • 55
  • Ok! And what abound DDRB, PINB, PORTB ? With DDRB you can assign the port as input/output? With PINB you can read the current state? Is this correct? and what can you do with PORTB ? – Mike_NotGuilty Jan 08 '14 at 11:17
  • @Mike_NotGuilty With DDRB you can make portB as input/output. With PINB you can read PortB data. With PORTB you can assign value to the portB. – Chinna Jan 08 '14 at 11:24