Skip to content

Instantly share code, notes, and snippets.

@fjrg76-com
Last active June 6, 2022 17:47
Show Gist options
  • Save fjrg76-com/4b0238dd53e66f3c89c7e6820da5bad2 to your computer and use it in GitHub Desktop.
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.
/*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