Saturday, January 27, 2018

Arduino & seven-Segment: Counter-Down / Counter-Up


To be easy to understand, a counter-up or counter-down is a term to mean 'to count by incrementing' or 'to count by decrementing'. However in the electronics term a counter is digital circuitry constructed by a number of flip-flops connected in cascades, it simply counts pulse signal that is commonly generated by an oscillator.
In this article today, we are going to try to implement the concept of this counter-up/down using arduino and sevent-segment. I am also going to explain a bit about bitmask and left-shift bitwise operations '<<'.

Firstly, just make the circui like presented below using proteus simulation:
Figure 1 Counter-up/down Simulation

Secondly, type the script below on the arduino IDE: 

const char segment_ch[] = {0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f};

const uint8_t seg_sum = 8;
uint8_t counter = 0;
char add_or_dec = 1;

void setup() {
  // put your setup code here, to run once:

  for (uint8_t i = 0; i < seg_sum; i++)

    pinMode(i, OUTPUT);

}

void loop() {
  // put your main code here, to run repeatedly:
 
  displaySeg(counter);

  counter += add_or_dec;

  if(counter >= 9) add_or_dec = -1;

  else if(counter <= 0) add_or_dec = 1;

  delay(250);

}

void displaySeg(const uint8_t seg) {

  for (uint8_t i = 0; i < seg_sum; i++)

    digitalWrite(i, (segment_ch[seg] << i) & 0x80);

}

Compile the code (Ctrl+R) and then paste the hex file that is on the arduino IDE into our simulation.


The following is an explanation of some pieces of source code above:
  • const char segment_ch[] = {0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f};
    -
    This line of code is a list of array characters (0-9) that we use for the seven-segment.

  • for (uint8_t i = 0; i < seg_sum; i++)
    pinMode(i, OUTPUT);
    -
    As we have known that on the arduino we need to configure the pin(s) first in the setup() scope. Here we need eight loop iterations for each of the pin that we are going to activate (use); from pin-0 to pin-7 are configured as OUTPUT (the pins are activated).
     
  • displaySeg(counter);
    -
    This function is to show integer figures of the seven-segment, as an example if counter==2, the character figure going to be displayed is 2. The explanation of this function is explained later.
     
  • counter += add_or_dec; //increment or decrement
    if(counter >= 9) add_or_dec = -1; //having reached 9 do decrementing
    else if(counter <= 0) add_or_dec = 1; //having reach 0 do incrementing
    delay(250); //wait for 0.25 second
    -
    In this line of code is where we apply the concept of cointer-up/down that is discussed earlier. For the state of add_or_dec variable will be altered (plus or minus) automatically when the value has reached 9 or 0: if add_or_dec is 1 (positive) the counter is increasing (counter-up) otherwise the value -1 (negative) the counter is decreasing (counter-down). Therefore on this line of code the integer figures showed on the seven-segment is decreasing (by one) or increasing (by one) continously with the delay of 0.25 second in each operation.
     
  • void displaySeg(const uint8_t seg) {
    for (uint8_t i = 0; i < seg_sum; i++)
    digitalWrite(i, (segment_ch[seg] << i) & 0x80);
    }
    -
    The displaySeg() function above as stated before will show any integer figure (0-9) accroding to any integer value passed to it.
    For the beginner the content of this function may look strange at first, but never mind! that is a process of learning beacause we cannot do any magic about code, even magicians need to learn, don't they? :D so let's go back to the topic, the content of the function contains:

    digitalWrite(i, (segment_ch[seg] << i) & 0x80);

    As we know that digitalWrite() function is used to trigger a pin of a particular register (for this case the pin-0 to the pin-7 is on D register), or for more simple term it is to control the pins state on the arduino board to be activated (HIGH) or deactivated (LOW). for example:

    digitalWrite(2,HIGH);

    note: HIGH and LOW can be represented by 1 and 0, therefore the above line of code is equivalent to:
    digitalWrite(2,1);
    The above code makes the pin-2 on the arduino boaard (PD2) in the HIGH state: This is how our program above works in which the each of the eight bits of the array element, segment_ch[] is shiftet and assigned one by one on the digitalWrite().
    To understand more easily of the concept, below is the sequence process of the operation:

    when i = 0, and seg = 2 (e.g., we want to display '2' integer figure to the seven-segment):

    pin-0(i) = (segment_ch[2] << 0) & 0x80 equivalent to
    pin-0(i) = (0x5b << 0) & 0x80 quivalent to
    pin-0(i) = (0b01011011 << 0) & 0b10000000 (in binary form), shift-left as 0
    pin-0(i) = 0b01011011 & 0b10000000
    0b01011011
    AND   0b10000000
    pin-0(i)= 0b00000000 (just take the 7th bit (MSB), since it's the mask-bit)
    pin-0(i)= 0 (LOW)

    On the sequence above, the shifting bit to the left is 0-fold (or there is no any shifting here) and masking the eight bits with 0x80 (& 0x80). This process results in state of LOW (0), therefore the arduino pin, the pin-0 has LOW (0) state.
    when i = 1:

    pin-1(i) = (segment_ch[2] << 1) & 0x80 equivalent to
    pin-1(i) = (0x5b << 1) & 0x80 equivalent to
    pin-1(i) = (0b01011011 << 1) & 0b10000000 (in binary form), shift-left as 1
    pin-1(i) = 0b10110110 & 0b10000000
    0b10110110
    AND   0b10000000
    pin-1(i)= 0b10000000 (just take the 7th bit (MSB), since it's the mask-bit)
    pin-1(i)= 1 (HIGH)

    On the sequence above, the shifting bit to the left is 1-fold and masking the eight bits with 0x80 (& 0x80). This process results in state of HIGH (1), therefore the arduino pin, the pin-1 has HIGH (1) state.
    when i = 2:

    pin-2(i) = (segment_ch[2] << 2) & 0x80 equivalent to
    pin-2(i) = (0x5b << 2) & 0x80 equivalent to
    pin-2(i) = (0b01011011 << 2) & 0b10000000 (in binary form), shift-left as 2
    pin-2(i) = 0b01101100 & 0b10000000
    0b01101100
    AND   0b10000000
    pin-2(i)= 0b00000000 (just take the 7th bit (MSB), since it's the mask-bit)
    pin-2(i)= 0 (LOW)

    On the sequence above, the shifting bit to the left is 2-fold and masking the eight bits with 0x80 (& 0x80). This process results in state of LOW (0), therefore the arduino pin, the pin-2 has LOW (0) state, and so forth till i is equal to 7 (as stated by for-loop above). The final result of this bit sequence to be:

    pin-0 = 0
    pin-1 = 1
    pin-2 = 0
    pin-3 = 1
    pin-4 = 1
    pin-5 = 0
    pin-6 = 1
    pin-7 = 1

    And then according to the circuitry on the figure 1 the character shown is '2'.

    Actually there is an easier way to show just any integer figure (0-9) like above, it is:

    switch(konter){
    ..
    case 2 :

    digitalWrite(0,LOW);
    digitalWrite(1,HIGH);
    digitalWrite(2,LOW);
    digitalWrite(3,HIGH);
    digitalWrite(4,HIGH);
    digitalWrite(5,LOW);
    digitalWrite(6,HIGH);
    digitalWrite(7,HIGH);

    break;

    case 3: ...
    case 4: ...
    ..
    }

    Unfortunately by this way, our arduino IDE space, where we type our code will be burnt up and also tmay eat our arduino memory much if compared with the earlier code above.


So that is how to make our counter-up/down using arduino & seven-segment.

May you learn something new ..

Monday, January 22, 2018

Compiling avr program in Linux


there are many ways to do to compile C/C++ program Avr, for example IDEs (Integrated Development Environment) like Eclipse, Avr-studio, Codevision Avr, or any other else out there. but for you having been familiar with linux, or you have just known about this avr. it will be better to know how to compile any C/C++ code directly using commands on a terminal. Here we use an open-source compiler i.e., GNU avr-gcc compiler.

so, just let's begin how to do it..!
now when we want to compile any code, there must be a code to compile, right :) so I take a simple case here as to make a led lighting using atmega8535 MCU (Microcontroller Unit). then make a circuit like below, here I'm using Proteus simulator.

Figure 1 atmega8535 led Circuitry

then type a program below, here I use Geany text editor, I love to use it. you can use your favourite text editor here:

#include <avr/io.h>

int main(){

   DDRA |= 0x01;        //activate A register, the first bit 
   PORTA |= 0x01;      //tassign the HIGH logic to the first bit of A Register

   while(1);            //avoid program counter to keep increasing
   return 0;
}

Now, save the program above with main.c name, open the terminal (ctrl+alt+t), align to directory/folder where we save this main.c and type these 3 scripts below, one-by-one:

avr-gcc -Wall -Os -mmcu=atmega8535 -DF_CPU=2000000UL -c main.c (enter)

avr-gcc -mmcu=atmega8535 -o main.out main.o (enter)

avr-objcopy -O ihex -R .flash main.out main.hex (enter)

after we execute the three scripts above, the main.hex file will be generated (this file is usually called by hex file), and then drop the hex file onto the MCU on our Proteus simulation by double clicking the MCU figure on the Proteus simulation, FINISH... to test just run the simulation.

Figure 2 the hex file and clock setting

For more detail, here is a bit explanation about the three scripts above.

The compilation process happens on the first line of the 3 scripts:
  • -Wall indicates compiler to show warning message when compiling, e.g., if there is unused variable declared.
  • -Os indicates compiler to optimized code meaning that, for efficient space usage (at the possible expense of code execution speed).
  • -mmcu=atmega8535 tells the compiler to use the type of MCU, in this case atmega8535.
  • -DF_CPU=2000000UL tells compiler that the clock speed of MCU is 2MHz, and UL here means the integer type used, Unsigned Long.
  • -c indicates compiler to compile and stop - do not do any linking.
This, the first line of the 3 scripts above will create us an object file, that is main.o
  • The second line of the 3 scripts above is linking process. Note that linking process needs to specify the MCU type, mmcu . Without doing it, compiler will use 8515 processor as default.
  • The last line of the 3 scripts above is the process to generate hex file of the program we have made above, by avr-objcopy.

Hello-world - blinking led with atmega8535 avr


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
    N
    ext 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
  1. OR Bitwise Operator '|'
    As the example above:
    PORTA |= 0x01; equivalent to
    PORTA = PORTA | 0x01; equivalent to
    PORTA = 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 00000001
    result 00000001 (the 0th bit active, HIGH logic)

  2. AND Bitwise Operator '&'
    As the example above:
    PORTA &= ~0x01; equivalent to
    PORTA = PORTA & ~0x01; equivalent to
    PORTA = PORTA & ~0b00000001; equivalent to
    PORTA = 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)

  3. 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 second

    PORTA = 0x00; //assign LOW logic to the whole bit of the Register A
    _delay_ms(500); //wait for 250 milli second

    But 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..

Arduino & seven-Segment: Counter-Down / Counter-Up

To be easy to understand, a counter-up or counter-down is a term to mean 'to count by incrementing' or 'to count by decreme...