#define ROWS 6
#define COLS 4
//Matrix of output pins
int ledMat[ROWS][COLS] = {
{22, 23, 24, 25},
{26, 27, 28, 29},
{30, 31, 32, 33},
{34, 35, 36, 37},
{38, 39, 40, 41},
{42, 43, 44, 45}
};
//state of pin matrix
bool ledStat[ROWS][COLS];
//position of cursor
int cursorRow = 0; //当前选择LED的光标位置
int cursorCol = 0;
//column that user has choosen
int column;
// 非阻塞闪烁相关变量
unsigned long previousBlinkMillis = 0;
bool blinkState = LOW;
/*----------JOYSTICK-----------*/
const int SW_pin = 2; // Joystick按钮引脚
const int X_pin = A0; // X轴模拟引脚
const int Y_pin = A1; // Y轴模拟引脚
// 摇杆状态阈值
const int THRESHOLD_LOW = 400; // 低阈值
const int THRESHOLD_HIGH = 600; // 高阈值
const int CENTER_THRESHOLD = 50; // 中心位置阈值(512±50)
// 方向信号延时触发相关变量
unsigned long directionStartTime = 0; // 方向信号开始时间
const unsigned long DIRECTION_DELAY = 100; // 方向信号延时(0.1秒)
String lastDirection = ""; // 上次检测到的方向
bool directionTriggered = false; // 方向信号是否已触发
// 按钮状态相关变量
unsigned long debounce_duration = 50; // 防抖时间(50ms)
int previous_button_state = HIGH; // 上次按钮状态
int current_button_state = HIGH; // 当前按钮状态
unsigned long last_debounce_time = 0; // 上次状态变化时间
unsigned long press_start_time; // 按钮按下开始时间
unsigned long release_time; // 按钮按下持续时间
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
column = 0;
for (int row = 0; row < ROWS; row++)
{
for (int col = 0; col < COLS; col++)
{
pinMode(ledMat[row][col], OUTPUT);
ledStat[row][col] = 0;
}
}
// 初始化摇杆引脚
pinMode(X_pin, INPUT);
pinMode(Y_pin, INPUT);
pinMode(SW_pin, INPUT_PULLUP); // 启用内部上拉电阻
}
void loop() {
// put your main code here, to run repeatedly:
Serial.print("This system is used to fill in positions in a table!\n");
Serial.print("Please select a colum(1~4):\n");
while(Serial.available() == 0) {}
column = Serial.parseInt();
while (Serial.available()) { Serial.read();}//丢掉换行符
Serial.println(column);
cursorRow = 0; // 光标初始在第一行
cursorCol = 0; // 用户输入的列数转换为索引(0-3)
if (column == 0) { //如果输入的是字符char,也将结束循环,因为默认输入的是数字
Serial.println("No table to be filled in, code ending!");
while (true) {} // 结束程序,进入无限循环
}
if (column < 1 || column > 4)
{
Serial.println("Invalid input! Please enter a number between 1 and 4.");
Serial.println("_____________________________________________________");
delay(1000);
return; // 如果输入无效,直接返回,等待下一次输入
}
while(1)
{
updateLEDs(ledStat, cursorRow, cursorCol);
// 检测按键状态
checkJoystick(); // 检测摇杆方向
checkButton(); // 检测按键状态
//Serial.println("Waiting for button press...");
delay(100);
}
}
/*========================================================================*/
void updateLEDs(bool ledStat[ROWS][COLS], int cursorRow, int cursorCol) {
for (int row = 0; row < ROWS; row++) {
for (int col = 0; col < COLS; col++) {
if (row == cursorRow && col == cursorCol) {
// 光标LED闪烁
blink(ledMat[row][col], 150);
} else {
// 根据ledStat状态点亮或熄灭LED
digitalWrite(ledMat[row][col], ledStat[row][col] ? HIGH : LOW);
}
}
}
}
void blink(int led, long interval) {
unsigned long currentMillis = millis();
if (currentMillis - previousBlinkMillis >= interval) {
previousBlinkMillis = currentMillis;
blinkState = !blinkState;
digitalWrite(led, blinkState ? HIGH : LOW);
}
}
/*--------------------检测摇杆状态(UP, DOWN, LEFT, RIGHT)--------------*/
// 检测摇杆状态(UP, DOWN, LEFT, RIGHT)
void checkJoystick() {
int xValue = analogRead(X_pin); // 读取X轴值
int yValue = analogRead(Y_pin); // 读取Y轴值
// 判断是否在中心位置
bool isCentered = (abs(xValue - 512) < CENTER_THRESHOLD && abs(yValue - 512) < CENTER_THRESHOLD);
// 如果回到中心位置,重置方向触发标志
if (isCentered) {
directionTriggered = false;
lastDirection = "";
return;
}
// 判断方向
String currentDirection = "";
if (xValue < THRESHOLD_LOW) {
currentDirection = "RIGHT";
} else if (xValue > THRESHOLD_HIGH) {
currentDirection = "LEFT";
} else if (yValue < THRESHOLD_LOW) {
currentDirection = "DOWN";
} else if (yValue > THRESHOLD_HIGH) {
currentDirection = "UP";
}
// 如果方向发生变化,重置计时器
if (currentDirection != lastDirection) {
directionStartTime = millis();
lastDirection = currentDirection;
directionTriggered = false; // 方向变化时重置触发标志
}
// 如果方向维持时间超过设定延时且未触发过,则触发信号
if (currentDirection != "" && !directionTriggered && millis() - directionStartTime >= DIRECTION_DELAY) {
Serial.println(currentDirection);
directionTriggered = true; // 标记方向信号已触发
// 根据方向更新光标位置
if (currentDirection == "UP") {
cursorRow = (cursorRow - 1 + ROWS) % ROWS; // 向上移动,循环处理
} else if (currentDirection == "DOWN") {
cursorRow = (cursorRow + 1) % ROWS; // 向下移动,循环处理
} else if (currentDirection == "LEFT") {
cursorCol = (cursorCol - 1 + column) % column; // 向左移动,循环处理
} else if (currentDirection == "RIGHT") {
cursorCol = (cursorCol + 1) % column; // 向右移动,循环处理
}
}
}
// 检测按钮状态(单击、双击、长按)
void checkButton() {
if (debounced_button_press_check(SW_pin, LOW)) {
press_start_time = millis(); // 记录按钮按下时间
while (!debounced_button_press_check(SW_pin, HIGH)); // 等待按钮释放
release_time = millis() - press_start_time; // 计算按钮按下持续时间
if (release_time > 1000) {
Serial.println("LONG PRESSING"); // 长按
stopPB();
} else {
// 检测单击或双击
while (1) {
if (debounced_button_press_check(SW_pin, LOW)) {
Serial.println("DOUBLE CLICK"); // 双击
ledStat[cursorRow][cursorCol] = 0;
cursorCol = (cursorCol + 1) % column; // 移动到下一列
if (cursorCol == 0) {
cursorRow = (cursorRow + 1) % ROWS; // 如果到达最后一列,移动到下一行
}
break;
}
if ((millis() - press_start_time) > 400) {
Serial.println("CLICK"); // 单击
ledStat[cursorRow][cursorCol] = 1;
cursorCol = (cursorCol + 1) % column; // 移动到下一列
if (cursorCol == 0) {
cursorRow = (cursorRow + 1) % ROWS; // 如果到达最后一列,移动到下一行
}
break;
}
}
}
}
}
// 检测防抖后的按钮状态
bool debounced_button_press_check(int pin, bool expected_state) {
int button_reading = digitalRead(pin);
// 如果按钮状态变化,重置防抖计时器
if (button_reading != previous_button_state) {
last_debounce_time = millis();
}
previous_button_state = button_reading;
// 如果状态稳定超过防抖时间,则认为是有效状态
if ((millis() - last_debounce_time) > debounce_duration) {
if (button_reading != current_button_state) {
current_button_state = button_reading;
if (current_button_state == expected_state) {
return true; // 返回true,表示检测到预期状态
}
}
}
return false; // 返回false,表示未检测到预期状态
}
void stopPB() {
Serial.println("Program stopped!");
delay(100); // 让消息有足够时间发送
cli(); // 关闭所有中断(彻底停住 Arduino)
while (true) {} // 进入死循环,程序完全停止
}