Operácie

Hlukomer: Rozdiel medzi revíziami

Zo stránky SensorWiki

StudentMIPS (diskusia | príspevky)
Bez shrnutí editace
StudentMIPS (diskusia | príspevky)
Bez shrnutí editace
 
(34 medziľahlých úprav od rovnakého používateľa nie je zobrazených.)
Riadok 1: Riadok 1:
Záverečný projekt predmetu MIPS / LS2023 - '''Sabadash'''
Záverečný projekt predmetu MIPS / LS2023 - '''Mykyta Sabadash'''




Riadok 10: Riadok 10:


'''Literatúra:'''  
'''Literatúra:'''  
* [http://ap.urpi.fei.stuba.sk/sensorwiki/index.php/Acrob_technical_description Dokumentácia k doske Acrob]
* [https://senzor.robotika.sk/sensorwiki/index.php/Acrob_technical_description Dokumentácia k doske Acrob]
* [http://www.humanbenchmark.com/tests/reactiontime/index.php Vyskúšajte si zmerať reakciu on-line]
* [https://senzor.robotika.sk/sensorwiki/index.php/A/D_prevodn%C3%ADk A/D prevodnik (mikrofon)]
* [https://senzor.robotika.sk/sensorwiki/index.php/A/D_prevodn%C3%ADk A/D prevodnik (hlukomer)]
* [https://senzor.robotika.sk/sensorwiki/index.php/LCD_displej_s_radi%C4%8Dom_HD44780 LCD display]
* [https://senzor.robotika.sk/sensorwiki/index.php/LCD_displej_s_radi%C4%8Dom_HD44780 LCD display]


Riadok 27: Riadok 26:


[[Súbor:MIPS_ADC-Schema02.png|400px|thumb|center|Zapojovaci diagram pre A/D prevodnik.]]
[[Súbor:MIPS_ADC-Schema02.png|400px|thumb|center|Zapojovaci diagram pre A/D prevodnik.]]
[[Súbor:MSCHM.png|400px|thumb|center|Zapojovaci diagram pre Senzor zvuku.]]




=== Algoritmus a program ===
=== Algoritmus a program ===
Algoritmus programu je taky ze:


Sme inicializujeme A/D prevodnik, inicializujeme LCD displej a overujeme spravnost ho zapojenia, ked je vsetko v pohode tak displej sa zapne a sa zacnu ukazovat tam data ktore sme tam posleme (ked ze sme tam nieco posleme).
Sme inicializujeme A/D prevodnik, inicializujeme LCD displej a overujeme spravnost ho zapojenia, ked je vsetko v pohode tak displej sa zapne a sa zacnu ukazovat tam data ktore sme tam posleme (ked ze sme tam nieco posleme).
Riadok 38: Riadok 37:


Teraz, zadefinujeme riadok, "vypocitame" hodnotu ktoru chceme vidiet na displeji, a vypiseme to pomocou funkcie "sprintf()".
Teraz, zadefinujeme riadok, "vypocitame" hodnotu ktoru chceme vidiet na displeji, a vypiseme to pomocou funkcie "sprintf()".
A vypocitame nase Db
dB = 20 * log10(napatie_senzora / referencne_napatie)
Kde:
    * dB je vysledná hodnota v decibeloch
    * log10 je desiatkovy logaritmus
    * napatie_senzora je vstupne napätie senzora zvuku (value)
    * referencne_napatie je referencne napatie senzora (~3.3v)


A, pre lepsi vyhlad nasho hlukomeru pridame mu stupnicu hluku, ktoru vytvorime z ciernych stvorcekov a "pustych miest" (space key)
A, pre lepsi vyhlad nasho hlukomeru pridame mu stupnicu hluku, ktoru vytvorime z ciernych stvorcekov a "pustych miest" (space key)
Riadok 123: Riadok 133:


  lcd_puts("Db  -->  ");                        // vystup co sa vypise na displeji
  lcd_puts("Db  -->  ");                        // vystup co sa vypise na displeji
     measuredValue = adc_read(4);
     measuredValue = adc_read(4);                       // hodnota ktoru nam 'hovori' a/d prevodnik (mikrofon)
     printf("%u\n",measuredValue); // pre serial plot   
     printf("%u\n",measuredValue); // pre serial plot   
  _delay_ms(100);
  _delay_ms(100);
   
   
       lcd_command(0x0C);
       lcd_command(0x0C);                   // schovat kurzor
   
   
  char riadok[]= {"                "};
  char riadok[]= {"                "};
      int value = measuredValue - 350;   
        int value = measuredValue - 350;  // premenna, ktorej hodnotu by sme chceli zobrazit na LCD (350 podla toho ze
 
                                  // je to akoby nulova velicina nasho 'kvalitneho' hlukomera, ked ze sa vypisuju zle hodnoty na displej
// premenna, ktorej hodnotu by sme chceli zobrazit na LCD (350 podla toho ze je to akoby nulova velicina nasho 'kvalitneho' hlukomera, ked ze sa vypisuju zle hodnoty na displej tak musime menit prave tuto hodnotu, aby nulovy hluk zodpovedal 'nule' na displeji)
                                          // tak musime menit prave tuto hodnotu, aby nulovy hluk zodpovedal 'nule' na displeji)
 
        int db = log10(value/3.3);
  sprintf(riadok,"%d",value);  
          sprintf(riadok,"%d",db);
         
   
  int i = (value/12);
  int i = (value/12);
  int o = 16-i;                  // tu sme pocitame nase "stvorceky" pre stupnicu
  int o = 16-i;                  // tu sme pocitame nase "stvorceky" pre stupnicu
Riadok 141: Riadok 153:
  {
  {
  lcd_puts(riadok);            // dB
  lcd_puts(riadok);            // dB
  lcd_data(0x10);               
  lcd_data(0x10);              // put space key
   
   
  pocitadlo =0;
  pocitadlo =0;
Riadok 147: Riadok 159:
  pocitadlo++;    
  pocitadlo++;    
   
   
  lcd_command(0xC0);
  lcd_command(0xC0);           // prestup do noveho riadka
   
   
  for(i;i>0;i--)                 // stupnica
  for(i;i>0;i--)               // stupnica
  {
  {
  lcd_data(0xFF);
  lcd_data(0xFF);       // put ■
    }    
    }    
for(o;o>0;o--)
for(o;o>0;o--)
  {
  {
  lcd_data(0x10);
  lcd_data(0x10);       // put space key
    }  
    }  
  lcd_command(0x70);
  lcd_command(0x70);             // 0x10 (Cursor/display shift) + 0x20 (Function set) + 0x40 (Set CGRAM address)
   
   
       lcd_command(1<<LCD_HOME);  /* Set cursor to home position */
       lcd_command(1<<LCD_HOME);  /* Set cursor to home position */
Riadok 167: Riadok 179:
}
}
</source></tab>
</source></tab>
<tab name="filename.h"><source lang="c++" style="background: LightYellow;">
 
<tab name="adc.h"><source lang="c++" style="background: LightYellow;">
#include <avr/io.h>
#include <avr/io.h>


Riadok 174: Riadok 187:
unsigned int adc_read(char a_pin);
unsigned int adc_read(char a_pin);
</source></tab>
</source></tab>
<tab name="adc.c"><source lang="c++" style="background: LightYellow;">
#include <avr/io.h>
void adc_init (void){
ADMUX = (1<<REFS0);
ADCSRA = (1<<ADEN)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);
}
unsigned int adc_read(char a_pin){
a_pin &= 0x07;
ADMUX = (ADMUX & 0xF8)|a_pin;
ADCSRA |= (1<< ADSC);
while ( ADCSRA & (1<< ADSC));
return (ADC);
}
</source></tab>
<tab name="lcd_ch.h"><source lang="c++" style="background: LightYellow;">
/*
* lcd_ch.h
*
* Created: 3/10/2021 7:05:39 PM
*  Author: Admin
*/
#ifndef F_CPU
#define F_CPU 16000000UL /* Define CPU frequency here 16MHz */
#endif
#ifndef LCD_CH_H_
#define LCD_CH_H_
#include <avr/io.h>
#include <util/delay.h>
/* Nasledovne define rozhodne ktora cast programu sa prelozi:
  LCD Shiled (WR ma pripojene na GND) a
  priradenie pinov je dane napr.:
  "zamrzne"
  -    https://www.14core.com/wiring-the-lcd-16x2-keypad-shield-on-arduino/
  - https://wiki.dfrobot.com/LCD_KeyPad_Shield_For_Arduino_SKU__DFR0009
  resp.
  LCD, zapojenie vid. stranka MIPS
  "zamrzne"
  - http://senzor.robotika.sk/sensorwiki/index.php/LCD_displej_s_radi%C4%8Dom_HD44780
  , ktore ma pripojene aj pin WR
  t.j. moze sa testovat aj pin BF
*/
/* !!!!!!!
    #define _Shield_LCD
    !!!!!!! */
extern unsigned char kon_vyp;
#ifdef _Shield_LCD
#define LCD_CTRL_DDR DDRB
#define LCD_CTRL_PORT PORTB
#define LCD_DATA_DDR DDRD
#define LCD_DATA_PORT PORTD
// Riadiaca zbernica display-a
#define LCD_RS_pin 0
//#define LCD_RW_pin = 0
#define LCD_EN_pin 1
// Datova zbernica
#define LCD_D4_pin 4
#define LCD_D5_pin 5
#define LCD_D6_pin 6
#define LCD_D7_pin 7
#else
// LCD  klasik yapojenie vid. MIPS
#define LCD_CTRL_DDR DDRD
#define LCD_CTRL_PORT PORTD
#define LCD_DATA_DDR DDRB
#define LCD_DATA_PORT PORTB
#define LCD_DATA_PIN PINB
#define LCD_RS_pin 2
#define LCD_RW_pin 3
#define LCD_EN_pin 4
#define LCD_D4_pin 1
#define LCD_D5_pin 2
#define LCD_D6_pin 3
#define LCD_D7_pin 4
#endif
// Oneskorenie 6 SC
#define NOP() asm("nop")
#define LCD_DELAY NOP();NOP();NOP();NOP();NOP();NOP();
#ifdef _Shield_LCD
// formatovanie dat
#define PORT_DATA_WR_H(x) LCD_DATA_PORT &=0b00001111; LCD_DATA_PORT |= (x & 0xF0 )
#define PORT_DATA_WR_L(x) LCD_DATA_PORT &=0b00001111; LCD_DATA_PORT |= (x & 0x0F )<<4
#else
#define PORT_DATA_WR_H(x) LCD_DATA_PORT &=0b11100001; LCD_DATA_PORT |= (x & 0xF0 )>>3
#define PORT_DATA_WR_L(x) LCD_DATA_PORT &=0b11100001; LCD_DATA_PORT |= (x & 0x0F )<<1
#define PORT_DATA_RD_H ((LCD_DATA_PIN & ((1<<LCD_D4_pin)|(1<<LCD_D5_pin)|(1<<LCD_D6_pin)|(1<<LCD_D7_pin)))<<3)
#define PORT_DATA_RD_L ((LCD_DATA_PIN & ((1<<LCD_D4_pin)|(1<<LCD_D5_pin)|(1<<LCD_D6_pin)|(1<<LCD_D7_pin)))>>1)
#endif
/* Public functions for your use */
#ifndef _Shield_LCD
int8_t lcd_read_AC(void); 
void def_spec_znaky_AC(void);
#endif
void lcd_init(void);
void lcd_data(unsigned char );
void lcd_command(unsigned char );
// void lcd_puts(const char *s);  /* deklaracia funkcie  */
void ini_ports(void);
void En_imp(void);
void wr_data (unsigned char );
unsigned char busy_flag(void);
void zob_text(char *);
void def_Clear_spec_znaky(void);
void def_znak(unsigned char *,unsigned char );
void def_spec_znaky(void);
#endif /* LCD_CH_H_ */
</source></tab>
<tab name="lcd_ch.c"><source lang="c++" style="background: LightYellow;">
#include "lcd_ch.h"
unsigned char Znak_OO[8]= {0x00,0x00,0x00,0x00,0x00,0x00,0x15,0};// Prazdny znak dole su tri bodky
unsigned char Znak_SC[8]= {0x1A,0x1D,0x04,0x04,0x04,0x05,0x02,0};// st.C
unsigned char Znak_SM[8]= {0x0A,0x04,0x0E,0x10,0x0E,0x01,0x1E,0};//  s + mekcen
unsigned char Znak_CM[8]= {0x0A,0x04,0x0E,0x10,0x10,0x11,0x0C,0};//  c + mekcen
// Vynulovanie CGRAM
void def_Clear_spec_znaky(void){
// zapis specialneho znaku do CGRAM  na poziciu 0, a 7
for(char i = 0; i < 8; i++) def_znak( Znak_OO,i);
lcd_command(0x80);
}
void def_spec_znaky(void){
// zapis vlastnych znakkov do CGRAM  na poziciu 4, a 0 (8)
def_znak( Znak_SC,4);// stupen Celzia
def_znak( Znak_SM,0);// s + makcen
// obnovenie obsahu AC. AC nastvime na 1. riadok, 1. znak
lcd_command(0x80);
}
#ifndef _Shield_LCD
void def_spec_znaky_AC(void){
// zapis specialneho znaku do CGRAM  na poziciu 7
unsigned char obsah_AC = 0;
obsah_AC = lcd_read_AC(); //   
// kon_vyp = obsah_AC;
// sem dame nove vlastne znaky
def_znak( Znak_CM,7);// c + makcen
// obnovenie obsahu AC. AC
lcd_command(0x80|obsah_AC);
}
#endif
void ini_ports(void){
/* inicializacia portov - vstupy / vystupy
oba typy displaja */
// nasledovna # riadky su spolocne pre oba typy LCD
LCD_CTRL_DDR |= (1<<LCD_EN_pin); //  (Enable)    output 
LCD_CTRL_DDR |= (1<<LCD_RS_pin); //  (RS)        output 
LCD_DATA_DDR |= (1<<LCD_D4_pin)|(1<<LCD_D5_pin)|(1<<LCD_D6_pin)|(1<<LCD_D7_pin); // Datove piny ako Output (Data pre display)
#ifndef _Shield_LCD
// klasicky LCD vyuziva aj RW pin
LCD_CTRL_DDR |= (1<<LCD_RW_pin); // (RW)        output 
#endif
}
void En_imp(void) {
LCD_CTRL_PORT |= (1<<LCD_EN_pin); // -> "log.1"
LCD_DELAY; // cca 400 ns
LCD_CTRL_PORT &= ~(1<<LCD_EN_pin); // -> "log.0"  spolu cca 500 ns
}
void lcd_init(void)
{  // 4 bitove pripojenie display-a
ini_ports(); // inicializacia porov
_delay_ms(15);
// 1. -------------------
LCD_CTRL_PORT &= ~(1 << LCD_RS_pin); // set RS  = to "log. 0"  Instruction Register (IR)
#ifndef _Shield_LCD
LCD_CTRL_PORT &= ~(1 << LCD_RW_pin) ; // set R/W = to "log. 0" - Write
#endif
// RS R/W  DB7 DB6 DB5 DB4  DB3 DB2 DB1 DB0
// 0  0    0  0  1  1    x  x  x  x    = 0x30
PORT_DATA_WR_H(0x30); // 8-bitove pripojenie
En_imp();
// 2. -------------------
    // zopakujem  8-bitove pripojenie
_delay_ms(5); En_imp();
// 3. -------------------
// zopakujem  8-bitove pripojenie
    _delay_ms(1); En_imp(); // -  stacilo by delay 0.1ms, momentalne najkratsie nastavitelne je 1ms
// 4. -------------------
// zmenim na 4-bitove pripojenie
// RS R/W    DB7 DB6 DB5 DB4    DB3 DB2 DB1 DB0
// 0  0      0  0  1  0      x  x  x  x    = 0x20
PORT_DATA_WR_H(0x20); // 4-bitove pripojenie
_delay_ms(1); En_imp(); // -  stacilo by delay 0.04ms
// -------------------
// LCD function set      mod: DL = 0 - 4-bitove data, N = 1 - 2 riadky, F = 0 - 5x7 dots
// RS R/W    DB7 DB6 DB5 DB4    DB3 DB2 DB1 DB0
// 0  0      0  0  1  DL    N  F  x  x    = 0x28
// Mozeme nasledujuci prikaz pre LCD vynechat?
_delay_ms(2);
lcd_command(0x28);
// RS R/W    DB7 DB6 DB5 DB4    DB3 DB2 DB1 DB0
// 0  0      0  0  0  0     0  0  0  1    = 0x01 , Display clear
// Mozeme nasledujuci prikaz pre LCD vynechat?
_delay_ms(2);
lcd_command(0x01);
// RS R/W    DB7 DB6 DB5 DB4    DB3 DB2 DB1 DB0
// 0  0      0  0  0  0     0  1  I/D S      = 0x06, I/D = 1 - inkrement
// Mozeme nasledujuci prikaz pre LCD vynechat?
_delay_ms(2);
lcd_command(0x02);
// RS R/W    DB7 DB6 DB5 DB4    DB3 DB2 DB1 DB0
// 0  0      0  0  0  0     1  D  C  B      = 0x0E, D = C = 1 - Display a kurzor zapnute
// Mozeme nasledujuci prikaz pre LCD vynechat?
_delay_ms(2);
lcd_command(0x0E); // B = 0 - blikanie vypnute
}
//zapis data do Data Register
void lcd_data(unsigned char data){
while(busy_flag() & 0x80);
//while(rd_BF()); // test BF sa da realizovat pre klasicky LCD, nie Shield
LCD_CTRL_PORT |= (1<<LCD_RS_pin); //    (RS = High) 
#ifndef _Shield_LCD
LCD_CTRL_PORT &= ~(1<<LCD_RW_pin); //    (RW = Low, write)
#endif
wr_data (data);
}
// zapis commandu do Instruction Register
void lcd_command(unsigned char command){
while(busy_flag() & 0x80);
//while(rd_BF()); // test BF sa da realizovat pre klasicky LCD, nie Shield
LCD_CTRL_PORT &= ~(1<<LCD_RS_pin); //  (RS = Low) 
#ifndef _Shield_LCD
LCD_CTRL_PORT &= ~(1<<LCD_RW_pin); //  (RW = Low, write)
#endif
wr_data (command);
}
void wr_data(unsigned char data) {
PORT_DATA_WR_H(data); // data High nibble
En_imp();
PORT_DATA_WR_L(data); // data Low nibble
En_imp();
}
#ifdef _Shield_LCD
// namiesto testu BF "pockam".
// LCD typu Shield ma WR pripojene na GND
unsigned char busy_flag(void){
_delay_ms(2);
return(0);
}
#else
// namiesto testu BF "pockam". Vacsine prikazov tento cas vyhovuje
/*int busy_flag(void){
_delay_ms(2);
return(0);
}
*/
// klasicke pripojenie LCD umozni aj test BF
unsigned char busy_flag(void){ //  rd_BF 
unsigned char pom = 0;
LCD_CTRL_PORT &= ~(1<<LCD_RS_pin); //  (RS = Low) 
LCD_CTRL_PORT |= (1<<LCD_RW_pin); //    (RW = High, read)
    // port B, datove bity budu teraz input
LCD_DATA_DDR &= ~((1<<LCD_D4_pin)|(1<<LCD_D5_pin)|(1<<LCD_D6_pin)|(1<<LCD_D7_pin)); //  Datove piny nastavime  na input, (Data pre disp)
// Spravne by sa malo vycitavaj pred dobeznou hranou EN impulzu. Je tam urcity presah. Mozeme vycitat aj po dobeznej hrane.
En_imp();
pom = PORT_DATA_RD_H; // vycitam High nibble AC
En_imp();
pom |= PORT_DATA_RD_L; // vycitam Low nibble AC
// datove bity zase output
LCD_DATA_DDR |= (1<<LCD_D4_pin)|(1<<LCD_D5_pin)|(1<<LCD_D6_pin)|(1<<LCD_D7_pin); // Datove piny nastavime na output (Data pre disp)
//if(pom & 0x80) return 1; // display je busy
//else
return pom; // display je not busy
}
#endif
void zob_text(char *s){
register unsigned char c;
while((c = *s++)) lcd_data(c); // retazec konci "nulou"
}
void def_znak(unsigned char *ZnakArray,unsigned char kam) {
lcd_command(0x40|(kam << 3)); //nastavenie adresy znaku v CGRAM
for(unsigned char i = 0; i < 8; i++) lcd_data( *(ZnakArray + i));
}
/* ******************************************************** */
/* vypis retazca na poziciu, resp. podla nasledovnych */
/* formatovacich prikazov                                      */
/*  : \r - prechod na novy riadok                              */
/*  : \f - prechod na zaciatok displeja                        */
/* ********************************************************    */
void lcd_puts(  const char *s)  /* definicia funkcie */
{
  // sem pride vas vlastny kod... 
}
#ifndef _Shield_LCD
int8_t lcd_read_AC(void){ //  rd_BF 
char pom_AC ;
while((pom_AC = busy_flag( )) & 0x80);
// kedze po BF = 0 este cca 4us sa nezmenil obsah AC
// treba vycitat este raz 
pom_AC = busy_flag( );
return pom_AC; // display not busy
}
#endif
</source></tab>
<tab name="uart.h"><source lang="c++" style="background: LightYellow;">
/*
* uart.h
*
* Created: 4/6/2022 11:26:15 AM
*  Author: Admin
*/
#ifndef UART_H_
#define UART_H_ 1
#include <avr/io.h>
#define F_CPU 16000000UL
#define B_R_9600
#ifdef B_R_9600
#define BAUDRATE      9600
#define BAUD_PRESCALE ((F_CPU + BAUDRATE * 4UL) / (BAUDRATE * 8UL) - 1UL)
#else
#define BAUDRATE      9600
#define BAUD_PRESCALE  (((F_CPU / (BAUDRATE * 16UL))) - 1)  // vzorcek z datasheetu
#endif
void uart_init( void );
   
extern  int uart_putchar(const char c);
char uart_getc( void );
#endif /* UART_H_ */
</source></tab>
<tab name="uart.c"><source lang="c++" style="background: LightYellow;">
/*
* uart.c
*
* Created: 4/6/2022 11:25:56 AM
*  Author: Admin
*/
#include "uart.h"
//#include "p_f.h"
void uart_init( void ){
#ifdef B_R_115200
UCSR0A |= (1<<U2X0);
#endif
  // 0.1. UBRR0 = (unsigned char)BAUD_PRESCALE;        // Set baud rate: Load the UBRR register
  UBRR0 = (unsigned int)BAUD_PRESCALE;                // Set baud rate: Load the UBRR register
  UCSR0C = (1<<UCSZ01)|(1<<UCSZ00);                    // Set format: 8data, 1stop bit
  UCSR0B = (1 << RXEN0) | (1 << TXEN0);                // Enable receiver and transmitter
};
   
int uart_putchar( const char c){
  if (c == '\n'){
  uart_putchar( '\r' );
  while ( !( UCSR0A & (1<<UDRE0)) );
/* Put data into buffer, sends the data */
UDR0 = c;
  return 0;
  }
  while ( !( UCSR0A & (1<<UDRE0)) );                  // Wait for empty transmit buffer
  // Do nothing until UDR is ready for more data to be written to it
      UDR0 = c;                            // Echo back the modified received byte
return 0;
};
void uart_puts( const char *s );
// treba dorobit
char uart_getc( void ){
while (!(UCSR0A & (1 << RXC0)) );              // Wait for data to be received
    // Do nothing until data have been recieved and is ready to be read from UDR
      return(UDR0);                              // Fetch the recieved byte
};
</source></tab>
</tabs>
</tabs>


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: [[Médiá:projektMenoPriezvisko.zip|zdrojaky.zip]]
Zbalený kompletný projekt
 
Zdrojový kód: [https://senzor.robotika.sk/sensorwiki/index.php/S%C3%BAbor:HlukomerMykytaSabadash.zip Hlukomer.zip]




=== Overenie ===
=== Overenie ===


Na používanie našej aplikácie stačia dve tlačítka a postup používania je opísaný v sekcii popis riešenia.  
Vytvorime hluk a uvidime "reakciu" hlukomeru, na LCD mame vypisane "hodnoty hluku" v cislach a pomocou stupnice.
Na konci uvádzame fotku záverečnej obrazovky pred resetom. Vypísaný je tu priemerný čas a najlepší čas.  
 
Tak isto vidime na konci videa to ze hodnoty idu do minusu a ne mame stupnicu, co znamena ze hlukomer nam ustalil na hodnote mensej ako 350, ktoru sme pouzili v kode, a musime sme ju zmensit, aby minimalnym/ustalenym vypisom bola nula, a potom znov zacat meranie.


'''Video:'''
'''Video:'''
<center><youtube>_l02MBu41n0</youtube></center>
<center><youtube>1nKjUhUQs04</youtube></center>
 
Kľúčové slová 'Category', ktoré sú na konci stránky nemeňte.


[[Category:AVR]] [[Category:MIPS]]
[[Category:AVR]] [[Category:MIPS]]

Aktuálna revízia z 10:32, 8. jún 2023

Záverečný projekt predmetu MIPS / LS2023 - Mykyta Sabadash


Zadanie

Hlukomer: - naprogramujte zariadenie na meranie úrovne hluku so zobrazením na LCD displej. Zobrazenie v dB a pomocou stupnice.

Hlukomer.


Literatúra:


Analýza a opis riešenia

Zapojenie nasho Hlukomeru bolo urobene strucne podla cviceni, kde nas senzor zvuku je A/D prevodnikom (cvicenie 9) a LCD displej z cvicenia 4

Prikladam schemy zapojenia z cviceni

Schéma pripojenia LCD displeja k procesoru.
Zapojovaci diagram pre A/D prevodnik.
Zapojovaci diagram pre Senzor zvuku.


Algoritmus a program

Sme inicializujeme A/D prevodnik, inicializujeme LCD displej a overujeme spravnost ho zapojenia, ked je vsetko v pohode tak displej sa zapne a sa zacnu ukazovat tam data ktore sme tam posleme (ked ze sme tam nieco posleme).

Dalej, zadefinujeme "lcd_puts" (funkcia na vypis veci na displej), zoberieme nase vstupne data z funkcie "adc_read()", a vyskusame ich v Serial Plot-e. Tu sme overime ci je nas senzor zvuku funkcny/zapojeny, lebo priebeh data ktore on nam posiela vidime na diagrame.

Teraz, zadefinujeme riadok, "vypocitame" hodnotu ktoru chceme vidiet na displeji, a vypiseme to pomocou funkcie "sprintf()".

A vypocitame nase Db

dB = 20 * log10(napatie_senzora / referencne_napatie)

Kde:

   * dB je vysledná hodnota v decibeloch
   * log10 je desiatkovy logaritmus
   * napatie_senzora je vstupne napätie senzora zvuku (value)
   * referencne_napatie je referencne napatie senzora (~3.3v)

A, pre lepsi vyhlad nasho hlukomeru pridame mu stupnicu hluku, ktoru vytvorime z ciernych stvorcekov a "pustych miest" (space key)

Kedy nam to vsetko vypise, dostaneme vypis na LCD ktory bude vyzerat priblizne takto:

Db --> 25

■■

Alebo napriklad takto

Db --> 100

■■■■■■■■

Potom, sme vratime kurzor na zaciatok, a urobime meranie a vypis znova


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


#define LCD_CTRL_PORT DDRD
#define LCD_DATA_PORT DDRB
#define LCD_CLR               0      /* DB0: clear display                  */
#define LCD_HOME              1      /* DB1: return to home position        */

#define LCD_RS_pin 2
#define LCD_RW_pin 3
#define LCD_EN_pin 4

#define LCD_D4_pin 1
#define LCD_D5_pin 2
#define LCD_D6_pin 3
#define LCD_D7_pin 4


FILE uart_stream = FDEV_SETUP_STREAM(uart_putchar, NULL, _FDEV_SETUP_WRITE);

int main(void)
{
	
 int pocitadlo =0;
	
 
 /* inicializacia a/d prevodnika*/
  unsigned int measuredValue;
  adc_init();                                          // Init A/D converter
  uart_init(); 
  stdout =  &uart_stream;
  
  /* inicializacia portov - vstupy / vystupy */
	
	DDRD |= (1<<LCD_EN_pin);	// Pin D4 (Enable)  PORTD  output  
	DDRD |= (1<<LCD_RW_pin);	// Pin D3 (RW)      PORTD  output  
	DDRD |= (1<<LCD_RS_pin);	// Pin D2 (RS)      PORTD  output  
  
	LCD_DATA_PORT |= (1<<LCD_D4_pin)|(1<<LCD_D5_pin)|(1<<LCD_D6_pin)|(1<<LCD_D7_pin);	// Piny 1,2,3,4, PORTB ako output (Data pre display)
	
	/* displej je spravne pripojeny, mozeme zacat inicializaciu podla datasheetu */
	
	lcd_init();  // definiciu pozri v -> lcd_ch.h resp. lcd_ch.c

	/* po ukonceni inicializacie je displej zapnuty a ak posleme nejake data, tak 
	   sa zacnu zobrazovat od prvej pozicie (riadok 0, stlpec 0)               */
  
  
  void lcd_puts(const char *s)
	{
	register unsigned char c;
	while((c = *s++))	lcd_data(c); // retazec konci "nulou"
    }

  while (1)
  {
     

	  lcd_puts("Db  -->  ");                        // vystup co sa vypise na displeji
    measuredValue = adc_read(4);                        // hodnota ktoru nam 'hovori' a/d prevodnik (mikrofon)
    printf("%u\n",measuredValue);			// pre serial plot  
 _delay_ms(100);
 
      lcd_command(0x0C);                   // schovat kurzor
	  
	  char riadok[]= {"                "};	
        int value = measuredValue - 350;   // premenna, ktorej hodnotu by sme chceli zobrazit na LCD (350 podla toho ze
	                                   // je to akoby nulova velicina nasho 'kvalitneho' hlukomera, ked ze sa vypisuju zle hodnoty na displej
                                           // tak musime menit prave tuto hodnotu, aby nulovy hluk zodpovedal 'nule' na displeji)
        int db = log10(value/3.3);
          sprintf(riadok,"%d",db);
          
    
	  int i = (value/12);
	  int o = 16-i;                  // tu sme pocitame nase "stvorceky" pre stupnicu
	  
	  if (pocitadlo == 5)            // pocitadlo na vypisovanie dB jeden krat za 5 cyklusov programu,  aby bolo to prehliadne
	  {
	   lcd_puts(riadok);             // dB
	   lcd_data(0x10);               // put space key
	   
	  pocitadlo =0;
	   } 
	   pocitadlo++;	  
	   
	   lcd_command(0xC0);            // prestup do noveho riadka
	   
	   for(i;i>0;i--)                // stupnica
	   {
		  lcd_data(0xFF);        // put ■
	    }		  
		for(o;o>0;o--)
	   {
		  lcd_data(0x10);        // put space key
	    }		 
	  lcd_command(0x70);             // 0x10 (Cursor/display shift) + 0x20 (Function set) + 0x40 (Set CGRAM address)
	  
      lcd_command(1<<LCD_HOME);  /* Set cursor to home position */


  }

  return(0);
}
#include <avr/io.h>

void adc_init(void);                                   // A/D converter initialization

unsigned int adc_read(char a_pin);
#include <avr/io.h>

void adc_init (void){
	
	ADMUX = (1<<REFS0);	
	ADCSRA = (1<<ADEN)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);
}

unsigned int adc_read(char a_pin){
	a_pin &= 0x07;
	ADMUX = (ADMUX & 0xF8)|a_pin;
	ADCSRA |= (1<< ADSC);
	while ( ADCSRA & (1<< ADSC));
		return (ADC);
}


/*
 * lcd_ch.h
 *
 * Created: 3/10/2021 7:05:39 PM
 *  Author: Admin
 */ 
#ifndef F_CPU
 #define F_CPU 16000000UL	/* Define CPU frequency here 16MHz */
#endif

#ifndef LCD_CH_H_
 #define LCD_CH_H_

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

/* Nasledovne define rozhodne ktora cast programu sa prelozi: 
   LCD Shiled (WR ma pripojene na GND) a
   priradenie pinov je dane napr.:
   "zamrzne"
   -    https://www.14core.com/wiring-the-lcd-16x2-keypad-shield-on-arduino/
   -	https://wiki.dfrobot.com/LCD_KeyPad_Shield_For_Arduino_SKU__DFR0009
   resp. 
   LCD, zapojenie vid. stranka MIPS
   "zamrzne"
   - http://senzor.robotika.sk/sensorwiki/index.php/LCD_displej_s_radi%C4%8Dom_HD44780
   , ktore ma pripojene aj pin WR
   t.j. moze sa testovat aj pin BF
*/

 /* !!!!!!! 
    #define _Shield_LCD
    !!!!!!! */
extern unsigned char kon_vyp;

#ifdef _Shield_LCD
	#define LCD_CTRL_DDR DDRB
	#define LCD_CTRL_PORT PORTB

	#define LCD_DATA_DDR DDRD
	#define LCD_DATA_PORT PORTD

	// Riadiaca zbernica display-a
	#define LCD_RS_pin 0
	//#define LCD_RW_pin = 0
	#define LCD_EN_pin 1
	// Datova zbernica
	#define LCD_D4_pin 4
	#define LCD_D5_pin 5
	#define LCD_D6_pin 6
	#define LCD_D7_pin 7

#else
	// LCD  klasik yapojenie vid. MIPS
	#define LCD_CTRL_DDR DDRD
	#define LCD_CTRL_PORT PORTD

	#define LCD_DATA_DDR DDRB
	#define LCD_DATA_PORT PORTB
	#define LCD_DATA_PIN PINB

	#define LCD_RS_pin 2
	#define LCD_RW_pin 3
	#define LCD_EN_pin 4

	#define LCD_D4_pin 1
	#define LCD_D5_pin 2
	#define LCD_D6_pin 3
	#define LCD_D7_pin 4

#endif
 



// Oneskorenie 6 SC
#define NOP() asm("nop")	
#define LCD_DELAY	NOP();NOP();NOP();NOP();NOP();NOP();

#ifdef _Shield_LCD
// formatovanie dat
#define PORT_DATA_WR_H(x) LCD_DATA_PORT &=0b00001111; LCD_DATA_PORT |= (x & 0xF0 )
#define PORT_DATA_WR_L(x) LCD_DATA_PORT &=0b00001111; LCD_DATA_PORT |= (x & 0x0F )<<4
#else

#define PORT_DATA_WR_H(x) LCD_DATA_PORT &=0b11100001; LCD_DATA_PORT |= (x & 0xF0 )>>3
#define PORT_DATA_WR_L(x) LCD_DATA_PORT &=0b11100001; LCD_DATA_PORT |= (x & 0x0F )<<1

#define PORT_DATA_RD_H ((LCD_DATA_PIN & ((1<<LCD_D4_pin)|(1<<LCD_D5_pin)|(1<<LCD_D6_pin)|(1<<LCD_D7_pin)))<<3)
#define PORT_DATA_RD_L ((LCD_DATA_PIN & ((1<<LCD_D4_pin)|(1<<LCD_D5_pin)|(1<<LCD_D6_pin)|(1<<LCD_D7_pin)))>>1)


#endif

/* Public functions for your use */
#ifndef _Shield_LCD
int8_t lcd_read_AC(void);   
void def_spec_znaky_AC(void);
#endif
	
void lcd_init(void);
void lcd_data(unsigned char );
void lcd_command(unsigned char );
// void lcd_puts(const char *s);  /* deklaracia funkcie  */



void ini_ports(void);
void En_imp(void);

void wr_data (unsigned char );
unsigned char busy_flag(void);

void zob_text(char *);
void def_Clear_spec_znaky(void);


void def_znak(unsigned char *,unsigned char );
void def_spec_znaky(void);

#endif /* LCD_CH_H_ */
#include "lcd_ch.h"

unsigned char Znak_OO[8]= {0x00,0x00,0x00,0x00,0x00,0x00,0x15,0};// Prazdny znak	dole su tri bodky

unsigned char Znak_SC[8]= {0x1A,0x1D,0x04,0x04,0x04,0x05,0x02,0};// st.C	
unsigned char Znak_SM[8]= {0x0A,0x04,0x0E,0x10,0x0E,0x01,0x1E,0};//  s + mekcen	
unsigned char Znak_CM[8]= {0x0A,0x04,0x0E,0x10,0x10,0x11,0x0C,0};//  c + mekcen

// Vynulovanie CGRAM
void def_Clear_spec_znaky(void){
	// zapis specialneho znaku do CGRAM  na poziciu 0, a 7
	for(char i = 0; i < 8; i++) def_znak( Znak_OO,i);
	lcd_command(0x80);
} 


 
void def_spec_znaky(void){
// zapis vlastnych znakkov do CGRAM  na poziciu 4, a 0 (8) 


 def_znak( Znak_SC,4);// stupen Celzia
 def_znak( Znak_SM,0);// s + makcen
 // obnovenie obsahu AC. AC nastvime na 1. riadok, 1. znak
 lcd_command(0x80);
}		

#ifndef _Shield_LCD
void def_spec_znaky_AC(void){
// zapis specialneho znaku do CGRAM  na poziciu 7

unsigned char obsah_AC = 0;
 obsah_AC = lcd_read_AC(); //     
 // kon_vyp = obsah_AC;

 // sem dame nove vlastne znaky
 def_znak( Znak_CM,7);// c + makcen
 // obnovenie obsahu AC. AC 

lcd_command(0x80|obsah_AC);

}		
#endif
	 
void ini_ports(void){	 	
	
/* inicializacia portov - vstupy / vystupy 
oba typy displaja */
// nasledovna # riadky su spolocne pre oba typy LCD
LCD_CTRL_DDR |= (1<<LCD_EN_pin);	//  (Enable)    output  
LCD_CTRL_DDR |= (1<<LCD_RS_pin);	//  (RS)        output  	
LCD_DATA_DDR |= (1<<LCD_D4_pin)|(1<<LCD_D5_pin)|(1<<LCD_D6_pin)|(1<<LCD_D7_pin);	// Datove piny ako Output (Data pre display)
#ifndef _Shield_LCD
// klasicky LCD vyuziva aj RW pin
LCD_CTRL_DDR |= (1<<LCD_RW_pin);	//	(RW)        output  
	
#endif
}	


void En_imp(void) {
	LCD_CTRL_PORT |= (1<<LCD_EN_pin);	// -> "log.1"
	LCD_DELAY;							// cca 400 ns
	LCD_CTRL_PORT &= ~(1<<LCD_EN_pin);	// -> "log.0"  spolu cca 500 ns
}

void lcd_init(void)
{   // 4 bitove pripojenie display-a
	ini_ports(); // inicializacia porov
	 _delay_ms(15);
	// 1. -------------------
	LCD_CTRL_PORT &= ~(1 << LCD_RS_pin);		// set RS  = to "log. 0"  Instruction Register (IR)
#ifndef _Shield_LCD
	LCD_CTRL_PORT &= ~(1 << LCD_RW_pin)	;		// set R/W = to "log. 0" - Write
#endif
	// RS R/W   DB7 DB6 DB5 DB4   DB3 DB2 DB1 DB0
	// 0  0     0   0   1   1     x   x   x   x     = 0x30
	PORT_DATA_WR_H(0x30);	// 8-bitove pripojenie
	En_imp();
	// 2. -------------------
    // zopakujem  8-bitove pripojenie
	_delay_ms(5); En_imp();
	// 3. -------------------
	// zopakujem  8-bitove pripojenie
    _delay_ms(1); En_imp(); // -  stacilo by delay 0.1ms, momentalne najkratsie nastavitelne je 1ms
	// 4. -------------------
	// zmenim na 4-bitove pripojenie
	// RS R/W    DB7 DB6 DB5 DB4    DB3 DB2 DB1 DB0
	// 0  0      0   0   1   0      x   x   x   x    = 0x20
	PORT_DATA_WR_H(0x20);	// 4-bitove pripojenie
	_delay_ms(1); En_imp(); // -  stacilo by delay 0.04ms
	// -------------------		
	// LCD function set      mod: DL = 0 - 4-bitove data, N = 1 - 2 riadky, F = 0 - 5x7 dots 
	// RS R/W    DB7 DB6 DB5 DB4    DB3 DB2 DB1 DB0
	// 0  0      0   0   1   DL     N   F   x   x     = 0x28
	// Mozeme nasledujuci prikaz pre LCD vynechat?
	_delay_ms(2);
	lcd_command(0x28);
	// RS R/W    DB7 DB6 DB5 DB4    DB3 DB2 DB1 DB0
	// 0  0      0   0   0   0	    0   0   0   1     = 0x01 , Display clear
	// Mozeme nasledujuci prikaz pre LCD vynechat?
	_delay_ms(2);
	lcd_command(0x01);
	// RS R/W    DB7 DB6 DB5 DB4    DB3 DB2 DB1 DB0
	// 0  0      0   0   0   0	    0   1   I/D S      = 0x06, I/D = 1 - inkrement
	// Mozeme nasledujuci prikaz pre LCD vynechat?
	_delay_ms(2);
	lcd_command(0x02);
	// RS R/W    DB7 DB6 DB5 DB4    DB3 DB2 DB1 DB0
	// 0  0      0   0   0   0	    1   D   C   B      = 0x0E, D = C = 1 - Display a kurzor zapnute
	// Mozeme nasledujuci prikaz pre LCD vynechat?
	_delay_ms(2);
	lcd_command(0x0E);								// B = 0 - blikanie vypnute
}


//zapis data do Data Register
void lcd_data(unsigned char data){	
	while(busy_flag() & 0x80);
	//while(rd_BF()); // test BF sa da realizovat pre klasicky LCD, nie Shield
	LCD_CTRL_PORT |= (1<<LCD_RS_pin);		//    (RS = High)  
#ifndef _Shield_LCD
	LCD_CTRL_PORT &= ~(1<<LCD_RW_pin);		//    (RW = Low, write) 
#endif
	wr_data (data);	
}

// zapis commandu do Instruction Register
void lcd_command(unsigned char command){ 
	while(busy_flag() & 0x80);
	//while(rd_BF()); // test BF sa da realizovat pre klasicky LCD, nie Shield
	LCD_CTRL_PORT &= ~(1<<LCD_RS_pin);		//   (RS = Low)  
#ifndef _Shield_LCD
	LCD_CTRL_PORT &= ~(1<<LCD_RW_pin);		//   (RW = Low, write) 
#endif
	wr_data (command);	
}

void wr_data(unsigned char data) { 
	PORT_DATA_WR_H(data);		// data High nibble
	En_imp();
			
	PORT_DATA_WR_L(data);		// data Low nibble
	En_imp();
}

#ifdef _Shield_LCD
// namiesto testu BF "pockam". 
// LCD typu Shield ma WR pripojene na GND		
unsigned char busy_flag(void){
	_delay_ms(2);	
	return(0);
}	
#else
// namiesto testu BF "pockam". Vacsine prikazov tento cas vyhovuje		
/*int busy_flag(void){
	_delay_ms(2);	
	return(0);
}
*/
// klasicke pripojenie LCD umozni aj test BF
unsigned char busy_flag(void){ //  rd_BF   
	unsigned char pom = 0;
	LCD_CTRL_PORT &= ~(1<<LCD_RS_pin);	//   (RS = Low)  
	LCD_CTRL_PORT |= (1<<LCD_RW_pin);	//    (RW = High, read) 
    // port B, datove bity budu teraz input
	LCD_DATA_DDR &= ~((1<<LCD_D4_pin)|(1<<LCD_D5_pin)|(1<<LCD_D6_pin)|(1<<LCD_D7_pin));	//  Datove piny nastavime  na input, (Data pre disp)
	
	// Spravne by sa malo vycitavaj pred dobeznou hranou EN impulzu. Je tam urcity presah. Mozeme vycitat aj po dobeznej hrane.
	En_imp();
	pom = PORT_DATA_RD_H; // vycitam High nibble AC
	En_imp();
	pom |= PORT_DATA_RD_L; // vycitam Low nibble AC
	// datove bity zase output
	LCD_DATA_DDR |= (1<<LCD_D4_pin)|(1<<LCD_D5_pin)|(1<<LCD_D6_pin)|(1<<LCD_D7_pin);	// Datove piny nastavime na output (Data pre disp) 
	//if(pom & 0x80) return 1;	// display je busy
	//else 
	return pom;					// display je not busy
}

#endif
	

void zob_text(char *s){
	register unsigned char c;
	while((c = *s++))	lcd_data(c); // retazec konci "nulou"
}

void def_znak(unsigned char *ZnakArray,unsigned char kam) {	
	lcd_command(0x40|(kam << 3)); //nastavenie adresy znaku v CGRAM
	for(unsigned char i = 0; i < 8; i++) lcd_data( *(ZnakArray + i));		
}


/* ********************************************************	*/
/* vypis retazca na poziciu, resp. podla nasledovnych 		*/
/* formatovacich prikazov                                       */
/*   : \r - prechod na novy riadok                              */
/*   : \f - prechod na zaciatok displeja                        */
/* ********************************************************     */
 void lcd_puts(  const char *s)   /* definicia funkcie		*/
{
  // sem pride vas vlastny kod...  
}

#ifndef _Shield_LCD
int8_t lcd_read_AC(void){ //  rd_BF   
	char pom_AC ;
	
	while((pom_AC = busy_flag( )) & 0x80);
	// kedze po BF = 0 este cca 4us sa nezmenil obsah AC
	// treba vycitat este raz  
	pom_AC = busy_flag( );
	return pom_AC;					// display not busy
	
}
#endif
/*
 * uart.h
 *
 * Created: 4/6/2022 11:26:15 AM
 *  Author: Admin
 */ 


#ifndef UART_H_
#define UART_H_ 1
#include <avr/io.h>

#define F_CPU 16000000UL
#define B_R_9600

#ifdef B_R_9600
#define BAUDRATE       9600
#define BAUD_PRESCALE ((F_CPU + BAUDRATE * 4UL) / (BAUDRATE * 8UL) - 1UL)
#else
#define BAUDRATE       9600
#define BAUD_PRESCALE  (((F_CPU / (BAUDRATE * 16UL))) - 1)  // vzorcek z datasheetu

#endif


void uart_init( void );
     
extern  int uart_putchar(const char c);


char uart_getc( void );






#endif /* UART_H_ */
/*
 * uart.c
 *
 * Created: 4/6/2022 11:25:56 AM
 *  Author: Admin
 */ 

#include "uart.h"
//#include "p_f.h"

void uart_init( void ){
	#ifdef B_R_115200
	UCSR0A |= (1<<U2X0);
	#endif
   // 0.1. UBRR0 = (unsigned char)BAUD_PRESCALE;         // Set baud rate: Load the UBRR register
   UBRR0 = (unsigned int)BAUD_PRESCALE;                 // Set baud rate: Load the UBRR register
   UCSR0C = (1<<UCSZ01)|(1<<UCSZ00);                     // Set format: 8data, 1stop bit 

   UCSR0B = (1 << RXEN0) | (1 << TXEN0);                 // Enable receiver and transmitter
};

     
 int uart_putchar( const char c){
   if (c == '\n'){
	  uart_putchar( '\r' );
	  while ( !( UCSR0A & (1<<UDRE0)) );
	/* Put data into buffer, sends the data */
	UDR0 = c;
   return 0;
  }

   while ( !( UCSR0A & (1<<UDRE0)) );                  // Wait for empty transmit buffer 
	   // Do nothing until UDR is ready for more data to be written to it

      UDR0 = c;                             // Echo back the modified received byte 
return 0;
};

void uart_puts( const char *s );
// treba dorobit

char uart_getc( void ){
	while (!(UCSR0A & (1 << RXC0)) );              // Wait for data to be received 
	    // Do nothing until data have been recieved and is ready to be read from UDR
      return(UDR0);                               // Fetch the recieved byte 
};


Zbalený kompletný projekt

Zdrojový kód: Hlukomer.zip


Overenie

Vytvorime hluk a uvidime "reakciu" hlukomeru, na LCD mame vypisane "hodnoty hluku" v cislach a pomocou stupnice.

Tak isto vidime na konci videa to ze hodnoty idu do minusu a ne mame stupnicu, co znamena ze hlukomer nam ustalil na hodnote mensej ako 350, ktoru sme pouzili v kode, a musime sme ju zmensit, aby minimalnym/ustalenym vypisom bola nula, a potom znov zacat meranie.

Video: