Senzor farieb TCS230: Rozdiel medzi revíziami
Zo stránky SensorWiki
Bez shrnutí editace |
Bez shrnutí editace |
||
(19 medziľahlých úprav od jedného ďalšieho používateľa nie je zobrazených) | |||
Riadok 8: | Riadok 8: | ||
Chceme dosiahnúť toho, aby sme vedeli rozoznávať rôzne farby za pomocy senzora TCS230. | Chceme dosiahnúť toho, aby sme vedeli rozoznávať rôzne farby za pomocy senzora TCS230. | ||
'''Literatúra:''' | '''Literatúra:''' | ||
* [http://ap.urpi.fei.stuba.sk/sensorwiki/index.php/Acrob_technical_description Dokumentácia k doske Acrob] | * [http://ap.urpi.fei.stuba.sk/sensorwiki/index.php/Acrob_technical_description Dokumentácia k doske Acrob] | ||
* [ | * [https://pdf1.alldatasheet.com/datasheet-pdf/view/96470/ETC/TCS230.html Dokumentácia k senzoru TCS230] | ||
Riadok 20: | Riadok 18: | ||
== Analýza a opis riešenia == | == Analýza a opis riešenia == | ||
Senzor pracuje na takom princípe, že keď ho zapojíme do vývojovej dosky, tak bude naspäť posielať obdĺžnikový signál, ktorého frekvencia je priamo úmerná intenzite svetla. Senzor má 64 fotodiód, z čoho 16 má červený filter, 16 má zelený filter, 16 ma modrý filter a 16 má čistích, teda bez filtru. Tieto filtre dokážeme nastavovať pomocou pinov S2 a S3 (S2 -> log.0 a S3 -> log.0 = červený filter, atď). Takže nám stačí nastaviť filter na určitú frabu a potom stačí zmerať frekvenciu zo senzora. Na meranie frekvencii sme použili počítadlo 1 (TIMER1), čo je 16 bitové počítadlo, ktorým sme merali čas od dobežnej hrany po nábežnú a keď sme tým podelili polovicu frekvencie kryštálu (polovicu preto, lebo sme merali iba pól periódu, pretože ten signál ma 50% duty), tak dostaneme výslednú frekvenciu pri danom filtri. Potom to už len zobrazíme na LCD displaj. | |||
[[Súbor:TCS230.jpg|400px|thumb|center|TCS230 senzor farieb.]] | |||
Schéma zapojenia LCD displeja. | |||
[[Súbor:MIPS lcdDemoSchema01.png|400px|thumb|center|Schéma zapojenia LCD displeja.]] | |||
Schéma zapojenia senzora TCS230. | |||
[[Súbor:SchemazapojeniasenzoraTCS230.png|400px|thumb|center| Schéma zapojenia senzora TCS230.]] | |||
=== Algoritmus a program === | === Algoritmus a program === | ||
Riadok 38: | Riadok 39: | ||
#include <avr/io.h> | #include <avr/io.h> | ||
#include <avr/io.h> | |||
#include <stdio.h> | |||
#define F_CPU 16000000UL | |||
#include <util/delay.h> | |||
#include "lcd_ch.h" | |||
#define LCD_CLR 0 // DB0: Vyčistenie displeja | |||
#define LCD_CTRL_PORT DDRD | |||
#define LCD_DATA_PORT DDRB | |||
#define LCD_RS_pin 2 // Zadefinovanie pinov LCD displaja | |||
#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 | |||
#define S0_PIN PD5 // Zadefinovanie pinov snímača | |||
#define S1_PIN PD6 | |||
#define S2_PIN PD7 | |||
#define S3_PIN PB0 | |||
#define OUT_PIN PB5 | |||
void setup() | |||
{ | |||
//LCD: | |||
DDRD |= (1<<LCD_EN_pin); // Pin D4 (Enable) PORTD výstup | |||
DDRD |= (1<<LCD_RW_pin); // Pin D3 (RW) PORTD výstup | |||
DDRD |= (1<<LCD_RS_pin); // Pin D2 (RS) PORTD výstup | |||
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 výstupy (Dáta pre display) | |||
//Snimac: | |||
DDRD |= (1 << S0_PIN) | (1 << S1_PIN) | (1 << S2_PIN); // Nastavenie pinov S0, S1 a S2 ako výstupy | |||
DDRB |= (1 << S3_PIN); // Nastavenie S3 ako výstup | |||
DDRB &= ~(1 << OUT_PIN); // Nastavenie pinu OUT ako vstup | |||
// Nastavenie frekvenčnej škály na 2% | |||
PORTD &= ~(1 << S0_PIN); // S0: 0 | |||
PORTD |= (1 << S1_PIN); // S1: 1 | |||
} | |||
uint16_t TCSMeasure() | |||
{ | { | ||
if(!(PINB & (1 << OUT_PIN))) // | |||
{ | |||
while(!(PINB & (1 << OUT_PIN))); //Čakanie na nábežnú hranu | |||
} | |||
while(PINB & (1 << OUT_PIN)); //Čakanie na dobežnú hranu | |||
return(0); | TCNT1=0x0000; //Resetovanie počítadla | ||
TCCR1B=(1<<CS10); //Nastavenie predeličky = F_CPU/1 (začiatok počítania) | |||
while(!(PINB & (1 << OUT_PIN))); //Čakanie na nábežnú hranu | |||
TCCR1B=0x00; //Zastavenie počítadla | |||
return ((float)8000000UL/TCNT1); //Vrátenie nameranej frekvenice | |||
} | |||
long map(long x, long in_min, long in_max, long out_min, long out_max) //Funkcia map na kalibráciu snímača | |||
{ | |||
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; //(premenná, najmenšia daná hodnota, najväčšia daná hodnota, najmenšia požadovaná hodnota, najväčšia požadovaná hodnota) | |||
} | |||
int main(void) | |||
{ | |||
setup(); // iniciácia nastavenia vstupov | |||
lcd_init(); // iniciácia LCD | |||
void lcd_puts(const char *s) // funkcia na použivanie puts | |||
{ | |||
register unsigned char c; | |||
while((c = *s++)) | |||
lcd_data(c); | |||
} | |||
char vysledokR[ ]={ }; // nastavenie premenných na zapisovanie hodnôt | |||
char vysledokG[ ]={ }; | |||
char vysledokB[ ]={ }; | |||
while(1) | |||
{ | |||
lcd_command(1 << LCD_CLR); // vyčistenie displaja | |||
lcd_command(0b00001100); //odstranenie kurzora | |||
lcd_command(0x80); // nastavenie na prvý riadok | |||
lcd_puts("Namerana farba:"); //Napiseme do prveho riadku | |||
PORTD &= ~(1 << S2_PIN); //Nastavenie červeného filtra | |||
PORTB &= ~(1 << S3_PIN); | |||
unsigned int hodnotaR = TCSMeasure(); //Uloženie nameranej hodnoty pre červený filter | |||
hodnotaR = map(hodnotaR,433,2195,0,255); //upravenie na požadované hodnoty | |||
lcd_command(0xC0); // nastavenie na druhý riadok | |||
lcd_puts("R="); | |||
sprintf(vysledokR,"%d",hodnotaR); // prepísanie čísla ako charakter | |||
lcd_puts(vysledokR); //Vypisanie na LCD displaj | |||
_delay_ms(10); | |||
PORTD |= (1 << S2_PIN); //Nastavenie zeleného filtra | |||
PORTB |= (1 << S3_PIN); | |||
unsigned int hodnotaG = TCSMeasure(); //Uloženie nameranej hodnoty pre zelený filter | |||
hodnotaG = map(hodnotaG,384,1300,0,255); //Upravenie na požadované hodnoty | |||
lcd_command(0xC0+5); // nastavenie na 6tu pozíciu na druhom riadku | |||
lcd_puts("G="); | |||
sprintf(vysledokG,"%d",hodnotaG); // prepísanie čísla ako charakter | |||
lcd_puts(vysledokG); //Vypisanie na LCD displaji | |||
_delay_ms(10); | |||
PORTD &= ~(1 << S2_PIN); //Nastavenie modrého filtra | |||
PORTB |= (1 << S3_PIN); | |||
unsigned int hodnotaB = TCSMeasure(); //Uloženie nameranej hodnoty pre modrý filter | |||
hodnotaB = map(hodnotaB,487,2100,0,255); //Upravenie na požadované hodtoty | |||
lcd_command(0xC0+10); // Nastavenie na 11tu poziciu na druhom riadku | |||
lcd_puts("B="); | |||
sprintf(vysledokB,"%d",hodnotaB); // prepísanie čísla ako charakter | |||
lcd_puts(vysledokB); //Vypisanie na LCD dispaji | |||
_delay_ms(200); | |||
} | |||
} | } | ||
</source></tab> | </source></tab> | ||
<tab name=" | <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 <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 | |||
void | /* 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> | </source></tab> | ||
</tabs> | </tabs> | ||
Kompletný projekt: | |||
Zdrojový kód: [[Médiá:TCS230_kód.zip|TCS230_kód.zip]] | |||
=== Overenie === | === Overenie === | ||
Na používanie našej aplikácie | Na používanie našej aplikácie stačí TCS230 senzor a LCD displej na ktorý budeme vypisovať hodnoty zo senzora. Na konci je tu fotka reálneho zapojenia. | ||
Na konci | |||
[[Súbor: | [[Súbor:TCS230_foto.jpg|400px|thumb|center|Zapojenie v realite.]] | ||
'''Video:''' | '''Video:''' | ||
<center><youtube> | <center><youtube>b1qcSQIM-3A</youtube></center> | ||
[[Category:AVR]] [[Category:MIPS]] | [[Category:AVR]] [[Category:MIPS]] |
Aktuálna revízia z 16:39, 14. jún 2023
Záverečný projekt predmetu MIPS / LS2023 - Tomáš Bečvarov
Zadanie
Senzor farieb - naprogramujte senzor na rozpoznávanie jednotlivých farieb, zobrazenie na LCD displej.
Chceme dosiahnúť toho, aby sme vedeli rozoznávať rôzne farby za pomocy senzora TCS230.
Literatúra:
Analýza a opis riešenia
Senzor pracuje na takom princípe, že keď ho zapojíme do vývojovej dosky, tak bude naspäť posielať obdĺžnikový signál, ktorého frekvencia je priamo úmerná intenzite svetla. Senzor má 64 fotodiód, z čoho 16 má červený filter, 16 má zelený filter, 16 ma modrý filter a 16 má čistích, teda bez filtru. Tieto filtre dokážeme nastavovať pomocou pinov S2 a S3 (S2 -> log.0 a S3 -> log.0 = červený filter, atď). Takže nám stačí nastaviť filter na určitú frabu a potom stačí zmerať frekvenciu zo senzora. Na meranie frekvencii sme použili počítadlo 1 (TIMER1), čo je 16 bitové počítadlo, ktorým sme merali čas od dobežnej hrany po nábežnú a keď sme tým podelili polovicu frekvencie kryštálu (polovicu preto, lebo sme merali iba pól periódu, pretože ten signál ma 50% duty), tak dostaneme výslednú frekvenciu pri danom filtri. Potom to už len zobrazíme na LCD displaj.
Schéma zapojenia LCD displeja.
Schéma zapojenia senzora TCS230.
Algoritmus a program
Algoritmus programu je....
#include <avr/io.h>
#include <avr/io.h>
#include <stdio.h>
#define F_CPU 16000000UL
#include <util/delay.h>
#include "lcd_ch.h"
#define LCD_CLR 0 // DB0: Vyčistenie displeja
#define LCD_CTRL_PORT DDRD
#define LCD_DATA_PORT DDRB
#define LCD_RS_pin 2 // Zadefinovanie pinov LCD displaja
#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
#define S0_PIN PD5 // Zadefinovanie pinov snímača
#define S1_PIN PD6
#define S2_PIN PD7
#define S3_PIN PB0
#define OUT_PIN PB5
void setup()
{
//LCD:
DDRD |= (1<<LCD_EN_pin); // Pin D4 (Enable) PORTD výstup
DDRD |= (1<<LCD_RW_pin); // Pin D3 (RW) PORTD výstup
DDRD |= (1<<LCD_RS_pin); // Pin D2 (RS) PORTD výstup
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 výstupy (Dáta pre display)
//Snimac:
DDRD |= (1 << S0_PIN) | (1 << S1_PIN) | (1 << S2_PIN); // Nastavenie pinov S0, S1 a S2 ako výstupy
DDRB |= (1 << S3_PIN); // Nastavenie S3 ako výstup
DDRB &= ~(1 << OUT_PIN); // Nastavenie pinu OUT ako vstup
// Nastavenie frekvenčnej škály na 2%
PORTD &= ~(1 << S0_PIN); // S0: 0
PORTD |= (1 << S1_PIN); // S1: 1
}
uint16_t TCSMeasure()
{
if(!(PINB & (1 << OUT_PIN))) //
{
while(!(PINB & (1 << OUT_PIN))); //Čakanie na nábežnú hranu
}
while(PINB & (1 << OUT_PIN)); //Čakanie na dobežnú hranu
TCNT1=0x0000; //Resetovanie počítadla
TCCR1B=(1<<CS10); //Nastavenie predeličky = F_CPU/1 (začiatok počítania)
while(!(PINB & (1 << OUT_PIN))); //Čakanie na nábežnú hranu
TCCR1B=0x00; //Zastavenie počítadla
return ((float)8000000UL/TCNT1); //Vrátenie nameranej frekvenice
}
long map(long x, long in_min, long in_max, long out_min, long out_max) //Funkcia map na kalibráciu snímača
{
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; //(premenná, najmenšia daná hodnota, najväčšia daná hodnota, najmenšia požadovaná hodnota, najväčšia požadovaná hodnota)
}
int main(void)
{
setup(); // iniciácia nastavenia vstupov
lcd_init(); // iniciácia LCD
void lcd_puts(const char *s) // funkcia na použivanie puts
{
register unsigned char c;
while((c = *s++))
lcd_data(c);
}
char vysledokR[ ]={ }; // nastavenie premenných na zapisovanie hodnôt
char vysledokG[ ]={ };
char vysledokB[ ]={ };
while(1)
{
lcd_command(1 << LCD_CLR); // vyčistenie displaja
lcd_command(0b00001100); //odstranenie kurzora
lcd_command(0x80); // nastavenie na prvý riadok
lcd_puts("Namerana farba:"); //Napiseme do prveho riadku
PORTD &= ~(1 << S2_PIN); //Nastavenie červeného filtra
PORTB &= ~(1 << S3_PIN);
unsigned int hodnotaR = TCSMeasure(); //Uloženie nameranej hodnoty pre červený filter
hodnotaR = map(hodnotaR,433,2195,0,255); //upravenie na požadované hodnoty
lcd_command(0xC0); // nastavenie na druhý riadok
lcd_puts("R=");
sprintf(vysledokR,"%d",hodnotaR); // prepísanie čísla ako charakter
lcd_puts(vysledokR); //Vypisanie na LCD displaj
_delay_ms(10);
PORTD |= (1 << S2_PIN); //Nastavenie zeleného filtra
PORTB |= (1 << S3_PIN);
unsigned int hodnotaG = TCSMeasure(); //Uloženie nameranej hodnoty pre zelený filter
hodnotaG = map(hodnotaG,384,1300,0,255); //Upravenie na požadované hodnoty
lcd_command(0xC0+5); // nastavenie na 6tu pozíciu na druhom riadku
lcd_puts("G=");
sprintf(vysledokG,"%d",hodnotaG); // prepísanie čísla ako charakter
lcd_puts(vysledokG); //Vypisanie na LCD displaji
_delay_ms(10);
PORTD &= ~(1 << S2_PIN); //Nastavenie modrého filtra
PORTB |= (1 << S3_PIN);
unsigned int hodnotaB = TCSMeasure(); //Uloženie nameranej hodnoty pre modrý filter
hodnotaB = map(hodnotaB,487,2100,0,255); //Upravenie na požadované hodtoty
lcd_command(0xC0+10); // Nastavenie na 11tu poziciu na druhom riadku
lcd_puts("B=");
sprintf(vysledokB,"%d",hodnotaB); // prepísanie čísla ako charakter
lcd_puts(vysledokB); //Vypisanie na LCD dispaji
_delay_ms(200);
}
}
/*
* 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
Kompletný projekt:
Zdrojový kód: TCS230_kód.zip
Overenie
Na používanie našej aplikácie stačí TCS230 senzor a LCD displej na ktorý budeme vypisovať hodnoty zo senzora. Na konci je tu fotka reálneho zapojenia.
Video: