1
\$\begingroup\$

I'm trying to write a code for an up/down binary counter. The task is to light up 8 leds (up counter)and then reverse the lighting order (down counter). I am trying to create an array of leds in my code but I don't know how to do so.Whenever I use PORTB[i] I get and error pointer required and if I use PORTB.i I also get an error how can i access the leds connected to portb ? It's my first experience with microcontrollers in general and I would really appreciate your help. Here's my code:

char LED_Direction at TRISB;
void main() {
int i;
LED_Direction=0x00;
PORTB=0x00;
//sbit LED_Array [8] = {PORTB.0,PORTB.1,PORTB.2,PORTB.3,PORTB.4,PORTB.5,PORTB.6,PORTB.7};
    while(1)
   {
      for (i=0; i<8; i++)
      {
          //LED_Array [i] = 1;
          PORTB[i]=1;
          Delay_ms(1000);
          //LED_Array [i] = 0;
          PORTB.i=1;
      }
      for (i=7; i>-1; i--)
      {
          //LED_Array [i] = 1;
          PORTB.i=1;
          Delay_ms(1000);
          PORTB.i=0;
          //LED_Array [i] = 0;
      }
    }
}
\$\endgroup\$
  • \$\begingroup\$ And the question is? \$\endgroup\$ – PlasmaHH Feb 23 '16 at 20:18
  • \$\begingroup\$ Whenever I use PORTB[i] I get and error pointer required and if I use PORTB.i I also get an error how can i access the leds connected to portb ? @PlasmaHH \$\endgroup\$ – Nemo Feb 23 '16 at 20:21
  • \$\begingroup\$ Which compiler are you using? \$\endgroup\$ – Dan Laks Feb 23 '16 at 21:37
  • \$\begingroup\$ I am using mikroC PRO for PIC v.6.4.0 @DanLaks \$\endgroup\$ – Nemo Feb 26 '16 at 19:05
1
\$\begingroup\$

PORTB is neither a structure nor is it an array (or pointer).
Each bit in the value you assign to it directly corresponds to a pin.
Your line of code near the beginning of main() which reads 'PORTB=0x00;' is the correct way to assign values to PORTB.
If you want the least-significant bit high with the rest low, then set it to 0x01 (0b00000001).
If you want the most-significant bit high with the rest low, 0x80 (0b10000000) is your value.
A 0x55 (0b01010101) or 0xAA (0b10101010) will set alternating bits high & low.
You can also use bit-shifting to set a particular bit, so (1 << 3) will set the 3rd bit, and so on.

\$\endgroup\$
3
\$\begingroup\$

PORTB acts like an integer (int). When you write to PORTB, you write to all 8 bits at once. You can only use square brackets (var[i]) with arrays, and fields (var.fieldname) with structures. You can't use a variable in a field name.

To set and clear bits, use the C bitwise operators:

PORTB |= (1 << 4);    //Set bit 4
PORTB &= ~(1 << 6);   //Clear bit 6
PORTB = 0x03;         //Set bits 0 and 1, clear all others

These let you use a variable:

PORTB |= (1 << i);    //Set bit i
PORTB &= ~(1 << i);   //Clear bit i

There are other ways of doing what you want, but this is the simplest.

Your code seems to turn on one LED at a time, going from B0 to B7 and back again. If you want to make an actual binary counter, you could do something like this:

unsigned long i;

while (1)
{
    for (i = 0; i < 256; i++)
    {
        PORTB = i;
        Delay_ms(1000);
    }
}

On more recent PICs, it's better style to use LATB instead of PORTB.

UPDATE: The basic rule for bitwise operators is that you OR with 1 to set bits, AND with 0 to clear bits, and XOR with 1 to toggle bits. The bitwise operations I used above work like this:

PORTB |= (1 << 4);  //Set bit 4

is equivalent to:

PORTB = PORTB | (1 << 4);  //Set bit 4

(1 << 4) is equal to 00010000. In decimal, that's \$1 \cdot 2^4\$, or 16. In hexadecimal, it's 0x10. The OR operator (|) works by ORing each bit in PORTB with the corresponding bit in (1 << 4). For example, if PORTB only has bit 1 set to start with, you get:

  00000010   PORTB
| 00010000   (1 << 4)
-----------
  00010010   PORTB with bit 4 set

Bit 4 is set without disturbing any other bit.

Clearing a bit is only slightly more complicated. Let's start with a binary example. If PORTB has bits 2 and 4 set, and you want to clear bit 2, you need to do this:

  00010100   PORTB
& 11111011   Mask value for clearing bit 2
-----------
  00010000   PORTB with bit 2 cleared

Bit 2 is cleared without disturbing any other bit. See how the second operand is almost all ones? We could write that out manually in hexadecimal as 0xFB:

PORTB = PORTB & 0xFB;  //Clear bit 2

But then whoever reads the code has to stop and think about which bit(s) 0xFB clears. We can make the code more clear by setting the one bit we care about to 2 and inverting the result:

~(1 << 2);

which gives:

~ 00000100   (1 << 2)
-----------
  11111011   ~(1 << 2) = Mask value for clearing bit 2

This is more clear than a hexadecimal value:

PORTB = PORTB & ~(1 << 2);  //Clear bit 2

which shortens to:

PORTB &= ~(1 << 2)  //Clear bit 2

When you're setting or clearing multiple bits, it's more common to use hexadecimal values instead of writing out a bunch of shifted values. The only way to deal with this is to learn how hexadecimal relates to binary, which isn't very hard.

One important caveat is that C compilers interpret numbers as signed ints. If an int is only 16 bits (which is often true in MCUs), then something like this can fail:

unsigned long a;

a = (1 << 20);  //Needs more than 16 bits -- too big for an int!
                //a could now be equal to zero

You can fix this by casting one of the numbers to a long. Make it unsigned to avoid warnings when the top bit is set:

unsigned long a;

a = (1UL << 20);  //Should now work correctly

It's almost always better to use uint8_t, uint16_t, and uint32_t from instead of the native C types, but using the UL suffix is still common.

\$\endgroup\$
  • \$\begingroup\$ That PIC appears to pre-date the PORTB/LATB separation. For more recent PICs which do have a LATx, that is certainly the 'proper' one to use. \$\endgroup\$ – brhans Feb 23 '16 at 20:42
  • \$\begingroup\$ @AdamHaun, your example code for a binary counter has a potential bug. If variable i is an unsigned char (as it should be), then comparing it to 256 can have unexpected behavior. \$\endgroup\$ – Dan Laks Feb 23 '16 at 21:44
  • \$\begingroup\$ @DanLaks Wow, can't believe I missed that. Thanks for the correction! \$\endgroup\$ – Adam Haun Feb 23 '16 at 21:50
  • \$\begingroup\$ @AdamHaun - You can just use an (unsigned) int. The C standard requires an int to be at least 16-bits. \$\endgroup\$ – Jon Feb 23 '16 at 23:25
  • \$\begingroup\$ @AdamHaun thanks a lot for the great explanation. I was wondering though about this expression PORTB |= (1 << 4); //Set bit 4 does it shift the number 1 four bits to the left then or the result with the previous value for PORTB? I am also confused about PORTB &= ~(1 << 6) \$\endgroup\$ – Nemo Feb 26 '16 at 19:02
0
\$\begingroup\$
char LED_Direction at TRISB;
void main()
{
    int i;
    LED_Direction=0x00;
    PORTB=0x00;
    //sbit LED_Array [8] = {PORTB.0,PORTB.1,PORTB.2,PORTB.3,PORTB.4,PORTB.5,PORTB.6,PORTB.7};
    while(1)
   {
      for (i=0; i<8; i++)
      {
          PORTB = 1 << i;
          Delay_ms(1000);
      }
      for (i=7; i>-1; i--)
      {
          PORTB = 1 << i;
          Delay_ms(1000);
      }
      PORTB = 0;
    }
}
\$\endgroup\$
  • \$\begingroup\$ A better answer would describe why changed what you changed, instead of just giving the end solution to the OP. \$\endgroup\$ – Dan Laks Feb 23 '16 at 21:35
  • \$\begingroup\$ Agree, but I thought that is implicit. You can see what is changed... \$\endgroup\$ – Darko Feb 24 '16 at 11:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.