Skip to content

Instantly share code, notes, and snippets.

@Gavinok
Last active March 8, 2023 21:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Gavinok/21378fd10b9833e07a1c2c482fe76b72 to your computer and use it in GitHub Desktop.
Save Gavinok/21378fd10b9833e07a1c2c482fe76b72 to your computer and use it in GitHub Desktop.
/*
FreeRTOS V9.0.0 - Copyright (C) 2016 Real Time Engineers Ltd.
All rights reserved
VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION.
This file is part of the FreeRTOS distribution.
FreeRTOS is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License (version 2) as published by the
Free Software Foundation >>>> AND MODIFIED BY <<<< the FreeRTOS exception.
***************************************************************************
>>! NOTE: The modification to the GPL is included to allow you to !<<
>>! distribute a combined work that includes FreeRTOS without being !<<
>>! obliged to provide the source code for proprietary components !<<
>>! outside of the FreeRTOS kernel. !<<
***************************************************************************
FreeRTOS 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. Full license text is available on the following
link: http://www.freertos.org/a00114.html
***************************************************************************
* *
* FreeRTOS provides completely free yet professionally developed, *
* robust, strictly quality controlled, supported, and cross *
* platform software that is more than just the market leader, it *
* is the industry's de facto standard. *
* *
* Help yourself get started quickly while simultaneously helping *
* to support the FreeRTOS project by purchasing a FreeRTOS *
* tutorial book, reference manual, or both: *
* http://www.FreeRTOS.org/Documentation *
* *
***************************************************************************
http://www.FreeRTOS.org/FAQHelp.html - Having a problem? Start by reading
the FAQ page "My application does not run, what could be wwrong?". Have you
defined configASSERT()?
http://www.FreeRTOS.org/support - In return for receiving this top quality
embedded software for free we request you assist our global community by
participating in the support forum.
http://www.FreeRTOS.org/training - Investing in training allows your team to
be as productive as possible as early as possible. Now you can receive
FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers
Ltd, and the world's leading authority on the world's leading RTOS.
http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products,
including FreeRTOS+Trace - an indispensable productivity tool, a DOS
compatible FAT file system, and our tiny thread aware UDP/IP stack.
http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate.
Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS.
http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High
Integrity Systems ltd. to sell under the OpenRTOS brand. Low cost OpenRTOS
licenses offer ticketed support, indemnification and commercial middleware.
http://www.SafeRTOS.com - High Integrity Systems also provide a safety
engineered and independently SIL3 certified version for use in safety and
mission critical applications that require provable dependability.
1 tab == 4 spaces!
*/
/*
FreeRTOS is a market leading RTOS from Real Time Engineers Ltd. that supports
31 architectures and receives 77500 downloads a year. It is professionally
developed, strictly quality controlled, robust, supported, and free to use in
commercial products without any requirement to expose your proprietary source
code.
This simple FreeRTOS demo does not make use of any IO ports, so will execute
on any Cortex-M3 of Cortex-M4 hardware. Look for TODO markers in the code for
locations that may require tailoring to, for example, include a manufacturer
specific header file.
This is a starter project, so only a subset of the RTOS features are
demonstrated. Ample source comments are provided, along with web links to
relevant pages on the http://www.FreeRTOS.org site.
Here is a description of the project's functionality:
The main() Function:
main() creates the tasks and software timers described in this section, before
starting the scheduler.
The Queue Send Task:
The queue send task is implemented by the prvQueueSendTask() function.
The task uses the FreeRTOS vTaskDelayUntil() and xQueueSend() API functions to
periodically send the number 100 on a queue. The period is set to 200ms. See
the comments in the function for more details.
http://www.freertos.org/vtaskdelayuntil.html
http://www.freertos.org/a00117.html
The Queue Receive Task:
The queue receive task is implemented by the prvQueueReceiveTask() function.
The task uses the FreeRTOS xQueueReceive() API function to receive values from
a queue. The values received are those sent by the queue send task. The
queue receive task increments the ulCountOfItemsReceivedOnQueue variable each
time it receives the value 100. Therefore, as values are sent to the queue
every 200ms, the value of ulCountOfItemsReceivedOnQueue will increase by 5
every second. http://www.freertos.org/a00118.html
An example software timer:
A software timer is created with an auto reloading period of 1000ms. The
timer's callback function increments the ulCountOfTimerCallbackExecutions
variable each time it is called. Therefore the value of
ulCountOfTimerCallbackExecutions will count seconds.
http://www.freertos.org/RTOS-software-timer.html
The FreeRTOS RTOS tick hook (or callback) function:
The tick hook function executes in the context of the FreeRTOS tick interrupt.
The function 'gives' a semaphore every 500th time it executes. The semaphore
is used to synchronise with the event semaphore task, which is described next.
The event semaphore task:
The event semaphore task uses the FreeRTOS xSemaphoreTake() API function to
wait for the semaphore that is given by the RTOS tick hook function. The task
increments the ulCountOfReceivedSemaphores variable each time the semaphore is
received. As the semaphore is given every 500ms (assuming a tick frequency of
1KHz), the value of
ulCountOfReceivedSemaphores will increase by 2 each second.
The idle hook (or callback)
function: The idle hook function queries the amount of free FreeRTOS heap
space available. See vApplicationIdleHook().
The malloc failed and stack
overflow hook (or callback) functions: These two hook functions are provided
as examples, but do not contain any functionality.
*/
#define configUSE_TIMERS 1
/* Standard includes. */
#include "stm32f4_discovery.h"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
/* Kernel includes. */
#include "../FreeRTOS_Source/include/FreeRTOS.h"
#include "../FreeRTOS_Source/include/queue.h"
#include "../FreeRTOS_Source/include/semphr.h"
#include "../FreeRTOS_Source/include/task.h"
#include "../FreeRTOS_Source/include/timers.h"
#include "stm32f4xx.h"
#include <stdbool.h>
/*-----------------------------------------------------------*/
#define queue_LENGTH 100
typedef uint32_t LightState; // define
#define LIGHT_MASK 0b00000000000000000001110000000000
#define POSTLINE_MASK 0b00000000111111111110000000000000
#define PRELINE_MASK 0b11111111000000000000000000000000
#define GREEN_DURATION 7500
#define YELLOW_DURATION 2500
#define RED_DURATION 7500
#define MAX_TRAFFIC 4
enum TrafficLight {
RED,
YELLOW,
GREEN,
};
static void prvSetupHardware(void);
/*
* The queue send and receive tasks as described in the comments at the top of
* this file.
*/
static void TGen_Task(void *pvParameters);
static void Display_Task(void *pvParameters);
static void Light_Task(void *pvParameters);
static void Traffic_Level_Task(void *pvParameters);
LightState shiftTraffic(LightState lightState, bool addCar);
// Going to TGen_Task
xQueueHandle xQueue_to_TGen = 0;
// Going to Light_Task
xQueueHandle xQueue_to_Light = 0;
// Going to display from Light
xQueueHandle xQueue_from_Light = 0;
// Going to display from tgen
xQueueHandle xQueue_from_TGen = 0;
/*-----------------------------------------------------------*/
enum TrafficLight traffic_lighttmr;
int main(void) {
prvSetupHardware();
/* Configure the system ready to run the demo. The clock configuration
can be done here if it was not done before main() was called. */
xQueue_to_Light = xQueueCreate(1, sizeof(uint16_t));
vQueueAddToRegistry(xQueue_to_Light, "to light");
xQueue_to_TGen = xQueueCreate(1, sizeof(uint16_t));
vQueueAddToRegistry(xQueue_to_TGen, "to tgen");
xQueue_from_TGen = xQueueCreate(queue_LENGTH, sizeof(uint16_t));
vQueueAddToRegistry(xQueue_from_TGen, "from tgen");
xQueue_from_Light = xQueueCreate(queue_LENGTH, sizeof(uint16_t));
vQueueAddToRegistry(xQueue_from_Light, "from light");
enum TrafficLight initialTrafficLight = GREEN;
xQueueSend(xQueue_from_Light, &initialTrafficLight, 0);
xTaskCreate(Display_Task, "Display", configMINIMAL_STACK_SIZE, NULL, 2, NULL);
xTaskCreate(TGen_Task, "New Traffic", configMINIMAL_STACK_SIZE, NULL, 2,
NULL);
xTaskCreate(Light_Task, "Traffic Light Task", configMINIMAL_STACK_SIZE, NULL,
2, NULL);
xTaskCreate(Traffic_Level_Task, "Pot monitor Task", configMINIMAL_STACK_SIZE,
NULL, 2, NULL);
/* Start the tasks and timer running. */
vTaskStartScheduler();
return 0;
}
uint16_t adc_conv() {
// get adc val
uint16_t x;
ADC_SoftwareStartConv(ADC1);
while (!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC))
;
x = ADC_GetConversionValue(ADC1);
uint16_t t = x % 1000;
x = ((x - t) / 1000); // Value will return in the bound 0 <= x <= 4
return x;
}
static void Traffic_Level_Task(void *pvParameters) {
uint16_t pot_level;
while (1) {
vTaskDelay(250); // Update level of traffic every 250ms
pot_level = adc_conv();
if (!xQueueOverwrite(xQueue_to_TGen, &pot_level)) {
printf("Failed to send the new pot value!\n");
}
if (!xQueueOverwrite(xQueue_to_Light, &pot_level)) {
printf("Failed to send the new pot value!\n");
}
}
}
static void TGen_Task(void *pvParameters) {
uint16_t traffic_level = 0;
bool addCar = false;
uint16_t generator = 0;
uint16_t lastCar = 0;
while (1) {
vTaskDelay(750); // Generate car or empty space every 500ms
xQueueReceive(xQueue_to_TGen, &traffic_level, 1000);
// Decide whether to add a car
generator = rand() % MAX_TRAFFIC;
if (generator < traffic_level || lastCar >= 6) {
addCar = true;
lastCar = 0;
} else {
addCar = false;
lastCar++;
}
xQueueSend(xQueue_from_TGen, &addCar, 0);
}
}
// Callback function to change lights
static void vUpdateLightStatus(xTimerHandle xTimer) {
xQueueReceive(xQueue_from_Light, &traffic_lighttmr, 0);
if (traffic_lighttmr == GREEN) {
traffic_lighttmr = YELLOW;
} else if (traffic_lighttmr == YELLOW) {
traffic_lighttmr = RED;
} else if (traffic_lighttmr == RED) {
traffic_lighttmr = GREEN;
}
xQueueSend(xQueue_from_Light, &traffic_lighttmr, 0);
}
int updateTime(TickType_t currentTime, uint16_t traffic_level,
uint16_t traffic_level_old) {
// Default to the CURRENT TIME if POT HAS NOT CHANGED or YELLOW LIGHT
// Get Current Time (expiry time minus the current task tick count/runtime
switch (traffic_light) {
case GREEN:
if (traffic_level > traffic_level_old) // traffic level has increased
{
// Increase remaining time by up to 2x based on the change in traffic
// level
return currentTime *
(1 + ((float)traffic_level - (float)traffic_level_old) /
(MAX_TRAFFIC));
} else if (traffic_level < traffic_level_old) // traffic level has decreased
{
// decrease remaining time by up to 0.5x based on the change in
// traffic level
return currentTime *
(1 - ((float)traffic_level_old - (float)traffic_level) /
(2 * MAX_TRAFFIC));
}
case RED:
if (traffic_level > traffic_level_old) // traffic level has increased
{
// decrease remaining time by up to 0.5x based on the change in
// traffic level
return currentTime *
(1 - ((float)traffic_level - (float)traffic_level_old) /
(2 * MAX_TRAFFIC));
} else if (traffic_level < traffic_level_old) // traffic level has decreased
{
// Increase remaining time by up to 2x based on the change in
// traffic level
return currentTime *
(1 + ((float)traffic_level_old - (float)traffic_level) /
(MAX_TRAFFIC));
}
default:
// Yellow Light is fixed duration, so don't change it
return currentTime;
}
}
float newTimerDuration(uint16_t traffic_level,
enum TrafficLight traffic_light) {
switch (traffic_light) {
case GREEN:
return ((float)0.5 + 0.5 * (float)traffic_level / (float)MAX_TRAFFIC) *
GREEN_DURATION / portTICK_RATE_MS;
case RED:
return ((float)1 - 0.5 * (float)traffic_level / (float)MAX_TRAFFIC) *
RED_DURATION / portTICK_RATE_MS;
case YELLOW:
return YELLOW_DURATION / portTICK_RATE_MS;
}
}
static void Light_Task(void *pvParameters) {
// Current State of the LED traffic lights
enum TrafficLight traffic_light = GREEN;
uint16_t traffic_level = 0;
uint16_t traffic_level_old = 0;
xTimerHandle xTrafficLightTimer = NULL;
xTrafficLightTimer =
xTimerCreate("TrafficLightTimer", GREEN_DURATION / portTICK_RATE_MS,
pdFALSE, (void *)0, vUpdateLightStatus);
xTimerStart(xTrafficLightTimer, 0);
TickType_t currentTime;
while (1) {
vTaskDelay(250); // Run every 250ms
xQueueReceive(xQueue_to_Light, &traffic_level, 250);
xQueuePeek(xQueue_from_Light, &traffic_light, 250);
if (xTrafficLightTimer == NULL) {
for (;;)
;
}
// Actively update timer to dynamically adjust to change in traffic level
if (xTimerIsTimerActive(xTrafficLightTimer) != pdFALSE) {
currentTime =
xTimerGetExpiryTime(xTrafficLightTimer) - xTaskGetTickCount();
int updatedTime =
updateTime(currentTime, traffic_level, traffic_level_old);
if (updatedTime >= 0)
updatedTime = 1;
xTimerChangePeriod(xTrafficLightTimer, updatedTime, 0);
} else {
// Timer has completed and needs to be restarted
xTimerChangePeriod(xTrafficLightTimer, newTimerDuration(traffic_level),
0);
traffic_level_old = traffic_level;
}
}
}
/*Shifts traffic when light is green, i.e. all vehicles shift one.
Parameters:
lightState - current state of board
addCar - add new vehicle or add empty space
Returns:
New state of board
*/
LightState shiftTraffic(LightState lightState, bool addCar) {
uint32_t newCar;
if (addCar == 1) {
newCar = 0x80000000;
} else {
newCar = 0x00000000;
}
// constructing new lightState
return 0x00000000 | ((PRELINE_MASK | POSTLINE_MASK) & lightState >> 1) |
(LIGHT_MASK & lightState) | newCar;
}
// Shifts traffic when light is yellow or red, i.e. piles up any cars before
// stopline
LightState shiftTrafficPileup(LightState lightState, bool addCar) {
uint32_t newCar;
if (addCar == 1) {
newCar = 0x80000000;
} else {
newCar = 0x00000000;
}
// Find first 0 in pile up region
int currentPileup = 0;
for (int i = 8; i >= 0; i--) {
if (((lightState >> (32 - i)) & (0x1)) == 0x0) {
currentPileup = (8 - i);
break;
}
}
// generate mask of pileup for all cars you want to shift
uint32_t newMask = 0xFF000000;
newMask = newMask << currentPileup;
// constructing new lightstate
return 0x00000000 | ((lightState & newMask) >> 1) |
((lightState & ~newMask & PRELINE_MASK)) |
((lightState & POSTLINE_MASK) >> 1) | (lightState & LIGHT_MASK) |
(newCar);
}
LightState changeTrafficLight(LightState lightState, enum TrafficLight light) {
LightState lights = LIGHT_MASK & lightState;
switch (light) {
case GREEN:
lights = (lights & ~lights) | 0b00000000000000000000010000000000;
break;
case YELLOW:
lights = (lights & ~lights) | 0b00000000000000000000100000000000;
break;
case RED:
lights = (lights & ~lights) | 0b00000000000000000001000000000000;
break;
}
return (lightState & (PRELINE_MASK | POSTLINE_MASK)) | lights;
}
void updateLights(LightState lightState) {
for (int i = 0; i < 32; i++) {
LightState lshift = (lightState >> i) & 0x1;
if ((lshift) == 0x1) {
GPIO_Write(GPIOB, GPIO_Pin_5);
} else {
GPIO_Write(GPIOB, 0x0000);
}
GPIO_ToggleBits(GPIOB, GPIO_Pin_4);
}
}
static void Display_Task(void *pvParameters) {
bool addCar = false;
LightState currentState = 0x80000400;
updateLights(currentState);
enum TrafficLight light = GREEN;
while (1) {
xQueuePeek(xQueue_from_Light, &light, 0);
if (xQueueReceive(xQueue_from_TGen, &addCar, 100) == pdTRUE) {
if (light == GREEN) {
// set green
currentState = shiftTraffic(currentState, addCar);
currentState = changeTrafficLight(currentState, GREEN);
updateLights(currentState);
} else {
currentState = shiftTrafficPileup(currentState, addCar);
currentState = changeTrafficLight(currentState, light);
updateLights(currentState);
}
}
vTaskDelay(10);
}
}
/*-----------------------------------------------------------*/
void vApplicationMallocFailedHook(void) {
/* The malloc failed hook is enabled by setting
configUSE_MALLOC_FAILED_HOOK to 1 in FreeRTOSConfig.h.
Called if a call to pvPortMalloc() fails because there is insufficient
free memory available in the FreeRTOS heap. pvPortMalloc() is called
internally by FreeRTOS API functions that create tasks, queues, software
timers, and semaphores. The size of the FreeRTOS heap is set by the
configTOTAL_HEAP_SIZE configuration constant in FreeRTOSConfig.h. */
for (;;)
;
}
/*-----------------------------------------------------------*/
void vApplicationStackOverflowHook(xTaskHandle pxTask,
signed char *pcTaskName) {
(void)pcTaskName;
(void)pxTask;
/* Run time stack overflow checking is performed if
configconfigCHECK_FOR_STACK_OVERFLOW is defined to 1 or 2. This hook
function is called if a stack overflow is detected. pxCurrentTCB can be
inspected in the debugger if the task name passed into this function is
corrupt. */
for (;;)
;
}
/*-----------------------------------------------------------*/
void vApplicationIdleHook(void) {
volatile size_t xFreeStackSpace;
/* The idle task hook is enabled by setting configUSE_IDLE_HOOK to 1 in
FreeRTOSConfig.h.
This function is called on each cycle of the idle task. In this case it
does nothing useful, other than report the amount of FreeRTOS heap that
remains unallocated. */
xFreeStackSpace = xPortGetFreeHeapSize();
if (xFreeStackSpace > 100) {
/* By now, the kernel has allocated everything it is going to, so
if there is a lot of heap remaining unallocated then
the value of configTOTAL_HEAP_SIZE in FreeRTOSConfig.h can be
reduced accordingly. */
}
}
/*-----------------------------------------------------------*/
static void prvSetupHardware(void) {
/* Ensure all priority bits are assigned as preemption priority bits.
http://www.freertos.org/RTOS-Cortex-M3-M4.html */
NVIC_SetPriorityGrouping(0);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
GPIO_InitTypeDef bitshifty = {
GPIO_Pin_5 | GPIO_Pin_4, // Pin A9 = data, Pin A8 = Clock
GPIO_Mode_OUT,
GPIO_Speed_2MHz, // This is max speed rn
GPIO_OType_PP, GPIO_PuPd_DOWN,
};
GPIO_Init(GPIOB, &bitshifty);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE); // GPIO for adc
GPIO_InitTypeDef adc_in = {
GPIO_Pin_0, // Pin C0 = ADC input
GPIO_Mode_AN,
GPIO_Speed_2MHz, // This is max speed rn
GPIO_OType_PP, GPIO_PuPd_DOWN,
};
GPIO_Init(GPIOC, &adc_in);
ADC_DeInit();
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
ADC_InitTypeDef adc_config = {
ADC_Resolution_12b,
DISABLE, // single channel conversion
ENABLE, // continuous conversion enabled
ADC_ExternalTrigConvEdge_None, // no edge trigger
DISABLE, // no edge trigger
ADC_DataAlign_Right,
1};
ADC_Init(ADC1, &adc_config);
ADC_Cmd(ADC1, ENABLE);
ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 1, ADC_SampleTime_144Cycles);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment