Hlukomer: Rozdiel medzi revíziami
Zo stránky SensorWiki
Bez shrnutí editace |
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:''' | ||
* [ | * [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 | |||
// 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",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--) | 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=" | |||
<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> | ||
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: