//インクルード
#include <Arduino_FreeRTOS.h>
#include <event_groups.h>
#include <semphr.h>
//入出力定義
#define CAR_BLUE 11
#define CAR_YELLOW 10
#define CAR_RED 9
#define L_RED 13
#define L_BLUE 12
#define R_RED 8
#define R_BLUE 7
#define L_BTN 3
#define R_BTN 2
//各時間定義
#define CAR_BLUE_PERIOD 8000 //車青信号の点灯時間
#define CAR_YELLOW_PERIOD 2000 //車黄色信号の点灯時間
#define BTN_TO_YELLOW_PERIOD 1000 //ボタンを押して信号が変わるまでの時間
#define WALKER_BLINK_PERIOD 300 //歩行者青信号の点滅周期
#define SWITCHING_PERIOD 2000 //車信号と歩行者信号の間の時間
#define TASK_BTN_PERIOD 50 //タスクボタンの周期時間
#define TASK_SERIAL_PERIOD 100 //タスクシリアルの周期時間
//タスクフラグ初期化
const int TASK_CAR_FLAG = (1 << 0); //車信号タスクフラグ
const int TASK_WALKER_FLAG = (1 << 1); //歩行者信号タスクフラグ
//各タスクハンドラ生成
TaskHandle_t TaskCar_Handler;
TaskHandle_t TaskWalker_Handler;
TaskHandle_t TaskBtnPress_Handler;
TaskHandle_t TaskSerial_Handler;
EventGroupHandle_t eventGroup; //イベントハンドル生成
SemaphoreHandle_t periodMutex; //ミューテックス生成
//各変数初期化
int WALKER_BLUE_PERIOD = 10000; //歩行者青信号の点灯時間(シリアルで変更可)
int L_btnState = HIGH;
int R_btnState =HIGH;
bool is_button_react = false; //歩行者ボタンの反応時間内か確認する変数
bool is_btn_pressed = false; //歩行者ボタンが押されたかどうか確認する変数
unsigned long lastDebounceTime = 0; //最後にボタンが押された時間
const unsigned long debounceDelay = 150; //デバウンス時間(ミリ秒)
//セットアップ関数
void setup() {
Serial.begin(9600);
Serial.println("システムスタート");
eventGroup = xEventGroupCreate(); //イベントグループ代入
periodMutex = xSemaphoreCreateMutex(); //ニューテックス代入
//タスク生成
xTaskCreate(TaskCar, "TaskCar", 128, NULL, 2, &TaskCar_Handler); //車信号タスク
xTaskCreate(TaskWalker, "TaskWalker", 128, NULL, 2, &TaskWalker_Handler); //歩行者信号タスク
xTaskCreate(TaskBtnPress, "TaskBtnPress", 128, NULL, 2, &TaskBtnPress_Handler); //歩行者ボタンタスク
xTaskCreate(TaskSerial, "TaskSerial", 128, NULL, 1, &TaskSerial_Handler); //シリアルタスク
//ピン入出力
pinMode(CAR_BLUE, OUTPUT);
pinMode(CAR_YELLOW, OUTPUT);
pinMode(CAR_RED, OUTPUT);
pinMode(L_BLUE, OUTPUT);
pinMode(L_RED, OUTPUT);
pinMode(R_BLUE, OUTPUT);
pinMode(R_RED, OUTPUT);
pinMode(L_BTN, INPUT_PULLUP);
pinMode(R_BTN, INPUT_PULLUP);
xEventGroupSetBits(eventGroup, TASK_CAR_FLAG); //車信号タスクフラグ立てる
vTaskStartScheduler(); //タスクスタート
}
void loop() {} //使わない
//車信号タスク
void TaskCar(void *p){
digitalWrite(CAR_BLUE, HIGH); //車青 -> 点灯(初期状態)
while(1){
//イベントフラグ待ち
xEventGroupWaitBits(eventGroup, TASK_CAR_FLAG, pdTRUE, pdFALSE, portMAX_DELAY);
Serial.println("車両信号タスクスタート");
digitalWrite(CAR_RED, LOW); //車赤 -> 消灯
//================ 歩行者ボタン反応圏開始 ================
is_button_react = true;
digitalWrite(CAR_BLUE, HIGH); //車青 -> 点灯
vTaskDelay(CAR_BLUE_PERIOD/ portTICK_PERIOD_MS); //車青信号点灯時間
is_button_react = false;
//================ 歩行者ボタン反応圏終了 ================
vTaskDelay(BTN_TO_YELLOW_PERIOD / portTICK_PERIOD_MS); //ボタン入力による調整時間
//歩行者ボタンが起動時コンティニュー
if(is_btn_pressed){
is_btn_pressed = false;
continue;
}
digitalWrite(CAR_BLUE, LOW); //車青 -> 消灯
digitalWrite(CAR_YELLOW, HIGH); //車黄 -> 点灯
vTaskDelay(CAR_YELLOW_PERIOD/ portTICK_PERIOD_MS); //車黄色信号点灯時間
digitalWrite(CAR_YELLOW, LOW); //車黄 -> 消灯
digitalWrite(CAR_RED, HIGH); //車赤 -> 点灯
vTaskDelay(SWITCHING_PERIOD / portTICK_PERIOD_MS); //信号切り替わり時間(両信号赤)
xEventGroupSetBits(eventGroup, TASK_WALKER_FLAG); //歩行者信号フラグ立てる
}
}
//歩行者信号タスク
void TaskWalker(void *p){
walkerLedRedHigh(); //歩赤 -> 点灯
while(1){
//イベントフラグ待ち
xEventGroupWaitBits(eventGroup, TASK_WALKER_FLAG, pdTRUE, pdFALSE, portMAX_DELAY);
Serial.println("歩行者信号タスクスタート");
walkerLedRedLow(); //歩赤 -> 消灯
walkerLedBlueHigh(); //歩青 -> 点灯
if (xSemaphoreTake(periodMutex, portMAX_DELAY) == pdTRUE) { //ミューテックスロック
int localWalkerBluePeriod = WALKER_BLUE_PERIOD; //変更された時間をローカル変数に代入しリエントラント
xSemaphoreGive(periodMutex); //ミューテックスアンロック
vTaskDelay(localWalkerBluePeriod / portTICK_PERIOD_MS); //歩行者青信号点灯時間
}
//歩行者青信号点滅
for(int i = 0; i < 5; i++){
walkerLedBlueLow(); //歩青 -> 点灯
vTaskDelay(WALKER_BLINK_PERIOD / portTICK_PERIOD_MS);
walkerLedBlueHigh(); //歩青 -> 消灯
vTaskDelay(WALKER_BLINK_PERIOD / portTICK_PERIOD_MS);
}
walkerLedBlueLow(); //歩青 -> 消灯
walkerLedRedHigh(); //歩赤 -> 点灯
vTaskDelay(SWITCHING_PERIOD / portTICK_PERIOD_MS); //信号切り替わり時間(両信号赤)
xEventGroupSetBits(eventGroup, TASK_CAR_FLAG); //車タスクのフラグ立てる
}
}
//歩行者ボタンタスク
void TaskBtnPress(void *p){
while(1){
unsigned long currentTime = millis(); //現在の時間を変数carrentTimeに代入
//両ボタンの状態を変数btnStateに代入
L_btnState = digitalRead(L_BTN);
R_btnState = digitalRead(R_BTN);
//反応可能圏内でボタンを押されたら、車信号を早めに黄->赤に変える
if((!L_btnState || !R_btnState)&& is_button_react && currentTime - lastDebounceTime > debounceDelay){
Serial.println("ボタンが押されました");
lastDebounceTime = currentTime; //最後にボタンが押された時間を代入(チャタリング防止のため)
vTaskDelay(BTN_TO_YELLOW_PERIOD / portTICK_PERIOD_MS); //ボタンが押されてから車信号が黄色になるまでの時間
digitalWrite(CAR_BLUE, LOW); //車青 -> 消灯
digitalWrite(CAR_YELLOW, HIGH); //車黄 -> 点灯
vTaskDelay(CAR_YELLOW_PERIOD/ portTICK_PERIOD_MS); //車黄色信号点灯時間
digitalWrite(CAR_YELLOW, LOW); //車黄 -> 消灯
digitalWrite(CAR_RED, HIGH); //車赤 -> 点灯
resetBtn(); //各確認変数リセット
vTaskDelay(SWITCHING_PERIOD / portTICK_PERIOD_MS); //信号切り替わり時間(両信号赤)
xEventGroupSetBits(eventGroup, TASK_WALKER_FLAG); //歩行者信号フラグ立てる
}
vTaskDelay(TASK_BTN_PERIOD / portTICK_PERIOD_MS); //ボタンタスク周期時間待機
}
}
//シリアルタスク
void TaskSerial(void *p){
//厳密な周期制御のための変数処理
TickType_t xLastWakeTime;
const TickType_t xDelaySerial = pdMS_TO_TICKS(TASK_SERIAL_PERIOD);
int inputTime_s; //シリアル通信で受け取った値を入れる変数(秒)
xLastWakeTime = xTaskGetTickCount(); //現在の時間を代入
//シリアル通信で値を受け取る
while(1){
if(Serial.available()){
if(Serial.peek() >= '0' && Serial.peek() <= '9'){
inputTime_s = Serial.parseInt();
period_control(inputTime_s);
}else{
Serial.println("不正な入力です");
}
while (Serial.available()) { Serial.read(); } //シリアルメモリ内をキャッシュ->二重受け取り防止
}
vTaskDelayUntil(&xLastWakeTime, xDelaySerial); //待機時間
}
}
//歩行者青信号制御関数
void period_control(int time_s){
if(xSemaphoreTake(periodMutex, portMAX_DELAY) == pdTRUE){ //ミューテックスロック
WALKER_BLUE_PERIOD = time_s * 1000; //受け取った秒をミリ秒にして歩行者青信号ピリオドに代入
Serial.println("歩行者青信号の時間が変更されました");
xSemaphoreGive(periodMutex); //ミューテックスアンロック
}
}
//両歩行者青信号点灯関数
void walkerLedBlueHigh(){
digitalWrite(L_BLUE, HIGH);
digitalWrite(R_BLUE, HIGH);
}
//両歩行者青信号消灯関数
void walkerLedBlueLow(){
digitalWrite(L_BLUE, LOW);
digitalWrite(R_BLUE, LOW);
}
//両歩行者赤信号点灯関数
void walkerLedRedHigh(){
digitalWrite(L_RED, HIGH);
digitalWrite(R_RED, HIGH);
}
//両歩行者赤信号消灯関数
void walkerLedRedLow(){
digitalWrite(L_RED, LOW);
digitalWrite(R_RED, LOW);
}
//ボタン関連変数リセット関数
void resetBtn(){
is_btn_pressed = true;
is_button_react = false;
L_btnState = HIGH;
R_btnState =HIGH;
}