Jump to content


Photo

HX711 si MikroC


  • Please log in to reply
9 replies to this topic

#1 OFFLINE   Stefan Nicolae

Stefan Nicolae

    Opozitia

  • Moderatori
  • 4,645 posts
  • Locatie:KN24KU

Posted 22 August 2016 - 10:00 AM

Are cineva un exemplu de program in MikroC pentru citirea ADC-ului HX711?
Am folosit biblioteca asta http://libstock.mikr...8/hx711-library dar citesc valori aiurea (seamana cu ce trebuie dar nu "rasare"). Unele functii din biblioteca nu sunt convins ca functioneaza corect dau nu am intepes cum se folosesc si PDF-ul cu "Ajutor" are greseli.

Cu ATmega328 (exemple arduino) am citit HX711 din prima, deci e exclusa o greseala hardware.

Nu ma intrebati de ce vreau sa folosc MikroC.

Multumesc.

Cu_reclama

Cu_reclama
  • Membri

#2 OFFLINE   cristi7521

cristi7521

    Membru

  • Membri
  • PipPip
  • 220 posts
  • Locatie:Pitesti

Posted 22 August 2016 - 11:12 AM

Poate te ajuta

https://www.ccsinfo....ic.php?p=195622



#3 OFFLINE   Stefan Nicolae

Stefan Nicolae

    Opozitia

  • Moderatori
  • 4,645 posts
  • Locatie:KN24KU

Posted 22 August 2016 - 08:00 PM

Spre sfarsitul saptamanii portez codul in MikroC si vedem ce iese.

Merci



#4 OFFLINE   mars01

mars01

    上昇

  • Membri
  • PipPip
  • 912 posts
  • Locatie:Bucuresti / Pitesti

Posted 23 August 2016 - 12:58 PM

Salut,

 

Astept si eu placutele cu HX711 asa ca am portat un driver Arduino in mikroC pentru PIC16F (am preluat de aici: https://codebender.c...HX711#HX711.cpp.

Nu l-am incercat, posteaza si tu un feedback aici daca ai timp sa il incerci.

 

Fisierul header HX711.h

#ifndef HX711_H
#define HX711_H

#define HIGH   1
#define LOW    0

#define OUTPUT 0
#define INPUT  1

typedef unsigned char uint8t;
typedef signed char   int8t;
typedef signed int    int16t;
typedef unsigned int  uint16t;
typedef unsigned long  uint32t;
typedef signed long  int32t;



extern sfr sbit SCK;
extern sfr sbit SCK_dir;
extern sfr sbit DOUT;
extern sfr sbit DOUT_dir;

// check if HX711 is ready
// from the datasheet: When output data is not ready for retrieval, digital output pin DOUT is high. Serial clock
// input PD_SCK should be low. When DOUT goes to low, it indicates data is ready for retrieval.
uint8t is_ready();

// set the gain factor; takes effect only after a call to read()
// channel A can be set for a 128 or 64 gain; channel B has a fixed 32 gain
// depending on the parameter, the channel is also set to either A or B
void set_gain(uint8t gain);

// waits for the chip to be ready and returns a reading
uint32t read();

// returns an average reading; times = how many times to read
uint32t read_average(uint8t times);

// returns (read_average() - OFFSET), that is the current value without the tare weight; times = how many readings to do
float get_value(uint8t times );

// returns get_value() divided by SCALE, that is the raw value divided by a value obtained via calibration
// times = how many readings to do
float get_units(uint8t times);

// set the OFFSET value for tare weight; times = how many times to read the tare value
void tare(uint8t times);

// set the SCALE value; this value is used to convert the raw data to "human readable" data (measure units)
void set_scale(float scale);

// set OFFSET, the value that's subtracted from the actual reading (tare weight)
void set_offset(int32t offset);

// puts the chip into power down mode
void power_down();

// wakes up the chip after power down mode
void power_up();


#endif /* HX711_H */

Fisierul sursa HX711.c

#include "HX711.h"

uint8t GAIN;
int32t OFFSET;
float SCALE;

uint8t is_ready() {
   return DOUT == LOW;
}

void set_gain(uint8t gain) {
   switch (gain) {
          case 128:                // channel A, gain factor 128
             GAIN = 1;
             break;
          case 64:                // channel A, gain factor 64
             GAIN = 3;
             break;
          case 32:                // channel B, gain factor 32
             GAIN = 2;
        break;
   }
   SCK = LOW;
   read();
}

uint32t read() {
   uint8t i,j;
   uint8t data_read[3];
   
   // wait for the chip to become ready
        while (is_ready() == 0);

        // pulse the clock pin 24 times to read the data
        for (j = 3; j--;) {
            for (i = 8; i--;) {
                SCK = HIGH;
                  if (DOUT) {
                    data_read[j] |= (1 << i);
                  }
                  else {
                    data_read[j] &= ~(1 << i);
                  }
                SCK = LOW;
        }
   }

  // set the channel and the gain factor for the next reading using the clock pin
  for (i = 0; i < GAIN; i++) {
    SCK = HIGH;
    SCK = LOW;
  }

   data_read[2] ^= 0x80;
   return ((uint32t) data_read[2] << 16) | ((uint32t) data_read[1] << 8) | (uint32t) data_read[0];
}

uint32t read_average(uint8t times) {
   uint32t sum = 0;
   uint8t i;
   for (i = 0; i < times; i++) {
      sum += read();
   }
   return sum / times;
}

float get_value(uint8t times) {
   return read_average(times) - OFFSET;
}

float get_units(uint8t times) {
   return get_value(times) / SCALE;
}

void tare(uint8t times) {
   float sum = read_average(times);
   set_offset(sum);
}

void set_scale(float scale) {
   SCALE = scale;
}

void set_offset(int32t offset) {
   OFFSET = offset;
}

void power_down() {
   SCK = LOW;
   SCK = HIGH;
}

void power_up() {
   SCK = LOW;
}

Atasat ai un proiect netestat cu un exemplu.

Attached Files


Edited by mars01, 23 August 2016 - 01:11 PM.


#5 OFFLINE   Stefan Nicolae

Stefan Nicolae

    Opozitia

  • Moderatori
  • 4,645 posts
  • Locatie:KN24KU

Posted 24 August 2016 - 08:08 PM

@mars01 Nu am respectivul microcontroller asa ca am recompilat pentru 16F887 cu LCD-ul la portul D.

La fiecare alimentare obtin cate o alta valoare si asa sta indiferent cat apas pe senzorii de greutate. Exemplu: 2513714176,  2066951522, 2228015744, 4133903104, 3053755136.

Daca am valoarea 3053755136 si scot comunicatia de la HX711 obtin 3062160640. Fara HX711 conectat la PIC se comporta asemanator.

 

Intre 16F887 si HX711 am 20cm de fir iar cele doua liniile SCK si DO nu sunt "trase" spre plus sau minus.

 

Legat de codul din CCS nu am incercat nimic.

 

De ce GAIN primeste valorile respective? Nu trebuia 25, 27, 26? :84

 

void set_gain(uint8t gain) {
   switch (gain) {
          case 128:                // channel A, gain factor 128
             GAIN = 1;
             break;
          case 64:                // channel A, gain factor 64
             GAIN = 3;
             break;
          case 32:                // channel B, gain factor 32
             GAIN = 2;
        break;
   }
   SCK = LOW;
   read();
}

 

...

 

// set the channel and the gain factor for the next reading using the clock pin
  for (i = 0; i < GAIN; i++) {
    SCK = HIGH;
    SCK = LOW;
  }



#6 OFFLINE   mars01

mars01

    上昇

  • Membri
  • PipPip
  • 912 posts
  • Locatie:Bucuresti / Pitesti

Posted 24 August 2016 - 11:13 PM

Daca citim datasheet-ul (lucru care din lene nu l-am facut decat dupa ce am portat codul - daca o faceam inainte de portare probabil ca nici nu imi mai bateam capul sa caut pe net dupa un cod ci il faceam din start eu) o sa vedem ca lucrul cu acest cip este extrem de usor. Nu are registri, totul se face din timing-uri si numar de biti.

 

Cu alte cuvinte.

 

Primim cei 24 de biti care contin informatia, pe 24 pulsuri ale clock-ului (clock-ul este trimis de ucontroller). Daca dupa acesti 24 de biti (primiti pe 24 pulsuri clock) mai trimitem un puls clock atunci urmatoarea citire (cei 24 de biti) va fi cu un gain 128 (automat se selecteaza canalul A).

Daca dupa cele 24 de pulsuri clock (pentru a "extrage cei 24 de biti de informatie - pe pinul SDO) trimitem inca doua pulsuri de clock atunci urmatoarea citire se va face cu un gain 32 (se selecteaza automat canalul B).

Daca trimitem insa 3 pulsuri de clock suplimentare atunci urmatoarea citire se va face cu un gain 64 si se selecteaza automat canalul A.

 

2z8uu8k.jpg

Secventa:

SCK = HIGH;
SCK = LOW;

Genereaza un puls de clock pe iesirea controlerului, un pin botezat in program SCK (in realitate este un pin fizic, putem sa alegem orice pin controller care se poate seta ca iesire digitala).

 

Daca pulsul de HIGH pe CLK dureaza mai mult de 60us atunci cipul intra in modul power down. Deci trebuie sa facem pulsurile de clock mai rapid de 60us ca sa nu punem cipul in power down. 

 

Ce mai trebuie stiut este ca informatia din cei 24 de biti este stocata in complement fata de 2 pentru a putea stoca valorile negative primite pe intrarea diferentiala.

 

 

In datasheet este un exemplu de driver minimal in C:


// atribuim anumiti pini unor variabile, cea de date, ADDO, si cea de clock, ADSK
sbit ADDO = P1^5;  // se asociaza un pin controler variabilei ADDO - al 5-lea din portul 1 ?
sbit ADSK = P0^0;  // se asociaza un pin controler variabilei ADSK - pinul 0 din portul 0 ?

// functia de citire ADC
unsigned long ReadCount(void){
  unsigned long Count; // variabila care va contine informatia citita din ADC
  unsigned char i;   // variabila contor pentru ciclul FOR
  ADDO=1;   // se initializeaza variabila care contine informatia DATA cu 1 pentru a putea incepe procesul de shiftare cu un 1 logic pe bitul 0
  ADSK=0; // clockul este LOW
  Count=0; // informatia citita din ADC este la acest moment, nula
  while(ADDO); // cat timp citim HIGH pe pinul asociat ADDO stai si asteapta LOW-ul care indica activitate ADC
  for (i=0;i<24;i++){ // executam de 24 de ori continutul buclei 
    ADSK=1;  // facem pinul clock HIGH si astfel incepem un puls de clock
    Count=Count<<1; // shiftam la stanga variabila Count care contine rezultatul 
    ADSK=0; // facem pinul clock LOW si astfel finalizam un puls clock - deci vor fi 24 de pulsuri clock
    if(ADDO) Count++; // daca pe pinul de date asociat variabilei ADDO avem un HIGH atunci fa bitul 0 din rezultat HIGH - si va fi shiftat data viitoare la stanga
  }
  ADSK=1; // pinul clock HIGH - incepem un puls de clock
  Count=Count^0x800000; // facem toggle la bitul cel mai semnificativ
  ADSK=0; // pinul clock LOW - finalizam pulsul de clock si cum este un singur puls in final avem 24 + 1 = 25 pulsuri de clock deci un GAIN de 128.
  return(Count); // returnam valoarea citita de ADC.
} 

Edited by mars01, 24 August 2016 - 11:53 PM.


#7 OFFLINE   mars01

mars01

    上昇

  • Membri
  • PipPip
  • 912 posts
  • Locatie:Bucuresti / Pitesti

Posted 25 August 2016 - 01:05 AM

Mda, asa se intampla cand doar portezi codul altuia. Functia read() este gresita, mai exact ofera datele in complementul lui doi. Practic ce iese din functie trebuie inversat bit cu bit si adunat cu 1.

Maine revin cu o biblioteca corectata.



#8 OFFLINE   mars01

mars01

    上昇

  • Membri
  • PipPip
  • 912 posts
  • Locatie:Bucuresti / Pitesti

Posted 27 August 2016 - 03:41 PM

Poti folosi urmatoarea functie corectata. Diferenta este ca in program va trebui sa testezi valoarea  bitului 31 din rezultatul functiei pentru a afla:

- daca este 1 atunci rezultatul este unul negativ, asigura-te ca faci bitul acesta LOW inainte de a folosi rezultatul

- daca este 0 atunci rezultatul este unul pozitiv si il folosesti ca atare

 

Ceva de genul (programul postat mai sus in arhiva trebuie modificat):

enum semn {
pozitiv;
negativ;
};

res = read();
if (res) {
   res ^= 0x80000000;
   semn = negativ;
   // codul tau
      ......   
}
else {
   semn = pozitiv;
   // codul tau
} 

Si functia modificata:

uint32t read() {
   uint8t i,j;
   uint8t data_read[3];
   uint32t result;

   // wait for the chip to become ready
        while (is_ready() == 0);

        // pulse the clock pin 24 times to read the data
        for (j = 3; j--;) {
            for (i = 8; i--;) {
                SCK = HIGH;
                  if (DOUT) {
                    data_read[j] |= (1 << i);
                  }
                  else {
                    data_read[j] &= ~(1 << i);
                  }
                SCK = LOW;
        }
   }

  // set the channel and the gain factor for the next reading using the clock pin
  for (i = 0; i < GAIN; i++) {
    SCK = HIGH;
    SCK = LOW;
  }

  // if the reading is a negative one, bit23 is HIGH then,
  if (data_read[2] & 0x80) {
    // invert each bit in the result
    data_read[2] = ~data_read[2];
    data_read[1] = ~data_read[1];
    data_read[0] = ~data_read[0];
    
    // put everything into a LONG variable and add 1
    result = (((uint32t) data_read[2] << 16) | ((uint32t) data_read[1] << 8) | ((uint32t) data_read[0])) + 1;
    
    /* on bit 31 place a HIGH value so when you get in the main program this bit with  HIGH  value,
    you'll know you have a negative value
    Make sure that after you tested this bit, if HIGH you make this bit LOW so it will not be interpreted as a uint32 negative value
    */
    result |= 0x80000000;
    return result;
  }
  else {
    return ((uint32t) data_read[2] << 16) | ((uint32t) data_read[1] << 8) | (uint32t) data_read[0];
  }
}

Edited by mars01, 27 August 2016 - 03:43 PM.


#9 OFFLINE   NicolaPr

NicolaPr

    Nou venit

  • Membri
  • Pip
  • 1 posts

Posted 09 January 2017 - 05:19 PM

Hello everybody. Thank very much Mars01 for ported code. I took it as a basis for mikroC, but during the week the library reworked under my requirements and i found a few mistakes. The most serious error is incorrectly read RAW data from the HX711. After a few days spent in the shuffle, the solution to this problem was very trivial. I added a delay of one microsecond between the pulses. I think Arduino working more slowly than PIC16 so in the original library is not necessary. The second mistake is the use of unsigned variables while the HX711 outputs 24 bits of signed data +/-. I also tried to reduce memory consumption by replacing 32-bit to 16-bit and remove floating point. So, here is my library and photos. Thank you all for your help! schematic.jpgIMG_4207_.JPGhttp://homus.com.ua/...11_NicolaPr.zip

http://homus.com.ua/...1/schematic.jpg

http://homus.com.ua/...1/IMG_4207_.JPG


Edited by NicolaPr, 09 January 2017 - 05:21 PM.


Cu_reclama

Cu_reclama
  • Membri

#10 OFFLINE   mars01

mars01

    上昇

  • Membri
  • PipPip
  • 912 posts
  • Locatie:Bucuresti / Pitesti

Posted 09 January 2017 - 06:46 PM

Good job Nicola,

 

At the moment when I've ported the code I had no HX711 IC (or board) available so I could not test it. In the meantime I did receive the boards ordered from eBay but ... I kind of drifted toward other projects and did not take the time to actually test the ported code.

So thank you for doing this work and posting it here so others can benefit from it.


Edited by mars01, 09 January 2017 - 06:49 PM.





0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users