Operácie

Jednoduchý P regulátor: Rozdiel medzi revíziami

Zo stránky SensorWiki

StudentMIPS (diskusia | príspevky)
StudentMIPS (diskusia | príspevky)
Riadok 24: Riadok 24:


LCD modul EA-DOGM163 — rozširujúci modul pre dosku ACROB s displejom 3×16 znakov, oranžovým podsvietením, troma tlačidlami a jednou LED diódou.  
LCD modul EA-DOGM163 — rozširujúci modul pre dosku ACROB s displejom 3×16 znakov, oranžovým podsvietením, troma tlačidlami a jednou LED diódou.  
[[Obrázok:LCD_AppMod.jpg|right|400px]]
[[Obrázok:LCD_AppMod.jpg|400px|thumb|center|Vývojová doska ACROB.]]
 
Obrázok:LCD_AppMod.jpg
Potenciometer — externý otočný potenciometer umiestnený v krabičke so stupnicou 0–10. Slúži ako vstupné zariadenie pre nastavenie polohy textu. Prepojený s doskou páskowym káblom.
Potenciometer — externý otočný potenciometer umiestnený v krabičke so stupnicou 0–10. Slúži ako vstupné zariadenie pre nastavenie polohy textu. Prepojený s doskou páskowym káblom.



Verzia z 17:39, 6. jún 2026

Záverečný projekt predmetu MIPS / LS2026 - Dariia Dordiai


Zadanie

Cieľom projektu je zobraziť text na LCD displeji EA-DOGM163, ktorý je pripojený k vývojovej doske ACROB s mikrokontrolérom ATmega328P. Text je možné posúvať vľavo a vpravo po riadku displeja pomocou potenciometra. Poloha potenciometra je snímaná analógovo-digitálnym prevodníkom (ADC) a prepočítaná na pozíciu kurzora na displeji. Program umožňuje zobraziť ľubovoľný text.

Literatúra:


Analýza a opis riešenia

Systém sa skladá z dvoch hlavných častí: vývojovej dosky ACROB s mikrokontrolérom ATmega328P a rozširujúceho modulu s LCD displejom EA-DOGM163. Externe je pripojená ovládacia krabička s potenciometrom, ktorá je k doske prepojená páskowym káblom. Otočením potenciometra sa mení napätie na analógovom vstupe, ktoré mikrokontrolér prevedie na pozíciu textu a zobrazí ho na displeji.


Použité komponenty

Vývojová doska ACROB — doska postavená okolo mikrokontroléra ATmega328P.

Vývojová doska ACROB.

LCD modul EA-DOGM163 — rozširujúci modul pre dosku ACROB s displejom 3×16 znakov, oranžovým podsvietením, troma tlačidlami a jednou LED diódou.

Vývojová doska ACROB.

Obrázok:LCD_AppMod.jpg Potenciometer — externý otočný potenciometer umiestnený v krabičke so stupnicou 0–10. Slúži ako vstupné zariadenie pre nastavenie polohy textu. Prepojený s doskou páskowym káblom.

Algoritmus a program

Program je napísaný v jazyku C pre mikrokontrolér ATmega328P. Po inicializácii UART, LCD displeja a ADC vstupuje program do nekonečnej slučky. V každom cykle sa načíta hodnota z potenciometra. Aby sa eliminoval šum, meranie sa opakuje päťkrát s prestávkou 5 ms a výsledok sa spriemeruje. Ak je výsledná hodnota väčšia ako 1000, automaticky sa zaokrúhli na 1023 — táto mŕtva zóna zabraňuje chveniu textu pri krajnej polohe potenciometra. Hodnota ADC sa prepočíta na pozíciu kurzora v rozsahu 0–10 podľa vzorca: pos = ((1023 - val) × MAX_COL) / 1023 Text sa prepíše na displej len vtedy, keď sa pozícia zmení oproti predchádzajúcemu cyklu. Tým sa predchádza zbytočnému blikaniu displeja. Riadok sa najprv vymaže funkciou lcd_clearline(), následne sa nastaví kurzor na novú pozíciu a vypíše sa text pomocou lcd_puts().


#include <avr/io.h>

#include <avr/io.h>
#include <util/delay.h>
#include <stdio.h>
#include "uart.h"
#include "lcd.h"
#include "adc.h"

FILE mystdout = FDEV_SETUP_STREAM(uart_putc, NULL, _FDEV_SETUP_WRITE);

#define TEXT "DARIIA"   // zobrazovaný text
#define TEXT_LEN 6      // dĺžka textu
#define MAX_COL 10      // maximálna pozícia kurzora

int main(void)
{
    uart_init();        // inicializácia UART
    stdout = &mystdout;
    lcd_init();         // inicializácia LCD displeja
    lcd_bklt(1);        // zapnutie podsvietenia
    adc_init();         // inicializácia ADC
    int last_pos = -1;  // posledná pozícia textu

    while(1)
    {
        // načítanie hodnoty z potenciometra (priemer 5 meraní)
        unsigned int val = 0;
        for (uint8_t i = 0; i < 5; i++) {
            val += adc_read(4);  // čítanie ADC kanála 4
            _delay_ms(5);
        }
        val /= 5;  // výpočet priemeru

        // mŕtva zóna — stabilizácia pri krajnej polohe
        if (val > 1000) val = 1023;

        // prepočet hodnoty ADC na pozíciu kurzora (0 - 10)
        int pos = ((1023 - val) * MAX_COL) / 1023;

        // aktualizácia displeja len pri zmene pozície
        if (pos != last_pos)
        {
            lcd_clearline(1);   // vymazanie riadku
            _delay_ms(2);       // krátka pauza po vymazaní
            lcd_setCursor(1, pos); // nastavenie kurzora na novú pozíciu
            lcd_puts(TEXT);     // vypísanie textu
            last_pos = pos;     // uloženie aktuálnej pozície
        }
        _delay_ms(150);  // pauza pred ďalším cyklom
    }
    return 0;
}
#define BAUD       9600
#define F_CPU 16000000UL

#include <avr/io.h>
#include <util/setbaud.h>

void uart_init( void ) 
{
    UBRR0H = UBRRH_VALUE;
    UBRR0L = UBRRL_VALUE;

#if USE_2X
    UCSR0A |= _BV(U2X0);
#else
    UCSR0A &= ~(_BV(U2X0));
#endif

    UCSR0C = _BV(UCSZ01) | _BV(UCSZ00); /* 8-bit data */
    UCSR0B = _BV(RXEN0) | _BV(TXEN0);   /* Enable RX and TX */
}


void uart_putc(char c) 
{
   if (c == '\n') 
    {
       uart_putc('\r');
    }
   loop_until_bit_is_set(UCSR0A, UDRE0); /* Wait until data register empty. */
   UDR0 = c;
}


void uart_puts(const char *s)
{
 int i=0;
 for(i=0;s[i]!='\0';i++)
 uart_putc(s[i]);
}

char uart_getc(void) {
    loop_until_bit_is_set(UCSR0A, RXC0); /* Wait until data exists. */
    return UDR0;
}
 ifndef UART_H_
#define UART_H_


void uart_init( void );
     
void uart_putc( char c );
void uart_puts( const char *s );

char uart_getc( void );



#endif /* UART_H_ */
 #include "lcd.h"

/* Primitívne funkcie, nepredpokladá sa ich využitie užívateľom */

/* Funkcia zapíše 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 )         // najvyšší bit zamaskujeme
            PORTB |= (1<<3);         // a pošleme ho na zbernicu
        else
            PORTB &= ~(1<<3);        // cez vodič MOSI
        _delay_us(5);                // vygenerujeme jeden hodinový pulz
        PORTB &= ~(1<<5);            // na vývod CLK
        _delay_us(6);
        PORTB |= (1<<5);

        c_data = c_data << 1;        // na najvyššie miesto posunieme ďalší bit
        index--;

    } while (index > 0);             // opakujeme 8-krát

    _delay_ms( 2 );
    DISPLAY_CSB_PORT |= (1<<DISPLAY_CSB_PIN);   // zdvihneme /CS do log.1
}


/* Funkcia zapíše jeden bajt do riadiaceho (Control) registra */
void lcd_command( char instruction )
{
    DISPLAY_RS_PORT &= ~(1<<DISPLAY_RS_PIN);  // RS = 0 → príkaz
    _delay_us( 1 );
    lcd_write( instruction );
}


/* Funkcia zapíše jeden bajt do dátového (Data) registra */
void lcd_data( char data )
{
    DISPLAY_RS_PORT |= 1<<DISPLAY_RS_PIN;  // RS = 1 → dáta
    _delay_us( 7 );
    lcd_write( data );
}


/* Užívateľské funkcie */

/* Inicializácia displeja podľa datasheetu pre radič ST7036 */
void lcd_init(void)
{
    DDRB |= (1<<PB3) | (1<<PB5);      // MOSI + SCK ako výstupy
    DISPLAY_RS_DDR  |= 1<<DISPLAY_RS_PIN;    // RS ako výstup
    DISPLAY_CSB_DDR |= 1<<DISPLAY_CSB_PIN;   // CSB ako výstup
    DISPLAY_BKLT_DDR|= 1<<DISPLAY_BKLT_PIN;  // podsvietenie ako výstup

    PORTB |= (1<<PB5);                        // CLK do log.1
    DISPLAY_CSB_PORT |= (1<<DISPLAY_CSB_PIN); // CSB do log.1
    DISPLAY_RS_PORT  &= ~(1<<DISPLAY_RS_PIN); // RS do log.0

    _delay_ms(50);       // čakáme viac ako 40ms na ustálenie napájacieho napätia

    lcd_command( 0x39 ); // nastavenie funkcie: 8-bit, 2 riadky, tabuľka inštrukcií 1
    _delay_us(50);

    lcd_command( 0x1d ); // nastavenie Bias: BS 1/5, 3-riadkový displej
    _delay_us(50);

    lcd_command( 0x50 ); // Booster vypnutý, nastavenie kontrastu C5, C4
    _delay_us(50);

    lcd_command( 0x6c ); // nastavenie napäťového sledovača a zosilnenia
    _delay_ms( 500 );    // čakáme viac ako 200ms

    lcd_command( 0x7c ); // nastavenie kontrastu C3, C2, C1
    _delay_us(50);

    lcd_command( 0x38 ); // zapnutie displeja
    _delay_us(50);

    lcd_command( 0x0c ); // displej zapnutý, kurzor vypnutý
    _delay_us(50);

    lcd_command( 0x01 ); // vymazanie displeja, kurzor na začiatok
    _delay_ms(400);

    lcd_command( 0x06 ); // automatický posun kurzora doprava
    _delay_us(50);
}

/* Funkcia zobrazí na pozícii kurzora jeden znak */
void lcd_putc( char znak )
{
    lcd_data(znak);
}

/* Funkcia vypíše reťazec znakov na displej */
void lcd_puts(char *string)
{
    while (*string) {
        lcd_data(*string);  // posiela znaky jeden po druhom
        string++;
    }
}

/* Funkcia nastaví kurzor na pozíciu riadok, stĺpec */
void lcd_setCursor(char row, char col)
{
    // adresy riadkov pre 3-riadkový displej 16x3
    const char row_offsets[] = {0x00, 0x10, 0x20};
    lcd_command(0x80 | (row_offsets[row] + col));
}

/* Funkcia vymaže jeden riadok displeja */
void lcd_clearline( unsigned char riadok )
{
    unsigned char index;
    lcd_setCursor( riadok, 0 );
    for (index=1; index<20; index++) lcd_data( ' ' );  // vypĺňame medzerami
}

/* Funkcia vymaže celý displej */
void lcd_clear( void )
{
    lcd_clearline( 0 );  // vymazanie 1. riadku
    lcd_clearline( 1 );  // vymazanie 2. riadku
    lcd_clearline( 2 );  // vymazanie 3. riadku
}

/* Funkcia zapne alebo vypne podsvietenie displeja */
void lcd_bklt( char OnOff)
{
    if (OnOff)
        DISPLAY_BKLT_high;  // podsvietenie zapnuté
    else
        DISPLAY_BKLT_low;   // podsvietenie vypnuté
}

/* Funkcia uloží užívateľom definovaný znak do CG RAM na adresu */
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
}
 #include <avr/io.h>

void adc_init(void)
{
ADMUX = (1<<REFS0); // Vref bude Avcc
ADCSRA = (1<<ADEN) // AD Enable - "zapnutie" ADC
|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0); // nastavenie preddelica
}

unsigned int adc_read(char channel)
{
channel &= 0x0F;
ADMUX = (ADMUX & 0xF0)|channel;
ADCSRA |= (1<<ADSC); //inicial prevod
while(ADCSRA & (1<<ADSC))
{ /* wait */ } 
return (ADC);
}
 
#include <avr/io.h>

void adc_init(void)
{
ADMUX = (1<<REFS0); // Vref bude Avcc
ADCSRA = (1<<ADEN) // AD Enable - "zapnutie" ADC
|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0); // nastavenie preddelica
}

unsigned int adc_read(char channel)
{
channel &= 0x0F;
ADMUX = (ADMUX & 0xF0)|channel;
ADCSRA |= (1<<ADSC); //inicial prevod
while(ADCSRA & (1<<ADSC))
{ /* wait */ } 
return (ADC);
}

Pridajte sem aj zbalený kompletný projekt, napríklad takto (použite jednoznačné pomenovanie, nemôžeme mať na serveri 10x zdrojaky.zip:

Zdrojový kód: zdrojaky.zip

Overenie

Funkčnosť zariadenia bola overená manuálnym testovaním. Po zapnutí napájania sa na LCD displeji zobrazí text na predvolenej pozícii. Otočením potenciometra vľavo sa text posúva smerom doľava, otočením vpravo sa posúva doprava. Text sa aktualizuje plynule a bez blikania. Overenie prebehlo úspešne — text sa zobrazuje správne na všetkých pozíciách v rozsahu 0–10 a zariadenie reaguje na zmenu polohy potenciometra bez oneskorenia.

Aplikácia.

Video:

Čo by som urobila inak

Do budúcna by som implementovala plynulejší pohyb textu pomocou prerušení namiesto oneskorení _delay_ms(). Použitie časovača s prerušením by umožnilo presnejšie časovanie a mikrokontrolér by mohol počas čakania vykonávať iné úlohy.


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