Last active
March 8, 2023 21:39
-
-
Save Gavinok/21378fd10b9833e07a1c2c482fe76b72 to your computer and use it in GitHub Desktop.
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
/* | |
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