As we know how to compile C/C++ avr code on the previous article. Now
we let's apply the method again to to make our hello-world program,
it is a simple blinking led program. here the explanation will be
more concerned about the programming itself. beginning from header
file, main() function and _delay_ms() function, register accessing,
and a little bit about bitwise operations.
As always, a proteus application will be used as our simulation here
and also avr from GNU compiler to compile our code to generate hex
file.
That's it!, just make a circuit like below in the proteus program
Figure 1 Blinking Led Circuitry |
Then type the program below on your favorite text editor:
#include <avr/io.h> //DDRA, PORTA
#include <util/delay.h> //_delay_ms()
int main(){ //“main” function must exist
DDRA |= 0x01; //activate A register on the first bit
while(1){ //endless loop
PORTA |= 0x01; //assign HIGH logic to the first bit of A Register,
led turns on
_delay_ms(250); //wait for 250 milli second
PORTA &= ~0x01; //assign LOW logic, led turns off
_delay_ms(500); //wait for 250 milli second
}
return 0; //integer return type for our main function
}
Then compile the above program (by previous artile method), and
insert the hex file having been generated into the MCU of proteus
simulation. Don't forget to set the CPU speed according to the speed
when we compile the program (-DF_CPU=2000000UL),
in this case, the clock speed is 2MHz.
Figure 2 inserting hex file and setting CPU speed |
Run the program to see the result, it must be blinking.
Now let's come back to the program we have made:
-
Header File
When we are working with C/C++, firstly, we have to include somthing, here we have seen the include syntax: #include <…>, this is usually called by header file. the name inside the brackets is just a file name containing configuration of the specific MCU, in this case atmega8535. e.g.,:
#include <avr/io.h> //contains DDRA, PORTA, that can be accessed directly. but now how about the form of the value accessed??
The form of the value here is an integer, with 8-bit resolution, meaning that it can hold value of 0 to 255.
-
Main()
function and _delay_ms() function
Next we have seen int main(). this is also called by function name, that must be declared in C/C++ program. This is where the program firstly starts the execution. For the _delay_ms() function, it's in util/delay.h. as the name of the function, this function will stop the program flow for the specified time in unit of millisecond. e.g., we pass parameter of 250 (as the above example above), then the program flow (at where the function is called) will be paused for 0.25 second. For the people being familiar with C/C++ programming language, it will be so easy.
Figure 3 A Register, DDRA (source: Atmega85353 datasheet) |
-
Register Accessing
Data Direction Register A (DDRA) is used to activate or deactivate a particular bit in it, in other words we use it to set pins labeled PORTA on the MCU (proteus simulation) as seen on figure 1. DDRA |= 0x01, or equivalent to DDRA = DDRA | 0x01 in hexa form, or DDRA = DDRA | 0b00000001 in binary form. It means to activate pin 0 in the register. see figure 3. As in figure 3 shows the sequence of the bit number of 0 to 7 with the default value in each is 0. So when we assign the 0th bit of DDRA by 1, so the bit will be active, meaning that we can set the logic in this bit to LOW state (0 volt) or HIGH state (5 volt) via PORTA register.See the figure 4.Figure 4 A register, PORTA (source: Atmega8535 datasheet)
A little bit about bitwise operator
-
OR Bitwise Operator '|'As the example above:PORTA |= 0x01; equivalent toPORTA = PORTA | 0x01; equivalent toPORTA = PORTA | 0b00000001; binary format (8-bit)something that needs to be remembered is that a major number of atmega8535 register such as PORTA and DDRA have 8-bit resolution, if we try to assign it with the value that's bigger than 8-bit e.g., 0b100010001 (9-bit) then there is a big chance of your program to have undefined behavior. From the above example, we can clarify:PORTA 00000000 (default register value)OR 00000001result 00000001 (the 0th bit active, HIGH logic)
- AND Bitwise Operator '&'As the example above:PORTA &= ~0x01; equivalent toPORTA = PORTA & ~0x01; equivalent toPORTA = PORTA & ~0b00000001; equivalent toPORTA = PORTA & 0b11111110; binary binary(8-bit)From the above example, we can clarify:PORTA 00000001 (the value from the previous operation above)AND 11111110 (the result of '~' operator)hasil 00000000 (the 0th bit or the whole bit in the resiter not active; LOW logic)
- Complement Bitwise Operator '~'This operator is usually used to invert the logic stored in a register. for example: 0b00010010, if we place '~' operator in front if it ~0b00010010, so this sequence of value becomes 0b11101101.
-
Note:There might be some readers that realize and ask; "why don't we access into the register directly?, without any bitwise operator..". In this case, in this example above, we may do that actually, as:PORTA = 0x01; //assign HIGH logic to the first bit and LOW to the rest of//the bit_delay_ms(250); //wait for 250 milli secondPORTA = 0x00; //assign LOW logic to the whole bit of the Register A_delay_ms(500); //wait for 250 milli secondBut in the other cases, such accessing 16x2 lcd's register will endanger the process or we would fail to access corectly the register at all, because accessing the register directly means to reset all bit in it and assign it with the new ones. For this reason, I recommend to use the above way even if we just access a single bit in a single register. Such this method is called by mask-bit so that only specific bits that will be updated.
So,, it's how we can make the
blinking led, but with an extra explanation on the register
:D. May you
like it and becomes useful..
No comments:
Post a Comment