Skip to content
/ AdxFSM Public

Non-preemptive Event-driven Operating System for 8/16-bit MCU

License

Notifications You must be signed in to change notification settings

apolv/AdxFSM

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

AdxFSM Label

AdxFSM

AdxFSM1 is non-preemptive event-driven OS for 8/16-bit small embedded systems. It was originally designed as a framework for RF-block control applications in Test&Measurement instruments. See AdxFSM Reference Manual for more details.

Supported Devices and Toolchain

AdxFSM is tested on ATxmega256A3U device.
Currently supported devices:

  • ATxmega256A3U
  • ATxmega192A3U
  • ATxmega128A3U
  • ATxmega64A3U.

Kernel and core code may also work properly on other xmega devices, but not sure for peripheral hardware drivers. Other platforms require port of Kernel code and TimerEvent_t class since it uses hardware timer/counter. Also some attention should be made to the alignment, padding and data packing when porting on 16 or 32-bit platforms.

Compiler/Linker: avr-g++, (AVR 8 bit GNU Toolchain 3.6.2 1778) 5.4.0;

Language version: C++14 (-std=c++14);

Library: compiled and linked against library pack XMEGAA_DFP 1.1.68

Main Concept

AdxFSM is designed to simplify user application logic implementation which can be represented in terms of abstract model – as a set of finite state machines (FSMs), fig. 1.
MainConcept_AbstractFsmModel
Figure 1. Abstract FSM Model
Each FSM in this abstract model has one or more states, each state represents waiting process for this FSM, when nothing is executed in this FSM. One of the states is initial state. Each state has one or more out transitions to other states. The FSM is activated and the transition is executed when any event assigned to this transition happened. Transition may contain any user application logic (fig. 2), including subroutine call which contains its own waiting states and transitions, fig. 3, i.e. FSMs can be nested.2

FSM states and transitions Nested FSM
Figure 2. FSM states and transitions; Figure 3. Nested FSM

It should be noted that FSM is able to wake up from the waiting state if it waits for particular event, set of events, or any event assigned to the FSM and one of the events that are waited for has happened. Events can be generated by other FSM in user application logic while executing transition or by hardware interrupt while executing ISR.3 FSMs run and synchronize to each other by means of events, i.e. FSMs won't run (wake up) if no event is generated.

Demo Examples

Two Task Synchronization -- Producer and Consumer Demo

This demo shows behavior of Events: they are catched by WaitFor() regardless of the time it was sent -- before or after waiting. Execution order is the following regardless of inserted random delays: A1/A (concurrent) → B → C1/C (concurrent) → D → A/A1 → ...

//$Id: main.cpp 342 2025-01-05 00:40:58Z apolv $
//	MyProdTask:						MyConsTask:
//	(A)	Random delay				(A1)Random delay
//	(B)	Send "Produced" event	->	Wait for "Produced" event
//	(C1)Random delay				(C)	Random delay
//	Wait for "Consumed" event	<-	(D)	Send "Consumed" event

#include "core/adx_TimerEvent.h"
using namespace adx_fsm;

void MyProdTask() __attribute__ ((OS_task));
void MyConsTask() __attribute__ ((OS_task));
FSM_t MyProdFsm{ uint32_t(MyProdTask), 64}, MyConsFsm{ uint32_t(MyConsTask), 64};
TimerEvent_t MyProdTimEvt{ &MyProdFsm }, MyConsTimEvt{ &MyConsFsm };
Event_t ProducedEvt{&MyConsFsm}, ConsumedEvt{&MyProdFsm};

uint8_t Random()
{
	static uint8_t x = 1;
	x = (x<<3) - 1 + x;
	return x>>6;
}

void MyProdTask()
{
	while(true)
	{
		SleepFor(&MyProdTimEvt,Random());	//(A)
		ProducedEvt();						//(B)
		SleepFor(&MyProdTimEvt,Random());	//(C1)
		WaitFor(&ConsumedEvt);
	}
}
void MyConsTask()
{
	while(true)
	{
		SleepFor(&MyConsTimEvt,Random());	//(A1)
		WaitFor(&ProducedEvt);
		SleepFor(&MyConsTimEvt,Random());	//(C)
		ConsumedEvt();						//(D)
	}
}

int main(void)
{
	{
		CriticalSection_t cs;
		//Hardware can be initialized here...
		FSM_t::Start();
	}
	
	// Scheduler's Main Loop
	SystemStatus_t Status;
	while (ProcessSystemStatus(Status))
	{
		if (ProcessNextEventInQue())
		{
			//Do something On Idle here...
		}
	}
}

UART Open/Close, Read/Write Demo

This demo shows simple data transfer protocol with packet auto synchronization when some data is lost.
Two Tasks writes their own data packets to UART independently, third Task reads from UART and tries to sync to packet and checks the CheckSum. Consistency of Tx packets (when mutual writing to UART) for each writing Task is provided by OpenWrite()/CloseWrite() methods based on mutex. Read_bl() and Write_bl() may block Task and return control to Scheduler if UART buffer is emty/full accordingly.
To run this demo – connect Tx pin with Rx pin of the selected UART on your board, otherwise reading Task will be waiting for data forever. Writing Tasks do not wait for reading Task, they write packets independently with delay of 1 tick (5ms by default).

//$Id: main.cpp 345 2025-01-06 09:19:22Z apolv $

#include "drivers/adx_UsartDriver.h"
#include "drivers/adx_SystemClock.h"
#include "core/adx_TimerEvent.h"
using namespace adx_fsm;

ADX_USART(MyUart,USARTE0)

void MyWriteTaskN()  __attribute__ ((OS_task));
void MyReadTask()  __attribute__ ((OS_task));
FSM_t WrFsm_array[] = {{uint32_t(MyWriteTaskN),192}, {uint32_t(MyWriteTaskN),192}};
FSM_t RdFsm{uint32_t(MyReadTask),160};
Event_t MyRdEvt{&RdFsm};

static const uint8_t PackSiganture = 0xaa;
union Packet_t
{
	uint8_t Buf[7];
	struct
	{
		uint8_t Header[2];//= {PackSiganture, CmdId}
		uint8_t Data[4];
		uint8_t CheckSum;
	};
};


uint8_t CalcCheckSum(Packet_t* ptrPack)
{
	uint8_t result = 0;
	for(uint8_t i = 0; i < sizeof(Packet_t)-sizeof(Packet_t::CheckSum); i++) result = result + ptrPack->Buf[i];
	return result-1;
}

void MyWriteTaskN()
{
	TimerEvent_t TimEvt{GetRunningFsm()};
	Event_t MyWrEvt{GetRunningFsm()};
	uint8_t Id = GetRunningFsm()->GetFsmID();
	Packet_t Pack{PackSiganture,Id,static_cast<uint8_t>(Id<<2), static_cast<uint8_t>((Id<<2)+1), static_cast<uint8_t>((Id<<2)+2), static_cast<uint8_t>((Id<<2)+3),0};
	Pack.CheckSum = CalcCheckSum(&Pack);
	while(true)
	{
		auto hUart = MyUart.OpenWrite(&MyWrEvt);
		Write_bl(hUart,Pack.Header,sizeof(Packet_t::Header));
		Write_bl(hUart,Pack.Data,sizeof(Packet_t::Data));
		Write_bl(hUart,&Pack.CheckSum,sizeof(Packet_t::CheckSum));
		MyUart.CloseWrite();
		SleepFor(&TimEvt,1);
	}
}

void MyReadTask()
{
	Packet_t Pack;
	uint8_t CheckSum;
	uint8_t Counter0 = 0, Counter1 = 0;
	
	while(true)
	{
		auto hUart = MyUart.OpenRead_bl(&MyRdEvt);
		Read_bl(hUart,Pack.Buf,sizeof(Packet_t));
		CheckSum = CalcCheckSum(&Pack);
		while(CheckSum != Pack.CheckSum || Pack.Header[0] != PackSiganture)
		{//Search for PackSiganture and try again
			do{
				Read_bl(hUart,Pack.Header,1);
			} while(Pack.Header[0] != PackSiganture);
			Read_bl(hUart,Pack.Buf+1,sizeof(Packet_t)-1);
			CheckSum = CalcCheckSum(&Pack);
			if(CheckSum == Pack.CheckSum) break;
		}
		//Packet below is valid, count received packets with Id==0 and Id==1:
		if(Pack.Header[1] == 0)
			Counter0++;
		else
			Counter1++;
		MyUart.CloseRead();
	}
}

const UsartConfig_t MyCfg PROGMEM = GetConfig(
	32'000'000,
	115'200,
	UsartSettings_t::FrameLen::b8,
	UsartSettings_t::Parity::None,
	UsartSettings_t::StopBits::One,
	UsartSettings_t::RxInterruptPriority::Middle,
	UsartSettings_t::TxInterruptPriority::Middle);

int main(void)
{
	{
		CriticalSection_t cs;
		SetSysAndPerClk_32MHzFrom16MHzXTAL();
		MyUart.Config(&MyCfg);
		FSM_t::Start();
	}
	
	// Scheduler's Main Loop
	SystemStatus_t Status;
	while (ProcessSystemStatus(Status))
	{
		if (ProcessNextEventInQue())
		{
			//Do something On Idle here...
		}
	}
}

Footnotes

  1. Advantex Finite State Machine

  2. Blocking Read/Write operations are the most widely used example of nested FSMs.

  3. Interrupt Service Routine

Releases

No releases published

Packages

No packages published