#include "SevSeg.h"
SevSeg sevseg; //Instantiate a seven segment controller object
//LED pin
int pin47=47;
//note frequencies
#define E 1517 //659 Hz
#define C 1912 //523 Hz
#define G 1276 //784 Hz
#define g 2551//392 Hz
#define R 0 //0 HZ
//notes for the mario theme
int song[] = {E, R, E, R, R, E, R, R, E, C, R, E, R, R, G, R, R, R, R ,R, g, R};
// Task states definitions, representing the lifecycle of a task.
#define STATE_RUNNING 0 // Task is currently running.
#define STATE_READY 1 // Task is ready to run.
#define STATE_SLEEP 2 // Task is waiting for its sleep time to expire before it can run again.
#define STATE_DEAD 3 // Task has completed its function or has been terminated.
#define N_MAX_TASKS 10 // Maximum number of tasks that can be managed by the system.
int task_delays[] = {750, 250, 100, 6200}; // Array defining delays for tasks in milliseconds. Adjust these values to change the blinking rate.
// Structure defining a Task Control Block (TCB), which holds information about each task.
typedef struct TCBstruct {
int taskID; // Unique identifier for the task within the task list.
char taskName[20]; // Descriptive name for the task. Useful for debugging and readability.
int numCalls; // Counts how many times the task has been called. Useful for monitoring task activity.
unsigned short int state; // Current state of the task (e.g., READY, SLEEP, DEAD).
void (*functionptr)(); // Pointer to the function that the task needs to execute.
unsigned int sleeptime; // Time in milliseconds the task needs to wait before next execution.
} task;
task tasklist[N_MAX_TASKS]; // Array to hold tasks. Acts as the Task Control Block (TCB) registry.
int deadlist[N_MAX_TASKS]; // Array to keep track of tasks that have been terminated.
int time_count; // Counter used to simulate time passing.
int quit_flag; // A flag indicating whether a task has requested to terminate itself.
void setup() {
// Initializes digital pins connected to LEDs as outputs. Sets the initial state of LEDs to OFF for safety.
DDRD |= 1<< DDL2; //pin 47
TCCR4A=0;
TCCR4B=0;
TCCR4C=0;
TCCR4A |= (1<<COM4A0);
TCCR4B |= (1<<CS41);
TCCR4B |= (1<<WGM42);
TCNT4=0;
DDRH |= 1 <<DDH3;
PORTH |= 1<<DDH3;
//7-seg setup from the Arduino SegSev Example
byte numDigits = 4;
byte digitPins[] = {2, 3, 4, 5};
byte segmentPins[] = {26, 7, 8, 9, 10, 11, 12, 13};
bool resistorsOnSegments = false; // 'false' means resistors are on digit pins
byte hardwareConfig = COMMON_ANODE; // See README.md for options
bool updateWithDelays = false; // Default 'false' is Recommended
bool leadingZeros = false; // Use 'true' if you'd like to keep the leading zeros
bool disableDecPoint = false; // Use 'true' if your decimal point doesn't exist or isn't connected
sevseg.begin(hardwareConfig, numDigits, digitPins, segmentPins, resistorsOnSegments,
updateWithDelays, leadingZeros, disableDecPoint);
sevseg.setBrightness(90);
time_count = 0; // Reset time counter.
quit_flag = 0; // Ensure quit flag is reset at start.
// Initialize the task list with tasks for toggling LEDs. Each task is assigned a function, initial state, and sleep time.
// The pattern is to have one task for turning an LED on and another for turning it off, creating a blinking effect.
initTask(0, "LED1_on", &task1_on, STATE_READY, 0);
initTask(1, "LED1_off", &task1_off, STATE_SLEEP, 100); // LED2 blinks with a period of 500 ms (250 ms on, 250 ms off).
initTask(2, "song_on", &task2_on, STATE_READY, 0);
initTask(3, "song_off", &task2_off, STATE_SLEEP, 2); // LED2 blinks with a period of 500 ms (250 ms on, 250 ms off).
}
void loop() {
// Main loop iterates over the tasklist, executing tasks based on their state and sleep time.
for (int i = 0; tasklist[i].functionptr != NULL; i++) {
// Check if task is ready and sleep time has elapsed.
if (tasklist[i].state == STATE_READY && tasklist[i].sleeptime == 0) {
tasklist[i].functionptr(); // Execute the task's function.
tasklist[i].numCalls++; // Increment the call count for monitoring.
tasklist[i].state = STATE_SLEEP; // Set task to sleep state.
tasklist[i].sleeptime = task_delays[i % 4]; // Assign sleep time from the predefined delays array.
} else if (tasklist[i].state == STATE_SLEEP) {
// If task is sleeping, decrement its sleep time.
tasklist[i].sleeptime--;
if (tasklist[i].sleeptime <= 0) {
// Once sleep time is up, set task state to ready for next execution.
tasklist[i].state = STATE_READY;
}
}
// Handle task self-termination logic.
if (quit_flag != 0) {
tasklist[i].state = STATE_DEAD;
deadlist[i] = tasklist[i].taskID;
quit_flag = 0; // Reset quit flag after handling.
}
}
increment(); // Increment the simulated time counter.
}
// Increment function simulates time passing.
void increment() {
delay(1); // Simulates a 1 ms time step.
if (time_count < 30000) { // Simulate for a specific duration (30 seconds in this example).
time_count++;
} else {
time_count = 0; // Reset time counter to loop the simulation.
}
}
// Task functions to control LEDs.
void task1_on() { digitalWrite(pin47, HIGH); }
void task1_off() { digitalWrite(pin47, LOW); }
static int noteIndex = 0; // Static variable to keep track of current note index
static int freq=0; //varriable for displaying the frequency of notes.
//turns on the buzzer and plays a short burst of the mario theme song
void task2_on(){
countdown(0); //turns off the countdown while music plays
DDRH |= (1 << PH3);
if (noteIndex < 22) {
choseFreq(song[noteIndex]);
OCR4A = song[noteIndex]; // Set the frequency for the note
seg7(freq);
noteIndex++;
}
else {
OCR4A = 0;
}
sevseg.refreshDisplay(); // Must run repeatedly
}
//turns off the buzzer
void task2_off(){
countdown(1);
OCR4A = 0;
noteIndex = 0; // Reset noteIndex if it exceeds the number of notes
}
//helper function to convert the Arudino value for notes to frequency in Hz for the display
void choseFreq(int num){
if(num==1517){
freq=659;
}
else if(num==1912){
freq=523;
}
else if(num==1276){
freq=784;
}
else if(num==2551){
freq=392;
}
else{
freq=0;
}
}
//Starts a 4000ms count down till song plays again, Shows countdown on display in deci-seconds.
void countdown(bool go){
static int timer=0;
static int val = 40;
if(go){
if (millis() - timer >= 100) {
timer+=100;
// Increment the displayed value
val--;
//If the count exceeds 9, reset it to 0
if (val == 0) {
val = 40;
}
// Display the current count on the 7-segment display
seg7(val);
}
sevseg.refreshDisplay(); // Must run repeatedly
}
else if(!go){
val=40;
timer=millis();
}
}
void seg7(int count){
sevseg.setNumber(10);
//sevseg.refreshDisplay(); // Must run repeatedly
}
//displays the smile for task 5
void smile(){
int left=B01100100;
int mid=B00001001;
int right=B01010010;
uint8_t segs[4]={left,mid,mid,right};
sevseg.setSegments(segs);
sevseg.refreshDisplay(); // Must run repeatedly
}
// Utility function to initialize tasks.
void initTask(int id, const char* name, void (*func)(), unsigned short int initialState, unsigned int initialSleep) {
tasklist[id].taskID = id;
strncpy(tasklist[id].taskName, name, sizeof(tasklist[id].taskName) - 1); // Ensure name is null-terminated.
tasklist[id].functionptr = func;
tasklist[id].state = initialState;
tasklist[id].sleeptime = initialSleep;
tasklist[id].numCalls = 0;
}
// Function to terminate a task from within itself.
void task_self_quit() {
quit_flag = 1; // Set quit flag to indicate a task wishes to terminate.
}
// Function to restart a terminated task.
void task_start(int taskID) {
if (deadlist[taskID] != -1) { // Check if task is in the deadlist.
deadlist[taskID] = -1; // Remove task from deadlist.
initTask(taskID, tasklist[taskID].taskName, tasklist[taskID].functionptr, STATE_READY, 0); // Reinitialize the task.
}
}
//custom delay function for synchronization
void myDelay(unsigned long ms) {
unsigned long startMillis = millis();
while (millis() - startMillis < ms) {
// Do nothing, just wait
}
}