Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

PIC 16F887 > LM335 > Can not display value of more than 1 decimal

Status
Not open for further replies.

kawauso

New Member
Bonjour,

Would like to display 2 or 3 decimals on LCD.
For example, display 22,5; or 22,50; or 22,500.
But would like to display 22,57; or 22,524 for example.

Code:
// MCU : PIC 16F887
// External clock : Quartz 4 MHz
// LED YELLOW is connected to pin 17 (portc.b2).
// LED GREEN is connected to pin 18 (portc.b3).
// Switch is connected to pin 21 (portd.b2).
// Switch is connected to pin 22 (portd.b3).
// LM35 temperature sensor : pin GND to GND,
//                           pin DQ to pin 5 (AN3),
//                           pin VCC to VCC.

/******************************************************************************/

//#include <math.h>                     // A re tester en décochant dans View > Library Manager > C_Math.
                                        // Fonction floor() dans float_to_ASCII_with_2_decimals_v4

/******************************************************************************/

// LCD module connections :
                                        // VSS (pin  1) -> GND
                                        // VDD (pin  2) -> VCC
                                        // VEE (pin  3) -> middle pin contrast potentiometer
sbit LCD_RS at RB4_bit;                 // RS  (pin  4) -> portB.b4 (pin 37)
// RW not used.                         // RW  (pin  5) -> GND
sbit LCD_EN at RB5_bit;                 // EN  (pin  6) -> portB.b5 (pin 38)
                                        // D0  (pin  7) -> GND
                                        // D1  (pin  8) -> GND
                                        // D2  (pin  9) -> GND
                                        // D3  (pin 10) -> GND
sbit LCD_D4 at RB0_bit;                 // D4  (pin 11) -> portB.b0 (pin 33)
sbit LCD_D5 at RB1_bit;                 // D5  (pin 12) -> portB.b1 (pin 34)
sbit LCD_D6 at RB2_bit;                 // D6  (pin 13) -> portB.b2 (pin 35)
sbit LCD_D7 at RB3_bit;                 // D7  (pin 14) -> portB.b3 (pin 36)
                                        //     (pin 15) -> GND
                                        //     (pin 16) -> GND

sbit LCD_RS_Direction at TRISB4_bit;
sbit LCD_EN_Direction at TRISB5_bit;
sbit LCD_D4_Direction at TRISB0_bit;
sbit LCD_D5_Direction at TRISB1_bit;
sbit LCD_D6_Direction at TRISB2_bit;
sbit LCD_D7_Direction at TRISB3_bit;

/******************************************************************************/

#define CMD 0
#define DATA 1
#define LCD_PORT PORTB
#define LCD_COLUMNS 16

/******************************************************************************/

// Declaration of variables :

char texte[64];                         // 0 to 255
                                        // Variables ci-dessous sorties du main() pour les déclarer en global.
                                        // Le PIC 16F a moins de mémoire RAM et ROM que le PIC 18F.
unsigned char *pointeur_de_char;        // Déclaration d'un pointeur (*) de char "pointeur_de_char".

unsigned char i;

char ROW[] = {0x80, 0xC0};

/******************************************************************************/

// Routines :

void ADC_initialization()
{
 ADCON0 = 0b01000001;              // b0 = 1 (ADON) : le convertisseur A/N interne est activé.
                                   // b1 (GO/DONE)  : A/D Conversion Status bit.
                                   // b2, b3, b4, b5 (CHS0, CHS1, CHS2, CHS3) : Analog Channel Select bits.
                                   // b6 = 1 (ADSC0), b7 = 0 (ADSC1)          : fosc / 8 = 4 MHz / 8 = 0,5 MHz = 500 KHz
                                   //                                           tosc = 1 / fosc = 1 / 500 KHz = 0,002 mS = 2 uS.
                                   // 2 uS est le temps de conversion A/D d'un bit, TAD.
                                   // Pour une conversion totale sur 10 bits il faut 12 TAD.
                                   // Pour une conversion correcte il faut que TAD = 1,6 uS au minimum.
                                   // TAD = 2 uS / bit. 12 TAD = 24 uS pour 10 bits.

 ADCON1 = 0b10000000;              // b0 = b1 = b2 = b3 = 0 : Unimplemented. Read as '0'.
                                   // b4 = 0 (VCFG0)        : Voltage reference VDD.
                                   // b5 = 0 (VCFG1)        : Voltage reference VSS.
                                   // b6 = 0                : Unimplemented. Read as '0'.
                                   // b7 = 1                : ADFM (A/D result ForMat) = 1 (format justifié à droite).
                                   // Explication :
                                   //          ADRESH        |         ADRESL
                                   // b7|b6|b5|b4|b3|b2|b1|b0|b7|b6|b5|b4|b3|b2|b1|b0
                                   //  0| 0| 0| 0| 0| 0| r| r| r| r| r| r| r| r| r| r
                                   //    les 6 MSB = 0 |   résultat r sur 10 bits
}

unsigned int ADC_read(unsigned char channel)
// Si message d'erreur "'ADC_read' Identifier redefined" pendant compilation, dans Library Manager décocher ADC_Read.
// ADC_read est ma propre routine écrite pour apprentissage. Je n'utilise pas ADC_Read de la librairie Mikroelektronika.
{
 static unsigned int k;            // Variable locale (static).
 ADCON0 = ADCON0 & 0b11000011;     // ADCON0 = 01000001
                                   //        & 11000011 (masque)
                                   // ADCON0 = 01000001
                                   // masque :
                                   // b0 = 1 (ADON)                               : pour prendre en compte l'état du convertisseur A/D, en service (0) ou à l'arrêt (1).
                                   // b1 = 1 (GO/DONE)                            : pour prendre en compte l'état de la conversion A/D, en cours (1) ou terminée (0).
                                   // b2 = b3 = b4 = b5 = 0 (CHS0 CHS1 CHS2 CHS3) : pour re initialiser au premier canal de conversion, le canal 0 (CHS0 = CHS1 = CHS2 = CHS3 = 0).
                                   // b6 = b7 = 1 (ADSC0 ADSC1)                   : pour prendre en compte la valeur du diviseur, de la vitesse de conversion, choisi.
 channel = channel << 2;           // Décalage channel de 2 bits à gauche pour placer la valeur de channel dans bits b2 b3 b4 b5 (CHS0 CHS1 CHS2 CHS3).
 ADCON0 = ADCON0 | channel;        // OU logique bit à bit entre ADCON0 et channel.
 // ou ADCON0 |= channel;
 Delay_ms(2);                      // Délais de 2 mS minimum. >>> A ajuster si besoin. <<<
 ADCON0.GO_DONE = 1;               // Déclenchement de la conversion A/N.
                                   // ou ADCON0.b2 = 1;
 _asm NOP;                         // Recommandé par Microchip.
 while (ADCON0.GO_DONE == 1);      // Attendre que le bit GO.DONE passe à 0.
 {
  k = ADRESL + (ADRESH * 256);     // XXX A REVOIR XXX
  //ou k = ADRESL + (ADRESH << 8);
 }
 return(k);
}

void LCD_E_Pulse(void)
{
 LCD_EN = 1;
 Delay_us(8);                                                     // Delay between 5 uS and 10 uS.
 LCD_EN = 0;
 Delay_us(500);                                                   // Delay between 5 uS and 1000 uS (1 mS).
}

void LCD_Write(unsigned char cmd_or_data, unsigned char byte)
{
 unsigned char low, high;
 high = (byte >> 4) & 0x0F;                                       // b7 b6 b5 b4 of byte are shifted to b3 b2 b1 b0 of byte and saved in high.
 low = byte & 0x0F;                                               // b3 b2 b1 b0 of byte are saved in low.
 LCD_PORT = high;                                                 // Send higher nibble of byte to the LCD.
 LCD_RS = cmd_or_data;
 LCD_E_Pulse();
 LCD_PORT = low;                                                  // Send lower nibble of byte to the LCD.
 LCD_RS = cmd_or_data;
 LCD_E_Pulse();
}

void StrConstRamCpy(unsigned char *dest, const code char *source) // Copie le texte de la FLASH ROM vers la RAM.
{
 while (*source)*dest ++ = *source ++;
 *dest = 0;                                                       // Terminateur "0" fin de chaine de caractère.
}

void LCD_Write_String(char *msg)                                  // Variante avec pointeur msg non modifié.
{
 int k;
 k = 0;
 while(*(msg + k) > 0)
 {
  LCD_Write(DATA, (*(msg + k)));                                  // Data pointée par (msg + k).
  k++;
  if (k == LCD_COLUMNS) break;                                    // Si k = 16 sortie de la boucle while ...
 }
}

void LCD_Write_String_At(char line, char column, char *msg)
{
 LCD_Write(CMD, ROW[line]|(column & 0x0F));                       // Print message on desired line and desired column.
 LCD_Write_String(msg);                                           // OK.
}

void print_float_v11(char *flt, long number, char decimals)
{
 if (number < 0)
    {
     number = - number;
     *(flt) = '-';
    }
    else
       {
        *(flt) = ' ';
       }

 if (decimals == 0)
    {
     *(flt + 1) = ' ';
     *(flt + 2) = number / 10000 + '0';
     *(flt + 3) = ((number % 10000) / 1000) + '0';
     *(flt + 4) = ((number % 1000) / 100) + '0';
     *(flt + 5) = ((number % 100) / 10) + '0';
     *(flt + 6) = (number % 10) + '0';
     *(flt + 7) = ' ';
     *(flt + 8) = ' ';
     *(flt + 9) = ' ';
     *(flt + 10) = ' ';
     *(flt + 11) = ' ';
     *(flt + 12) = ' ';
    }
 if (decimals == 1)
    {
     *(flt + 1) = ' ';
     *(flt + 2) = number / 10000 + '0';
     *(flt + 3) = ((number % 10000) / 1000) + '0';
     *(flt + 4) = ((number % 1000) / 100) + '0';
     *(flt + 5) = ((number % 100) / 10) + '0';
     *(flt + 6) = ',';
     *(flt + 7) = (number % 10) + '0';
     *(flt + 8) = ' ';
     *(flt + 9) = ' ';
     *(flt + 10) = ' ';
     *(flt + 11) = ' ';
     *(flt + 12) = ' ';
    }
 if (decimals == 2)
    {
     *(flt + 1) = ' ';
     *(flt + 2) = number / 10000 + '0';
     *(flt + 3) = ((number % 10000) / 1000) + '0';
     *(flt + 4) = ((number % 1000) / 100) + '0';
     *(flt + 5) = ((number % 100) / 10) + '0';;
     *(flt + 6) = ',';
     *(flt + 7) = (number % 10) + '0';
     *(flt + 8) = '0';
     *(flt + 9) = ' ';
     *(flt + 10) = ' ';
     *(flt + 11) = ' ';
     *(flt + 12) = ' ';
    }
 if (decimals > 2)
    {
     *(flt + 1) = ' ';
     *(flt + 2) = number / 10000 + '0';
     *(flt + 3) = ((number % 10000) / 1000) + '0';
     *(flt + 4) = ((number % 1000) / 100) + '0';
     *(flt + 5) = ((number % 100) / 10) + '0';;
     *(flt + 6) = ',';
     *(flt + 7) = (number % 10) + '0';
     *(flt + 8) = '0';
     *(flt + 9) = '0';
     *(flt + 10) = ' ';
     *(flt + 11) = ' ';
     *(flt + 12) = ' ';
    }

 //i = 0;                                                         // OK.
 i = 2;                                                           // OK.

 while(flt[i] != ',')
 {
  if (flt[i] == '0')
     {
      portc.b3 = 1;                                               // Green LED ON.
      flt[i] = ' ';
      if (flt[i - 2] == '-')
         {
          flt[i - 2] = ' ';
          flt[i - 1] = '-';
         }
      if (flt[i + 1] == ',')
         {
          portc.b2 = 1;                                           // Yellow LED ON.
          flt[i] = '0';
         }
     }
     else
        {
         break;
        }
  i++;
 }
}

void LCD_Init_v4 (void)
{
 Delay_ms(15);                                                    // LCD power ON initialization time >= 15 mS.
 LCD_Write(CMD, 0x30);                                            // 4 datas bits > Initialization of LCD with nibble method (4 datas bits).
 LCD_Write(CMD, 0x02);                                            // 4 datas bits > Initialization of LCD with nibble method (4 datas bits).
 LCD_Write(CMD, 0x28);                                            // 4 datas bits > 2 lines display, 5 × 8 dot character font.
 LCD_Write(CMD, 0x0C);                                            // 4 datas bits > Display ON. Cursor OFF.
 LCD_Write(CMD, 0x06);                                            // 4 datas bits > Auto increment cursor.
 LCD_Write(CMD, 0x01);                                            // 4 datas bits > Clear display.
 Delay_ms(1);                                                     // Ajustable ... (indispensable, sinon affichage erratique à la mise sous tension)
}

/******************************************************************************/

void main()
{
 PORTB = 0;                                                 // Initialisation du PORT B à 0.
 PORTC = 0;                                                 // Initialisation du PORT C à 0.
 PORTD = 0;                                                 // Initialisation du PORT D à 0.

 ANSEL = 0b00001000;                                        // b3 = 1 (ANS3) : sets pin 5 (AN3) as analog input.
 ANSELH = 0b00000000;

 TRISB = 0b00000000;                                        // PORT B : b0 à b7 configurés en sortie.
 TRISC = 0b00000000;                                        // PORT C : b0 à b7 configurés en sortie.
 TRISD = 0b00000000;                                        // PORT D : b0 à b7 configurés en sortie.

 C1ON_bit = 0;                                              // CMC1CON register > b7 > C1ON bit = 0 > Disable comparator 1.
 C2ON_bit = 0;                                              // CMC2CON register > b7 > C1ON bit = 0 > Disable comparator 2.

 SCS_bit = 0;                                               // OSCCON register > b0 > SCS bit = 0 > External oscillator (quartz) is used as a clock source.

 LCD_Init_v4 (void);

 pointeur_de_char = &texte[0];                              // pointeur_de_char pointe sur le premier élément du tableau "texte", soit texte[0].
                                                            // Autrement dit, pointeur_de_char contient l'adresse (&) de texte[0].
 do
  {
   float adc;
   float volt, temp;

   char txt[13];

   ADC_initialization();

   TRISD = 0x00;

    while(1)
    {
     adc = (ADC_read(3));                                   // Reads analog values.
     volt = adc * 4.88281;                                  // Converts it into the voltage.
     temp = volt / 10.0;                                    // Gets the temperature values.
     temp = temp - 273;                                     // Converts Farenheit to Celcius.

     StrConstRamCpy(pointeur_de_char, "Temperature ...  "); // OK.
     LCD_Write_String_At(0, 0, pointeur_de_char);           // OK. Voir dans void LCD_Write_String(char *msg), le while(*(msg + k) > 0).

     //print_float_v11(&txt[0], temp, 0);
     //print_float_v11(&txt[0], temp, 1);
     print_float_v11(&txt[0], temp, 2);
     //print_float_v11(&txt[0], temp, 3);

     LCD_Write_String_At(1, 0, &txt[0]);                    // Write txt in 2nd row, starting at 1st digit.
     LCD_Write_String_At(1, 12, " °C");                     // Write " °C" in 2nd row, starting at 13th digit.

     delay_ms(3000);
    }
  }
 while(1);
}
 
Depending on the compiler, an "int" may only be eight bits, while they are being used as 16b bit in your program.

It's safest to use explicit variable sizes. If you use the stdint.h definitions, like
#include <stdint.h>
that should allow you to use such as int8_t, int16_t, uint8_t, uint16_t etc.
 
When I wrote that print float function... I used it for fixed point arithmetic..

PrintFloat() wont accept a floating point

If you want to use this method.
print_float_v11(&txt[0], (int) temp *100 , 2); for two decimal places and.
print_float_v11(&txt[0], (int) temp *1000 , 3 ); for three decimal places

But to use three decimals change the print float to this " 3 digits 3 decimal places"
C:
if (decimals == 3)
    {
     *(flt + 1) = ' ';
     *(flt + 2) = number / 100000 + '0';
     *(flt + 3) = ((number % 100000) / 10000) + '0';
     *(flt + 4) = ((number % 10000) / 1000) + '0';
     *(flt + 5) = '.';
     *(flt + 6) = ((number % 1000) / 100) + '0';
     *(flt +7) =  ((number % 100) / 10) + '0';
     *(flt +8) = (number % 10) + '0';
     *(flt + 9) = 0;    //terminator...
     *(flt + 10) =  0;
     *(flt + 11) =  0;
     *(flt + 12) =  0;
    }
 
Merci !
But to be honest, I founded a routine that paulfjugo dropped in a former discussion. Who fits and runs. But I do not understand almost nothing …
 
Last edited:
void float2aascii (float x, unsigned char *str, char precision)
{
// Converts ra floating point number to an ASCII string.
// Version limited at 5 decimals maximum.
// x is stored into str, which should be at least 30 chars long.

int ie, i, k, ndig;
double y;

if (precision >= 5)
{
precision = 5;
}
else
{
precision++;
}

ndig = precision;

ie = 0;

// If x is negative, write minus and reverse.

if (x < 0.00000)
{
*str++ = '-';
x = -x;
}

// Put x in range 1 <= x < 10.

if (x > 0.000000)
{
while (x < 1.000000)
{
x *= 10.000; // A la place de =*
ie--;
}
}

while (x >= 10.0000)
{
x = x / 10.0000;
ie++;
}

// In f format, number of digits is related to size.

ndig += ie; // A la place de =+

// round x is between 1 and 10 and ndig will be printed to right of decimal point …

for (y = i = 1; i < ndig; i ++)
{
y = y / 10.0000;
}

x += y / 2.0000; //

if (x >= 10.0000)
{
x = 1.0000;
ie++;
}

if (ie < 0)
{
*str++ = '0';
*str++ = '.';
if (ndig < 0)
{
ie = ie - ndig;
}
for (i = -1; i > ie; i --)
{
*str++ = '0';
}
}

for (i = 0; i < ndig; i ++)
{
k = x;
*str++ = k + '0';
if (i == ie)
{
*str++ = '.';
}
x -= (y = k);
x *= 10.0000;
}

*str = '\0';
}
 
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top