U.B.O.R
The [U]seless [B]ox [O]rganizing [R]obot. A FreeRTOS study project written in C which implements a multitasked control unit for a belt conveyor system with robotic sorting arms.
bcs.c
Go to the documentation of this file.
1 /*****************************************************************************
2  *
3  * This program is free software; you can redistribute it and/or
4  * modify it under the terms of the GNU General Public License
5  * as published by the Free Software Foundation; either version 2
6  * of the License, or (at your option) any later version.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program; if not, write to the Free Software
15  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
16  * MA 02110-1301, USA.
17  *
18  *****************************************************************************/
19 
25 
26 
27 
28 #include "display.h"
29 #include <FreeRTOS.h>
30 #include <stdio.h>
31 #include <task.h>
32 #include <queue.h>
33 #include <semphr.h>
34 #include <stdbool.h>
35 #include <string.h>
36 #include "ucan.h"
37 #include "bcs.h"
38 
39 // -------------------- Configuration ------------
40 #define STACKSIZE_TASK 256
41 #define PRIORITY_TASK 2
42 
43 #define MAX_BLOCK_COUNT 3
44 
45 // ------------------ Implementation --------------
46 
47 #define SWITCH ((volatile unsigned char*)(0x6C000400))
48 
52 typedef struct {
53  uint16_t subid;
54  uint8_t length;
55  uint8_t data[];
56 } message_t;
57 
58 
62 typedef struct __attribute__((__packed__))
63 {
64  uint8_t error;
65  uint8_t engine;
66  uint8_t lightbarrier;
67  uint8_t detection;
68  uint16_t position;
69  int8_t location;
70 }
72 
73 //Messages for conveyer system
74 static const message_t msg_status_request = {0,1,{1}};
75 static const uint16_t msg_status_response_id = 1;
76 static const message_t msg_cmd_start = {2,3,{1,0,0}};
77 static const message_t msg_cmd_stop = {2,3,{2,0,0}};
78 static const message_t msg_cmd_stoppos = {2,3,{3,0x00,0xB6}};
79 static const message_t msg_cmd_done= {2,3,{4,0,0}};
80 static const message_t msg_cmd_reset= {0xF,0};
81 
82 //Messages for dispatcher
83 static const message_t msg_cmd_disp_initial_pos = {0x142, 3, {1, 0, 100}};
84 static const message_t msg_cmd_disp_start_right = {0x142, 3, {1, 0xE4, 100}};
85 static const message_t msg_cmd_disp_move_right = {0x142, 3, {1, 0x32, 50}};
86 static const message_t msg_cmd_disp_start_left = {0x142, 3, {1, 0x1C, 100}};
87 static const message_t msg_cmd_disp_move_left = {0x142, 3, {1, 0xCE, 50}};
88 
89 //Messages Queues to receive data from can
90 static QueueHandle_t ucan_queue_mid;
91 static QueueHandle_t ucan_queue_left;
92 static QueueHandle_t ucan_queue_right;
93 
94 //Semaphores
95 static SemaphoreHandle_t bcs_left_start_semaphore; //Given by mid task, Taken by left
96 static SemaphoreHandle_t bcs_mid_start_semaphore; //Given by arm Tasks, taken by Mid task
97 static SemaphoreHandle_t bcs_right_start_semaphore; //Given by mid task, Taken by right
98 static SemaphoreHandle_t bcs_left_free_semaphore; //Given by left task, Taken by arm task
99 static SemaphoreHandle_t bcs_mid_free_semaphore; //Given by mid task, Taken by left/right task
100 static SemaphoreHandle_t bcs_right_free_semaphore; //Given by right task, Taken by arm task
101 
102 static QueueHandle_t bcs_left_end_queue; //Given by left task, Taken by Arm Left
103 static QueueHandle_t bcs_right_end_queue; //Given by right task, taken by Arm Right
104 
105 
106 
114 static void bcs_send_msg(const message_t* msg, uint16_t baseaddr)
115 {
116  ucan_send_data(msg->length,baseaddr + msg->subid, msg->data);
117  vTaskDelay(5);
118 }
119 
120 
129 static status_t* bcs_await_block(enum belt_select belt, QueueHandle_t ucan_queue, CARME_CAN_MESSAGE* tmp_message)
130 {
131  uint8_t statR = display_log(DISPLAY_NEWLINE,"Waiting on block...");
132  uint16_t wait_count = 0;
133  status_t* status;
134 
135  /* Wait until the block is fully detected */
136  while(true) {
137 
138  /* Request status */
139  bcs_send_msg(&msg_status_request,belt);
140 
141  /* Wait on status response */
142  do {
143  if(xQueueReceive(ucan_queue,tmp_message,4000)==pdFALSE) {
144  wait_count++;
145  display_log(statR,"Waiting on block (%u): timeout", wait_count);
146  }
147  } while(tmp_message->id != belt+msg_status_response_id); //repeat until correct response message arrives
148 
149  status = (status_t*)&(tmp_message->data);
150  wait_count++;
151 
152  /* Block detected */
153  if(status->detection == 3) {
154  display_log(statR,"Waiting on block. Found! position %04x location %d", status->position, status->location );
155  return status;
156  }
157 
158  display_log(statR,"Waiting on block (%u): detection: %u pos: %04x",wait_count,status->detection, status->position);
159  vTaskDelay(100);
160 
161  /* Timeout */
162  if(wait_count >= 100) {
163  bcs_send_msg(&msg_cmd_done,belt);
164  display_log(statR,"Waiting on block (%u): Aborted",wait_count);
165  return NULL;
166  }
167  }
168 }
169 
170 
171 
179 {
180 
181  switch(belt) {
182  case belt_left:
183  xSemaphoreTake(bcs_left_free_semaphore,portMAX_DELAY);
184  break;
185  case belt_right:
186  xSemaphoreTake(bcs_right_free_semaphore,portMAX_DELAY);
187  break;
188  case belt_mid:
189  xSemaphoreTake(bcs_mid_free_semaphore,portMAX_DELAY);
190  break;
191 
192  }
193 }
194 
202 {
203  switch(belt) {
204  case belt_left:
205  xSemaphoreGive(bcs_left_start_semaphore);
206  break;
207  case belt_right:
208  xSemaphoreGive(bcs_right_start_semaphore);
209  break;
210  case belt_mid:
211  xSemaphoreGive(bcs_mid_start_semaphore);
212  break;
213 
214  }
215 }
216 
224 {
225  switch(belt) {
226  case belt_left:
227  xSemaphoreGive(bcs_left_free_semaphore);
228  break;
229  case belt_right:
230  xSemaphoreGive(bcs_right_free_semaphore);
231  break;
232  case belt_mid:
233  xSemaphoreGive(bcs_mid_free_semaphore);
234  break;
235 
236  }
237 }
238 
246 static void bcs_await_drop(enum belt_select belt, bool allow_skip)
247 {
248 
249  switch(belt) {
250  case belt_left:
251  xSemaphoreTake(bcs_left_start_semaphore,portMAX_DELAY);
252  break;
253  case belt_right:
254  xSemaphoreTake(bcs_right_start_semaphore,portMAX_DELAY);
255  break;
256  case belt_mid:
257  if(allow_skip) {
258  xSemaphoreTake(bcs_mid_start_semaphore,2000); //try to aquire mutex anyway, in case it was already there
259  //if mutex could not be taken => go on (skipping is allowed)
260  } else {
261  xSemaphoreTake(bcs_mid_start_semaphore,portMAX_DELAY);
262  }
263  break;
264 
265  }
266 
267 }
268 
269 
276 int8_t bcs_grab(enum belt_select belt)
277 {
278  int8_t pos;
279  if(belt == belt_left) {
280  xQueueReceive(bcs_left_end_queue,&pos,portMAX_DELAY);
281  } else if(belt == belt_right) {
282  xQueueReceive(bcs_right_end_queue,&pos,portMAX_DELAY);
283  }
284  return pos;
285 }
286 
293 static void bcs_task(void *pv_data)
294 {
295  enum belt_select belt = (enum belt_select)pv_data;
296 
297  QueueHandle_t ucan_queue;
298 
299  switch(belt) {
300  case belt_left:
301  ucan_queue = ucan_queue_left;
302  break;
303  case belt_right:
304  ucan_queue = ucan_queue_right;
305  break;
306  case belt_mid:
307  ucan_queue = ucan_queue_mid;
308  break;
309 
310  }
311 
312 
313  //only for mid task
314  bool move_left = true; //whether the dispatcher should move left or right
315  uint8_t mid_start_without_mutex_count = 0; //The number of times we started the mid band without awaiting the mutex
316 
317 
318  while(true) {
319  //----- Step 0: Reset band
320  bcs_send_msg(&msg_cmd_reset,belt);
321  display_log(DISPLAY_NEWLINE,"reset band");
322 
323  //----- Step 1: Wait on a block (take semaphore), before we start the band ------------------
324  bool allow_skip = false;
325  if(belt == belt_mid) {
326  if(mid_start_without_mutex_count < MAX_BLOCK_COUNT) { //we are in the init phase
327  mid_start_without_mutex_count++;
328  allow_skip = true;
329  }
330 
331  display_log(DISPLAY_NEWLINE,"Reset dispatcher");
332  bcs_send_msg(&msg_cmd_disp_initial_pos,0);
333  }
334 
335  bcs_await_drop(belt,allow_skip);
336 
337 
338  //----- Step 2: Start the band, and wait till we detect the block
339  bcs_send_msg(&msg_cmd_start,belt);
340  bcs_send_msg(&msg_cmd_stoppos,belt);
341  display_log(DISPLAY_NEWLINE,"start band");
342 
343  CARME_CAN_MESSAGE tmp_message;
344  status_t* status = bcs_await_block(belt,ucan_queue,&tmp_message);
345  if(status==NULL) { //timeout
346  while(true);
347  //TODO: Listen on button press
348  }
349 
350  //----- Step 3 (only mid band): Move the dispatcher so we don't interfere with the coming block
351  if(belt== belt_mid) {
352  if(*SWITCH&0x01) { //Manual Direction selection
353  move_left = *SWITCH&0x02; //read direction from switch
354  }
355  display_log(DISPLAY_NEWLINE,"Making dispatcher ready for moving %s",move_left ? "left" : "right");
356  bcs_send_msg(move_left ? &msg_cmd_disp_start_left : &msg_cmd_disp_start_right,0);
357  }
358  vTaskDelay(2000); //let block move to the end of the band
359 
360 
361  //----- Step 4: Mark the block ready for further processing (give semaphore) and optionally move the dispatcher (only mid band).
362  switch(belt) {
363  case belt_left:
364  //bcs_prepare_pickup(belt_left)
365  xQueueSend(bcs_left_end_queue,&(status->location),portMAX_DELAY);
366  break;
367  case belt_right:
368  //bcs_prepare_pickup(belt_right)
369  xQueueSend(bcs_right_end_queue,&(status->location),portMAX_DELAY);
370  break;
371  case belt_mid:
372  bcs_prepare_drop(move_left ? belt_left : belt_right);
373 
374  display_log(DISPLAY_NEWLINE,"Dispatcher moves %s",move_left ? "left" : "right");
375  bcs_send_msg(move_left ? &msg_cmd_disp_move_left : &msg_cmd_disp_move_right,0);
376  vTaskDelay(1000); //let dispatcher move block away
377 
378  bcs_signal_dropped(move_left ? belt_left : belt_right);
379  move_left = ! move_left;
380  break;
381 
382 
383  }
384 
385  //---- Step 5: Tell the band that we're finished
386  bcs_send_msg(&msg_cmd_done,belt);
387  if(belt == belt_mid) {
389  }
390  }
391 }
392 
398 void bcs_init()
399 {
400  bcs_left_start_semaphore = xSemaphoreCreateBinary();
401  bcs_right_start_semaphore = xSemaphoreCreateBinary();
402  bcs_mid_start_semaphore = xSemaphoreCreateBinary();
403 
404  bcs_left_free_semaphore = xSemaphoreCreateBinary();
405  bcs_mid_free_semaphore = xSemaphoreCreateBinary();
406  bcs_right_free_semaphore = xSemaphoreCreateBinary();
407  xSemaphoreGive(bcs_left_free_semaphore);
408  //xSemaphoreGive(bcs_mid_free_semaphore);
409  xSemaphoreGive(bcs_right_free_semaphore);
410 
411  bcs_left_end_queue = xQueueCreate(1,sizeof(int8_t));
412  bcs_right_end_queue = xQueueCreate(1,sizeof(int8_t));
413 
414  xTaskCreate(bcs_task,"mid",STACKSIZE_TASK,(void*)belt_mid,PRIORITY_TASK,NULL);
415  xTaskCreate(bcs_task,"left",STACKSIZE_TASK,(void*)belt_left,PRIORITY_TASK,NULL);
416  xTaskCreate(bcs_task,"right",STACKSIZE_TASK,(void*)belt_right,PRIORITY_TASK,NULL);
417 
418  ucan_queue_right = xQueueCreate(1,sizeof(CARME_CAN_MESSAGE));
419  ucan_queue_left = xQueueCreate(1,sizeof(CARME_CAN_MESSAGE));
420  ucan_queue_mid = xQueueCreate(1,sizeof(CARME_CAN_MESSAGE));
421 
422  ucan_link_message_to_queue_mask(0xFF0,belt_mid,ucan_queue_mid);
423  ucan_link_message_to_queue_mask(0xFF0,belt_left,ucan_queue_left);
424  ucan_link_message_to_queue_mask(0xFF0,belt_right,ucan_queue_right);
425 }
426 
void bcs_init()
Initializes the belt conveyer system and starts the belt tasks global.
Definition: bcs.c:398
#define MAX_BLOCK_COUNT
Number of blocks to work with. Must be between 2 and 4.
Definition: bcs.c:43
status_t
Definition: bcs.c:71
#define DISPLAY_NEWLINE
Definition: display.h:10
void bcs_signal_band_free(enum belt_select belt)
Signal that a block has been removed from a belt and the belt is free again global.
Definition: bcs.c:223
uint8_t data[]
CAN data.
Definition: bcs.c:55
uint8_t length
Data-Length of the CAN message.
Definition: bcs.c:54
#define SWITCH
Definition: bcs.c:47
the right belt
Definition: bcs.h:10
void bcs_signal_dropped(enum belt_select belt)
Signal that a block has been dropped on a belt global.
Definition: bcs.c:201
bool ucan_send_data(uint8_t n_data_bytes, uint16_t msg_id, const uint8_t *data)
Send data to the can output message queue global.
Definition: ucan.c:264
#define STACKSIZE_TASK
Stack size of all bcs tasks.
Definition: bcs.c:40
the left belt
Definition: bcs.h:8
void bcs_prepare_drop(enum belt_select belt)
Prepares a block drop operation to a specific belt global.
Definition: bcs.c:178
uint8_t display_log(uint8_t id, const char *fmtstr,...)
Logs a message to the display global.
Definition: display.c:70
the middle belt
Definition: bcs.h:9
int8_t bcs_grab(enum belt_select belt)
Instructs the system that we want to grab a block from the bcs global.
Definition: bcs.c:276
bool ucan_link_message_to_queue_mask(uint16_t mask, uint16_t message_id, QueueHandle_t queue)
Set a message mask to map multiple message to a queue global.
Definition: ucan.c:178
belt_select
The belt_select enum differenciates between the different belt tasks. The members point to the base a...
Definition: bcs.h:8
uint16_t subid
Id offset of the base-id.
Definition: bcs.c:53
BCS can message structure.
Definition: bcs.c:52
#define PRIORITY_TASK
Priority of all bcs tasks.
Definition: bcs.c:41
struct __attribute__((__packed__))
Status message received by belt conveyer system.
Definition: bcs.c:62