Author Topic: Float Counter  (Read 1263 times)

0 Members and 1 Guest are viewing this topic.

Offline george.manson.69Topic starter

  • Newbie
  • Posts: 5
  • Country: mx
Float Counter
« on: July 25, 2020, 05:18:18 pm »
Hello Everyone,

I have some problems, I have been doing a code to show a float number in a 4 digits of 7 segments,
for example,

I have a number 0.123, so in the display must show each number in display [0.][1][2][3]
but I put a printf function to show what happens to each number, and i got this.

0,0,12,123


of course in the display shows nothing correct.

This is a part of the code to convert to float to array number for the 4 digits.

(I use a PIC16F18877 in MPLAB X IDE with XC8)

Code: [Select]
#include "mcc_generated_files/mcc.h"

/*
0011 1111 -> 0
0000 0110 -> 1
0101 1011 -> 2
0100 1111 -> 3
0110 0110 -> 4
0110 1101 -> 5
0111 1101 -> 6
0000 0111 -> 7
0111 1111 -> 8
0110 1111 -> 9
 
 */
const uint8_t pattern[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};
uint8_t mask = 0;
uint8_t* buffer[4];
uint8_t number[4];

float cont;

void shift_data(uint8_t data){

   
    RCLK_SetHigh();
    for(uint8_t i=0;i<8;i++){
   
        PORTDbits.RD1 = (data << i) & 0x80?1:0;
        SRCLK_SetHigh();    //Clock
        SRCLK_SetLow();
       
    }

    RCLK_SetLow();

   
     
}


void converter_uint_to_array(uint16_t dataToConvert,uint8_t* buff){

    uint8_t mm,cc,dd,uu;
   
    PIE0bits.TMR0IE = 0;
   
    mm = (uint8_t)(dataToConvert / 1000);
    cc = (uint8_t)(dataToConvert / 100);
    cc = cc - ((uint8_t)(cc/10))*10;
   
    dd = (uint8_t)(dataToConvert / 10);
    dd = dd - ((uint8_t)(dd/10))*10;
   
    uu = dataToConvert - ((uint8_t)(dataToConvert/10))*10; 
   
    *buff++ = mm;
    *buff++ = cc;
    *buff++ = dd;
    *buff = uu;
   
    PIE0bits.TMR0IE = 1;
}

void converter_float_to_array(float dataToConvert,uint8_t* buff){

    uint16_t d1,d2,d3,d4,aux = 0;

    PIE0bits.TMR0IE = 0;

    if(dataToConvert>= 0.000 && dataToConvert < 10.00){
   
        d1 = (uint16_t)(dataToConvert);
        d2 = (uint16_t)((float)(dataToConvert - (float)d1));
       
        d3 = (uint16_t)dataToConvert*10;

        d3 = (uint16_t)(10*((float)(((float)dataToConvert*10) - (float)d3)));

        d4 = (uint16_t)dataToConvert*100;

        d4 = (uint16_t)(10*((float)(((float)dataToConvert*100) - (float)d4))); 
        printf("4 %u,%u,%u,%u\r\n",d1,d2,d3,d4);

    }else if(dataToConvert>= 10.00 && dataToConvert < 100.0){

        d1 = (uint16_t)(dataToConvert / 10);
        d1 = d1 - ((uint16_t)(d1/10))*10;
        d2 = (uint16_t)(dataToConvert - ((uint16_t)(dataToConvert/10))*10);
       
        d3 = (uint16_t)dataToConvert;
        d3 = (uint16_t)((float)((float)dataToConvert - (float)d3) * 10);
       
        d4 = (uint16_t)dataToConvert*10;
        d4 = (uint16_t)((float)((float)(dataToConvert*10) - (float)d3) * 10);   
       
        printf("2 %u,%u,%u,%u%\r\n",d1,d2,d3,d4);
   
    }
   
   

    *buff++ = (uint8_t)d1;
    *buff++ = (uint8_t)d2;
    *buff++ = (uint8_t)d3;
    *buff = (uint8_t)d4;   
       
    PIE0bits.TMR0IE = 1;
}


/*
                         Main application
 */
void main(void)
{
    // initialize the device
    SYSTEM_Initialize();

    // When using interrupts, you need to set the Global and Peripheral Interrupt Enable bits
    // Use the following macros to:

    // Enable the Global Interrupts
    INTERRUPT_GlobalInterruptEnable();

    // Enable the Peripheral Interrupts
    INTERRUPT_PeripheralInterruptEnable();

    // Disable the Global Interrupts
    //INTERRUPT_GlobalInterruptDisable();

    // Disable the Peripheral Interrupts
    //INTERRUPT_PeripheralInterruptDisable();

    LATD = 0x00;
   
    number[0] = 0;
    number[1] = 0;
    number[2] = 0;
    number[3] = 0;
   
    buffer[0] = &number[0];
    buffer[1] = &number[1];
    buffer[2] = &number[2];
    buffer[3] = &number[3];
   
   
    while (1)
    {
       
        converter_float_to_array(0.123,&number);
        __delay_ms(500);
        // Add your application code
    }
}

I tried many times to resolve this issue if you have any idea that could help me, will be great!



regards!
 

Offline Nominal Animal

  • Super Contributor
  • ***
  • Posts: 8349
  • Country: fi
    • My home page and email address
Re: Float Counter
« Reply #1 on: July 25, 2020, 09:50:49 pm »
Why are you using float on an 8-bit microcontroller without hardware float support?  Using e.g. int32_t to describe the values in thousandths (so 1 displays as 0.001 and -1 displays as -.001) would be just as precise, but much faster.

Here's what I'd use:
Code: [Select]
#include <stdint.h>

#define  display_none 0x00
#define  display_dot  0x80
#define  display_neg  0x40
#define  display_over 0x5C
static const uint8_t  display_digit[10] = {
    0x3F, /* "0": 0011 1111 */
    0x06, /* "1": 0000 0110 */
    0x5B, /* "2": 0101 1011 */
    0x4F, /* "3": 0100 1111 */
    0x66, /* "4", 0110 0110 */
    0x6D, /* "5", 0110 1101 */
    0x7D, /* "6", 0111 1101 */
    0x07, /* "7", 0000 0111 */
    0x7F, /* "8", 0111 1111 */
    0x6F, /* "9", 0110 1111 */
};

/* [0] is rightmost, [3] leftmost */
uint8_t  display_pattern[4];

void display_thousandths(int32_t  value)
{
    uint_fast32_t  u, v;
    int_fast8_t    i, s, d;

    display_pattern[0] = display_none;
    display_pattern[1] = display_none;
    display_pattern[2] = display_none;
    display_pattern[3] = display_none;

    if (value >= 9999500) {
        /* Too large to display. */
        display_pattern[2] = display_over;
        return;
    } else
    if (value >= 1000000) {
        u = (value + 500) / 1000;
        s = 0;
        d = 0;
    } else
    if (value >= 100000) {
        u = (value + 50) / 100;
        s = 0;
        d = 1;
    } else
    if (value >= 10000) {
        u = (value + 5) / 10;
        s = 0;
        d = 2;
    } else
    if (value > 0) {
        u = value;
        s = 0;
        d = 3;
    } else
    if (value == 0) {
        display_pattern[0] = display_digit[0];
        return;
    } else
    if (value <= -999500) {
        /* Too large to display */
        display_pattern[3] = display_neg;
        display_pattern[2] = display_over;
        return;
    } else
    if (value <= -100000) {
        u = (500 - value) / 1000;
        s = 1;
        d = 0;
    } else
    if (value <= -10000) {
        u = (50 - value) / 100;
        s = 1;
        d = 1;
    } else
    if (value <= -1000) {
        u = (5 - value) / 10;
        s = 1;
        d = 2;
    } else {
        u = (-value);
        s = 1;
        d = 3;
    }

    /* Optional: Remove trailing fractional zeros. */
    while (d > 1 && !(u % 10)) {
        u /= 10;
        d--;
    }

    /* Save the value. We'll need it later. */
    v = u;

    /* Convert to digit patterns. */
    i = 0;
    do {
        display_pattern[i++] = display_digit[u % 10];
        u /= 10;
    } while (u > 0);

    /* Zeros up to decimal point. */
    while (i < d) {
        display_pattern[i++] = display_digit[0];
    }

    /* Room for an unit zero? Need one? */
    if (d > 0 && s + i < 4) {
        if ((d == 1 && v < 10) ||
            (d == 2 && v < 100) ||
            (d == 3 && v < 1000)) {
            display_pattern[i++] = display_digit[0];
        }
    }

    /* Negative? */
    if (s && i < 4) {
        display_pattern[i] = display_neg;
    }

    /* Decimal point? */
    if (d > 0) {
        display_pattern[d] |= display_dot;
    }

    /*
     * TODO: Update display based on display_pattern[].
    */
}
and in your case, you can use the following to display floats via using the above:
Code: [Select]
void display_float(float value)
{
    if (value > 9999.999f)
        display_thousandths(9999999);
    else
    if (value >= 0.0f)
        display_thousandths((int32_t)(1000.0f * value + 0.5f));
    else
    if (value < -999.999f)
        display_thousandths(-999999);
    else
        display_thousandths((int32_t)(1000.0f * value - 0.5f));
}
Note that the 0.5f above are used for correct rounding.

If you want to explore what that looks like, add
Code: [Select]
#include <stdlib.h>
#include <stdio.h>

static const unsigned char lcd_mask[7][8] = {
    { 0x21, 0x01, 0x01, 0x01, 0x03, 0x00, 0x00, 0x00 },
    { 0x20, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00 },
    { 0x20, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00 },
    { 0x70, 0x40, 0x40, 0x40, 0x46, 0x00, 0x00, 0x00 },
    { 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00 },
    { 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00 },
    { 0x18, 0x08, 0x08, 0x08, 0x0C, 0x00, 0x80, 0x00 },
};   

int main(int argc, char *argv[])
{
    float val;
    int   arg, r, c, d;
    char  dummy;

    for (arg = 1; arg < argc; arg++) {
        if (sscanf(argv[arg], " %f %c", &val, &dummy) != 1) {
            fprintf(stderr, "%s: Invalid value.\n", argv[arg]);
            return EXIT_FAILURE;
        } else {
            printf("%s:\n", argv[arg]);
            display_float(val);
            for (r = 0; r < 7; r++) {
                for (d = 3; d >= 0; d--) {
                    for (c = 0; c < 8; c++) {
                        if (display_pattern[d] & lcd_mask[r][c]) {
                            fputc('X', stdout);
                        } else {
                            fputc(' ', stdout);
                        }
                    }
                }
                fputc('\n', stdout);
            }
            fflush(stdout);
        }
    }

    return EXIT_SUCCESS;
}
so you can compile it on any C compiler, to be run on your development system.  Supply one or more numbers as a command-line parameter, and it'll display a crude ASCII version of the expected seven-segment display.  For example, for -99.25 it will print
Code: [Select]
-99.25:
        XXXXX   XXXXX   XXXXX   
        X   X   X   X       X   
        X   X   X   X       X   
XXXXX   XXXXX   XXXXX   XXXXX   
            X       X       X   
            X       X       X   
        XXXXX   XXXXX X XXXXX   
and for 92.127
Code: [Select]
XXXXX   XXXXX       X   XXXXX   
X   X       X       X       X   
X   X       X       X       X   
XXXXX   XXXXX       X   XXXXX   
    X   X           X       X   
    X   X           X       X   
XXXXX   XXXXX X     X   XXXXX   
The displayable range is -999 to 9999.  The code is smart enough to format -0.001 as -.001, but will use a leading zero if one fits.
 

Offline george.manson.69Topic starter

  • Newbie
  • Posts: 5
  • Country: mx
Re: Float Counter
« Reply #2 on: July 26, 2020, 03:41:24 am »
Why are you using float on an 8-bit microcontroller without hardware float support?  Using e.g. int32_t to describe the values in thousandths (so 1 displays as 0.001 and -1 displays as -.001) would be just as precise, but much faster.


Excellent, I'm going to use it and will tell you the results!,

Thanks!
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf

 

- - -
-->