// Arduino Eldiven Simulator Kumandası
// V0.5 Alfa - Zafer ŞAHİN
#include "I2Cdev.h"
#include "MPU6050_6Axis_MotionApps20.h"
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
#include "Wire.h"
#endif
#include <EEPROM.h>
// TEMEL AYARLAR.
#define CH_ANL 4 // kanal sayısı - ANALOG ( potansiyometre, v.b. )
#define CH_DGT 4 // kanal sayısı - DIGITAL ( anahtar v.b. )
#define CENTER_VAL 1500 // varsayılan servo merkez sinyali
#define PWM_H_LEN 500 // varsayılan pwm sinyal genişliğinin yarısı, yani maksimum sinyal = CENTER_VAL + PWM_H_LEN, minimum sinyal = CENTER_VAL - PWM_H_LEN
#define PPM_FRLEN 22500 // mikrosaniye cinsinden PPM çerçeve uzunluğu (1ms = 1000µs)
#define PPM_PULSELEN 300 // PPM başlangıç darbesinin uzunluğu
#define ONSTATE 0 // darbelerin kutuplaması, 0 negatif, 1 pozitif
#define SIGPIN 10 // PPM sinyali için arduino'nun çıkış pini
#define T_LIMIT 255 // EPA ve TRIM için limit değer
#define TRIM_ENABLE 0 // TRIM ayarlamasının açık olup olmadığını belirtir. 0 kapalı, >0 açık.
#define EPA_ENABLE 0 // EPA ayarlamasının açık olup olmadığını belirtir. 0 kapalı, >0 açık.
const int CHANNELS = CH_ANL + CH_DGT; // toplam kanal sayısı - ANALOG + DIJITAL
int ppm_val[CHANNELS]; // kanallarda tutulacak sinyal değerleri
int input_pins[CHANNELS] = {18,19,20,21,14,15,16,17}; // A4, A5, A6, A7, A0, A1, A2, A3
int trim_pins[2*CH_ANL] = {2,3,4,5,6,7,8,9}; // D2, D3, D4, D5, D6, D7, D8, D9
int trim_addr[CH_ANL] = {11,13,15,17}; // trim değerlerini depolayacak EEPROM adresleri, integer depolayabilmek için ardışık 2 adres gerekiyor
int trim_val[CH_ANL] = {0,0,0,0}; // trim için başlangıç değerleri, TRIM_ENABLE 0 için gerekli
int epa_addr[2*CH_ANL] = {21,23,25,27,29,31,33,35}; // epa değerlerini depolayacak EEPROM adresleri, integer depolayabilmek için ardışık 2 adres gerekiyor
int epa_val[2*CH_ANL] = {0,0,0,0}; // epa için başlangıç değerleri, EPA_ENABLE 0 için gerekli
int pwm_max = CENTER_VAL + PWM_H_LEN; // PWM servo sinyali için üst limit
int pwm_min = CENTER_VAL - PWM_H_LEN; // PWM servo sinyali için alt limit
MPU6050 mpu;
bool dmpReady = false;
uint8_t mpuIntStatus;
uint8_t devStatus;
uint16_t packetSize;
uint16_t fifoCount;
uint8_t fifoBuffer[64];
Quaternion q; // [w, x, y, z] quaternion container
VectorInt16 aa; // [x, y, z] accel sensor measurements
VectorInt16 aaReal; // [x, y, z] gravity-free accel sensor measurements
VectorInt16 aaWorld; // [x, y, z] world-frame accel sensor measurements
VectorFloat gravity; // [x, y, z] gravity vector
float euler[3]; // [psi, theta, phi] Euler angle container
float ypr[3]; // [yaw, pitch, roll] yaw/pitch/roll container and gravity vector
float throttle,roll,pitch,yaw;
int dummy;
void setup(){
/* ######## ######## MPU6050 KODU ######## ########
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
Wire.begin();
TWBR = 24;
#elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE
Fastwire::setup(400, true);
#endif
mpu.initialize();
devStatus = mpu.dmpInitialize();
// offset değerleri
mpu.setXGyroOffset(220);
mpu.setYGyroOffset(76);
mpu.setZGyroOffset(-85);
mpu.setZAccelOffset(178:coolxf:;
if (devStatus == 0) {
mpu.setDMPEnabled(true);
packetSize = mpu.dmpGetFIFOPacketSize();
} else {
dummy = 0;
}
######## ######## MPU6050 KODU ######## ######## */
for (int i = 0; i < CHANNELS; ++i) {
if ( i < CH_ANL){
pinMode(input_pins[i], INPUT);
ppm_val[i] = CENTER_VAL;
#if TRIM_ENABLE > 0
// trim değerleri EEPROM'dan okunur ve T_LIMIT değeri ile kıyaslanarak, gerekli ise T_LIMIT değerine çekilir.
trim_val[i] = (EEPROM_rd_int(trim_addr[i]) > T_LIMIT ) ? T_LIMIT : EEPROM_rd_int(trim_addr[i]);
trim_val[i] = (trim_val[i] < -T_LIMIT ) ? -T_LIMIT : trim_val[i];
#endif
#if EPA_ENABLE > 0
// epa değerleri EEPROM'dan okunur ve T_LIMIT değeri ile kıyaslanarak, gerekli ise T_LIMIT değerine çekilir.
// epa_val[(2*i)+0] -> epa alt değer
epa_val[(2*i)+0] = (EEPROM_rd_int(epa_addr[(2*i)+0]) > T_LIMIT ) ? T_LIMIT : EEPROM_rd_int(epa_addr[(2*i)+0]);
epa_val[(2*i)+0] = (epa_val[(2*i)+0] < -T_LIMIT ) ? -T_LIMIT : epa_val[(2*i)+0];
// epa_val[(2*i)+1] -> epa tepe değer
epa_val[(2*i)+1] = (EEPROM_rd_int(epa_addr[(2*i)+1]) > T_LIMIT ) ? T_LIMIT : EEPROM_rd_int(epa_addr[(2*i)+1]);
epa_val[(2*i)+1] = (epa_val[(2*i)+1] < -T_LIMIT ) ? -T_LIMIT : epa_val[(2*i)+1];
#endif
} else {
pinMode(input_pins[i], INPUT_PULLUP);
ppm_val[i] = pwm_min;
}
}
pinMode(SIGPIN, OUTPUT);
digitalWrite(SIGPIN, !ONSTATE);
cli();
TCCR1A = 0; // tüm TCCR1 yazmaçları sıfırlanıyor
TCCR1B = 0;
OCR1A = 100; // compare match register,
TCCR1B |= (1 << WGM12); // CTC modu açılır, böylece "timer compare interrupt" bayrağının da ayarlanması ile beraber,
// "OCR1A" değeri her zamanaşımına uğradığında "ISR(TIMER1_COMPA_vect)" fonsiyonunun çağrılması sağlanmış olur.
TCCR1B |= (1 << CS11); // 8 prescaler: 16Mhz'de 0,5 mikrosaniye
TIMSK1 |= (1 << OCIE1A); // timer compare interrupt
sei();
}
void loop(){
/* ######## ######## MPU6050 KODU ######## ########
mpuIntStatus = mpu.getIntStatus();
fifoCount = mpu.getFIFOCount();
if ((mpuIntStatus & 0x10) || fifoCount == 1024) {
mpu.resetFIFO();
} else if (mpuIntStatus & 0x02) {
while (fifoCount < packetSize) fifoCount = mpu.getFIFOCount();
mpu.getFIFOBytes(fifoBuffer, packetSize);
fifoCount -= packetSize;
mpu.dmpGetQuaternion(&q, fifoBuffer);
mpu.dmpGetGravity(&gravity, &q);
mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
yaw = ypr[0] * 180/M_PI;
pitch = ypr[1] * 180/M_PI;
roll = ypr[2] * 180/M_PI;
throttle = dummy;
}
######## ######## MPU6050 KODU ######## ######## */
for (int i = 0; i < CHANNELS; ++i) {
if ( i < CH_ANL){
analogRead(input_pins[i]); // normal şartlarda analogRead'ın bu kadar sık okunması nedeni ile bu ekstra analogRead'a
// gerek olmadığını düşünüyorum fakat Sümer abi nedeni ile koyuldu :D ...
ppm_val[i] = trim_val[i] + map(analogRead(input_pins[i]), 0, 1023, pwm_min+epa_val[(2*i)+0], pwm_max+epa_val[(2*i)+1]); // analogRead ile okunan 0 ile 1023 arasındaki değerler, 1000 - 2000 mikrosaniye arasındaki sürelere dönüştürülür.
} else {
ppm_val[i] = map(digitalRead(input_pins[i]), 0, 1, pwm_min, pwm_max);
}
}
}
// EEPROM'un sadece byte değerleri kabul etmesi sebebi ile, ardışık olarak 2 byte boyundaki adresler kullanarak, integer kaydeden rutin...
void EEPROM_wr_int(int address, int value){
byte b_one = ((value >> 0) & 0xFF);
byte b_two = ((value >> :coolxf: & 0xFF);
EEPROM.write(address + 0, b_one);
EEPROM.write(address + 1, b_two);
}
// EEPROM'un sadece byte değerleri kabul etmesi sebebi ile, ardışık olarak 2 byte boyundaki adresler kullanarak, kaydedilmiş integer değerini okuyan rutin...
int EEPROM_rd_int(int address){
int i_one = EEPROM.read(address + 0);
int i_two = EEPROM.read(address + 1);
return ((i_one << 0) & 0xFF) + ((i_two << :coolxf: & 0xFF);
}
ISR(TIMER1_COMPA_vect){
static boolean bState = true; // PPM zincirinde, her bir kanalın başlangıcındaki 300 mikrosaniyelik darbeyi oluşturmak için kullanılacak mantıksal değişken.
TCNT1 = 0; // İşe başlamadan önce Timer sıfırlanır,
if(bState) {
digitalWrite(SIGPIN, ONSTATE); // 300 mikrosniyelik PPM darbesi başlangıcı için sinyal seviyesi (ONSTATE) hazırlanır.
OCR1A = PPM_PULSELEN * 2; // 0,5 mikrosaniyeye karşılık gelen tık süresi nedeni ile 300 mikrosaniyeyi elde etmek için OCR1A, 600 olarak belirlenir.
bState = false; // ISR(TIMER1_COMPA_vect) fonksiyonunun sonraki çağrılışında, PPM darbesinin kalanının üretebilmek amacı ile 300 milisaniyelik kısmın bittiği belirtilir
} else {
static byte bCurChan = 0; // kaç tane kanal varsa, bu kanalları hatırlamak için kullanacağımız değişken.
static unsigned int iPPMRest = 0; // PPM darbesinin geri kalan süresini oluşturmak için kullanılacak değişken.
digitalWrite(SIGPIN, !ONSTATE); // PPM darbesinin başlangıcı bitirilip, kalan süre için ters kutuplu darbe sinyali başlatılır.
bState = true; // ISR(TIMER1_COMPA_vect) fonksiyonunun sonraki çağrılışında, 300 mikrosaniyelik darbe başlangıcı üretebilmek için mantıksal değişken ayarlanır.
if(bCurChan < CHANNELS){ // 0'dan başlayarak her kanal sıra ile işlenir.
OCR1A = (ppm_val[bCurChan] - PPM_PULSELEN) * 2; // loop içerisinde oluşturulmuş olan, mevcut kanala ait ppm_val değerinden, PPM çerçevesinin başlangıcı olan 300 mikrosaniyelik kısım çıkarılır.
// 0,5 mikrosaniyeye karşılık gelen tık süresi nedeni ile 2 ile çarpılarak darbenin kalanına ait OCR1A değeri oluşturulur.
iPPMRest = iPPMRest + ppm_val[bCurChan]; // PPM senkronizasyon darbesinin oluşturulması için kullanılacak, iPPMRest değişkeni hazırlanır.
// Sıra ile toplanarak kanlların oluşturulması sırasındaki geçen zaman tutulur
bCurChan++;
} else { // tüm kanallar sıra ile işlenip, oluşturulduktan sonra en son olarak senkronizasyon darbesinin oluşturulması gerekir.
bCurChan = 0; // kanalların tekrar 0'dan işlenebilmesi için bCurChan sıfırlanır
iPPMRest = iPPMRest + PPM_PULSELEN; // iPPMRest değişkeni, önceki kanalların oluşturulması esnasında geçen zamanı tutmaktadır,
// darbe başlangıcını oluşturan 300 mikrosaniye de eklenerek, PPM çerçevesine ait olan geçen toplam zaman bulunmuş olur.
OCR1A = (PPM_FRLEN - iPPMRest) * 2; // senkronizasyon darbesinin kalanını üretebilmek amacı ile, iPPMRest ile tanımlanmış,
// şimdiye kadar geçen zaman toplam PPM çerçeve süresinden çıkarılarak, kalan süre hesaplanır ve tabii ki OCR1A değerini oluşturmak için 2 ile çarpılır.
iPPMRest = 0; // geçen süreyi hesaplamak için kullanılan iPPMRest değişkeni sıfırlanır.
}
}
}