What exactly is PINB | = 0b00000001;

Hi, I’m not familiar with this PINB | = 0b00000000;

  1. I think I understand it to be a “fast” set digital
    Pin High?
  2. And PINB is associated with 0-8 pins on arduino? …but how does that translate to say a teensy for example.
  3. Is this an arduino specific thing or can you do the same thing on other boards?
  4. How much “faster” would it really be vs digital write ?

*(edit)- members pointed out errors and that it should be PINB = 0b00000001; …at bare minimum.

That command does not look useful to me, and probably does nothing (at least on an AVR processor).

PINB on AVR processors is the input buffer for PORT B. However, a special feature of AVR processors is that writing a 1 to the register toggles an output bit:

PINB = 1; //toggle output bit 0

1 Like

Arduino uses standard 'C++' and 'C'. However, all the low-level commands are also available and even assembly code can be added.
The main goal of Arduino is to provide a common set of functions that will work on every Arduino board. Using a register called "PINB" which is only available for certain boards breaks with that rule.

  1. Using the registers of a microcontroller directly is faster.
  2. PINB is the input port of Port B which is associated with 8 pins. It does not translate to a Teensy, unless the direct register commands of the Teensy are used.
  3. No, this is low-level register access, which Arduino allows you to use if you want.
  4. A lot, but that is irrelevant if you have to ask.

Some think that it is a good thing to be faster, but it is not, it is very bad to use fancy incompatible code.
To blink a led every second, there is not need to be fast.
Writing code that will work only on one specific microcontroller is not very productive (although Arduino allows you to do so).

To make a sketch faster, you should remove all the delay() and check if the libraries use delay(). The millis() function can be used to do multiple things at the same time.
Some replace a slow Arduino board with a fast Arduino board, but if a sketch is full with delay(), then it will be just as slow.

An amazingly stupid way to toggle all set pins.

Where did you see that?

It is a VERY strange operation. First, it reads the state of the pins controlled by PORTB (Arduino UNO pins 8-13 plus a couple that aren't used on the UNO). Then it OR's all of those bits with 0 which does nothing. And finally, it writes the bits back to PINB. If any of the pins read as 1 (HIGH) either because they are an OUTPUT pin set HIGH or they are an INPUT or INPUT_PULLUP pin that reads HIGH, the corresponding bit in PORTB will be toggled. For OUTPUT pins that will toggle the pin if it was HIGH (setting it LOW). For INPUT pins that are HIGH it will toggle them to INPUT_PULLUP pins and for INPUT_PULLUP pins that are HIGH it will toggle them to INPUT pins. I can't think of any good use for this statement.

To do a '“fast” set digital Pin High' you would use something like:
PORTB |= 0b00001111;
That will set Pins 8-11 to HIGH if they are OUTPUT and INPUT_PULLUP if they are INPUT

PIND/PORTD/DDRD are Pins 0-7 on UNO/Nano
PINB/PORTB/DDRB are Pins 8-13 on UNO/Nano
There are no such registers on non-AVR processors and on other AVR processors (MEGA, LEONARDO/Micro) the mapping between ports and pins is different. See the pinout diagram.

It is AVR processor specific.

Figure it will take less than a microsecond to set/clear any combination of pins on a port. The digitalWrite() function takes about six microseconds and can only change one pin. Six microseconds is fairly quick so you would generally use digitalWrite() first and only switch to Direct Register Access if your code doesn't work because your pin manipulation is taking too long.

1 Like

It was in a snippet to run a pwm signal at 100hz basically using blink.
I think I’ll try straight digital write. Although 5milli slower (according to posts here) I think it may still work.

Thanks that explains a lot. I was looking at sone code that was manipulating to run pwm at around 100hz. Im not sure if 6 vs 1 will matter enough to impact so I’ll try digital write and see what happens. I just didn’t understand that direct code enough to compile for an alternate processor and figured I’d ask to learn more.

Thanks. I genuinely appreciate the info. In response to #4. I politely disagree. It’s relevant the to ask if you don’t know what the code does or how in order to understand and then choose what options best suit the need. In this case, I wasn’t blindly asking “if” it was faster as I had googled and had a basic concept of it, but rather “how much” faster. To my knowledge I did not have a reference for speed. This is how we learn new things when our own searching has produced less than clear understanding. I am thankful for anyone in the community willing to offer help and guidance and would not have posted a topic if it were crystal clear from searching first.

1. PINB is the symbolic name for all the pins of Port-B Register when its IO lines are configured to work as "input lines" (Fig-1).

PINB
Figure-1:

2. The following lines are valid codes (Edit: except (2), see Post-10):

(1)  byte y = PINB;    //reading input status of all port pins of Port-B Register
(2)  bool x = PINB0;   //reading 1-bit input value from Bit-0 of Port-B Register
(3)  bool x = digitalRead(8);    //same as (2) 

3. I personally do not like the following (though valid) code which writes on PINB; where, PINB is an input port. Traditionally, we write data onto an output port.

PINB = 0b00101010;       //?
1 Like

...is useless. Which means the comment is inaccurate. Ditto for the (3) comment; (2) and (3) are not the same.

Before you start an argument about PINB0, find the definition. Or find an example in the datasheet. Or find an example on the internet-at-large.

In case you change your mind, the final part of @GolamMostafa's post is accurate. Assign a constant to PINB where the high bits are the pins to toggle...

PINB = 0b00101010;

Bonus points if you use PINBn constants correctly.

The OneWire library uses direct register access. You can see it for the different boards here: https://github.com/PaulStoffregen/OneWire/blob/master/util/OneWire_direct_gpio.h.

With direct register access, a Arduino Uno could drive a small motor without extra hardware. Read the "README.md" tab in this Wokwi project: https://wokwi.com/arduino/projects/309655684318757441

A digitalWrite() for a Arduino Uno is about 5ms 5µs, that is 5000ns.
Changing a output pin with direct register access is 125ns.

The instruction set of the ATmega328P microcontroller (used in the Arduino Uno) is designed to change a output pin very fast.

@filmchaser, the code PINB |= 0b00000000; is a bug. That single line of code is indeed amazingly stupid as Coding_Badly wrote in reply #4.

[EDIT] Changed 5ms to 5µs. Oops :flushed:

Why was I so sure to express that if PINB was a variable, then PINB0 would also be a variable? (Probably, I created the definition from PINBn name without exploring/reading further!)

Now, I see (expetimentally) that PINB0, PINB2, ..., PINB5 are just constants and they return 0, 1, ..., 5 respectively.

Thank you for pointing my ignorance.

100 Hz is quite slow for PWM. Did you mean 100 kHz (100,000 Hz)? It seems odd that someone would be writing to registers for PWM when the AVR processor has hardware PWM features.

That is significantly faster! So, this code below is where it was used. That thread was closed so I can’t ask there. I wanted to understand the what and how first so I could understand how to modify it for another board. The issues, are: The pwm signal for the device I’m trying to drive has a a built in circuit that uses a fixed 100hz frequency with varying duty cycle from 10%-90%. The pot referenced below adjusts the duty cycle which is interpreted by the device to adjust position.” Analogwrite support PWM is 490hz and 900hz.” So, I need the100hz signal but I’d rather not use an UNO since I have other stuff to combine running on another board. Obviously, PINB won’t compile on another board so I was looking for alternative ways to handle it.
It’s not straightforward PWM.


byte pin8 = 8;
unsigned long currentMicros;
unsigned long previousMicros;
unsigned long elapsedMicros;
unsigned long period = 10000UL; // microseconds, 0.01S  1/.01 = 100 Hz
unsigned long highTime = 1000UL; // microseconds
unsigned long lowTime; // microseconds
byte highLow = 0; // flag to show which state output is in

int potValue;

void setup()
{
  pinMode (pin8, OUTPUT);
  digitalWrite (pin8, LOW);
  lowTime = period -  highTime; // with numbers above, 10000 - 1000 = 9000
  currentMicros = micros();
  previousMicros = currentMicros;
}

void loop()
{
  elapsedMicros = micros() - previousMicros;
  
  if ((elapsedMicros >= lowTime) && (highLow == 0))
  {
    previousMicros = previousMicros + lowTime;
    PINB = 0b00000001;
    highLow = 1; // started high period
  }

  else if ((elapsedMicros >= highTime) && (highLow == 1))
  {
    previousMicros = previousMicros + highTime;
    PINB = 0b00000001;
    highLow = 0; // started low period
  }

  // "most of the time" (processor-wise) the above do nothing as the proper time hasn't elapsed
  // use that downtime to adjust the duty cycle
  potValue = analogRead(A0);

  highTime = potValue * 10; // 1023 * 10 = 10230
  // cap the range by limiting to 10-90%
  if (highTime > 9000)
  {
    highTime = 9000;
  }
  if (highTime < 1000)
  {
    highTime = 1000;
  }
  lowTime = period - highTime;

}

Hi John. I just posted below the further explanation. In short the PWM signal does need to be at 100hz. The docs I saw for digitalwrite are around 490hz. So I think the direct command was either a timing choice or a user method that worked for Uno vs the digitalwrite. But that’s why I was inquiring to try to understand why that might have been chosen as again I haven’t used that before. So this is new to me. And if I were to change it and try it, I wouldn’t have known why it may or may not function. So just trying to educate myself and possibly learn new techniques for the future. Open to better approaches or something I may not know in handling the frequency to 100hz.

@filmchaser
It was a really, really bad idea to change the title and content of the OP, making most of the forum member's comments seem nonsensical.

In the future, DO NOT do that.

For the record, the question was:

Hi, I’m not familiar with this PINB |= 0b00000000;

Thanks. Good point I’ll change it back. I was correcting what was pointed out but I see what you are saying.

Using the toggle feature of PINB is not safe. If any toggle gets missed or repeated, your PWM is suddenly inverted.

  elapsedMicros = micros() - previousMicros;
  
  if (highLow)
  {
    // HIGH
    if (elapsedMicros >= highTime)
    {
      previousMicros += highTime;
      PORTB &= ~_BV(PB0);   // Clear Pin 8
      highLow = 0; // started low period
    }
  }
  else
  {
    // LOW
    if (elapsedMicros >= lowTime)
    {
      previousMicros += lowTime;
      PORTB |= _BV(PB0);  // Set Pin 8
      highLow = 1; // started HIGH period
    }
  }

In other words, if the program malfunctions, it malfunctions.

3 Likes

It should also be noted that there can be atomicity issues when using AVR raw port i/o.

Some forms of malfunction can be not so obvious, particularly to less experienced code writers and there are some cases than can trip up even advanced users.

This is because the AVR has very limited capabilities when it comes to setting and clearing bits. i.e. it can only atomically set/clear a single bit and even when it can, the address range that this can be done is very limited.
Some AVRs have registers, including i/o port registers that are outside of the range that supports bit set/clear instructions.
As a result, if interrupts are involved or multiple bits are being set/cleared or there is an attempt to set a single bit in a register that is outside the supported range it is possible that an attempt to set/clear bits can result in register corruption.

Consider this:

PORTB |= 0b00000001;

compiles into a single atomic SBI instruction with no concerns when using with ISR code that also modifies PORTB bits.
But this is not:

PORTB |= 0b00000011;

This second example of attempting to set multiple bits can cause register corruption if an ISR is also modifying PORTB.
This second example is also much slower than doing each bit separately which sets each bit atomically.
But while each bit is set atomically, there is no way on an AVR to set multiple bits in a register atomically without setting all the bits in the register.

PORTB |= 0b00000001;
PORTB |= 0b00000010;

Also, not all AVR registers support bit set and bit clear instructions.
This one can trip up even and advance user.
i.e. bit set/clear instructions not supported for any register above G.
For example on a Atmega 1280/2560

PORTK |= 0b00000001;

While it looks the same at the C source level, it will not generate the same instructions as a register a lower address such as 'H' or lower
since this K register is outside the supported range it will generate multiple AVR instructions and is not atomic.

Another thing due to the AVR limited instructions for setting/clearing bits, is if anything but a constant is used to set the bit, the compiler is unable to generate atomic code.

Example:

PORTB |= 1; // atomic
POTBB |= bitvalue; // not atomic if bitvalue is not a constant.

Some other processors have more powerful instructions that are not as limited.
--- bill