/*
* IVECO_TAKO.c
*
* Created: 21.06.2022 22:02:08
* Author : brehk
*
* Sinyal/km sabitini kullanarak sinyal girdisinden hızı hesaplar
* Hızı stepper konumuna çevirip analog göstergeye yansıtır
* Her kontak açılışında stepperi referansa çeker
* her 100 metrede km sayacını artırı artışında uygun birimde artırır.
* girişler
* - tako dijital input
* - referans dijital input
* - LDR için ADC input
* - debugging için ADC input
* - kontak dijital inputu
* çıkışlar
* - stepper fazları 4 tane
* - SPI master pinleri
* - hc595 latch pini
* - debugging led
*
*/
/*
Hız hesaplaması tako sinyali periyodunun hareketli ortalamasıyla yapılacak
Hesaplamayı basitleştirmek için önceden hesaplanmış bir sabitin, sinyal periyoduna bölünmesiyle heaplanacak.
Her sinyal geldiğinde sinyal periyodu bir değişkene yazılıp hız hesaplanacak.
Bir sinyal sayacı da artırılarak gidilen yolu hesaplamakta kullanılacak
SABİT, kilometre başına sinyal sayısı, timer1 tik süresi(sn), stepper adım açısı( derece) ve
gösterge adım açısı parametrelerini kullanarak bir kez hesaplanacak. Bu sayede bu bu kod bu parametreleri bilinen her göstergede çlışacaktır
teker turu başına şaft devri= 4,625
teker turu başına pulse 4*4,625=18,5
km başına teker turu 100.000/241,5 =414
km başına pulse 414*18,5=7.659 pulse
saatte 100km için saniyede 100*7.659/3600=212,75 pulse
pulse periyotu 1 kmh için 1/(212,75/100)=0,47 saniye
hız 0,47/pulse periyodu(saniye) olarak kmh cinsinden hesaplanmış olur.
Timer tik periyodunu da saniye cinsinden tespit ettikten sonra
1 kmh için temel timer tik sayısı 0,47 / (timer tik periyodu) olur
hız= temel timer tik sayısı / pulse timer tik sayısı olur.
sonraki adımda hızı step cinsinden çevirmek olacak
1kmh için adım sayısının tespiti için
SABİT=(3600/tik_KM)*(FCPU/prscl)*(ADIM/KM)*(örnek_sayısı)
örnek sayısı: hareketli ortalama için toplanacak örnek sayısı.
dışarıda hesaplanıp const long SABIT değişkenine atanır.
Mcu, hız hesabı için SABIT/Tik_periyodu formulünü kullanır.
*/
#define F_CPU 8000000
#include <avr/io.h>
#include <stdlib.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <avr/eeprom.h>
#include "avr/iom8a.h"
#define SABIT 323465l
#define KM_TIK 7659 // kilometredeki tik sayısı
#define TIMER_PRSCL 256
// kadran parametreleri
#define STEP_TUR 4096l
#define KM_180 93l // 180 derece ibrehareketinin kaç kmh süpürdüğü
#define REF_OFFSET 50l // referans sensörünün 0 kmh'den kaç step adimi geride olduğu
#define OLCEK_KM 10l // ibre ölçeğinin farklı olduğu kmh genelde 10 ya da 20 olur
// OLCEK_KM ile 0 kmh arası süpürülen kadranın normale oranı.
// meseala normalde: 1km/derece iken 0-10 arası 2km/derece ise oran 2 olur
#define OLCEK_ORAN 2l
#define LATCH_DDR DDRB
#define LATCH_PORT PORTB
#define LATCH_PIN PINB
#define LATCH_PIN_NO 1
#define LATCH_H (LATCH_PORT|=1<<LATCH_PIN_NO)
#define LATCH_L (LATCH_PORT&=~(1<<LATCH_PIN_NO))
#define LATCH_L_H_L LATCH_L;LATCH_H;LATCH_L
#define DATA_DDR DDRB
#define DATA_PORT PORTB
#define DATA_PIN PINB
#define DATA_PIN_NO 3
#define DATA_H (DATA_PORT|=1<<DATA_PIN_NO)
#define DATA_L (DATA_PORT&=~(1<<DATA_PIN_NO))
#define CLOCK_DDR DDRB
#define CLOCK_PORT PORTB
#define CLOCK_PIN PINB
#define clock_PIN_NO 5
#define CLOCK_H (CLOCK_PORT|=1<<clock_PIN_NO)
#define CLOCK_L (CLOCK_PORT&=~(1<<clock_PIN_NO))
#define CLOCK_L_H_L CLOCK_L;CLOCK_H;CLOCK_L;
#define SHIFT1 DATA_H;_delay_us(100);CLOCK_L;_delay_us(100);CLOCK_H;_delay_us(100);CLOCK_L;_delay_us(100)
#define SHIFT0 DATA_L;_delay_us(100);CLOCK_L;_delay_us(100);CLOCK_H;_delay_us(100);CLOCK_L;_delay_us(100)
#define LED_DDR DDRC
#define LED_PIN PINC2
#define LED_PORT PORTC
#define LED_ON (LED_PORT|=1<<LED_PIN)
#define LED_OFF (LED_PORT&=~(1<<LED_PIN))
#define LED_TOGGLE LED_PORT^=1<<LED_PIN
#define LDR_DDR DDRC
#define LDR_PORT PORTC
#define LDR_PIN PINC
#define LDR_PIN_NO 1
#define KONTAK_DDR DDRD
#define KONTAK_PORT PORTD
#define KONTAK_PIN PIND
#define KONTAK_PIN_NO 4
#define KONTAK_OKU ((KONTAK_PIN>>KONTAK_PIN_NO)&1)
// anot veya katod display seçimi
//#define ANOT
#define KATOT
// Segment ve basamakların hc595 bağlantıları
// 0: ikinci(taşma registeri) H biti, 15: birinci(taşan register) A biti
// bit shift operatörünün sağı değiştirilecek
#define A 1<<9
#define B 1<<13
#define C 1<<4
#define D 1<<1
#define E 1<<8
#define F 1<<11
#define G 1<<3
// basamaklar için sadece pin konumu
#define D0 1<<2 //en yüksek basamak
#define D1 1<<5
#define D2 1<<6
#define D3 1<<10
#define D4 1<<12
#define D5 1<<14 //en düşük basamak
// stepper ayarları
#define S1A_DDR DDRD
#define S1A_PORT PORTD
#define S1A_PIN_NO 0
#define S1A_H (S1A_PORT|=1<<S1A_PIN_NO)
#define S1A_L (S1A_PORT&=~(1<<S1A_PIN_NO))
#define S1B_DDR DDRD
#define S1B_PORT PORTD
#define S1B_PIN_NO 1
#define S1B_H (S1B_PORT|=1<<S1B_PIN_NO)
#define S1B_L (S1B_PORT&=~(1<<S1B_PIN_NO))
#define S2A_DDR DDRD
#define S2A_PORT PORTD
#define S2A_PIN_NO 2
#define S2A_H (S2A_PORT|=1<<S2A_PIN_NO)
#define S2A_L (S2A_PORT&=~(1<<S2A_PIN_NO))
#define S2B_DDR DDRD
#define S2B_PORT PORTD
#define S2B_PIN_NO 3
#define S2B_H (S2B_PORT|=1<<S2B_PIN_NO)
#define S2B_L (S2B_PORT&=~(1<<S2B_PIN_NO))
// stepper farklı sürüş modları
//#define TEKFAZ
//#define CIFTFAZ
#define YARIMADIM
// tek faz sürme modu
#ifdef TEKFAZ
#define DURUM_0 S1A_L;S1B_L;S2A_L;S2B_L; //tümü kapalı
#define DURUM_1 S1A_H;S1B_L;S2A_L;S2B_L;
#define DURUM_2 S1A_L;S1B_H;S2A_L;S2B_L;
#define DURUM_3 S1A_L;S1B_L;S2A_H;S2B_L;
#define DURUM_4 S1A_L;S1B_L;S2A_L;S2B_H;
#endif
// çift faz sürme modu
#ifdef CIFTFAZ
#define DURUM_0 S1A_L;S1B_L;S2A_L;S2B_L; //tümü kapalı
#define DURUM_1 S1A_H;S1B_H;S2A_L;S2B_L;
#define DURUM_2 S1A_L;S1B_H;S2A_H;S2B_L;
#define DURUM_3 S1A_L;S1B_L;S2A_H;S2B_H;
#define DURUM_4 S1A_H;S1B_L;S2A_L;S2B_H;
#endif
// yarım adım sürme modu
#ifdef YARIMADIM
#define DURUM_0 S1A_L;S1B_L;S2A_L;S2B_L; //tümü kapalı
#define DURUM_1 S1A_H;S1B_L;S2A_L;S2B_L;
#define DURUM_15 S1A_H;S1B_H;S2A_L;S2B_L;
#define DURUM_2 S1A_L;S1B_H;S2A_L;S2B_L;
#define DURUM_25 S1A_L;S1B_H;S2A_H;S2B_L;
#define DURUM_3 S1A_L;S1B_L;S2A_H;S2B_L;
#define DURUM_35 S1A_L;S1B_L;S2A_H;S2B_H;
#define DURUM_4 S1A_L;S1B_L;S2A_L;S2B_H;
#define DURUM_45 S1A_H;S1B_L;S2A_L;S2B_H;
#endif
#define REF_DDR DDRC
#define REF_PORT PORTC
#define REF_PIN PINC
#define REF_PIN_NO 3
#define REF_OKU ((REF_PIN>>REF_PIN_NO)&1)
#define TAKO_DDR DDRB
#define TAKO_PORT PORTB
#define TAKO_PIN PINB
#define TAKO_PIN_NO 0
#define TAKO_OKU ((TAKO_PIN>>TAKO_PIN_NO)&1)
#define TAKO_TOGGLE TAKO_PORT^=1<<TAKO_PIN_NO;
/*
#define R_0 (A|B|C|D|E|F);
#define R_1 (B|C);
#define R_2 (A|B|G|E|D);
#define R_3 (A|B|G|C|D);
#define R_4 (B|C|G|F);
#define R_5 (A|F|G|C|D);
#define R_6 (A|F|G|C|D|E);
#define R_7 (A|B|C);
#define R_8 (A|B|C|G|E|D|F);
#define R_9 (A|B|C|D|F|G);
uint16_t seg[10]={R_0,R_1,R_2,R_3,R_4,R_5,R_6,R_7,R_8,R_9};
*/
uint16_t seg[12]={(A|B|C|D|E|F),(B|C),(A|B|G|E|D),(A|B|G|C|D),(B|C|G|F),(A|F|G|C|D),
(A|F|G|C|D|E),(A|B|C),(A|B|C|G|E|D|F),(A|B|C|D|F|G),0,0};
// basamak maske değişkeni
// her bir basamak ayrı ayrı binary olarak ve tüm basamaklar binary olarak
//uint16_t bas_maske[7]={~(D0) , ~(D1) , ~(D2) , ~(D3) , ~(D4) , ~(D5) , D0|D1|D2|D3|D4|D5 };
uint16_t bas_maske[7]={~(D5) , ~(D4) , ~(D3) , ~(D2) , ~(D1) , ~(D0) , D0|D1|D2|D3|D4|D5 };
//const uint16_t
const uint16_t kalibre;
uint16_t disp[6]={1,2,3,4,5,6};
uint8_t EEMEM eeprom_basamak[7]={0,1,2,3,4,5,6};
uint8_t b_degeri[6];//= {1,2,3,4,5,6};
// rakam segment değişkenleri, 10 rakam ve bir karanlık
// seg[i]: binary olarak "i" rakamının gösterimi için
// açılacak segmentlerin hc595'in bacaklarında isabet ettiği yerler
//uint16_t seg[11];
volatile uint32_t perT;
int32_t gost[8],sabit,hedef,konum;
uint16_t sinyal,bufUint,bufUint1,s0,s1,tov0,okunanADC,ovf10,per[8],tiksay,kirim;
int16_t hiz,ovf1,ovf2,adim_per;
uint8_t asama,j,k,l,step_s;
uint8_t buffbyte,buffbyte1,p_i,p_b,per_up;
void km_up(void);
void disp_sur (void);
void disp_set (void);
void stepper_adim(int8_t yon);
void eeprom_put(void);
void eeprom_get (void);
void ibre_init(void);
void hiz_hesap(void);
ISR(TIMER0_OVF_vect)
{
tov0++;
ovf1++;
ovf2++;
}
ISR(ADC_vect)
{
okunanADC=ADCH;
}
ISR(TIMER1_OVF_vect)
{
b_degeri[0]=1;
}
ISR(TIMER1_COMPA_vect)
{
TCNT1=0;
hedef=0;
p_i=0;
per_up=0;
}
ISR(TIMER1_CAPT_vect)
{
per[p_i]=ICR1;
TCNT1=0;
tiksay++;
p_i++;
if(p_i>7){per_up=1;p_i=0;}// ilk 8 sinyal toplandıktan sonra hesap yap
if(per_up)// hareketli ortalama her sinyal gelişinde son 8 sinyali toplar.
{
perT=0;
for(uint8_t i=0;i<8;i++)
{
perT+=per[i];
}
perT/=8;
hiz_hesap();
if(hedef>3500)hedef=3500;
}
LED_TOGGLE;
if(tiksay>KM_TIK)
{
km_up();
tiksay=0;
}
}
int main(void)
{
LED_DDR|=1<<LED_PIN;
LATCH_DDR|=1<<LATCH_PIN_NO;
// SPI pinlerini output ayarla
DDRB|=1<<PINB3 | 1<<PINB5 | 1<<PINB2;
LATCH_L;
// SPI ayarı
// en düşük değerli bit önce transfer edilir.
// spi hızı maksimumda
SPCR |= (1<<SPE)|(1<<MSTR)|(1<<DORD);
SPSR |= 1<<SPI2X;
// timer0 ekranı zaman takibi için kullanılacak, makul bir taşma kesme süresiyle bir
TCCR0|= 0b10; //prscl 1024
TIMSK|=1<<TOIE0;
sei();
//Stepper ayarları çıkışları
S1A_DDR |= 1<<S1A_PIN_NO;
S1B_DDR |= 1<<S1B_PIN_NO;
S2A_DDR |= 1<<S2A_PIN_NO;
S2B_DDR |= 1<<S2B_PIN_NO;
//referans girişi pullup açık
REF_DDR&=~(1<<REF_PIN_NO);
REF_PORT|=1<<REF_PIN_NO;
//LDR
LDR_DDR&=~(1<<LDR_PIN_NO);
LDR_PORT|=1<<LDR_PIN_NO;
//kontak sense setup
KONTAK_DDR&=~(1<<KONTAK_PIN_NO);
KONTAK_PORT|=1<<KONTAK_PIN_NO; // pullup açık
//ADC setup
DDRC&=~(1<<PINC5);
PORTC&=~(1<<PINC5);
ADMUX|= 1<<REFS1 | 1<<ADLAR| 1<<REFS0;// | 1<<2 ;// adc5 pin28
ADCSRA|= 1<<ADEN | 1<<ADIE | 1<<ADFR | 0b101;
ADCSRA|= 1<<ADSC;
//TAKO sinyal girişi
TAKO_DDR|=(1<<TAKO_PIN_NO);
//Timer 1 input capture CTC
//TCCR1B|=1<<WGM13 | 1<<WGM12;
TCCR1B|=0b100; // prscl 0b1:1 0b10:8 0b11:64 0b100:256 0b101:1024
OCR1A=20000;
TIMSK|=1<<TICIE1;// | 1<<OCIE1A | 1<<TOIE1 ;
/*
eeprom_put();
while(1);
*/
eeprom_get();
ibre_init();
_delay_ms(300);
while(REF_OKU<1){stepper_adim(1);_delay_ms(1);}
konum=-REF_OFFSET;
disp_set();
while (1)
{
if(KONTAK_OKU)
{
DURUM_0;//stepper gücünü keser
eeprom_put();
_delay_ms(500);
}
if(tov0>4)
{
tov0=0;
disp_sur();
}
if(ovf1>adim_per)// 4050 için ölçülmüş 1 saniye periyot
{
ovf1=0;
if(hedef>konum)
{
stepper_adim(-1);
konum++;
}
else if(hedef<konum)
{
stepper_adim(1);
konum--;
}
// kaba bir pid daha doğrusu p
adim_per=75-abs(hedef-konum);
if(adim_per<5)adim_per=5;
}
if(ovf2>okunanADC)
{
TAKO_TOGGLE;
ovf2=0;
}
}
}
void disp_sur (void)
{
switch(asama)
{
case 0:
LATCH_L_H_L;// en son gönderilen disp değerini shift reg çıkışlarına yansıtma
// en düşük bitten başlanıyor, gönderime alt 8 biti gönderme
SPDR = 0xff&disp[j];
asama++;
break;
case 1:
SPDR=disp[j]>>8;
asama=0;
j++;//basamak takip değişkeni
if(j>5)//altıncı basamaktan sonra başa döner
{
j=0;
}
break;
default: asama=0;j=0;
}
}
void disp_set (void)
{
uint8_t i=0;;
for(i=0;i<5;i++)
{
if(b_degeri[i]==0)b_degeri[i]=11;// b_degeri[i]=11'in segment gösterim karşılığı hepsi kapalıdır >> seg[11]=0;
else break;
}
for(i=0;i<6;i++)
{
// gösterilecek segmentleri aç | bütün basamakları kapat & gösterilecek basamağı aç
// i=0: 100 binler basamağı, i=5: 1'ler basamağı
#ifdef KATOT
disp[i]= ( seg[b_degeri[i]] | bas_maske[6] ) & bas_maske[i];
#endif
#ifdef ANOT
disp[i]= ~(( seg[b_degeri[i]] | bas_maske[6] ) & bas_maske[i]);
#endif
}
}
void stepper_adim(int8_t yon)
{
//gösterge konumunu referansa göre adım cinsinden takip eder.
step_s+=yon;// step_s stepperin 4 farklı konumu temsil eder 1 ila 4 değerlerini alabilir
#ifdef YARIMADIM
if(step_s>8)step_s=1; // stepper durumu 4'ü aşarsa 1'den devam et
if(step_s==0)step_s=8; // stepper durumu 0 olursa 4'ten devam et
switch (step_s)
{
case 1:DURUM_1;break;
case 2:DURUM_15;break;
case 3:DURUM_2;break;
case 4:DURUM_25;break;
case 5:DURUM_3;break;
case 6:DURUM_35;break;
case 7:DURUM_4;break;
case 8:DURUM_45;break;
default: DURUM_0;break;
}
#else
if(step_s>4)step_s=1; // stepper durumu 4'ü aşarsa 1'den devam et
if(step_s==0)step_s=4; // stepper durumu 0 olursa 4'ten devam et
switch (step_s)
{
case 1:DURUM_1;break;
case 2:DURUM_2;break;
case 3:DURUM_3;break;
case 4:DURUM_4;break;
default: DURUM_0;break;
}
#endif
}
void eeprom_put (void)
{
for(uint8_t i=0;i<6;i++)
{
eeprom_update_byte((uint8_t*) &eeprom_basamak[i],b_degeri[i]);
while (!eeprom_is_ready());
}
eeprom_update_word((uint16_t*) 10,tiksay);
while (!eeprom_is_ready());
}
void eeprom_get (void)
{
for(uint8_t i=0;i<6;i++)
{
b_degeri[i]=eeprom_read_byte(& eeprom_basamak[i]);
while (!eeprom_is_ready());
}
tiksay=eeprom_read_word((uint16_t*)10);
while (!eeprom_is_ready());
}
void km_up(void)// her 100 metrede bir çalışır.
{
int8_t i=0;
b_degeri[5]++;
for(i=5;i>0;i--)
{
// basamak değeri 9'u geçen basamak sıfırlanır
// bir sonraki basamak değeri 1 artırılır
if(b_degeri[i]==10)
{
b_degeri[i]=0;
b_degeri[i-1]++;
if(b_degeri[i-1]>10)b_degeri[i-1]=1;
}
}
// sayıdan önceki basamakları kapat
for(i=0;i<5;i++)
{
if(b_degeri[i]==0)b_degeri[i]=11;// b_degeri[i]=11'in segment gösterim karşılığı hepsi kapalıdır >> seg[11]=0;
else break;
}
disp_set();
}
void basamak(int16_t sayi)//basamakları hesaplar basamak değerlerini b[i] array değişkene atar ve 7segment display için binary registerlarını tayin eder
{
uint8_t i;
{
gost[0]=sayi;
for(i=0;i<9;i++)//bas:basamak sayısını mod için yardımcı değerleri hesaplar
{
gost[i+1] = gost[i]/10;
if(gost[i+1]==0)i=8;
}
for(i=0;i<6;i++)// 10^i basamağının değerlerini hesaplar
{
b_degeri[i] = gost[i]-gost[i+1]*10;
}
}
}
void shift_16(uint16_t val)
{
for(char i=0;i<16;i++)
{
if(1&(val>>i)){DATA_PORT |=(1<<DATA_PORT);}
else {DATA_PORT &= ~(1<<DATA_PIN_NO);}
CLOCK_L_H_L;
if (i==15){LATCH_L_H_L;}
}
}
void hiz_hesap (void)
{
hedef=SABIT/perT;
if(hedef<kirim) hedef/=2;
else hedef-=kirim;
hedef+=REF_OFFSET;
}
void ibre_init (void/*parametreler*/)
{
kirim= OLCEK_KM*STEP_TUR/KM_180/2;
kirim/= OLCEK_ORAN;
}