scmRTOS. Механизм callback-ов.

Также эта статья относится ко всем RTOS в той или иной форме.

Недавно, работая с LUFA, заметил просто шикарную вещь как callback. В scmRTOS подобное есть и называется hook, на принцип действия которых я не обращал внимание. А зря.

callback — это когда делается вызов внешней функции(функция определяется в каждом проекте индивидуально) из функции/метода который находится в библиотеке/классе.
Яркие примеры это LUFA и FatFs.

Эту штуку очень удобно использовать когда нужно обеспечить атомарный доступ к ресурсу. Это относится только к RTOS.

И вот как это можно сделать.

Пример с SPI.

Сначала пишем код для SPI интерфейса.
namespace SPI
{

	void callback_captureSPI();
	void callback_releaseSPI();
	
	//work with SPI
	class CSpi
	{
	public:
		inline void init();						//init		
		inline char send(char data);			//RX/TX byte
			
		inline void SS_ON(char n);				//select device	
		inline void SS_OFF(char n);				//unselected device	
	};
	//======================================================================

	//init SPI
	void CSpi::init()
	{
		...................
	}//end init
	//======================================================================
		
	//select
	void CSpi::SS_ON(char n)
	{
		callback_captureSPI();
		
		switch (n)
		{
			case 0:							//AT45DB
			{
				ON(AT_CS);
				break;
			}
			case 1:							//CC2500
			{
				ON(RF_CS);		
				break;
			}
		}
	}//end SS_ON
	//======================================================================
	
	//unselected
	inline void CSpi::SS_OFF(char n)
	{	
		switch (n)
		{
			case 0:							//AT45DB
			{
				OFF(AT_CS);
				break;
			}
			case 1:							//CC2500
			{
				OFF(RF_CS);		
				break;
			}
		}
		
		callback_releaseSPI();
	}//end SS_OFF
	//======================================================================
	
	//TX/RX byte
	char CSpi::send(char data)
	{
		SPDR = data;

		while(!(SPSR & (1<<SPIF)));

		return SPDR;
	}//end send
	//======================================================================
};


В этом классе определены прототипы функций наших callback-ов:
— void callback_captureSPI(); — захват доступа
— void callback_releaseSPI(); — освобождение

Далее реализуем наши классы для работы с периферией:

#include "spi.h"

namespace AT45DB
{
	
	//work with AT45DB321D
	class CAt45db: public SPI::CSpi
	{
		public:		
			
                        ..................
		
		private:	
			inline void SS_ON();
			inline void SS_OFF();
	};
	//======================================================================

	void CAt45db::SS_OFF()
	{
		SPI::CSpi::SS_OFF(0);
	}//end SS_OFF
	//======================================================================
	
	void CAt45db::SS_ON()
	{
		SPI::CSpi::SS_ON(0);
	}//end SS_ON
	//======================================================================

}


Происходит наследование от SPI и перегрузка методов.

И соответственно для CC2500:
#include "spi.h"

namespace CC2500
{
	
	//work with CC2500
	class C_CC2500: public SPI::CSpi
	{
		public:		
			
                        ..................
		
		private:	
			inline void SS_ON();
			inline void SS_OFF();
	};
	//======================================================================

	void C_CC2500::SS_OFF()
	{
		SPI::CSpi::SS_OFF(1);
	}//end SS_OFF
	//======================================================================
	
	void C_CC2500::SS_ON()
	{
		SPI::CSpi::SS_ON(1);
	}//end SS_ON
	//======================================================================

}


Соответственно делается тоже самое.

Далее в нашем главном файле объявляем объекты и описываем callback-и.


#include "lib/spi.h"
#include "lib/AT45DB.h"
#include "lib/CC2500.h"

SPI::CSpi		MySpi;
AT45DB::CAt45db		MyFlash;
CC2500::C_CC2500     MyCC2500;
............
OS::TMutex MutexSPI;      //создаем мьютекс
...........
//---------------------------------------------------------------------------
//
//							 SPI callback
//

void SPI::callback_captureSPI()
{
	MutexSPI.Lock();
}

void SPI::callback_releaseSPI()
{
	MutexSPI.Unlock();
} 


Красота.

Теперь когда процесс обращается к периферии то происходит следующее:
— обращение — > SS_ON() -> SS_ON(x) — > callback_captureSPI() — > безопасная работа внутри библиотеке/классе -> SS_OFF() -> SS_OFF(x) — > callback_releaseSPI().

Если какой-то другой процесс произведет вытеснение и тоже обратится к SPI, то он уснет на callback_captureSPI().

Теперь не нужно делать вереницы в стиле:
захват мьютекса
работа с периферией
освобождение мьютекса
  • +3
  • 18 февраля 2012, 12:41
  • a9d

Комментарии (13)

RSS свернуть / развернуть
Не совсем понял, причем тут RTOS. Из относящегося к ней заметил только мьютекс.
Или с этими SS_ON/SS_OFF RTOS как-то сама работает?
0
  • avatar
  • Vga
  • 18 февраля 2012, 14:06
Это показано это можно использовать в RTOS. Т.е. в библиотеке/классе можно использовать объекты RTOS или исполнять какойто другой код без модификации самой библиотеки/класса.
Это пример использования.
0
Пост не о чем.
0
  • avatar
  • isz
  • 18 февраля 2012, 15:31
Тогда расскажи о более простом и красивом способе получения атомарного доступа.
0
Интересный пример.
Архив с проектом не приложите?
Хочется накладные расходы оценить в симуляторе.
0
Да вам все расходы и расходы. О каких расходах идет речь?
Если о памяти, так об этом компилятор заботится. И будет тоже самое, что и «вереница», но только код будет более читабельный.
Архив не дам. Это из проекта пример, он еще разрабатывается.
0
Да вам все расходы и расходы.
Да есть такое.
Ну вы понимаете: трудное, голодное детство программирование контроллеров с малым объёмом ОЗУ и низкой тактовой, да ещё иногда на ассемблере накладывает отпечаток. Так просто неизбавиться.
О каких расходах идет речь?
На вызовы и проч. Но они и так либо будут, либо нет, Хоть на асме пиши.
код будет более читабельный
Это большой плюс.

P.S. Сделать бы ещё такие классы с использованием шаблонов. Совсем красота была бы.
0
Если всё равно классы используются, почему вместо callback функций не использовать виртуальные функции? Или абстрактные…
0
И какоеже это даст преимущество?
0
Переопределение функции для каждого подкласса, а не одна функция на все подклассы.
0
Плюс, в функции получишь указатель Self, что даст обращаться к полям объекта. А при использовании callback функций для этого придётся тащщить ещё один параметр.
И вообще, callback функции применяются в итеративных языках именно из-за отсутствия там механизмов виртуальных функций.
0
Да. Впринципе это имеет смысл. Но мне как раз и надо, чтоб одна функция была на все подкласы.
От AT45DB еще идет наследование для SCSI класса(mass storage) и FAT.

А вот обращение к полям объекта, это уже весьма полезно.
0
Но мне как раз и надо, чтоб одна функция была на все подкласы.
В большинстве случаев функция базового класса вызывается из перекрывающей ее. За исключением тех случаев, когда перекрывается именно для подавления функционала базового класса.
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.