Hlukomer: Rozdiel medzi revíziami
Zo stránky SensorWiki
Bez shrnutí editace |
Bez shrnutí editace |
||
(32 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:''' | ||
* [ | * [https://senzor.robotika.sk/sensorwiki/index.php/Acrob_technical_description Dokumentácia k doske Acrob] | ||
* [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 ( | |||
* [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 === | ||
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; // premenna, ktorej hodnotu by sme chceli zobrazit na LCD (350 podla toho ze | 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) | // 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 i = (value/12); | ||
Riadok 140: | Riadok 153: | ||
{ | { | ||
lcd_puts(riadok); // dB | lcd_puts(riadok); // dB | ||
lcd_data(0x10); | lcd_data(0x10); // put space key | ||
pocitadlo =0; | pocitadlo =0; | ||
Riadok 146: | Riadok 159: | ||
pocitadlo++; | pocitadlo++; | ||
lcd_command(0xC0); | lcd_command(0xC0); // prestup do noveho riadka | ||
for(i;i>0;i--) | 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 166: | Riadok 179: | ||
} | } | ||
</source></tab> | </source></tab> | ||
<tab name=" | |||
<tab name="adc.h"><source lang="c++" style="background: LightYellow;"> | |||
#include <avr/io.h> | #include <avr/io.h> | ||
Riadok 173: | 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> | ||
Zdrojový kód: [ | Zbalený kompletný projekt | ||
Zdrojový kód: [https://senzor.robotika.sk/sensorwiki/index.php/S%C3%BAbor:HlukomerMykytaSabadash.zip Hlukomer.zip] | |||
=== Overenie === | === 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:''' | '''Video:''' | ||
<center><youtube> | <center><youtube>1nKjUhUQs04</youtube></center> | ||
[[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.
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
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: