/**
FreeRTOS Counting Semaphore Challenge
Challenge: use a mutex and counting semaphores to protect the shared buffer
so that each number (0 throguh 4) is printed exactly 3 times to the Serial
monitor (in any order). Do not use queues to do this!
Hint: you will need 2 counting semaphores in addition to the mutex, one for
remembering number of filled slots in the buffer and another for
remembering the number of empty slots in the buffer.
Date: January 24, 2021
Author: Shawn Hymel
License: 0BSD
*/
// You'll likely need this on vanilla FreeRTOS
#include <TM1637.h>
//4 digits display settings
const int CLK = 22;
const int DIO = 21;
TM1637 tm(CLK, DIO);
// Use only core 1 for demo purposes
#if CONFIG_FREERTOS_UNICORE
static const BaseType_t app_cpu = 0;
#else
static const BaseType_t app_cpu = 1;
#endif
// Settings
enum {BUF_SIZE = 4}; // Size of buffer array
static const int num_prod_tasks = 4; // Number of producer tasks
static const int num_cons_tasks = 2; // Number of consumer tasks
static const int num_writes = 3; // Num times each producer writes to buf
// Globals
static int buf[BUF_SIZE]; // Shared buffer
static int head = 0; // Writing index to buffer
static int tail = 0; // Reading index to buffer
static SemaphoreHandle_t bin_sem; // Waits for parameter to be read
SemaphoreHandle_t sem_empty; // Waits for parameter to be read
SemaphoreHandle_t sem_filled; // Waits for parameter to be read
SemaphoreHandle_t mutex; // Waits for parameter to be read
static int producerWorkload[BUF_SIZE] = {0, 0, 0, 0};
static int countMax = num_writes * num_prod_tasks ;
void initView() {
tm.set(BRIGHTEST);
tm.init();
}
void setupView() {
for (int i=0;i<BUF_SIZE;i++){
tm.display(i, producerWorkload[i]);
}
}
//*****************************************************************************
// Tasks
// Producer: write a given number of times to shared buffer
void producer(void *parameters) {
// Copy the parameters into a local variable
int num = *(int *)parameters;
// Release the binary semaphore
xSemaphoreGive(bin_sem);
// Fill shared buffer with task number
for (int i = 0; i < num_writes; i++) {
// Wait for empty slot in buffer to be available
xSemaphoreTake(sem_empty, portMAX_DELAY);
// Lock critical section with a mutex
xSemaphoreTake(mutex, portMAX_DELAY);
buf[head] = num;
head = (head + 1) % BUF_SIZE;
producerWorkload[num] += 1;
Serial.print("producing ");
for (int i = 0; i < num; i++) {
Serial.print(" ");
}
Serial.print(num);
Serial.println();
xSemaphoreGive(mutex);
// Signal to consumer tasks that a slot in the buffer has been filled
xSemaphoreGive(sem_filled);
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
// Delete self task
vTaskDelete(NULL);
}
// Consumer: continuously read from shared buffer
void consumer(void *parameters) {
int val;
// Copy the parameters into a local variable
int num = *(int *)parameters;
// Release the binary semaphore
xSemaphoreGive(bin_sem);
// Read from buffer
while (1) {
// Critical section (accessing shared buffer and Serial)
xSemaphoreTake(sem_filled, portMAX_DELAY);
xSemaphoreTake(mutex, portMAX_DELAY );
val = buf[tail];
tail = (tail + 1) % BUF_SIZE;
producerWorkload[val] -= 1;
countMax--;
Serial.print("consuming ");
for (int i = 0; i < val; i++) {
Serial.print(" ");
}
Serial.print(val);
Serial.println();
xSemaphoreGive(mutex);
xSemaphoreGive(sem_empty);
vTaskDelay(2000 / portTICK_PERIOD_MS);
}
}
void printDigits(void *parameters) {
//total changes
while ( countMax > -1) {
Serial.print("************************************");
Serial.println(countMax);
setupView();
if (countMax == 0) break;
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
vTaskDelete(NULL);
}
//*****************************************************************************
// Main (runs as its own task with priority 1 on core 1)
void setup() {
char task_name[12];
initView();
// Configure Serial
Serial.begin(9600);
// Wait a moment to start (so we don't miss Serial output)
vTaskDelay(1000 / portTICK_PERIOD_MS);
Serial.println();
Serial.println("---FreeRTOS Semaphore Alternate Solution---");
// Create mutexes and semaphores before starting tasks
bin_sem = xSemaphoreCreateBinary();
mutex = xSemaphoreCreateMutex();
sem_empty = xSemaphoreCreateCounting(BUF_SIZE, BUF_SIZE);
sem_filled = xSemaphoreCreateCounting(BUF_SIZE, 0);
xTaskCreatePinnedToCore(printDigits,
"show producers status",
1024 * 3,
NULL,
2,
NULL,
app_cpu);
vTaskDelay(1000 / portTICK_PERIOD_MS);
// Start producer tasks (wait for each to read argument)
for (int i = 0; i < num_prod_tasks; i++) {
sprintf(task_name, "Producer %d", i);
Serial.println(task_name);
xTaskCreatePinnedToCore(producer,
task_name,
1024 * 4,
(void *)&i,
1,
NULL,
app_cpu);
xSemaphoreTake(bin_sem, portMAX_DELAY);
vTaskDelay(3000 / portTICK_PERIOD_MS);
}
vTaskDelay(1000 / portTICK_PERIOD_MS);
// Start consumer tasks
for (int i = 0; i < num_cons_tasks; i++) {
sprintf(task_name, "Consumer %d", i);
Serial.println(task_name);
xTaskCreatePinnedToCore(consumer,
task_name,
1024 *4,
(void *)&i,
1,
NULL,
app_cpu);
xSemaphoreTake(bin_sem, portMAX_DELAY);
}
// Notify that all tasks have been created
Serial.println("All tasks created");
}
void loop() {
// Do nothing but allow yielding to lower-priority tasks
vTaskDelay(1000 / portTICK_PERIOD_MS);
}