Monday, 12 May 2014

microcontroller - Fastest way to get integer mod 10 and integer divide 10?


If a hardware doesn't support modulus or division operations, it takes many more CPU cycles to simulate modulus/division by software. Is there any faster way to calculate division and modulus if the operand is 10?


In my project I frequently need to calculate integer modulus 10. In particular, I'm working on PIC16F and need to show a number on an LCD. There are 4 digits to support, so there are 4 calls to modulus and division function (software implementation). That is, like the following:


digit = number % 10;    // call to an expensive function
number /= 10; // call to an expensive function
somehow_lit_segments();


digit = number % 10; // call to an expensive function
number /= 10; // call to an expensive function
somehow_lit_segments();

digit = number % 10; // call to an expensive function
number /= 10; // call to an expensive function
somehow_lit_segments();

digit = number % 10; // call to an expensive function

number /= 10; // call to an expensive function
somehow_lit_segments();

There are other areas that uses similar code.



Answer



Heres a binary to BCD algorithm I used several years ago based on one found here. I was using an external BCD to 7 seg display driver so the result could be written to the proper ports directly as packed BCD for output.


This is fairly fast if you have a hardware multiplier in the PIC, i was using a PIC18F97J60. If you don't have a hardware multiplier on your PIC, consider using shift + add for the multiplication.


This takes in an unsigned 16bit int and returns packed BCD with 5 digits, it could be modified and made faster for 4 digits. It uses shift + additions to approximate division by 10 but given the limited input range it is exact for this use. You may want to pack the result differently as well to line up with how your using the result.


void intToPackedBCD( uint16_t n, uint8_t *digits ) {


uint8_t d4, d3, d2, d1, d0, q; //d4 MSD, d0 LSD

d1 = (n>>4) & 0xF;
d2 = (n>>8) & 0xF;
d3 = (n>>12) & 0xF;

d0 = 6*(d3 + d2 + d1) + (n & 0xF);
q = (d0 * 0xCD) >> 11;
d0 = d0 - 10*q;


d1 = q + 9*d3 + 5*d2 + d1;
q = (d1 * 0xCD) >> 11;
d1 = d1 - 10*q;

d2 = q + 2*d2;
q = (d2 * 0x1A) >> 8;
d2 = d2 - 10*q;

d3 = q + 4*d3;
d4 = (d3 * 0x1A) >> 8;

d3 = d3 - 10*d4;

digits[0] = (d4<<4) | (d3);
digits[1] = (d2<<4) | (d1);
digits[2] = (d0<<4);
}

No comments:

Post a Comment

arduino - Can I use TI&#39;s cc2541 BLE as micro controller to perform operations/ processing instead of ATmega328P AU to save cost?

I am using arduino pro mini (which contains Atmega328p AU ) along with cc2541(HM-10) to process and transfer data over BLE to smartphone. I...