Sunday, 12 February 2017

Applied Embedded Operating systems KICK OFF weeks! - Job Baan

 

Introduction

First I will tell you a bit about my arrival to Ireland as an Erasmus student, then I will explain you a bit of the code wrote for the assignment along with a explanation of different methods used.
In the end I will tell a little more about myself.

Arrival in Ireland & kick off

In the first two weeks, I arrived in Ireland as an Erasmus student I had the honour to meet loads of new and wonderful people. I meet mostly other international or Erasmus students, but when I started this class it was my first experience with Irish engineers. As if you go to school for the first day, knowing no one and struggling to find your way around the campus, I ended up with my fellow classmates. Everyone knows the feeling when they walk in somewhere and something feels, safe, constructive and aiming for the best you know you entered a great place. This was my first experience with other people working on a project. 

We started with a short chat and a brainstorm session about the upcoming weeks and get a feeling for the project and the module. When I walked in, there were 3 lamps mounted on top of a table and just being a lamp as you expect, after 30 minutes I couldn’t see the lamps anymore, I saw moving robots, which react to different actuators. By the end of the class I was sold, a good project was in the making. We finished the class and were only going to meet up in 3 days. After the introduction day, we started with some “Hello world!” examples to get a feeling for the RTOS on the LPC1768. Lucky me, I already did some basic things with the RTOS and had an advantage over the others. After one week of coding, trying the examples and getting used to it, everyone was on the same level and we started getting our first serious assignment.


If you want to see the full code, have a look at the bottom of this post. bottu

The assignment


Create a program that uses input from a vision system to create a pan and tilt motion with two servos. This system must include at least 1 thread. Apart from that you could go as easy or as crazy as you want to go. So, I started as every well-educated student would with delaying the start for half a week, by the time I started my view on the assignment chanced and I decide to create a program that uses input from RoboRealm and convert that into movement for the servos. Next to that I wanted to create an Interrupt Service Routine to demonstrate my knowledge on doing a bit more than just moving the servos. 

My solution


I started by doing a bit of research on the mechanics of RoboRealm and getting familiar with the serial output that RoboRealm could produce. This turnout to be straight forward. Roborealm could spit out any value you want it to. The type of the send out variables are strings, this presented the first challenge I encountered. Moving and adjusting a servo needs to be done by integers. During the process of developing code I found different solutions to transfer a string to an int. The final solution I chose to use is the following. The code also includes the memory pool use but that will be explained later.



In the code above you see that the program checks if there is a something to read from the serial bus. If so, it will keep on adding the data to nr1[]. This is done when the code is near the end, this will keep going until the input is “ “. Then the data will be stored in a char which is then converted to an int via atoi. Then the program clears out the buffer with the for loop and continuous with the next number which will go through the same route. After the input of two values, everything is cleared up and the Thread will start over again from the beginning. 
 
In the code below I continue to use the input data to make sure that the movement of the servo will be logical and set to the centre point. This will be stored in a memory pool (If you would like to know a bit more about memory pools, have a look here) and send over to the servo threads to make the servo move to a new position. In the beginning, I struggled with increasing the value and still responding to new inputs. After a chat with Thomas Murphy and he showed his solution I knew I needed to implement something similar. Now I increment or decrement the value before transferring it over to the servo threads where it will be used.

The Servo

The servo implementation gave some other challenges, trying to let the servo move more than 90 degrees turned out to be more difficult than expected. Due to the limited time and the fact that the demands from the customer only wanted a prove of concept I decided to stay with the 90 degrees. In the end the code for the servo movement turned out to be simpler than expected. This thread now only reads out the memory pool and divide the number received, the number will be divided by highest input value and then outputted with PWM.

The Interrupt Service Routine

This part of the program is more of a demonstration of knowledge and ability to try something else. The need of this piece of code is therefore low but the use and the learning process that came with it is therefore not less important. When trying to implement this I found some boundaries in the RTOS and some useful new tricks. The tips and tricks that I found will be discussed in the chapters about each different example.
The code below shows a “fake” trigger instead of a DigitalIN, I used a RTOS timer. After the timer expired the ISR thread will be start and this will release a mutex which then lets the rgb light turn on and then release a semaphore which allows the speaker to make a sound.
The use of this construction is not there, but the demonstration of the use of mutexs and semaphores show that I can use them and implement them when needed.

 












What is RTOS?

A Real-Time Operating system (RTOS) is mainly a software package that schedules different tasks and let individual tasks run for a short period and then jumps back to another task. The speed at which an RTOS can do this makes it Real time, it looks like two tasks are running at the same time, but sadly that is not true. When a single core system is used for the implementation of an RTOS there can only be one program running on the core. This means that if you have multiple threads they won’t run at the same time but just after each other. To prevent the period being on the processor and the time off the processor be uneven the RTOS lets you executed one or more lines of code and then takes you of the processor, which gives other tasks the opportunity to run for a couple of lines. In the end you will be able to executed your whole section of code, I just takes a bit longer.
For the Applied Embedded OS’s module, we started learning basics about an RTOS and developed our skill set from there. While I was doing the introduction, I had a look at the RTOS Handbook presented on the mbed website and there I learned the following about Threads, Mutex, Semaphores, Message pools, queues, RTOS timers and signals.

Threads

Thread are different programs that run simultaneously and they can all have different uses and different lengths. The RTOS will take you off whenever you ask it to do so or when you have been on the processor for x amount of time. In figure 1, the basic code for a thread has been displayed.

 
                                                                                                                                                                                                                                          Figure 1: Basic use of Threads, this lets you turn a let on an of "simultaneously" with another led

Mutex

A mutex can be extremely useful when there is a shared resource pool. This means if you have two different threads running and both try to print something on the LCD that we used during the first period. (mbed application board) When I was creating my program that was due for this period, I found that if you let two different threads print something on the LCD without locking it, they will overwrite, write half of the information or just don’t print visually visible. To prevent this from happen you can use a mutex. In figure 2 is and bit of example code for the a mutex that gives you a good example of how they work.  Another good method you could use to describe a mutex is: You have 1 key for your bike and this is the only key that can unlock it. If you give your key to your brother, you won’t be able to unlock the bike unless he gives the key back.

Figure 2:  Code example of the use of mutex, only one led will change because they use a shared mutex














Semaphores
This uses a semaphores to lock the printf. But this is not the best example you can find for semaphores, whenever you try this example you have to keep in mind that the RTOS schedules the highest mentioned thread in the main first. This means that in this case Th 2 will publish twice while the other two threads only publish once.  Semaphores are like a mutex, they serve the same purpose, locking a shared resource pool and only releasing it when a thread is don’t with that resource. The good thing about semaphores compared to a mutex is that semaphores have multiple keys, as many as you specify. If we go back to the bike, now instead of having only 1 key you have 2. This means that you can unlock the bike even though your brother might not be around to give you the key back. In figure 3 you can see an example program for semaphores.

Figuur 3: an example code for the use of semaphores, BE AWARE this shows rtos scheduling












A important fact you have to keep in mind while programming semaphores or a mutex is that they can either be blocking or non-blocking. Which means that if you ask for a key using mutex.lock() and there is no key available you will be waiting for the mutex to be unlocked by another thread before your program continuous. You could counter this by using either if(semaphores.wait()) or mutex.trylock. This is something to keep in mind when programming, you could either see this as an advantage or as a disadvantage. In my code that I wrote for this period I used the blocking aspect of a semaphores and a mutex in my advantage.

Message Queue and Memory pools

Both are different functions in the RTOS but they need each other to exist and to perform the tasks. What is a memory pool and a queue, a memory pool is a place where you can store a message that then needs to be send to another thread. The use of a memory pool allows you to send multiple messages without overwriting them. This is thanks to the queue, when you created the message that needs to be send, you put it in the queue where it is stored for another function to collect it. This can all be achieved with different threads. The important thing to keep in mind while playing around with memory pools and queues is that the function that reads the message needs to send a signal to the queue that the message can be erased. Otherwise the queue will fill up and the sending function will block at the queue.put command. In the example in figure 4 you can see once more how implementation can be done.

Figure 4: an example code for the use of memory pools and queues

The mail function in rtos is very similair to the memory pool, therefore I decided to not go into that any further. If you wish to get more information on rtos mail you can have a look here
 




















RTOS timer

The RTOS timer is exactly what you expect it to be. When the timer is start the RTOS will count down the ms that where put in and then let the thread run that correspond with that particular timer. In figure 4 there is a example of a rtos timer. The developer assumed that you as an learner already have a good understanding of arrays, threads and pointers, but what is basically does is let the 4 different leds blink once when the time is expired.  I used this when I was playing around with my ISR. Something that needs to researched is can you use a RTOS timer and start it from a thread. If so, can this be useful for the design.
Figure 5: An example code for the use of timers. The developer assumes some understanding of programming

 

 

 

 

 

 

Signals

Figure 6: An example code for the use of signals
Signals are as simple as you can imagine, in your main you can set a signal to have a certain value and in a thread you can wait for that value. This will result in a blocking thread if the thread waits for a signal to run. I found that signals are not particulary useful due to the fact that you can only use a signal when it is set in the main. I might have overlooked something but as far as I could find do you need to set a signal from the main, which for me defeats the purpose of a signal. Therefore I would suggest that a mutex or a semaphores is used in this particulair setup because every thread can lock or unlock those. In figure 6 there is a piece of example code, if you are still interested.



The RTOS has more to offer than I just explained and is worth looking into. You can learn a lot about Real Time Operating Systems and how they might be a good solution for you problem, but even if RTOS is not the solution for your challenge it is always great to have a basic understanding of the things that are out there!

Who am I?

By the time, you made it this far in this post you must either be forced to read this or must be desperate for help. Well let me introduce myself so you have an idea who wrote this post and who I am.

I am an Engineering student from Windesheim Hogeschool in Zwolle(University of Applied Sciences), The Netherlands. At Windesheim I study Electrical and Electronical Engineering. In my last semester, I made my choice to focus more on the electronical part, I have done some modules on Wireless communication, embedded systems, developed PCBs and worked on different project. At the moment of writing this blog I am sitting in my kitchen in a land that I was unfamiliar with up until 34 days ago. I took a step in the dark, I decided that I wanted to study abroad, I belief that the world is bigger than the Netherlands and that there is more to learn and more to do than stay at home. Don’t get me wrong, getting out of my comfort zone stepping on a plan and ending up with people I didn’t knew existed until 2 weeks ago. Sounds a bit crazy, I can tell you, it is! But next to it being so strange to live in a country you are for the first time in forever, is the fact that I get to know all those amazing people who decide the same thing and jump on a plane and of course all the lovely Irish people that I had to honour to meet. 

So, that is being said, now a bit more about myself, I am a 20-year-old, ambitious, self-aware, focussed, always in for a chat! Every single day I get out of bed knowing that there will be people I can work together with and learn from! I have been play field hockey for almost 14 years, with a time out for the last 2 a 3 year. In this period, I decide I wanted to be an umpire/referee and be involved in the game on a different way. Now 4 years after I started umpiring for our regional association, I am proud to say I fly through Europe for practice tournaments and I even had the honour of going to Moscow for a tournament. A sportaholic is a term which you can use to describe me, I need my sport as much as I need a cold beer on a sunny day. 

My motto for life: If you can’t do it on your own, do it together!
------------------------------------------------------------------------------------------------------------------------------------------------
  1. #include "mbed.h"  
  2. #include "rtos.h"  
  3. #include "Servo.h"  
  4. #include "C12832.h"  
  5.   
  6. Serial pc(USBTX, USBRX); //Serial communication with PC  
  7. DigitalOut Led1(LED1); //LED1 on LPC  
  8. DigitalOut Led2(LED2); //LED2 on LPC  
  9. DigitalOut Led3(LED3); //LED3 on LPC  
  10. DigitalOut Led4(LED4); //LED4 on LPC  
  11. Servo myservo1(p21);    //Servo out1  
  12. Servo myservo2(p22);   //Servo out2  
  13. C12832 lcd(p5, p7, p6, p8, p11); //Pins for LCD  
  14. PwmOut RGBLED_r(p23);   //RGB red color   
  15. PwmOut RGBLED_g(p24);   //RGB green color  
  16. PwmOut RGBLED_b(p25);   //RGB blue color  
  17. PwmOut spkr(p26);       //Speaker output  
  18.   
  19.   
  20. Mutex m_rgb, m_lcd; //mutex, x = distance_x, y = distance_x, m_lcd = LCD use, m_rgb = rgb led use  
  21. char nr1[4], nr2[4], nr3[4];  
  22. int i=0;  
  23. Semaphore spkr_slot(1); //Semaphores, spkr_slot = speaker sound after ISR1  
  24.   
  25. typedef struct{  
  26.     int distance_xt;  
  27. } message_x;  
  28.   
  29. typedef struct{  
  30.     int distance_yt;  
  31. }message_y;  
  32.   
  33.   
  34. MemoryPoolmpool_x;  
  35. Queue queue;  
  36. MemoryPoolmpool_y;  
  37. Queue queue_y;  
  38.   
  39.   
  40.   
  41. //identifying of threads  
  42. void Servo_X();  
  43. void Servo_Y();  
  44. void Speaker();  
  45. void RGB();  
  46. void ISR1();  
  47. void S_IN();  
  48.          
  49.   
  50.   
  51.   
  52. // main, used as a thread starter, mutex locker, semaphores locker and as an infinited loop to prevent the LPC to stop running.  
  53. int main() {  
  54.       lcd.cls();    //LCD is cleared  
  55.       RGBLED_r = 1; //RGB is turned off.  
  56.       RGBLED_g = 1;  
  57.       RGBLED_b = 1;  
  58.       m_rgb.lock();  
  59.       spkr_slot.wait();  
  60.           Thread Serial_IN(S_IN);  
  61.           Thread Speaker_thread(Speaker);  
  62.           Thread Servo_thread_X(Servo_X);  
  63.           Thread Servo_thread_Y(Servo_Y);  
  64.           //Thread Interrupt_Service_routine(ISR1);   
  65.           Thread RGB_Controller(RGB);  
  66.           RtosTimer ISr(ISR1); //This needs to be changed to Thread Interrupt_Service_routine(ISR1); when a DigitalIn can be used as a trigger.  
  67.      //ISr.start(15000); //This starts a timer for the ISr to trigger an "fake" Interrupt event.  
  68.    while(1){   
  69.           Thread::wait(2000);  
  70.    }  
  71.         
  72. }  
  73.   
  74. void S_IN(){  
  75.     int distance_x = 0, distance_y = 0;  
  76.     int nr1_i = 0, nr2_i =0,a, b, i1=1, i2=1;  
  77.     int currStr = 0;  
  78.     char c;  
  79.     while(1){  
  80.         if(pc.readable()){  
  81.             c = pc.getc();  
  82.             if(c == ' '){  
  83.                 currStr++;  
  84.                 if(currStr == 1){  
  85.                     a = atoi(nr1);  
  86.                     if(a<95 a="" distance_x="" i1="" nbsp="" span="">
  87.                     if(a>105){a = a - (5*i1); distance_x = a; i1++;}   
  88.                     if(a>200){a = 200; distance_x = a;}  
  89.                     if(i1>18){i1 = 1;}                             
  90.                     message_x *message = mpool_x.alloc();  
  91.                     message->distance_xt = distance_x;  
  92.                     queue.put(message);                 
  93.                     m_lcd.lock();  
  94.                     lcd.locate(0,0);  
  95.                     lcd.printf("Distance_x: '%d'", distance_x);  
  96.                     m_lcd.unlock();  
  97.                     nr1_i = 0;  
  98.                     for(int i = 0; i<5 i="" nbsp="" span="">
  99.                         nr1[i] = '\0';  
  100.                     }  
  101.                 }  
  102.                 if(currStr == 2){  
  103.                     b = atoi(nr2);  
  104.                     if(b<95 b="" distance_y="" i2="" nbsp="" span="">
  105.                     if(b>105){b = b - (5* i2); distance_y = b; i2++;}  
  106.                     if(b>200){b = 200; distance_y = b;}  
  107.                     if(i2>18){i2=1;}  
  108.                     message_y *message_f = mpool_y.alloc();  
  109.                     message_f->distance_yt = distance_y;  
  110.                     queue_y.put(message_f);  
  111.                     m_lcd.lock();  
  112.                     lcd.locate(0,12);  
  113.                     lcd.printf("distance_y: '%d'", distance_y);  
  114.                     m_lcd.unlock();  
  115.                     nr2_i = 0;  
  116.                     for(int i = 0; i<5 i="" nbsp="" span="">
  117.                         nr2[i] = '\0';  
  118.                     }  
  119.                     currStr = 0;  
  120.                 }    
  121.             }  
  122.             if(currStr == 0){  
  123.                 nr1[nr1_i] = c;  
  124.                 nr1_i++;  
  125.             }  
  126.             if(currStr == 1){  
  127.                 nr2[nr2_i] = c;  
  128.                 nr2_i++;  
  129.             }  
  130.   
  131.         }       
  132.     }      
  133. }  
  134.   
  135.   
  136.   
  137.   
  138. //thread declaration of Speaker, this thread will only be executed if spkr_slot is released by Thread ISR1, here the use of semaphores is demonstrated.  
  139. void Speaker(){  
  140.     while(true) {   
  141.         spkr_slot.wait();  
  142.         for(int g=0; g<2 g="" nbsp="" span="">
  143.             spkr.period(1.0/800.0);  
  144.             spkr = 0.01;  
  145.             Thread::wait(500);      
  146.             spkr.period(1.0/969.0);  
  147.             spkr = 0.01;  
  148.             Thread::wait(500);     
  149.             i++;  
  150.         }   
  151.         if(i == 2){spkr = 1.00;}  
  152.         m_lcd.lock();  
  153.         lcd.locate(30,0);  
  154.         lcd.printf("running");  
  155.         m_lcd.unlock();  
  156.     }  
  157. }  
  158.   
  159. //thread declaration of RGB, this thread will only be executed if m_rgb is released by Thread ISR1, here the use of mutex is demonstrated   
  160. void RGB(){  
  161.     while(true) {  
  162.         m_rgb.lock();           
  163.         RGBLED_r = 0.5 + (rand() % 11)/20.0;  
  164.         RGBLED_g = 0.5 + (rand() % 11)/20.0;  
  165.         RGBLED_b = 0.5 + (rand() % 11)/20.0;  
  166.         Thread::wait(1667);    // wait 1.5s  
  167.         m_rgb.unlock();  
  168.     }  
  169. }  
  170. //thread declaration of ISR1, This thread is the Interrupt Service Routine Handler and will exectuded, if a digitalIn is high  
  171. void ISR1(){  
  172.     while(1){  
  173.   
  174.         m_rgb.unlock();  
  175.         lcd.cls();  
  176.           
  177.         m_lcd.lock();  
  178.         lcd.locate(0,0);  
  179.         lcd.printf("Trigger!!");  
  180.         m_lcd.unlock();  
  181.           
  182.         spkr_slot.release();  
  183.           
  184.         Thread::wait(1000);  
  185.           
  186.     }  
  187. }  
  188.   
  189. //Thread declaration of Servo_X, This thread controls the pan motion of the Servo's, this will try to keep the recieved x value within a range of 85 and 115  
  190. //The gap of 30 is introducted to give the Servo's more stablility.      
  191. void Servo_X()  
  192. {  
  193.      float distance_x;  
  194.      while(1){  
  195.         osEvent evt = queue.get();  
  196.         if(evt.status == osEventMessage){  
  197.             message_x *message = (message_x*)evt.value.p;  
  198.             distance_x = message->distance_xt;  
  199.             myservo1 = distance_x/200;  
  200.             mpool_x.free(message);  
  201.               
  202.         }     
  203.     }  
  204. }  
  205.   
  206. //Thread declaration of Servo_Y, The layout and built up of this thread is the same as the Servo_X thread.  
  207. //This thread controls the tilt motion of the Servo's, this will try to keep the recieved x value within a range of 85 and 115  
  208. //The gap of 30 is introducted to give the Servo's more stablility.      
  209.   
  210. void Servo_Y(){  
  211.     float distance_y;  
  212.       
  213.     while(1){  
  214.         osEvent evt = queue_y.get();  
  215.         if(evt.status == osEventMessage){  
  216.             message_y *message_f = (message_y*)evt.value.p;  
  217.             distance_y = message_f->distance_yt;  
  218.             mpool_y.free(message_f);  
  219.             myservo2 = distance_y/200;  
  220.            }  
  221.     }  
  222.   



No comments:

Post a Comment