How to take a number and express in binary on digital pins?

Hello all

So, I'm still plodding on with my long running arduino project.

I'm struggling with coding a particular part of the project.

How do I take a number (which is the product of one calculation) and express that in real time as a binary number on digital pins, BUT as individual characters?

So... For example...

The result of the calculation may be 3475.

I want to express that in binary, but NOT as one number (110110010011) but as separate characters on clusters of 4 pins for each digit, i.e.:

0011 | 0100 | 0111 | 0101

I'm trying to see how I would "break out" the result and code each digit separately, but not yet figured it out.

I've tried searching for examples, but found nothing that seems to fit yet and I am struggling with what to actually search for here.

Thanks

Dave

fall-apart-dave:
So... For example...
The result of the calculation may be 3475.
I want to express that in binary, but NOT as one number (110110010011) but as separate characters on clusters of 4 pins for each digit, i.e.:
0011 | 0100 | 0111 | 0101

https://www.google.com/search?q=binary+coded+decimal

I'm trying to see how I would "break out" the result and code each digit separately, but not yet figured it out.

Working from the right you use modulus by 10 to get the next digit and division by 10 to reduce the number. Repeat until zero or you reach the desired number of digits.

Bonus points for using the div function.

To break a byte into nibbles

byte myNum = 163; // = 1010 0011
byte myLowNibble;
byte myHighNibble;

myLowNibble = myNum & 0b00001111; // = 0000 0011
myHighNibble = (myNum & 0b1111000) >> 4; // = 0000 1010

You can extend that to deal with an unsigned int or an unsigned long

...R

I do not understand your "clusters of 4 pins". Do you mean physical pins, which don't come in clusters?

If you want to display so many bits, shift registers are a good choice for the output. Then you simply shift out the bytes of your variable into the registers, and each bit will appear at its related output pin.

Thank you I shall look at this.

@Robin, that is really helpful, thanks!

@DrDiettrich, yes I know shift registers etc will help however, for my project that simply adds more connections and more complications. I'm using an Arduino mega. "Clusters" - as in I am going to physically wire the pins and the groups I am going to call clusters. As in... these four pins here are for the "5" in my imaginary number, and those four pins I am going to call cluster one. Then this group of four pins here, I am going to call cluster two etc. I know there aren't any clusters of pins on an Arduino.
Also simply shifting the bytes out, I still have to convert from a four digit number to each digit being output expressed separately.

Robin2:
To break a byte into nibbles

byte myNum = 163; // = 1010 0011

byte myLowNibble;
byte myHighNibble;

myLowNibble = myNum & 0b00001111; // = 0000 0011
myHighNibble = (myNum & 0b1111000) >> 4; // = 0000 1010




You can extend that to deal with an unsigned int or an unsigned long

...R

Ah, no, that doesn't do what I want it to (I think?)

163 is indeed 1010001

BUT

I want to express that as three completely separate numbers, with four output pins per digit. So, I would want to output:

1, 6, and 3, so actually would want:

0001 0110 0011

You should find a Mega2560 pinout and identify the Ports. IO pins are wired to Ports, 8 per Port.

Your clusters are what's been called nybbles or nibbles for a long time now, perhaps into the 60's but for sure by the mid 70's.

The IO Ports have 3 registers each (look up direct port manipulation on this site) to read or write 8 bits at a time.
You stick the byte into the Portn register when the data direction register is set and your pins change according to the data.

Remove all doubt by learning. Look it up.

fall-apart-dave:
I want to express that as three completely separate numbers, with four output pins per digit. So, I would want to output:

1, 6, and 3, so actually would want:

0001 0110 0011

That is a useful clarification.

Then all you need to do is successive divides by 10, keeping the remainder. Something like

myVal = 163;
digitIndex = 0;
while (myVal > 0) {
   myTenth = myVal / 10;
   myDigits[digitIndex] = myVal - (myTenth * 10);
   myVal = myTenth;
   digitIndex ++;
}

...R

Robin2:
That is a useful clarification.

Then all you need to do is successive divides by 10, keeping the remainder. Something like

myVal = 163;

digitIndex = 0;
while (myVal > 0) {
  myTenth = myVal / 10;
  myDigits[digitIndex] = myVal - (myTenth * 10);
  myVal = myTenth;
  digitIndex ++;
}




...R

Hahaha why didn't I think of dividing by ten? DOH! I was looking for functions that parsed out the result of the calculation into individual fields for each number in the value. Divide by ten is way easier! Thank you!

A 4 bit cluster usually is called a "nibble".

So you want to output a BCD number, with every 4 pins representing a digit 0-9?

Then you take the number modulo 10 (n % 10) to get the lowest digit, then divide the number by 10. Repeat for the higher digits until you reach zero.

DrDiettrich:
A 4 bit cluster usually is called a "nibble".

So you want to output a BCD number, with every 4 pins representing a digit 0-9?

Then you take the number modulo 10 (n % 10) to get the lowest digit, then divide the number by 10. Repeat for the higher digits until you reach zero.

Gotchya. Terminology is key, thank you for the clarification. All this time I thought everyone was talking about confectionery...

Understand completely now, thank you!

GoForSmoke:
You should find a Mega2560 pinout and identify the Ports. IO pins are wired to Ports, 8 per Port.

Your clusters are what's been called nybbles or nibbles for a long time now, perhaps into the 60's but for sure by the mid 70's.

The IO Ports have 3 registers each (look up direct port manipulation on this site) to read or write 8 bits at a time.
You stick the byte into the Portn register when the data direction register is set and your pins change according to the data.

Remove all doubt by learning. Look it up.

I missed this one.

I am trying to learn and look things up. But when you don't know "what" to look up, that's really difficult.

I didn't realise I was talking about nibbles already. Apologies for the confusion.

I still have 2 70's calculators that run on 4-bit chips. They do BCD math to 20 places plus 2 digit exponents.

I think that Nick Gammon's Big Number library might use BCD math.

But still with the Mega you could copy 2 BCD values to 1 port and have the pins match those bits in a single write.

"I am trying to learn and look things up. But when you don't know "what" to look up, that's really difficult."

When I got here in 2011, I went through the Arduino site just following links, getting to know about what's available and bookmarking pages for later reference. When I use the IDE I also open a web browser and make a tab for every page that refers to what I want to do. That gives me the IDE and help at the same time.

@FoForSmoke, that’s what I do… But you are way ahead of me! And I dont have a great deal of time to decicate to learning this, but tutorials etc help. I often don’t understand too well what it is I am reading too, which is no great help.

Anyway, I have come up with this:

int Htime; //Time pin is high
int Ltime; //Time pin is low
float Ttime; //Time of one cycle
int RPM; //calculated RPM
int firstdigit; //first digit of RPM
int seconddigit; //second digit of RPM
int thirddigit; //third digit of RPM
int fourthdigit; //fourth digit of RPM


void setup() {
  
Serial.begin(9600);

pinMode(8,INPUT); //pin for counting pulses


}

void loop() {

Htime=pulseIn(8,HIGH); //read high time
Ltime=pulseIn(8,LOW); //read low time

Ttime=Htime+Ltime;

RPM=8333.33/Ttime; //Scaling factor to convert to pulses per minute, with two pulses per crank revolution, assuming Ttime is in microseconds. 

//This next part is to split the result (in 4 digits) into individual numbers. RPM will always be between 500 and 8000 when the engine is running

firstdigit = RPM % 10; 
seconddigit = RPM/10 %10;
thirddigit = RPM/100 %10;
fourthdigit = RPM/1000 %10;

// This is just so when I test I can make sure it is doing what I expect

Serial.print("First Digit: ");
Serial.println(firstdigit);
Serial.print("Second Digit: ");
Serial.println(seconddigit);
Serial.print("Third Digit: ");
Serial.println(thirddigit);
Serial.print("Fourth Digit: ");
Serial.print(fourthdigit);

//This bit, I think, is the correct way to chuck out each individual number as a nibble on digital pins? 

digitalWrite(11, firstdigit & B00001000);
digitalWrite(12, firstdigit & B00000100);
digitalWrite(13, firstdigit & B00000010);
digitalWrite(14, firstdigit & B00000001);

digitalWrite(15, seconddigit & B00001000);
digitalWrite(16, seconddigit & B00000100);
digitalWrite(17, seconddigit & B00000010);
digitalWrite(18, seconddigit & B00000001);

digitalWrite(19, thirddigit & B00001000);
digitalWrite(20, thirddigit & B00000100);
digitalWrite(21, thirddigit & B00000010);
digitalWrite(22, thirddigit & B00000001);

digitalWrite(23, fourthdigit & B00001000);
digitalWrite(24, fourthdigit & B00000100);
digitalWrite(25, fourthdigit & B00000010);
digitalWrite(26, fourthdigit & B00000001);
}

Does this look sensible? I have no way of testing it at the moment, as I only have my arduino mega with me and no LEDs or anything to show the output.

I need to put something in there that will return a 0000 when the engine is not running (no high/low shift).

Also, with this method, am I correct in thinking that the sketch will “hang” while it waits for a change in the states of pin 8? Would an interrupt be more sensible here?

Sensible … it’s not how I’d have done it but I picked up on bits and bytes early and I was a math head in school.

You have 10 digits,0 to 9, and each has a 4-bit pattern.

0000 = 0
0001
0010
0011
0100
0101
0110
0111
1000
1001 = 9

byte x = 5; // bit pattern is 00000101, in BCD it is 05

A byte is 8 bits, it can hold 2 4 bit patterns.
Once you have the byte, you write it to a Port you have wired up and the bits take care of themselves. You do not have to unpack bits to write to each pin one at a time.

x = x << 4; // moves the bits up 4 bit-places,pattern is now 01010000

// << is the left-shift operator, it moves bits in an integer ‘up’.
// >> is the right-shift operator, it moves bits down

// when I use these shift operators I almost always want to use unsigned variables to avoid sign bit complications.
// Arduino unsigned variables you can shifts bits in are 8-bit byte, 16-bit word and 32-bit unsigned long.
// You CAN shift bits in signed variables, it’s just got one look-out because sign is the top bit.

x = x + 7; // add the next digit 0 to 9 that can only fill 4 bits, will always fit in the 4 empty bits the left-shift vacated
// bit pattern is now 01010111

Yes, that is only 2 digits. The Ports are only 8 bits wide. It will take doing twice, should not be a problem.

I won’t go into using a loop to pull the 4 RPM digits out and etc, etc, but someone may mention that’s the way to do it.
Instead, I’ll use some of your code with some added

byte writeToPort; // this is a global variable declared up top

… in void setup() you will need to mode 2 Ports into OUTPUT
… let’s get what 2 Ports on your Mega2560 you will use/wire to and give you the 4 lines of code for that
… we’ll be looking at the pin map. PortD pins are labeled PD0, PD1, … PD7 right on the pin map, not hard.

… and down in void loop() we have

// firstdigit = RPM % 10;
// seconddigit = RPM/10 %10;
// thirddigit = RPM/100 %10;
// fourthdigit = RPM/1000 %10; // you don’t need 4 variables for these

writeToPort = ( RPM / 1000 % 10 ) << 4;
writeToPort = writeToPort + ( RPM / 100 % 10 ); // now has the 2 high-order digits

PORTE = writeToPort; // now those 8 bits are on those 8 Port E pins; bang, done.

writeToPort = ( RPM / 10 % 10 ) << 4;
writeToPort = writeToPort + ( RPM % 10 ); // now has the 2 low-order digits

PORTD = writeToPort; // now those 8 bits are on those 8 Port D pins; bang, done.

And that’s it for getting the pins set.

Picking Ports and those setup() lines is for another post.

It is getting harder to search the site and get the right hits up top even knowing what I want but here’s Port Manipulation 101 for those who want. There’s a link to the Bit Math Tutorial there.
https://playground.arduino.cc/Learning/PortManipulation

fall-apart-dave:
Also, with this method, am I correct in thinking that the sketch will "hang" while it waits for a change in the states of pin 8? Would an interrupt be more sensible here?

pulseIn() does block execution and delivers millisecond timing that IIRC uses the millis() clock value that ... without details, is not great when close timing is needed.

For things line getting RPM from short intervals, I use micros() timing that is good to the nearest 4 microseconds (hardware limits) which is 250x closer that millis() -can- be (yeah complicated but millis() can put time calculations off by 1 ms).

So I code to have void loop() run 10's of times per millisecond and use micros() for timing. I'd just watch the pin with one block of code in void loop() to detect LOW to HIGH transitions. That block would set a variable to microseconds since last LOW to HIGH.

The next block of code in void loop() would be

if ( elapsedTime > 0 )
{
// calculate RPM, set the ports, done

elapsedTime = 0; // so it only runs when a LOW to HIGH is detected, otherwise it runs over and over
}

Since I know that loop() is wayyy faster than 1 ms, I can claim close timing than pulseIn() will ever get.

Yes you can go to interrupts. There's a learning curve for that too.
If you don't need to mess with interrupts then don't. Later on you may want to add a library that uses one or you may want to sense something that really needs one. You have limited edge-triggered interrupts, save those for need.

Took three goes at reading to understand, but I think I get it.

So, I can simplify mine significantly and have:

int Htime; //Time pin is high
int Ltime; //Time pin is low
float Ttime; //Time of one cycle
int RPM; //calculated RPM
byte writeToPort;


void setup() {


pinMode(8,INPUT); //pin for counting pulses

//Define the ports here too, reading that now

}

void loop() {

Htime=pulseIn(8,HIGH); //read high time
Ltime=pulseIn(8,LOW); //read low time

Ttime=Htime+Ltime;

RPM=8333.33/Ttime; //Scaling factor to convert to pulses per minute, with two pulses per crank revolution, assuming Ttime is in microseconds.


writeToPort = ( RPM / 1000 % 10 ) << 4;
writeToPort = writeToPort + ( RPM / 100 % 10 );  // now has the 2 high-order digits

PORTE = writeToPort; // now those 8 bits are on those 8 Port E pins; bang, done.

writeToPort = ( RPM / 10 % 10 ) << 4;
writeToPort = writeToPort + ( RPM % 10 );  // now has the 2 low-order digits

PORTD = writeToPort; // now those 8 bits are on those 8 Port D pins; bang, done. 
}

That looks a lot tidier, thank you… (waits for the “No, you’ve misunderstood, get back on the dunce stool and pay attention, young man!” haha!)

GoForSmoke:
pulseIn() does block execution and delivers millisecond timing that IIRC uses the millis() clock value that ... without details, is not great when close timing is needed.

For things line getting RPM from short intervals, I use micros() timing that is good to the nearest 4 microseconds (hardware limits) which is 250x closer that millis() -can- be (yeah complicated but millis() can put time calculations off by 1 ms).

So I code to have void loop() run 10's of times per millisecond and use micros() for timing. I'd just watch the pin with one block of code in void loop() to detect LOW to HIGH transitions. That block would set a variable to microseconds since last LOW to HIGH.

The next block of code in void loop() would be

if ( elapsedTime > 0 )
{
// calculate RPM, set the ports, done

elapsedTime = 0; // so it only runs when a LOW to HIGH is detected, otherwise it runs over and over
}

Since I know that loop() is wayyy faster than 1 ms, I can claim close timing than pulseIn() will ever get.

Yes you can go to interrupts. There's a learning curve for that too.
If you don't need to mess with interrupts then don't. Later on you may want to add a library that uses one or you may want to sense something that really needs one. You have limited edge-triggered interrupts, save those for need.

Digesting this one. Thank you again@!

fall-apart-dave:
Digesting this one. Thank you again@!

Arduino setup() and loop() work directly to support automation, these chips are automation controllers.

Before about 1992, if you wanted to write real-time code it worked about the same way, you’d write the part that set things up and then you’d write what they call ‘the main loop’.

Everything inside of the loop runs even if to only check for a trigger condition, find there is none and let the next thing in the loop run.

Trigger condition can be if a pin is HIGH or LOW or different than last time (only do something when change is detected) or was-LOW-is-HIGH or vice versa.

Trigger can be if a variable is > 0 or 42 or whatever. Some other section of code sets the variable to trigger this section.

Trigger condition can be time duration, only runs when it should.

This allows you to break the job down to small pieces that share control through variables rather than structure.
When they actually run, the small pieces run quickly – generally < 50 micros but it does take 105+ micros just for 1 analog read.

Small pieces write, change and debug quicker. There’s less to be wrong! They can be taken and put into little test sketches then moved back into the project after whatever got straightened out.

Small pieces can be modular. A button handler that signals when the button is pushed can be changed out for a light detector.

You can look at the sections inside of void loop() as parts that work as a machine that does. Or go the woods and trees route…

Just when you do write, make each part work before making the next. Do not ever write up a mass of maybe’s before you even autoformat let alone attempt debugging all of them at once. You can easily find yourself fixing what works in a mistaken belief that it can’t be just based on what 2 or more other bugs giving that impression. Errors in data can bug up code that doesn’t check for them something wicked too.

Debugging is where programmers really live or die. So what you do is make it easier by writing small modules to handle inputs and outputs and the decision code that watches inputs and directs outputs and keep it all in small, simple pieces. Much easier.

I already planned to run this as a series of functions to call...

So the board will call (in no particular order):

Output to IN-9 nixie (Bargraph readout, simple case of read analogue output, do maths, output analogue voltage on PWM pins through filters)

Oil pressure - DONE, WORKING
Oil temp - DONE, WORKING
Turbo pressure - DONE, WORKING
Fuel level - PARTIALLY DONE - issue with sender
Fuel pressure - NOT DONE
Lambda - NOT DONE
Oil pressure warning switch - NOT DONE
charge warning switch - NOT DONE
battery charge voltage - DONE, WORKING
Revs - NOT DONE (will be product of the code I am now working on)

Output to In-12 Nixie tubes (numerical readout)
revs (the bit I am working on right now)
speed (will be done same way as revs)

Output to IN-14 Nixie tubes (numerical readout)
Digital clock with quarter mile timing function (this will just be for fun, and may use a second arduino board)

So I'll just call each function once I know each function works. That way I can add or drop them depending what I want for a particular setup.

Once all that^ works, I'll be writing something to log to SD and write to serial for live viewing (or, rather, refine the debugging serial prints to something more elegant)