FSM Lecture 87| Exercise-008:Implementation part 6

 

Exercise-008:Implementation part 6

 

 

In this article, to use the QP-Nanos Active Object Framework, you have to implement a couple of steps. Let’s discuss that now.

Figure 1. QF(Active Object Framework) QP-Nanos Active Object Framework
Figure 1. QF(Active Object Framework)
  • First, you have to create the event queues for the active objects that you have used in your application. In our application, we have used two active objects. We have to create two event queues. It is nothing but just a static array of QEvt structure.
  • After that, you have to create and initialize the Active object control block. It is nothing but there is a structure called QActiveCB. So, you have to create an array instance of this structure, and you have to initialize that. 
  • After that, you have to call QF_init, and you have to mention the number of active objects used by your application. And this is to initialize the Active object framework of QP-nano. It accesses all the Active objects what you have mentioned in the control block, and it initializes that. 

This also calls the init function of the underlying kernel. So, the underlying kernel in our case is QV, which is the cooperative vanilla kernel.

  • After that, you have to call QF_run. This also calls the scheduler of the kernel, and it never returns. 
  • And after that, if you want the framework to generate timeout events, a timeout could be a one-shot or periodic timeout. You have to use this function QF_tickXISR. This function should be called from your application’s tick ISR. That means your application now should provide one a periodic tick ISR, which is already there in our application. So, we have already used timer1 peripheral to generate a periodic interrupt for every 100 milliseconds. We will slightly modify that.

And we’ll use that as our tick ISR because every RTOS or kernel should use a background tick ISR. And from that background tick ISR, you have to call this function. So, this gives the framework a chance to update all its time-related events. 

 

Let’s implement all these steps. Let’s first start with step 1, Create event queues.

Now, let’s get back to the code main.cpp. We will create two arrays of QEvt type. We will create two arrays.

So, the first array is ClockAlarmQueue and ButtonQueues of type a QEvt, and we will give space for 5 events. These are statically defined Event queues for our Active objects. 

static QEvt ClockAlarmQueue[5];
static QEvt ButtonQueue[5];

 

The next step is to create and initialize an Active object control block. Now, let’s create a variable of QActiveCB. So, if you want to read about QActiveCB structures, check here in ‘qfn.h’, as shown in Figure 2.

QP-Nanos Active Object Framework
Figure 2. qfn.h file

 

QActiveCB represents the read-only information that the QF-nano needs to manage the active object. The QActiveCB objects are grouped in the array QF_active[].

Basically, it has three fields. Pointer to your active object, a pointer to the event queue of the active object, and here you must mention the length of the Event queue of your active object. 

So, you have to create a QF_active array now. Let’s do that.

QActiveCB const QF_active[] = {
  { (QActive*) 0, (QEvt*) 0, 0},
  { (QActive*) AO_ClockAlarm,(QEvt*)ClockAlarmQueue,Q_DIM(ClockAlarmQueue)},
  { (QActive*) AO_Button,(QEvt*)ButtonQueue,Q_DIM(ButtonQueue)}
};

QActiveCB

QActiveCB const QF_active[] and let’s initialize this with all the active objects. The first entry should be a Null entry, and you can do like this Qactive*. You can just typecast to QActive*. And the second member element is a pointer to the event queue; you can write like this QEvt*. And the third one is the length, which is 0. 

After that, the second entry you start with your first active object, that is AO_ClockAlarm; address of the Event queue, that is ClockAlarmQueue; And length. The length of this is ClockAlarmQueue. For this, you can use this macro Q_DIM; you mention the array name like this. You can do like for AO_Button.

Q_DIM macro helps you to calculate the number of elements in the given array(Figure 4). QActiveCB code is shown above. We created the Active object control block.

QP-Nanos Active Object Framework
Figure 4. Q_DIM macro

 

The third step is QF_init. This QF_init is there in qvn.c. The proper usage of this function is mentioned here (Figure 5).

You have to send a number of active objects your application uses, which can be calculated using Q_DIM macro over QF_active array variable. Let’s do that. 

QP-Nanos Active Object Framework
Figure 5. qvn.h, QF_init

 

After the button_ctor, we can do that. QF_init, let’s use the Q_DIM of this QF_active, as shown below.

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  display_init();
  attach_button_interrupts();
  Clock_Alarm_ctor();
  Button_ctor();
  QF_init(Q_DIM(QF_active));

  Timer1_setup();
}

QF_init code

And after that fourth step is QF_run.  QF_run can be called from the loop function because you cannot call  QF_run in the setup function. Because it never returns.

QF_run, as you can see here, is there in qvn.c. This has one giant loop here, which is a scheduler, you see that in Figure 6. It executes the events for the active objects in a cooperative manner. 

Figure 8. qvn.c,QF_run
Figure 6. qvn.c, QF_run

 

If there are no events to be processed, then it calls the Idle hook function. This Idle hook function must be implemented in your application.  

Figure 9. Idle hook function
Figure 7. Idle hook function

 

We will implement the QV_onIdle function in our main.c. So, you can here put the processor to sleep, that sleep-related function also given in the qfn_port.h. This is a port for the avr based microcontroller. You can see that(Figure 8), there is already a macro present QV_CPU_SLEEP. So, it enables the interrupt, and it just sleeps. We will call this function from there. Let’s go to the main.cpp, and we’ll call that function, as shown below.

void QV_onIdle(void){
   QV_CPU_SLEEP();
}

 

Figure 11. QV_CPU_SLEEP function
Figure 8. QV_CPU_SLEEP function

 

After that, the next step is to call the OF_tickXISR function from your application’s tick ISR. And we already have this Timer1_setup. What does it? It generates an interrupt for every 100 milliseconds. What we will do is we will modify this Timer1 interrupt code to generate an interrupt for every 1 millisecond. That will be our tick ISR.

And from that tick ISR, we will call that function. That’s why we have to slightly modify this now to generate the timer interrupt for every 1 millisecond.

Figure 12. Timer1_setup function
Figure 9. Timer1_setup function

 

So, for that, I’ll be using some macros. I’ll put that in main.h.

Let’s go to the main.h, and I’m going to use some macros. So, these macros are very simple, as shown in Figure 10.

Figure 13. Macros
Figure 10. Macros

 

We can modify the config items. Currently, I have set the TCCR1B_PRESCALER to TCCR1B_PRESCALER1, which means no clock division. And CONFIG_TIMER1_PRESCALER to this value TIMER1_PRESCALER_1. So basically, this is nothing but 1. 

CONFIG_TICKS_PER_SECOND represents the rate of the tick interrupts. TICKS_PER_SECOND means 1000 ticks per second. That means 100Hz. For every 1 second, there will be 1000 timer interrupts. That means 1 timer interrupt will arrive for every 1 millisecond. So, that’s a rate. 

And MS_PER_TICK is a macro, which calculates milliseconds per tick. You have to divide 1000 milliseconds by CONFIG_TICKS_PER_SECOND.

And then TIMER1_OC_MATCH_VALUE calculates the Timer1’s match value, the match value that you need to configure into the timer once output compare register to get the tick rate of this much (CONFIG_TICKS_PER_SECOND).

MS_TO_TICKS is a helper macro, which converts milliseconds to ticks. 

 

We will slightly modify our Timer1_setup code. Now I will change Timer1_setup to sys_tick_init. In the code, where is the timer1_setup, you change that to sys_tick_init. The modified code as shown below.

static void sys_tick_init(void){
   TCCR1A = TCCR1A_CTC_MODE;    //CTC mode 
   TCCR1B = (TCCR1B_CTC_MODE |TCCR1B_PRESCALER_1);    //prescaler=1,CTC mode
   TIMSK1 |= B00000010;    //Interrupt enable for OCR1A compare match
   OCR1A = TIMER1_OC_MATCH_VALUE;    //OC match value for CONFIG_TICKS_PER_SECOND time base generation
}

 

After that, you have to call this function QF_tickXISR() from the tick ISR.

Implementation part 6
Figure 11. QF_tickXISR()

 

Let’s go to the model file. In the ClockAlarm_SM.cpp, we have the ISR. From here you should call. And you should send one argument to that function.

Implementation part 6
Figure 12. ClockAlarm model file

 

Implementation part 6
Figure 13. QF_tickXISR(), QActive_armX()

 

Why do you need to call QF_tickXISR?

This gives the framework a chance to process all armed time events. The time events can be armed by using a QActive_armX and disarm function. You can arm for any timeout using the QActive_arm function. 

As this description says, this function must be called periodically from the time-tick ISR. And you have to mention one argument, which is system clock tick rate, and this value can be 0 to 3. Basically, this identifies the system tick rate. Each system tick rate post timeout events with different signals are as follows. If we have used tick rate 0 here, when you use this QActive_arm function, then with that function, you have to mention the associated tick rate value. If it is 0 for example, the framework sends a timeout event with this name after a timeout Q_TIMEOUT. If the tick rate value is 1, the framework sends you a timeout signal with a signal name Q_TIMEOUT1 like that.

So, we will use 0 here in the model file, as shown in figure 12. QFtickISR(0).

Whenever you arm for a timeout using this QActive_arm function, for every invocation of the QFtickISR function, the framework checks whether there is really a timeout or not.

If there is a timeout, then it sends an event with the signal name Q_TIMEOUT if tickRate is 0; otherwise, Q_TIMEOUT1 if the tickRate is 1, like that.

 

Whenever a button press is detected, either ISR1 runs or ISR2 runs. We have got 2 button ISRs. Inside the ISR, we will use a flag. If this flag is true, then Arm_timer for the BOUNCE_TIME. Basically, it’s a one-shot timer. So, we will arm and one-shot timer for the BOUNCE_TIME. And when that BOUNCE_TIME is over, a framework sends a timeout signal to the Event Queue of the button active object. When the button active object receives the Q_TIMEOUT signal, it comes to know that someone has pressed the button and the bounce time is over. It can safely read the button status or the button pad status.

It reads the button status, it decodes that. So, it comes to know whether it needs to send a SET signal, OK signal, or ABRT signal, and that signal will be sent to the event queue of the Clock_Alarm AO. 

Then, the button active object makes that flag = true. It will be a global flag shared between this button active object and the ISRs. So, you have to manage it in a thread-safe manner.

Implementation part 6
Figure 14. Button ISR()

 

Now, we will implement this logic. If the button is bouncing here in Button_ISR_1, then this flag is since it is already false; that’s why this function will not get called for the next time until the button active object makes it ‘true’ again here. Now, we will implement this one.

 

Let’s go back to the code. In the main.h, we will extern one variable. extern uint8_t flag_report_button_press, as shown in Figure 15. 

Implementation part 6 - QP-Nanos Active Object Framework
Figure 15. main.h file

 

Now, let’s go to the ISRs. In the main.cpp, we have the ISR. Let’s go to the SET ISR. Here, let’s first disable the interrupt. You can use ‘cli’ instruction or use the macro provided by this framework, that is, QF_INT_DISABLE().  

And after that, if that flag is true, then we will make it false, and we will use the QActive_armX function.  What is ‘me’ here? See Figure 17; The timeout signal gets directly posted into the event queue of the active object pointed by this me pointer. That’s why this timeout event should be sent to the button active object. I will use AO_Button, the next event tickRate that is 0. And a number of ticks, we actually want a one-shot timeout for the bounce time. Bounce time is around 50 milliseconds. Let’s use the macro MS_TO_TICKS(50), which converts a number of milliseconds into ticks. After that, interval. Interval is not there because it’s a one-shot timer, so mention it as 0. After that, you can Enable the interrupt.

The same code you should place in OK_handler. The code is shown in Figure 16. 

Implementation part 6 - QP-Nanos Active Object Framework
Figure 16. Implementing ISRs

 

Implementation part 6 - QP-Nanos Active Object Framework
Figure 17. QActive_armX()

 

After that, let’s go to the model and draw the state machine for the button active object. The state machine is very simple in this case. It just have to process, so there will be one state. I’ll call this state a Button. 

And in the state machine of the button also gives the initial transition, and there is nothing to initialize actually, because it doesn’t have any attributes.

It has only one transition, internal transition, whose name is Q_TIMEOUT.

Implementation part 6
Figure 18. A state machine for the button active object

 

Implementation part 6 - QP-Nanos Active Object Framework
Figure 19. Q_TIMEOUT Transition code

 

Whenever it receives a timeout, it should read the button status. The read the button pad status code shown in Figure 19.

So you have to post an event to the ClockAlarm active object. How to post an event? Let’s check.

Let’s go back to the APIs, Reference, and here it is QACTIVE_POST(). This is how you post. You have to call this macro QACTIVE_POST.

Implementation part 6 - QP-Nanos Active Object Framework
Figure 20. QACTIVE_POST

 

The ‘me’ pointer, the destination is an active object; AO → ClockAlarm, the signal is SET_SIG, and parameter. So, there is no parameter. Let me type 0. Let’s write this code for the OK signal and ABRT signal. And after that, make this flag = true again.  And in the button_SM.cpp, you also include the “main.h” header file because it needs to access these macros and this variable.

In the following lecture, we will draw the state machine for the Alarm.

 

FastBit Embedded Brain Academy Courses

Click here: https://fastbitlab.com/course1

 

FastBitLab

The FastBit Embedded Brain Academy uses the power of internet to bring the online courses related to the field of embedded system programming, Real time operating system, Embedded Linux systems, etc at your finger tip with very low cost. Backed with strong experience of industry, we have produced lots of courses with the customer enrolment over 3000+ across 100+ countries.