MSP430 İLE COCOOS RTOS UYGULAMASI

 

MSP430 İLE COCOOS RTOS UYGULAMASI


Merhabalar, bu yazımda size geçen seneden beri varlığından haberdar olduğum ancak şimdi uygulama fırsatı bulduğum CocoOS’tan bahsedeceğim. Öncelikle CocoOS (Hindistan Cevizi İşletim Sistemi) nedir ondan bahsedeyim. Yazının başlığında RTOS (Real Time Operating System/Gerçek Zamanlı İşletim Sistemi) olarak bahsettim ama sitesinde CocoOS’u “cooperative task scheduler”(Kooperatif Görev Zamanlayıcısı) olarak tanımlamış. RTOS olarak nitelendirmekte bir sakınca yok bence. CocoOS, CooCox’un CoOS RTOS’u ile karıştırılmasın aynı şey değil.

ÖRNEK RTOS ÇALIŞMA DİYAGRAMI

cocoos-coocoxun-coos-rtos-rtos

CocoOS’un başlıca avantajları ücretsiz, açık kaynak ve kolay taşınabilir olmasıdır. AVR, MSP430 gibi mikrodenetleyiciler için düşünülmüştür. CocoOS’un sitesinden daha detaylı bilgi edinilebilir. Biz uygulamaya geçelim.

Aslında uzun zamandır RTOS ile ilgili uygulama yapmak istiyordum. Şuydu buydu derken yapamadım kaldı. CocoOS’u keşfettikten sonra artık zamanıdır dedim uğraşmaya başladım.

CocoOS’u kullandığınız mikrodenetleyiciye uyarlamak için clock(saat) ayarları ile ilgili kodların sağlanması ve os_tick() fonksiyonunun periyodik olarak çalıştırılması gerekiyor. CocoOS’un sitesinde bazı Atmel denetleyiciler için örnek projeler ve saat ayarlamaları verilmiş. MSP430 denetleyiciler için herhangi bir örnek, clock ayarları vs. bulamadığım için kolları sıvadım ve Atmel örneklerinden de faydalanarak MSP430 denetleyiciler için gerekli kodları hazırladım. Hazırladığım clock.h ve clock.c dosyaları aşağıdaki gibidir.

CLOCK.H BAŞLIK DOSYASI

/************************************************************************
 * Designer: Erhan YILMAZ						*
 * Application:  CocoOS clock setup codes for MSP430			*
 * Date: 20-01-2015							*
 * Description:	Configures os_tick for selected value in ms		*
 * CPUC_CLOCK value must be defined to calculate timer count value	*
 * os_tick value depending on the CPU_CLOCK. Min os_tick time is 1ms	*
 * max os_tick value is 512ms when CPUCLOK=1MHz				*
 * max os_tick value is 32ms when CPUCLOK=16MHz				*
 * os_tick usually chosen as 1 or 10 ms. Tested on the MSP430G2553	*
 * with CCS 5.5.0 and TI's compiler. Used Timer0_A3 for timing. If	*
 * desired can be use other timers. Maybe some changes, it is not 	*
 * guaranteed that works with all msp430 devices			*
 * **********************************************************************/
 
#ifndef CLOCK_H
#define CLOCK_H
 
#define CPU_CLOCK 16000000UL		//CPU Clock frequency define
 
void clock_init(uint16_t tick_ms);
 
#endif

CLOCK.C

/************************************************************************
 * Designer: Erhan YILMAZ						*
 * Application:  CocoOS clock setup codes for MSP430			*
 * Date: 20-01-2015							*
 * Description:	Configures os_tick for selected value in ms		*
 * CPUC_CLOCK value must be defined to calculate timer count value	*
 * os_tick value depending on the CPU_CLOCK. Min os_tick time is 1ms	*
 * max os_tick value is 512ms when CPUCLOK=1MHz				*
 * max os_tick value is 32ms when CPUCLOK=16MHz				*
 * os_tick usually chosen as 1 or 10 ms. Tested on the MSP430G2553	*
 * with CCS 5.5.0 and TI's compiler. Used Timer0_A3 for timing. If	*
 * desired can be use other timers. Maybe some changes, it is not 	*
 * guaranteed that works with all msp430 devices			*
 * **********************************************************************/
 
#include <msp430.h>
#include "cocoos.h"
#include "clock.h"
uint16_t wTimerValue;				// Calculated timer value register
 
void clock_init(uint16_t tick_ms) {
  uint32_t lPulses;
  uint8_t bPrescaler=0;
  TACTL=0;
  // check prescaler 1,2,4,8 values
  while(bPrescaler<4){
  lPulses = ((CPU_CLOCK * tick_ms)/1000)/(1<<bPrescaler);	// Calculate pulses count
  if(lPulses <= 0x10000)					// if reasonable
  break;							// Finish calculating
  bPrescaler++;						// else increase prescaler value
  }
 
  // Set presclaer value if pulses count reasonable and prescaler value
  // less than 4 and tick value not equal zero
  if(lPulses <= 0x10000 && lPulses > 0 && bPrescaler<4 && tick_ms != 0)
  {
  TACTL = TASSEL_2 + MC_2 + (bPrescaler<<6); // SMCLK, contmode
  }
  else
  {
  // if time interval not possible or tick value equal zero then set tick value 1ms as default
  lPulses = CPU_CLOCK/1000;
  TACTL = TASSEL_2 + MC_2;                  // SMCLK, contmode
  }
 
  // Configure timerA0(CCRO) depending on the calculated values
  wTimerValue = (uint16_t)(lPulses-1);
  CCR0 = wTimerValue;
  CCTL0 = CCIE;                             // CCR0 interrupt enabled
}
 
 
// TimerA0 interrupt vector.
#pragma vector=TIMER0_A0_VECTOR
__interrupt void Timer_A (void)
{
  CCR0 += wTimerValue;			// Add Offset to CCR0
  os_tick();				// Run CocoOS os_tick function
}

Dosyaları inceleyecek olursak, clock.h dosyasında kullanılan fonksiyon prototipi ve işlemci hızı ile ilgili tanımlama mevcuttur. Uygulamada kullanılan saat frekansı CPU_CLOCK değeri ile belirtilmelidir. Örnek olarak değer 16MHz olarak tanımlanmıştır.

Clock.c kaynak dosyasında ise saat ayarlarını gerçekleştiren clock_init fonksiyonu bulunmaktadır. Bu fonksiyon parametre olarak 16bit tam sayı değeri alır. Bu değer ile os_tick zamanının kaç ms(milisaniye) olacağı belirtilir. Uygulamayı ve clock ayarları için gerekli kodları MSP430G2553 denetleyici ile CCS üzerinde geliştirdim. Zamanlayıcı olarak TimerA0 zamanlayıcısını kullandım. Bu timer 16bit olduğu için os_tick değerini istediğimiz kadar büyük seçemeyiz. os_tick için asgari değer 1ms’dir. Benzer şekilde azami değer ise 16MHZ için 32ms, 1MHZ için ise 512ms’dir. clock_init fonksiyonuna ms cinsinden girilen parametre ile fonksiyon timer ile ilgili bölücü ayarlarını vs. otomatik olarak yaparak zamanlayıcıyı ayarlar. os_init fonksiyonuna parametre olarak sıfır değeri yada mümkün olmayan bir değer girildiğinde (Örneğin 16MHZ’de 33ms) os_tick değeri varsayılan olarak 1ms’ye set edilir. Uygulamalarda gördüğüm kadarıyla RTOS’larda genellikle bu değer 1ms yada 10ms olarak ayarlanmış. Ben uygulamada 1ms olarak belirledim.

os_tick yada tick değeri CocoOS olsun yada başka bir RTOS olsun hepsinde olan bir değerdir. RTOS’un kalp atışıdır diyebiliriz. Bu değere göre işletim sistemi çalışmasını, zamanlamasını vs. düzenler. Sonuç olarak hazırladığım dosyalar ile CocoOS’u MSP430 denetleyicilerde rahatlıkla kullanabilirsiniz. Hazırladığım dosyalar TimerA0 zamanlayıcısı olan tüm MSP430 denetleyiciler ile çalışır yada küçük değişiklikler ile çalışabilir hale getirilebilir.

Zamanlayıcı ayarları bu şekilde, sonrasında CocoOS’u kullanabilmek için bazı basit tanımlamaların yapılması gerekir. Tüm tanımlamalar os_defines.h dosyasında yapılır.

OS_DEFİNES.H BAŞLIK DOSYASI

/*
 * Copyright (c) 2012 Peter Eckstrand
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted (subject to the limitations in the
 * disclaimer below) provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the
 *    distribution.
 *
 * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE
 * GRANTED BY THIS LICENSE.  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT
 * HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This file is part of the cocoOS operating system.
 * Author: Peter Eckstrand 
 */
 
/***************************************************************************************
 
 
    Change log:
    2009-07-06: 1.0.0 First release
    2010-06-13: Removed TRUE and FALSE definitions and Bool typedef
    added os_assert macro
 
    2010-10-23: NO_QUEUE macro
    2010-10-26: N_USER_SEMAPHORES and N_USER_EVENTS macros
    2010-11-01: Moved some macros to cocoos.h
    2012-01-04: Released under BSD license.
 
 
***************************************************************************************
*/
#ifndef _os_defs
#define _os_defs
 
/** @file os_defines.h cocoOS user configuration */
 
 
 
 
/** Max number of used tasks
* @remarks Must be defined. @n Allowed range: 0-254. Value must not be exceeded */
#define N_TASKS             3
 
 
/** Max number of used message queues
* @remarks Must be defined. @n Allowed range: 0-254. Value must not be exceeded */
#define N_QUEUES            0
 
 
/** Max number of used semaphores
* @remarks Must be defined. @n Allowed range: 0-254. Value must not be exceeded */
#define N_SEMAPHORES        0
 
 
/** Max number of used events
* @remarks Must be defined. @n Allowed range: 0-254. Value must not be exceeded */
#define N_EVENTS            1
 
 
/** Round Robin scheduling
* @remarks If defined, tasks will be scheduled ignoring the priorities */
#define ROUND_ROBIN
 
#endif

os_defines.h dosyasındaki tanımlamaları inceleyelim.

N_TASK: Bu tanımlama ile uygulamanızda kullanılacak task(görev) sayısı belirlenir. Görev RTOS yada benzer sistemlerin temel yapı taşıdır. Uygulamanın tamamı parçalara ayrılarak görevlere bölünür görevler ile yapılır. Ben deneme uygulamasında 3 adet görev tanımladım.

N_QUEUES: Mesaj kuyrukları görevler arası haberleşme yapmak için kullanılır. Belirli şartlar altında görevler birbiri ile haberleşebilir veri alışı verişi yapabilir. Örneğin uygulamanızda bulunan tuş takımını tarayan bir görev tuşa basılması durumunda tuşun değerini mesaj kuyruğu ile bir başka göreve gönderebilir. Mesaj kuyrukları yeni başlayanlar için kullanımı karmaşık gelebilir. Onun yerine başlangıç uygulamalarınızda olayları(events) ve statik değişkenleri kullanabilirsiniz. Ben örnek uygulamamda mesaj kuyruğu kullanmadığım için değeri 0 olarak tanımlıdır.

N_SEMAPHORES: Semafor, tanım olarak bayraklarla haberleşme anlamına gelir. Buradaki kullanımı ise görevler arası senkronizasyon içindir. Görevlerin birbirini çalıştırıp durdurması gibi işlemler semaforlar ile yapılabilir. Örnek uygulamada semafor kullanmadığım için bu değer 0 olarak tanımlıdır.

N_EVENTS: Olaylar, adı üstünde belirlenen olayların olması durumunda görevlerin istenilen işlemi yapmasını sağlar. Örneğin uygulamada kullandığınız seri porttan veri alan göreviniz belirli sayıda karakter geldiğinde olay sinyali oluşturarak bir başka görevi çalıştırabilir. Örnek uygulamada 1 adet olay kullanıldığı için bu değer 1 olarak tanımlanmıştır.

ROUND_ROBIN: Round robin RTOS’larda sıkça karşılaşılan çalışma biçimidir. Eğer uygulamada Round Robin tanımı yapılmış ise tüm görevler liste haline sıralı olarak işletilir. Görevler arası önceliklerin önemi yoktur. Round robin tanımı yapılmamışsa, her zaman öncelik seviyesi en yüksek olan görevin çalışma önceliği vardır. Öncelik seviyeleri 0-255 arasında seçilmelidir. 0 değeri en yüksek öncelik değerini belirtir. 2 görev aynı öncelik değerine sahip olamaz.

uint8_t task_create( taskproctype taskproc, uint8_t prio, Msg_t *msgPool, uint8_t poolSize, uint16_t msgSize )

Diğer önemli bir konu ise görev oluşturma işlemidir. Yukarıda CocoOS’un görev oluşturma, task_create fonksiyonu görülmektedir. Görüldü gibi bu fonksiyon 5 parametre alıp bir değer değer döndürmektedir.

taskproctype taskproc: görevin gövdesini oluşturan fonksiyonun adresidir. Görevin yapması istenilen kodları bu fonksiyon içine yazılır.

uint8_t prio: Görevin önceliğini belirtir. 0-255 arasında olmalıdır. 0 en yüksek öncelik değeridir.

Msg_t *msgPool: Görevin kullanacağı mesaj havuzunun adresi girilir. Mesaj havuzu görevler arası mesajlaşma için kullanılan bir alandır.

uint8_t poolSize: Kullanılan mesaj havuzunun boyutunu belirtir. Uygulamaya göre uygun bir boyut belirtilmelidir. Düşük seçilmesi durumunda dolma, yüksek seçilmesi durumunda fazla bellek tüketimine neden olabilir.

uint16_t msgSize: Tanımlanan mesajın boyutunu belirtir. Tanımlanan mesaj kuyruğunun boyutu girilmelidir.

Mesaj kuyruğu kullanılmaması durumunda ilgili değerlerin 0 olarak girilmesi yeterlidir.

Son olarak fonksiyon oluşturulan göreve ait id numarasını geri döndürür.

Anlattıklarım tanımlamalar vs. CocoOS dokümanından edindiğim bilgilerdir. CocoOS için geçerlidir. Diğer RTOS’larda da benzer ayarlar ve çalışma yapıları mevcuttur. CocoOS ile ilgili detaylı bilgiye örnekleriyle beraber www.cocoos.net adresinden ulaşabilirsiniz. Benzer şekilde güncelleştirmeleri vs. buradan takip edebilirsiniz.

Örnek uygulamayı üzerinde MSP430G2553 bulunan Launchpad üzerinde gerçekleştirdim. Uygulamada 3 adet görev bulunmaktadır. Uygulamaya ait kodlar aşağıdadır.

UYGULAMAYA AİT C KODLARI

/****************************************************************
 * Designer: Erhan YILMAZ					*
 * Application:  CocoOS Test					*
 * Date: 20-01-2015						*
 * Description:	Simple test application for CocoOS which	*
 * have 3 tasks and 1 event. 					*
 * *************************************************************/
 
#include 
#include "cocoOS_3.1.0/Source/cocoos.h"
#include "cocoOS_3.1.0/Source/clock.h"
 
Evt_t ButtonEvent;		// Buton olayı için değişken tanımla
 
// ButtonTask: Creates event(ButtonEvent) signal when button pressed and released
// ButtonTask: Butona basıp bırakınca buton olayı oluşturur.
void ButtonTask(void) {
    task_open();		// Görevi aç
    for (;;) {
    if(!(P1IN & BIT3)){		// Butona(P1.3) baasıldı mı?
    task_wait(20);	        // Buton arkı için 20 tick(20ms) bekle
    while(!(P1IN & BIT3));	// Butonun bırakılmasını bekle
    event_signal(ButtonEvent);	// Buton olayı oluştur
    }
    task_wait(100);		// 100ms'de bir görevi çalıştır.
    }
    task_close();		// Görevi kapat
}
 
// LEDToggleTask: Wait for the event(ButtonEvent) and toggle LED(P1.0) when event occurred
// LEDToggleTask: buton olayı oluşmasını bekler. Olay oluşunca LED'in(P1.0) durumunu değiştirir.
void LEDToggleTask(void) {
    task_open();		// Görevi aç
    for (;;) {
    event_wait(ButtonEvent);	// Buton olayı için bekle
    P1OUT ^= BIT0;		// Kırmızı LED'i(P1.0) tersle
    }
    task_close();		// Görevi kapat
}
 
// LEDBlinkTask: Toggle LED(P1.6) state in every 500ms
// LEDBlinkTask: Her 500ms'de bir LED'in(P1.6) durumunu değiştirir
void LEDBlinkTask(void) {
    task_open();		// Görevi aç
    for (;;) {
    P1OUT ^= BIT6;		// Yeşil LED'i(P1.6) tersle
    task_wait(500);		// 500 tick(500ms) bekle
    }
    task_close();		// Görevi kapat
}
 
int main(void) {
    WDTCTL = WDTPW | WDTHOLD;	// Watchdog timeri durdur
    BCSCTL1 = CALBC1_16MHZ;	// Dahili osilatörü 16 MHz olarak ayarla
    DCOCTL = CALDCO_16MHZ;	// Dahili osilatörü 16 MHz olarak ayarla
    P1DIR &= ~BIT3;			// P1.3'ü(buton) giriş olarak ayarla
    P1DIR |= BIT0+BIT6;			// P1.0(LED) ve P1.6'yı(LED) çıkış olarak ayarla
    P1REN |= BIT3;			// P1.3 Pull-Up/Down özelliğini aktif et
    P1OUT |= BIT3;			// P1.3 Pul-Up direncini aktif et
    P1OUT &= ~(BIT0+BIT6);		// LEDleri başlangıçta söndür.
 
    ButtonEvent = event_create();		// Buton olayını oluştur
// Görevler tanımlanır. OS round robin çalışacağı için tüm görevler sıralı çalıştırılır.
// Öncelik tanımlamalarının öenmi yoktur.
    task_create(ButtonTask,0,0,0,0);		// Button durmunu kontrol eden görev(task)
    task_create(LEDToggleTask,1,0,0,0);		// LED durumunu değiştiren görev(task)
    task_create(LEDBlinkTask,2,0,0,0);		// LED blink görevi(task)
 
    os_init();			// OS ayarlarını yap
    clock_init(1);		// Clock ayarları os_tick=1ms
    os_start();			// OS'yi başlat
    return 1;
}

Görüldüğü gibi uygulamada ButtonTask, LEDToggleTask ve LEDBlinkTask isimli 3 tane görev oluşturulmuştur. Uygulamanın main kısmında gerekli mikrodenetleyici ayarları yapılmıştır. Sonrasında buton ile ilgili olay ve görevler oluşturulmuştur. os_init ve clock_init fonksiyonları ile CocoOS ve saat ayarlamaları yapılır. Uygulamada os_tick değeri 1ms olarak seçilmiştir. Tüm ayarlamalar vs. yapıldıktan sonra os_start fonksiyonu ile CocoOS çalışmaya başlar ve tüm görevler sırası ile çalıştırılır. CocoOS’un ve genel olarak RTOS’ların yapısı itibariyle her görev bir kez çalışıp bir daha çalışmaz. Bu durumun önüne geçmek için görevler içerisinde for(;;) ifadesi ile sonsuz döngüler kullanılmıştır.

Sırasıyla görevlerin çalışmasını inceleyelim. ButtonTask, launchpad üzerinde bulunan butona(P1.3) basılmasını bekler bekler. butona basıp bırakınca ButtonEvent olayını tetikler. task_wait(100) fonksiyonu ile her 100 tick süresi sonunda(100ms) periyodik olarak bu görev çalıştırılır.

LEDToggleTask, ButtonEvent olayı oluşması durumunda launchpad üzerinde bulunan kırmızı ledin(P1.0) durumunu tersler.

LEDBlinkTask ise herhangi bir olay tetiklemez yada olayı beklemez. Her 500 tick süresi(500ms) sonunda launchpad üzerinde bulunan yeşil ledin(P1.6) durumunu değiştirir. Yanıp sönmesini sağlar.

Bu şekilde 3 görev sürekli olarak CocoOS tarafından çalıştırılır.

msp430g2553-msp430-cocoos-coocoxun-coos-rtos

Yorum Gönder

0 Yorumlar