Veri yapılarına yönelik içeriklerin bir çoğu herhangi bir kullanım şekli (use-case) senaryosuna değinmek yerine sadece ne olduğuna dair genel bir bilgi veriyor. Bu tarz öğrenme şekli benimle çok uyuşmadığı ve öğrenme metodu benim gibi olan bireylerin varlığı için temel veri yapılarından olan bağlı listeyi (linked list) kendimce bir tane kullanım senaryosuna yedirerek bir yazı yazmak istedim.
Yukarda bulunan görsel üzerinden bağlı liste için genel terminolojiyi konuşabiliriz.
- Hepsi kendi başına bir birey (node <-> düğüm)
- En solda bulunan arkadaş baş düğümümüz (head, root node),
- En sağda duran ve halinden çok memnun gözükmeyen arkadaşımız ise son düğümümüz (tail node).
- Bu 4 arkadaşın bir araya gelmesi ile oluşan yapıyı ise bağlı liste (linked list) olarak adlandırabiliriz. Daha spesifik isimlendirecek olursak bu tek yönlü bir bağlı listedir(single linked list). (Çift taraflı bağlı listenin (double linked list) bu görsele göre nasıl olacağını bir canlandırabilirsiniz.)
Gelelim bir kullanım senaryosuna, donanımın bize sağladığı bir timer mekanizmasını kullanarak kendi yazılımsal timer modülümüzü yazmaya çalışacağız. Parkura çıktığımızı ve kronometremizi tetiklediğimizi düşünelim(System tick olarak düşünebiliriz.) Yaldır yaldır koşmaya başladık (askeri lise mülakatında öyle koşmuştum, tam tamına 400 metre) bu sürecinin sonunda neyi hedeflediğimizi bir çıkaralım.
- Bitirmek istediğimiz bir süre olması gerekiyor, [Interval]
- Hedeflediğimiz sürede parkuru bitirdiğimizde devam mı edeceğiz yoksa tamam kardeşim diyerek yere mi yığılıyoruz [Single Shot or Auto Reloaded Timer]
- Sürenin sonunda almamız gereken bir aksiyon var mıdır ? Bir su içelim ? [Callback when the timer expired]
- Koşunun ortasında bakmak isteyeceğimiz diğer bir olay ise kalan zaman. [remaining time]
- Koşuya ne zaman başladığımız [starting timestamp]
Koda dökmek istersek;
typedef enum timer_type
{
SHOT,
LOOP
}timer_type_e;
typedef struct timer_s
{
struct timer_s* timer_list; ///< Next entry in the single linked list
timer_type_e type; ///< Single Shot or auto - reloaled
void *arg; ///< Placeholder for argument will pass when the timer is expired.
void (*exp_func)(void *); ///< Call the callback function When the timer is expired
ut32_timer remaining_time_to_expire; ///< Remaining time to the fire!
ut32_timer starting_timestamp; ///<Placeholder for starting timestamp
ut32_timer interval; ///< milisecond
}timer_t;
Parkurda koşmaya devam ediyoruz ve sen yaparsın diyen çocuğun(bkz) babasıda koşmaya başladı. Koşmaya yeni başlayan bireyi tutmak ve halihazırda koşan birisine bağlamak için kullandığımız nokta ise tam olarak şurası.
struct timer_s* timer_list;
İlk koşmaya başlayan kişiyi referans alarak diğer koşan bireyleri de tutmaya başladık. Peki ilk koşan kişiyi kim tutacak ? Hadi gelin;
typedef struct timer_mngmnt_s
{
timer_t* root; ///< Handle root
ut8_timer count_of_active_timers; ///< Total number of the running timers;
}timer_mngmnt_t;
static timer_mngmnt_t tmr_mngmnt = {.root = NULL, .count_of_active_timers = 0};
Şimdi parkur başına bir tane koşucuyu alalım ve yukarda bahsettiğimiz hedeflerini bize bir söylesin.
void init_timer(timer_t *tmr, timer_type_e type, void *arg, void(*exp_func)(void*), ut32_timer interval)
{
tmr -> timer_list = NULL;
tmr -> type = type;
tmr -> exp_func = exp_func;
tmr -> arg = arg;
tmr -> remaining_time_to_expire = 0;
tmr -> interval = interval;
tmr -> starting_timestamp = 0;
}
Koşmaya başlasın.
static void timer_add(timer_t *tmr)
{
if (NULL == tmr_mngmnt.root)
{
tmr_mngmnt.root = tmr;
}
else
{
timer_t *pos = tmr_mngmnt.root;
while (NULL != pos -> timer_list)
{
pos = pos -> timer_list;
}
pos -> timer_list = tmr;
}
++tmr_mngmnt.count_of_active_timers;
}
Eğer parkur boş ise koşan ilk kişiyi baş düğüm olarak belirledik. Şayet halihazırda parkurda koşan diğer kişiler var ise parkura ilk çıkan kişi üzerinden en son çıkanı bulduk ve onaşiii (onunla ilişkilendirdik yazmak istedim ama kedi klavyeye bastı ve anı kalması adına silmiyorum.)
Tabi koşmaya başlayan bireyler hayatlarının sonlarına kadar koşmayacaklar ve burada iki senaryo mevcut.
- Kişi istediği zaman koşmayı bırakabilir.
- Kişi zaten parkuru tek sefer koşmak için gelmiştir.
Şimdi şayet bu kişiyle ilişkilendirilmiş bir kişi var ise bu adam parkurdan ayrılırsa ben onu sonra nereden bulacağım ? O yüzden bu kişi parkurdan ayrılmadan yapmam gerekenler var.
Diyelim parkurda 3 kişi var. (1 -> 2 -> 3) Ve 2. arkadaşımız ayrılmak istiyor. Bu durumda 1 -> 3 arasında ilişkiyi kurmalıyım. Nasıl mı ?
static void unlink_timer(timer_t *tmr)
{
//Check timer is root ?
//Ex: root (t1) -> t2 -> t3
// (t1) == (unlinked_timer)
// root -> t2 -> t3
if (tmr_mngmnt.root == tmr)
{
tmr_mngmnt.root = tmr -> timer_list;
--tmr_mngmnt.count_of_active_timers;
return;
}
timer_t *prev = tmr_mngmnt.root;
for (timer_t* pos = tmr_mngmnt.root; NULL != pos; pos = pos -> timer_list)
{
// Ex; root (t1) -> t2 -> t3
// (t1 -- t2) == unlinked_timer
// t1 -> t3
if (prev -> timer_list == tmr)
{
prev -> timer_list = tmr -> timer_list;
--tmr_mngmnt.count_of_active_timers;
break;
}
//Update to previous one
prev = pos;
}
}
Koşan bireylerin parkura çıkmadan önce belirledikleri hedefleri bir göz gezdirelim.(linked list traverse) Ve bu hedefler doğrultusunda aksiyonlar alalım.
void timer_pool(void)
{
for (timer_t* pos = tmr_mngmnt.root; NULL != pos; pos = pos -> timer_list)
{
if ((get_systick() - pos -> starting_timestamp) >= pos -> interval)
{
/* Call expire func */
if (pos -> exp_func != NULL)
{
pos -> exp_func(pos -> arg);
}
if (pos -> type == SHOT)
{
unlink_timer(pos);
}
/* Reload the timer */
else if (pos -> type == LOOP)
{
pos -> starting_timestamp = get_systick();
}
}
}
}
Projenin Atmega328p üzerinde örnek çalışmasını görmek için github reposuna gelebilirsiniz.
Bu yazının ilhamı olan manzarama teşekkür ederek yazıyı sonlandırmak isterim.

Yorum yazabilmek için oturum açmalısınız.