Na początku garść informacji o samym czujniku. Czujnik posiada diody światłoczułe, macierz 4x4 z filtrem R, 4x4 z filtrem G, 4x4 z filtrem B oraz 4x4 całkowicie bez filtra. Poprzez piny możemy wybrać, z których diod odczytujemy wartość. Czujnik ma 8 wyprowadzeń, 2 od zasilania (GND, VCC) oraz 8 I/O (S0, S1,S2,S3,OE,OUT), które służą do sterowania czujnikiem.
czujnik TCS3200
Wejścia S0 do S3 służą do ustawienia filtra oraz częstotliwości, OE (Output Enabled - 0=aktywne) służy do włączania, wyłączania pomiaru koloru. Na OUT zaś możemy odczytać częstotliwość jaką zwraca nam czujnik po "odczytaniu" koloru.
W tabeli poniżej pokazane jest jak należy sterować urządzeniem.
Tabela ustawień czunika TCS3200
Aby odczytać częstotliwość na pinie OUT możemy wykorzystać kilka podejść. Najprostszym dla mnie było użycie przerwania sprzętowego INT i zliczanie wystąpień przerwania w ustalonym czasie, dla każdego filtra z osobna. W tym celu przygotowałem sobie klasę, która obsługuje sprzętowy INT oraz wykorzystałem gotową klasę Antystatycznego do obsługi UART. Poniżej przedstawiam pliki deklaracji klasy oraz główny program do odczytu kolorów. Oczywiście jest to tylko podstawa, którą można rozbudować wedle własnego uznania. Natomiast po klasę do obsługi USARTa proszę zgłosić się do Antystatycznego.
int.hpp
Kod: Zaznacz cały
//int.hpp - autor - WoodPaker
#include <avr/interrupt.h>
#pragma once
enum _EICRA {_LOW,_ANY,_FALING,_RASING};
class class_INT0
{
public:
/** Default constructor */
class_INT0();
class_INT0(uint8_t _EICRA, void(*callback_int)(void));
/** Default destructor */
void on(void);
void off(void);
~class_INT0();
protected:
private:
};
int.cpp
Kod: Zaznacz cały
//int.cpp - autor - WoodPaker
#include "_INT.h"
void (*callback_i)();
class_INT0::class_INT0()
{
EICRA=0x03;
EIMSK |= (1 << INT0);;
callback_i=nullptr;
}
class_INT0::class_INT0(uint8_t _EICRA, void(*callback_int)(void))
{
EICRA=_EICRA;
callback_i = callback_int;
}
void class_INT0::on(void)
{
EIMSK |= (1 << INT0);
}
void class_INT0::off(void)
{
EIMSK &= ~(1 << INT0);
}
class_INT0::~class_INT0()
{
//dtor
}
ISR (INT0_vect)
{
(*callback_i)();
}
main.cpp
Kod: Zaznacz cały
#include <stdint.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include "usart.hpp"
#include <stdlib.h>
#include "_INT.h"
#define S0 PC5
#define S1 PC4
#define S2 PC3
#define S3 PC2
#define OUT PD2
#define OE PD5
#define S0_high PORTC |= (1<<S0);
#define S0_low PORTC &= ~(1<<S0);
#define S1_high PORTC |= (1<<S1);
#define S1_low PORTC &= ~(1<<S1);
#define S2_high PORTC |= (1<<S2);
#define S2_low PORTC &= ~(1<<S2);
#define S3_high PORTC |= (1<<S3);
#define S3_low PORTC &= ~(1<<S3);
volatile uint32_t time;
volatile uint32_t R;
volatile uint32_t G;
volatile uint32_t B;
volatile uint32_t C;
#define R_max 14478
#define G_max 14478
#define B_max 14478
#define C_max 14480
#define R_min 172
#define G_min 176
#define B_min 236
#define C_min 835
CircularBuffer rxBuffer, txBuffer;
void sendStr(char *buf);
Usart myUsart(0, 115200, &rxBuffer, &txBuffer);
char textR[6];
char textG[6];
char textB[6];
char textC[6];
enum Colours {RED,GREEN,BLUE,CLEAR}; //Unumerate Colours RED,GREEN,BLUE and ClEAR
uint32_t ReadColour(uint8_t Colour)
{
switch(Colour)
{
case RED:
S2_low;
S3_low;
break;
case GREEN:
S2_high;
S3_high;
break;
case BLUE:
S2_low;
S3_high;
break;
case CLEAR:
S2_high;
S3_low;
break;
}
_delay_ms(10);
return time;
}
void INT_0()
{
time++;
}
int main()
{
class_INT0 Przerwanie(_RASING,INT_0);
DDRC |= (1<<S0) | (1<<S1) | (1<<S2) | (1<<S3); //Ustawjako wyjœcia
DDRD |= (1<<OE);
DDRD &= ~(1<<OUT);//Ustaw jako wejœcie
sei();
myUsart << "\n\rSTART - freq 100 procent\n\r";
S0_high;
S1_high;
PORTD |= (1<<OE);
while(1)
{
//RED
Przerwanie.on();
time=0;
R=ReadColour(RED);
Przerwanie.off();
//GREEN
Przerwanie.on();
time=0;
G=ReadColour(GREEN);
Przerwanie.off();
//BLUE
Przerwanie.on();
time=0;
B=ReadColour(BLUE);
Przerwanie.off();
//CLEAR
Przerwanie.on();
time=0;
C=ReadColour(CLEAR);
Przerwanie.off();
itoa(R,textR,10);
itoa(G,textG,10);
itoa(B,textB,10);
itoa(C,textC,10);
myUsart << "R="<<textR<<" G="<<textG<<" B="<<textB<<" C="<<textC;
myUsart << "\n\rKOLEJNY POMIAR\n\r";
_delay_ms(2000);
}
}
Jak działa program?
Otóż na początku trzeba stworzyć obiekty obsługujące przerwanie zewnętrzne INT0 oraz obiekt obsługujący UART. Zrobione jest to poprzez te dwie definicje:
Usart myUsart(0, 115200, &rxBuffer, &txBuffer);
class_INT0 Przerwanie(_RASING,INT_0);
W konstruktorze obiektu przerwania podajemy funkcję (a raczej jej adres) do obsługi przerwania, w tym przypadku jest to zwiększenie licznika, oraz przy jakiej zmianie na PINie przerwanie ma się pojawiać. Ja wybrałem sygnał wzrastający. Funkcja uint32_t ReadColour(uint8_t Colour) służy do odczytu ilości wystąpień przerwania w czasie 10ms. Następnie wartość ta składowana jest w zmiennej globalnej. Wiem, wiem, program główny jest lekko przerośnięty ale to dlatego, że robiłem testy, chcę jeszcze wprowadzić do niego funkcje przeliczającą odczytaną częstotliwość na RGB i stąd kompletny brak optymalizacji.
Jak odczytać kolor w postaci RGB?
Po pierwsze trzeba skalibrować czujnik. Czyli odczytujemy wartości częstotliwości dla kanałów RGB dla białego koloru i składujemy je jako zmienne (lub stałe) Rb,Gb,Bb. To samo robimy dla koloru czarnego i składujemy je jako Rc, Gc, Bc. A następnie trzeba przeskalować otrzymaną wartość dla danego koloru według następującego wzoru( (X-Xc)/(Xb-Xc))*255. Gdzie X to odczytana wartość danego kanału dla wybranego koloru, (RGB), Xc to wartość dla wybranego kanału dla koloru czarnego, a Xb to wartości dla koloru białego. Jednym matematycznym zdaniem X={R,G,B}.
I to tyle w temacie. Zapraszam do korzystania. I nie czepiać sie programu głównego bo służy on tylko do testów i jest całkowicie niezoptymalizowany. Można się czepiać wyłącznie procedur i ewentualnie klas