Last active
June 6, 2022 17:47
-
-
Save fjrg76-com/4b0238dd53e66f3c89c7e6820da5bad2 to your computer and use it in GitHub Desktop.
An example of a condition variable implementation using mutex'es from FreeRTOS for a NXP chip; however, it's easily portable to other architectures.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*Copyright (C) | |
* 2021 - fjrg76 at hotmail dot com | |
* | |
* This program is free software; you can redistribute it and/or | |
* modify it under the terms of the GNU General Public License | |
* as published by the Free Software Foundation; either version 2 | |
* of the License, or (at your option) any later version. | |
* | |
* This program is distributed in the hope that it will be useful, | |
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
* GNU General Public License for more details. | |
* | |
* You should have received a copy of the GNU General Public License | |
* along with this program; if not, write to the Free Software | |
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |
* | |
*/ | |
/* Some theory (in spanish) here: http://fjrg76.com/2021/06/08/variables_de_condicion_y_monitores_i/ */ | |
#include <cstdint> | |
#include <cstdlib> | |
// for rand() and friends | |
#include "board.h" | |
#include <cr_section_macros.h> | |
#include "FreeRTOSConfig.h" | |
#include "FreeRTOS.h" | |
#include "task.h" | |
#include "semphr.h" | |
//---------------------------------------------------------------------- | |
// Random number generator stuff: | |
//---------------------------------------------------------------------- | |
uint32_t xorshift32_state; | |
void xorshift_seed( uint32_t seed ) | |
{ | |
xorshift32_state = seed; | |
} | |
uint32_t xorshift32() | |
{ | |
uint32_t y = xorshift32_state; | |
y ^= y << 13; | |
y ^= y >> 17; | |
y ^= y << 5; | |
return( xorshift32_state = y ); | |
} | |
void ADC_Config() | |
{ | |
Chip_ADC_Init(LPC_ADC0, 0); | |
Chip_ADC_SetClockRate(LPC_ADC0, ADC_MAX_SAMPLE_RATE); | |
Chip_ADC_SetupSequencer( LPC_ADC0, ADC_SEQA_IDX, (ADC_SEQ_CTRL_CHANSEL(0) | ADC_SEQ_CTRL_MODE_EOS)); | |
Chip_SYSCTL_PowerUp(SYSCTL_POWERDOWN_TS_PD); | |
Chip_ADC_SetADC0Input(LPC_ADC0, ADC_INSEL_TS); | |
Chip_ADC_SetTrim(LPC_ADC0, ADC_TRIM_VRANGE_HIGHV); | |
Chip_ADC_StartCalibration(LPC_ADC0); | |
while (!(Chip_ADC_IsCalibrationDone(LPC_ADC0))) {} | |
Chip_ADC_EnableSequencer(LPC_ADC0, ADC_SEQA_IDX); | |
} | |
uint32_t calculate_seed() | |
{ | |
uint32_t seed = 0; | |
for( size_t i = 0; i < 16; ++i ){ | |
Chip_ADC_StartSequencer(LPC_ADC0, ADC_SEQA_IDX); | |
while( ( Chip_ADC_GetDataReg( LPC_ADC0, 0 ) & ( ADC_SEQ_GDAT_DATAVALID ) ) == false ){ ; } | |
seed += Chip_ADC_GetDataReg( LPC_ADC0, 0 ); //ADC_DR_RESULT( Chip_ADC_GetDataReg( LPC_ADC0, 0 ) ); | |
} | |
return seed * seed; | |
} | |
#define srand( x ) xorshift_seed( ( x ) ) | |
#define rand() xorshift32() | |
#define DEBUG_ENABLED 0 | |
#if DEBUG_ENABLED > 0 | |
#define DEBUG_PRINT0(...) printf(__VA_ARGS__) | |
#else | |
#define DEBUG_PRINT0(...) ; | |
#endif | |
//---------------------------------------------------------------------- | |
// Conditional variable stuff: | |
//---------------------------------------------------------------------- | |
template<size_t Len> | |
class ConditionV | |
{ | |
private: | |
TaskHandle_t queue[ Len ]; | |
size_t head{ 0 }; | |
size_t tail{ 0 }; | |
size_t max { Len }; | |
size_t len { 0 }; | |
public: | |
#if 0 | |
ConditionV( SemaphoreHandle_t& the_mutex ) : mutex{ the_mutex } | |
{ | |
// nothing | |
} | |
#endif | |
bool CWait( SemaphoreHandle_t& mutex, TickType_t ticks = portMAX_DELAY ) | |
{ | |
TaskHandle_t self; | |
taskENTER_CRITICAL(); | |
{ | |
self = xTaskGetCurrentTaskHandle(); | |
configASSERT( this->len < this->max ); | |
this->queue[ this->tail++ ] = self; | |
if( this->tail == this->max ) this->tail = 0; | |
++this->len; | |
} taskEXIT_CRITICAL(); | |
xSemaphoreGive( mutex ); | |
vTaskSuspend( self ); | |
return xSemaphoreTake( mutex, ticks ); | |
} | |
bool CWaitFor( SemaphoreHandle_t& mutex, TickType_t ticks ) | |
{ | |
return CWait( mutex, ticks ); | |
} | |
bool CWaitUntil( SemaphoreHandle_t mutex, TickType_t& last, TickType_t ticks ) | |
{ | |
volatile TickType_t now = xTaskGetTickCount(); | |
// don't optimize | |
TickType_t until = last + ticks - now; | |
// overflows are handled automatically | |
last = until; | |
return CWait( mutex, until ); | |
} | |
/** | |
* @brief Wakes up the task that has been waiting the longer. | |
*/ | |
void CNotify() | |
{ | |
TaskHandle_t task = nullptr; | |
taskENTER_CRITICAL(); | |
{ | |
if( this->len > 0 ){ | |
task = this->queue[ this->head++ ]; | |
if( this->head == this->max ) this->head = 0; | |
--this->len; | |
} | |
} taskEXIT_CRITICAL(); | |
if( task != nullptr ) vTaskResume( task ); | |
} | |
/** | |
* @brief Wakes up all tasks. | |
*/ | |
void CNotifyAll() | |
{ | |
taskENTER_CRITICAL(); | |
{ | |
while( this->len ) CNotify(); | |
} taskEXIT_CRITICAL(); | |
} | |
}; | |
//---------------------------------------------------------------------- | |
// Example: | |
//---------------------------------------------------------------------- | |
ConditionV<4> data_avail; | |
ConditionV<4> space_avail; | |
/* Queue stuff. Should be an abstract type (e.g. a C++ class): */ | |
char buf[ 8 ]; | |
size_t head{ 0 }; | |
size_t tail{ 0 }; | |
size_t max { 8 }; | |
size_t len { 0 }; | |
SemaphoreHandle_t mutex; | |
void put( char c ) | |
{ | |
xSemaphoreTake( mutex, portMAX_DELAY ); | |
while( len >= max ){ | |
DEBUG_PRINT0( "*\n" ); | |
space_avail.CWait( mutex ); | |
} | |
buf[ tail++ ] = c; | |
if( tail >= max ) tail = 0; | |
++len; | |
data_avail.CNotify(); | |
DEBUG_PRINT0( "/\n" ); | |
xSemaphoreGive( mutex ); | |
} | |
char get() | |
{ | |
xSemaphoreTake( mutex, portMAX_DELAY ); | |
while( len == 0 ){ | |
DEBUG_PRINT0( "+\n" ); | |
data_avail.CWait( mutex ); | |
} | |
char c = buf[ head++ ]; | |
if( head >= max ) head = 0; | |
--len; | |
space_avail.CNotify(); | |
DEBUG_PRINT0( "&\n" ); | |
xSemaphoreGive( mutex ); | |
return c; | |
} | |
void producer( void* pvParameters ) | |
{ | |
uint32_t offset = (uint32_t)pvParameters; | |
char cont = 0; | |
char* task_name = pcTaskGetName( NULL ); | |
char letter = offset; | |
while( 1 ) | |
{ | |
DEBUGOUT( "%s->%c\n", task_name, letter ); | |
put( letter++ ); | |
++cont; | |
if( cont == 10 ){ | |
cont = 0; | |
letter = offset; | |
} | |
vTaskDelay( pdMS_TO_TICKS( ( rand() % 50 ) + 20 ) ); | |
} | |
} | |
void consumer( void* pvParameters ) | |
{ | |
char* task_name = pcTaskGetName( NULL ); | |
while( 1 ) | |
{ | |
char c = get(); | |
DEBUGOUT( "%s<-%c\n", task_name, c ); | |
vTaskDelay( pdMS_TO_TICKS( ( rand() % 150 ) + 25 ) ); | |
} | |
} | |
//---------------------------------------------------------------------- | |
// Driver program: | |
//---------------------------------------------------------------------- | |
int main(void) { | |
SystemCoreClockUpdate(); | |
Board_Init(); | |
Board_LED_Set(0, false); | |
ADC_Config(); | |
srand( calculate_seed() ); | |
xTaskCreate( producer, "P0", 128, (void*) 'A', tskIDLE_PRIORITY + 1, NULL ); // prints out: ABC...J | |
xTaskCreate( producer, "P1", 128, (void*) 'a', tskIDLE_PRIORITY + 1, NULL ); // prints out: abc...j | |
xTaskCreate( producer, "P2", 128, (void*) '0', tskIDLE_PRIORITY + 1, NULL ); // prints out: 012...9 | |
xTaskCreate( consumer, "C0", 128, NULL, tskIDLE_PRIORITY, NULL ); | |
xTaskCreate( consumer, "C1", 128, NULL, tskIDLE_PRIORITY, NULL ); | |
mutex = xSemaphoreCreateMutex(); | |
vTaskStartScheduler(); | |
while( 1 ) | |
{ | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment