Now lets imagine we need only some of the ports as inputs, the rest as outputs.
Lets assume the port D pins 0-4 to be defined as outputs, the remaining pins 5-7 will function as inputs. For this purpose it is necessary to enter in the port D data direction registers DDRD the following bit configuration :
// as defined in included avr/io.h DDRD # include <avr/io.h> int main ( ) { // Set the bits 0,1,2,3 and 4 // binary hexadecimal 1F = 00011111 preceded by 0x as stated before DDRD = 0x1F; // Clear alternative in binary system would be // DDRD = 0b00011111; // Full notation: identical functionality, more typing // But clear and self-explanatory: // DDRD = (1 << DDD0) | (1 << DDD1) | (1 << DDD2) | (1 << DDD3) | (1 << DDD4); */
So , Maybe now is time to mention something else. We dont necessarily need to refer to the
pins always through the whole port. As you can probably notice in the last alternative
example above, you can also use DDD1 or DDB1 if it were in the Port B; PDx, and PINDx, DDDx for the port D or maybe simpler would be also PAx, PBx, PCx, etc... For the compiler
, the expressions (1 << PD5), (1 << DDD5) and (1 << PIND5) are identical to (1 << 5)
(more precisely, the preprocessor replaces the expressions (1 << PC7). to .(1 << 7)).
All this is specified in the file io.h of the avr-libc and serves only to improve code readability. *(Check last example in this post for a practical example of code)
You can always check also the file iom328p.h under /* Registers and associated bit numbers */ for a better clarification ( or the file corresponding to the chip you might be using).
So , now, lets look at other examples based on these options :
...// All pins of Port D defined as outputs: DDRD = 0xff; // Pin0 again left as input and the rest in their original state: DDRD &= ~ ( 1 << DDB0 ) , // pin 3 and 4 as inputs and the rest in their original state: DDRD &= ~ ( ( 1 << DDB3 ) | ( 1 << DDB4 ) ) , // pin 0 and 3 as outputs and rest in their original state: DDRD |= ( 1 << DDB0 ) | ( 1 << DDB3 ) ; // All input pins: DDRD = 0x00;...So based on what we have learned until now, lets set the pin 5 of port D to output// as defined in included avr/io.h DDRD : # include <avr/io.h> ... PORTD = 0x10; /* better would have been PORTD = (1 << PD4) */ // Clear alternative would be binary notation PORTD = 0b00010000; /* direct assignment - clear */PS: Note that the bits are always to be counted from 0 , so the least significant bit isbit number 0, not number 1 bit .
So now lets imagine we need to change an output pin, but want to leave the rest unchanged ( see next tutorial "Introduction to C language bitwise operators and relevant truth tables". )/* as defined in included avr/io.h DDRD : */ # Include <avr/io.h>
... PORTD = PORTD | 0x10; /* better would be : PORTD = PORTD | (1 << PD5) */ /* simplified by using the | = operator: */ PORTD | = ( 1 << PD5 ) ; /* Also several pins "simultaneous": */ PORTD | = ( 1 << PD4 ) | ( 1 << PD5 ) ; /* pins PD4 and PD5 "high" */Turn those same pins to LOW is somewhat analogous/* as defined in included avr/io.h DDRD : */
# include <avr/io.h> ... PORTB & = ~ ( 1 << PB2 ) ; /* delete Bit 2 and thus sets pin PB2 in PORTB to low */PORTB & = ~ ( ( 1 << PB4 ) | ( 1 << PB5 ) ) , /* Pins PB4 and PB5 pin "low" */
Now, we should also note the fact that If the initial state of outputs is critical, the sequence must be noted at which the data direction (DDRx) is set and the output value (PORTx) set:For output pins, which are to be initialized "HIGH" with initial value:
- First, the bits in the register set PORTx
- then set the data direction to output
Hence the sequence obtained for a pin that was previously configured as input with pull-up switched off:
- set PORTx: active internal pull-up
- DDRx set: output ("high")
In the first sequence DDRx and then PORTx there may be a short "low pulse", and cause the external pull-up resistors to be bypassed. The (unfavorable) sequence: input -> set DDRx : output ( on "low" because of PORTx after reset 0) -> set PORTx: output to high. As this might come to influence your design ( or not ) I would advise you to check the datasheet section Configuring the Pin on page 78 ( english version)
http://www.atmel.com/Images/doc8161.pdf .
or another way would be#include <avr/io.h> #include <util/delay.h> int main(void) // For now we are just going to turn a led on */ { DDRB = 0b00100000; // Data Direction Register setting pin0 to output and the remaining pins as inputwhile(1) { PORTB = 0b00100000 ; // Set pin PB5 = pin13 no Arduino Uno to 5 volts _delay_ms (1000); PORTB = 0b00000000 ; _delay_ms (1000); } // * Never reached * / return 0 ; }
#include <avr/io.h> #include <util/delay.h>int main(void)// For now we are just going to turn a led on */ { DDRB = 0b00100000; // Data Direction Register setting pin0 to output and the remaining pins as inputwhile(1) {
PORTB = 0b00100000; _delay_ms (1000); PORTB = 0b00000000; _delay_ms (1000); // Code would be in here if needed to execute over and over and over ... endlessly */ } // * Never reached * / return 0 ; }That is all for now, i hope this will have helped you to understand better the intricacies of the AVR C with AVR GCC !PS- this is in no way a detailed ultimate tutorial, but more the result of my understanding of it !!
So dont take my word for definite as there will be things to add, and more things
that i havent even touched . Remember i come from the Arduino perspective, and
for now just wanna deepen my knowledge, so i can pass to AVR GCC when ready ! Reference http://elecrom.wordpress.com/2008/02/12/avr-tutorial-2-avr-input-output/ http://www.nongnu.org/avr-libc/user-manual/modules.html http://www.mikrocontroller.net/articles/AVR-Tutorial ( Assembler, in German
but a lot of info to be dranked from here !
No comments:
Post a Comment
Feel free to contact me with any suggestions, doubts or requests.
Bless