Jednoduchá kuchynská váha do 1kg
Zo stránky SensorWiki
Záverečný projekt predmetu MIPS / LS2025 - Martin Biacovský
Zadanie
Cieľom tohto projektu je zostrojiť jednoduchú váhu s kapacitou do 1kg za použitia mikrokontroléra (ACROB), HX711 (ADC) a snímača zaťaženia (load cell).



Literatúra:
Analýza a opis riešenia
Nakoľko sa tento predmet nezameriava na Arduino, nie je povolené na projekt používať vývojové prostredie Arduino IDE, ani knižnice tohto prostredia.
Riešenie sa dialo v prostredí AVR studio a využívali sa knižnice z cvičení predmetu (konkrétne knižnica lcd).
Váha je zariadenie, ktoré meria hmotnosť predmetu. Pre naše účely bol týmto predmetom set závaží (200g, 100g, 50g, 20g).

Úlohou mikrokontroléra bolo spracovávať všetky vstupy a na základe algoritmu produkovať výstupy.
Najvýraznejším výstupom je LCD displej EA-DOGM-163

Tento displej je v prevedení shield (modul na priame zapojenie na dosku bez nutnosti iného zapájania) a disponuje aj 1x LED dióda a 3x tlačidlo.
Pre naše účely nám postačí len displej (pozor na správne zapojenie jumper konektorov! )
Na meranie hmotnosti sme použili kombo HX711 a snímača zaťaženia.
Táto konfigurácia znamená, že sa nám stačí zaoberať len s HX711. Pre snímač zaťaženia je dôležitá len jeho maximálna nosnosť 1kg a smer záťaže.
HX711 je 24-bitový analógovo-digitálny prevodník priamo určený pre takéto váhy. Na jeho doske nám vychádzajú 4 pripojenia:
GND - ground, teda nulový potenciál
DT - sériový výstup
SCK - signál pre ADC, kedy má vysielať hodnoty
VCC - napájanie pre HX711 a to 5V
Kým VCC a GND majú určené miesto na doske mikrokontroléra, DT a SCK sa pripájajú na piny GPIO (General Purpose Input Output).
My sme použili pin D3 pre DT a D5 pre SCK.
Výsledok vyzerá následne:



Algoritmus a program
//libraries
#include <avr/io.h>
#include "lcd.h"
#include <util/delay.h>
#define F_CPU 16000000UL
#define DT PD3
#define SCK PD5
//delay function
void delay(int delay)
{
for (int i=1; i<=delay; i++)
_delay_ms(1);
}
//function for readout
long hx711_read() {
long count = 0;
//wait for DT to go LOW
while (PIND & (1 << DT));
//read 24 bits from HX711
for (uint8_t i = 0; i < 24; i++)
{
PORTD |= (1 << SCK);
_delay_us(1);
count = count << 1;
if (PIND & (1 << DT))
{
count++;
}
PORTD &= ~(1 << SCK);
_delay_us(1);
}
//25th pulse to set gain = 128 (channel A)
PORTD |= (1 << SCK);
_delay_us(1);
PORTD &= ~(1 << SCK);
_delay_us(1);
//convert 24-bit to signed long
if (count & 0x800000)
{
count |= ~0xFFFFFF;
}
return count;
}
int main(void)
{
//set input or output (input is default)
DDRD &= ~(1 << DT);
DDRD |= (1 << SCK);
//make SCK LOW
PORTD &= ~(1 << SCK);
//initialize LCD (with backlight and no coursor)
lcd_init();
lcd_bklt(1);
lcd_command(0x0C);
//get zeroed out value
long weight;
long init = hx711_read();
//infinite cycle
while(1)
{
//offset = (value with mass - value without mass) / mass of known object in grams
//(305320 - 220150) / 100 = 851,7 => 851
//weight = ((current value - zeroed value) / offset) + manual adjustment
//mass shown was -3g => add 3 as manual adjustment
weight = ((hx711_read() - init) / 851) + 3;
//LCD writeout
lcd_puts("Hmotnost:");
lcd_setCursor(1,0);
char text[]= {" "};
sprintf(text,"%ld",weight);
lcd_puts(text);
lcd_puts("g");
_delay_ms(500);
lcd_command(0x01);
_delay_ms(2);
}
}
//end of program
#ifndef LCD_H_
#define LCD_H_
#include <stdlib.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#define CHARACTER_BUFFER_BASE_ADDRESS 0x80
#define CHARACTERS_PER_ROW 16
#define DISPLAY_RS_PORT PORTD
#define DISPLAY_RS_DDR DDRD
#define DISPLAY_RS_PIN 2
#define DISPLAY_CSB_PORT PORTD
#define DISPLAY_CSB_DDR DDRD
#define DISPLAY_CSB_PIN 4
#define DISPLAY_BKLT_PORT PORTD
#define DISPLAY_BKLT_DDR DDRD
#define DISPLAY_BKLT_PIN 6
#define DISPLAY_RS_low (DISPLAY_RS_PORT &= ~(1<<DISPLAY_RS_PIN))
#define DISPLAY_RS_high (DISPLAY_RS_PORT |= (1<<DISPLAY_RS_PIN))
#define DISPLAY_CSB_low (DISPLAY_CSB_PORT &= ~(1<<DISPLAY_CSB_PIN))
#define DISPLAY_CSB_high (DISPLAY_CSB_PORT |= (1<<DISPLAY_CSB_PIN))
#define DISPLAY_BKLT_low (DISPLAY_BKLT_PORT &= ~(1<<DISPLAY_BKLT_PIN))
#define DISPLAY_BKLT_high (DISPLAY_BKLT_PORT |= (1<<DISPLAY_BKLT_PIN))
//instructions (see the ST7036 instruction set for further information)
#define INSTRUCTION_CLEAR_DISPLAY 0b00000001
#define INSTRUCTION_FUNCTION_SET_INIT_0 0b00110011
#define INSTRUCTION_FUNCTION_SET_INIT_1 0b00110010
#define INSTRUCTION_FUNCTION_SET_INIT_2 0b00101001
#define INSTRUCTION_INSTRUCTION_SET_0 0b00101000
#define INSTRUCTION_INSTRUCTION_SET_1 0b00101001
#define INSTRUCTION_BIAS_SET 0b00010101
#define INSTRUCTION_POWER_CONTROL 0b01010011
#define INSTRUCTION_FOLLOWER_CONTROL 0b01101100
#define INSTRUCTION_CONTRAST_SET 0b01111111
#define INSTRUCTION_DISPLAY_ON 0b00001100
#define INSTRUCTION_ENTRY_MODE 0b00000110
void lcd_init(void);
void lcd_write( char data );
void lcd_data( char data );
void lcd_command( char instruction );
void lcd_putc( char znak );
void lcd_puts( char *string );
void lcd_clear( void );
void lcd_clearline( unsigned char zeile );
void lcd_setCursor(char row, char col);
void lcd_bklt( char OnOff);
void def_znak(unsigned char *ZnakArray, unsigned char Address);
#endif /* LCD_H_ */
#include "lcd.h"
/* Primitivne funkcie, nepredpoklada sa ich vyuzitie uzivatelom */
/* Funkcia zapise jeden bajt po SPI zbernici do zariadenia */
void lcd_write( char data )
{
signed char index = 8;
char c_data;
DISPLAY_CSB_PORT &= ~(1<<DISPLAY_CSB_PIN); // Chip-Select do log.0
c_data = data;
do
{
_delay_us(6);
if ( c_data & 0x80 ) // najyssi bit zamaskujeme
PORTB |= (1<<3); // a posleme ho na zbernicu
else
PORTB &= ~(1<<3); // cez vodic MOSI (alebo len SI)
_delay_us(5); // a vygenerujeme jeden hodinovy pulz
PORTB &= ~(1<<5); // na vyvod CLK
_delay_us(6);
PORTB |= (1<<5);
c_data = c_data << 1; // na najvyssie miesto posunieme dalsi bit
index--;
} while (index > 0); // a toto spravime dokola 8x
// a napokon zdvihneme /CS do log.1
_delay_ms( 2 );
DISPLAY_CSB_PORT |= (1<<DISPLAY_CSB_PIN);
}
/* Funkcia zapise jeden byte do riadiaceho (Control) registra */
void lcd_command( char instruction )
{
DISPLAY_RS_PORT &= ~(1<<DISPLAY_RS_PIN);
_delay_us( 1 );
lcd_write( instruction );
}
/* Funkcia zapise jeden byte do datoveho (Data) registra */
void lcd_data( char data )
{
DISPLAY_RS_PORT |= 1<<DISPLAY_RS_PIN;
_delay_us( 7 );
lcd_write( data );
}
/* Uzivatelske funkcie, ktore ma pouzivat uzivatel */
/* Inicializacia displeja podla datasheetu pre radic ST7036 */
void lcd_init(void)
{
DDRB |= (1<<PB3) | (1<<PB5); // MOSI + SCK as OUTPUTs
DISPLAY_RS_DDR |= 1<<DISPLAY_RS_PIN; // All signals as OUTPUT
DISPLAY_CSB_DDR |= 1<<DISPLAY_CSB_PIN;
DISPLAY_BKLT_DDR |= 1<<DISPLAY_BKLT_PIN;
PORTB |= (1<<PB5); // set_bit( ST7036_CLK );
DISPLAY_CSB_PORT |= (1<<DISPLAY_CSB_PIN); // set_bit( ST7036_CSB );
DISPLAY_RS_PORT &= ~(1<<DISPLAY_RS_PIN); // set_bit( ST7036_CSB );
_delay_ms(50); // pockame viac ako 40ms na ustalenie napajacieho napatia
lcd_command( 0x39 ); // Function set; 8-bit Datenlänge, 2 Zeilen, Instruction table 1
_delay_us(50); // mehr als 26,3µs warten
lcd_command( 0x1d ); // Bias Set; BS 1/5; 3 zeiliges Display /1d
_delay_us(50); // mehr als 26,3µs warten
lcd_command( 0x50 ); // Booster aus; Kontrast C5, C4 setzen /50
_delay_us(50); // mehr als 26,3µs warten
lcd_command( 0x6c ); // Spannungsfolger und Verstärkung setzen /6c
_delay_ms( 500 ); // mehr als 200ms warten !!!
lcd_command( 0x7c ); // Kontrast C3, C2, C1 setzen /7c
_delay_us(50); // mehr als 26,3µs warten
lcd_command( 0x38 ); // Display EIN, Cursor EIN, Cursor BLINKEN /0f
_delay_us(50); // mehr als 26,3µs warten
lcd_command( 0x0f ); // Display EIN, Cursor EIN, Cursor BLINKEN /0f
_delay_us(50); // mehr als 26,3µs warten
lcd_command( 0x01 ); // Display löschen, Cursor Home
_delay_ms(400); //
lcd_command( 0x06 ); // Cursor Auto-Increment
_delay_us(50); // mehr als 26,3µs warten
}
/* Funkcia zobrazi na pozicii kurzoru jeden znak */
/* Alias funkcia z dovodov kompatibility */
void lcd_putc( char znak )
{
lcd_data(znak);
}
/* Funkcia zobrazi na displeji nejaky text*/
void lcd_puts(char *string)
{
while(*string!='\0')
{
lcd_data(*string);
string++;
}
}
/* Funkcia nastavi kurzor na poziciu riadok, stlpec */
/* a text sa samozrejme vypise od kurzora dalej. */
void lcd_setCursor(char row, char col)
{
unsigned char address;
if (row == 0)
address = 0x00 + col;
else if (row == 1)
address = 0x10 + col;
else if (row == 2)
address = 0x20 + col;
else if (row == 3)
address = 0x30 + col;
else
return; // Nepodporovaný riadok
lcd_command(0x80 | address); // Nastav DDRAM adresu
}
void lcd_clearline( unsigned char riadok )
{
unsigned char index;
lcd_setCursor( riadok, 0 );
for (index=1; index<20; index++) lcd_data( ' ' );
}
void lcd_clear( void )
{
lcd_clearline( 0 );
lcd_clearline( 1 );
lcd_clearline( 2 );
}
/* Funkcia zapne alebo vypne podsvietenie displeja */
void lcd_bklt( char OnOff)
{
if (OnOff)
DISPLAY_BKLT_high;
else
DISPLAY_BKLT_low;
}
/* Funkcia ulozi uzivatelom definovany znak do CG RAM na Address */
void def_znak(unsigned char *ZnakArray, unsigned char Address)
{
lcd_command(0x40|(Address<<3)); //nastavenie adresy znaku v CGRAM
for(unsigned char i = 0;i<8; i++) lcd_data( *(ZnakArray + i));
lcd_command(0x80); // zmena na DD RAM
}
Zdrojový kód: Biacovsky_1kg.zip
Algoritmus je to čo ovláda hardvér. Sú to naše inštrukcie pre zariadenia.
Ako prvé priložíme knižnice pre I/O mikrokontroléra, LCD a oneskorenia (ATmega 328P inak podporuje max. 16ms)
a zadefinujeme frekvenciu procesora, ako aj GPIO pre DT a SCK.
Overenie
Ako ste overili funkciu, napríklad... Na používanie našej aplikácie stačia dve tlačítka a postup používania je opísaný v sekcii popis riešenia. Na konci uvádzame fotku hotového zariadenia.

Video:
Kľúčové slová 'Category', ktoré sú na konci stránky nemeňte.