Почему объявление глобальной переменной может изменить способ оценки несвязанного условного оператора?
Вот весь мой скетч. Это мой первый проект Arduino. Я включил все это, потому что не знаю, что важно. Я распечатываю на 4-значном 7-сегментном дисплее. Для этого я использую sprintf для форматирования содержимого char[], называемого tempString, и оно отправляется на дисплей. Я объявлял новый локальный char[] каждый раз, когда он мне был нужен. У меня возникла проблема с нехваткой памяти Arduino Pro Mini. Я попробовал объявить глобальную диаграмму[] и повторно использовать ее, когда она мне была нужна, но это изменило способ выполнения программы. Это было единственное изменение, которое я сделал.
После того, как я внес это изменение, Arduino всегда будет считать это правдой:
if (countDownTimer.TimeCheck(0, 0, 0))
CountDownTimer всегда вычислял время, равное 0, даже после того, как ему было назначено допустимое время и таймер не начал работать. Я проверил, что установил правильно, но к тому времени, когда условие было достигнуто, таймер вернулся к 0.
Если я удалю глобальное объявление для tempString[] и вернусь к локальным значениям, логика будет работать так, как задумано. Я уменьшил tempString[] до локального значения, и это, похоже, решило проблему с памятью, но мне хотелось бы знать, почему объявление глобального значения вызвало проблему.
#include <SoftwareSerial.h>
#include <CountUpDownTimer.h>
#include <EEPROM.h> //Необходимо для доступа к функциям чтения и записи EEPROM
//значения для EEPROM
int stepsToWrite = 0;
int stepsToRead = 0;
int priorValue = 0;
// адреса для записи и чтения
const int thirtySecondAddress = 0; // постоянный
const int threeMinuteAddress = 4; // постоянный
int addressToUse = 0; // это будет меняться в зависимости от времени, выбранного пользователем
// устанавливаем последовательный дисплей
const int displayTx = 5; // контакты, которые нам нужны для создания последовательного экземпляра программного обеспечения
const int displayRx = 7; // мы будем использовать только Tx
SoftwareSerial s7s(displayRx, displayTx);
//настраиваем таймер
CountUpDownTimer countDownTimer(DOWN);
const long displaychangeInterval = 10000; // в миллисекундах
const long countdownInterval = 1000; // в миллисекундах
long previousMillis = 0; // для переключения между оставшимся временем и количеством шагов
long previousTimerMillis = 0; // для обновления дисплея новым временем
const int thirtySecondTime = 30;
const int threeMinuteTime = 180; // 3 минуты, выраженные в секундах
int timerStartValue = 0; // необходимо для некоторой управляющей логики
// логические значения для управления тем, что отображается
bool doDisplayStepCount = false;
bool doDisplayCountDown = false;
bool doDisplayHighScore = true; // мы хотим отображать это при запуске
// настраиваем кнопки
const int timerbuttonPin = 6; // номер контакта кнопки таймера
const int resetbuttonPin = 4; // номер контакта кнопки сброса
int timerbuttonPushCounter = 0; // счетчик количества нажатий кнопок
int timerbuttonState = 0; // текущее состояние кнопки
int timerlastButtonState = 0; // предыдущее состояние кнопки
int resetbuttonPushCounter = 0; // счетчик количества нажатий кнопок
int resetbuttonState = 0; // текущее состояние кнопки
int resetlastButtonState = 0; // предыдущее состояние кнопки
int stepsTaken = 0; // количество шагов (прыжков)
boolean timerStarted = false;
// для силового резистора
const int FSR_PIN = A0; // Вывод подключен к FSR/резисторному делителю
const float VCC = 5.10; // Измеренное напряжение линии Ardunio 5В
const float R_DIV = 3265.0; // Измеренное сопротивление резистора 3,3 кОм
double currentForce = 0;
double previousForce = 0;
boolean previousPos = false;
boolean currentPos = false;
boolean doChangeDisplay = false;
char tempString[4];
void setup() {
boolean debug = true;
if (debug)
{
EEPROMWriteInt(thirtySecondAddress, 0); // обнуляем EEPROM
EEPROMWriteInt(threeMinuteAddress, 0); // обнуляем EEPROM
}
// запускаем последовательное соединение
Serial.begin(9600);
// устанавливаем пины
pinMode(FSR_PIN, INPUT);
pinMode(timerbuttonPin, INPUT);
pinMode(resetbuttonPin, INPUT);
// последовательный дисплей
// Должен начаться последовательный порт программного обеспечения s7s с правильной скоростью передачи данных.
// Очистка дисплея
// Значение по умолчанию для s7s — 9600.
s7s.begin(9600);
//очиститьдисплей(); // Очищаем дисплей, сбрасываем курсор
// s7s.print("-HI-");
// задержка(1000);
displayHighScores();
}
void loop() {
// Serial.print("freeMemory()=");
// Serial.println(freeMemory());
// Serial.println("Начальный цикл");
// нажатия кнопок
// еслиtinmerbuttonPushCounter = 1, установите timerValue равным 30 секундам
// если 2 нажатия, устанавливаем 3 минуты и сбрасываем счетчик нажатий на 0
//Serial.println("Чтение кнопки таймера, состояние: " + timerbuttonState);
timerbuttonState = digitalRead(timerbuttonPin);
// Serial.println("Чтение кнопки сброса, состояние: " +resetbuttonState);
resetbuttonState = digitalRead(resetbuttonPin);
// сравниваем состояние кнопки с предыдущим состоянием
if (timerbuttonState != timerlastButtonState)
{
Serial.println("selecting time");
if (timerbuttonState == HIGH)
{
doDisplayHighScore = false;
timerbuttonPushCounter++;
// если текущее состояние ВЫСОКОЕ, то кнопка таймера переключилась из выключенного состояния во включенное:
if (timerbuttonPushCounter == 1)
{
doChangeDisplay = false;
doDisplayCountDown = true;
timerStartValue = thirtySecondTime;
addressToUse = thirtySecondAddress;
Serial.println("timer button pushed: " + String(timerbuttonPushCounter));
Serial.println("start value: " + String(timerStartValue));
clearDisplay();
setDecimals(0b010000); // Включаем двоеточие
sprintf(tempString, "%04d", thirtySecondTime);
Serial.println("Timer set to: " + String(tempString));
s7s.print(tempString);
}
else if (timerbuttonPushCounter == 2)
{
timerStartValue = threeMinuteTime;
addressToUse = threeMinuteAddress;
doChangeDisplay = true;
int timerinminutes = timerStartValue / 60;
Serial.println("timer button pushed: " + timerbuttonPushCounter);
Serial.println("start value " + timerStartValue);
timerbuttonPushCounter = 0;
clearDisplay();
setDecimals(0b010000); // Включаем двоеточие
sprintf(tempString, "0%d00", timerinminutes);
Serial.println("Timer set to: " + String(tempString));
s7s.print(tempString);
}
} // конец, если таймер находится в высоком состоянии
// Небольшая задержка, чтобы избежать подпрыгивания
delay(50);
}
// сохраняем текущее состояние как последнее для следующего прохода цикла
timerlastButtonState = timerbuttonState;
// для кнопки сброса логика намного проще
// если состояние высокое, кнопка была нажата, поэтому все переустанавливаем
if (resetbuttonState != resetlastButtonState)
{
if (resetbuttonState == HIGH)
{
Serial.println("Reset button pushed!");
doDisplayHighScore = true;
resetStepCounter();
// Небольшая задержка, чтобы избежать подпрыгивания
delay(50);
}
}
resetlastButtonState = resetbuttonState;
// логика пошаговой панели
int fsrADC = analogRead(FSR_PIN);
previousForce = currentForce;
currentForce = calculateForce(fsrADC);
// пробуем это, используя принудительные значения
// когда мы делаем шаг, сила всегда положительна
// когда мы поднимаем ногу, сила всегда отрицательна
if (currentForce > 0)
{
currentPos = true;
Serial.println("Force detected!");
delay(1000);
}
if (previousForce < 0)
{
previousPos = true;
}
if (currentPos && previousPos)
{
// запускаем таймер на первом шаге
if (!timerStarted)
{
Serial.println("First steop!");
timerStarted = true;
// Serial.println("Начальное значение таймера: " +String(timerStartValue));
countDownTimer.SetTimer(0, 0, timerStartValue);
countDownTimer.StartTimer();
// символ tempstring[10];
sprintf(tempString, "%4d", countDownTimer.ShowMinutes(), countDownTimer.ShowSeconds());
Serial.println("Timer set to: " + String(tempString));
// Serial.println("Таймер запущен");
// задержка(2000);
doDisplayCountDown = true;
}
stepsTaken++;
Serial.println("Step Detected!");
currentPos = false;
previousPos = false;
}
// если таймер равен 0, у нас закончилось время
// это нужно сделать здесь, так как это нужно делать всегда и это не может зависеть
// если displayCountDown истинен
if (timerStarted)
{
if (countDownTimer.TimeCheck(0, 0, 0))
{
//Serial.println("Время истекло");
//задержка(500);
timerStarted = false; // устанавливаем timerRunning в значение false
countDownTimer.StopTimer();
if (timerStartValue == thirtySecondTime)
{
WriteHighScore(thirtySecondAddress, stepsTaken);
}
if (timerStartValue == threeMinuteTime)
{
WriteHighScore(threeMinuteAddress, stepsTaken);
}
Serial.println("wrote high score");
delay(500);
// ОчиститьДисплей();
//displayStepCount();
// сбросStepCounter();
// displayCountDownTimer();
}
//еще
if (!(countDownTimer.TimeCheck(0, 0, 0))) // && таймер запущен)
{
countDownTimer.Timer(); // запускаем таймер
Serial.println("Timer is running");
// логика таймера
// каждые 10 секунд меняем логические значения того, что отображается
unsigned long currentMillis = millis();
// последние 10 секунд отображают только количество шагов
//если (countDownTimer.TimeCheck(0, 0, 10))
// {
//
// displayStepCount();
// }
// еще
// {
if (doChangeDisplay)
{
if ((currentMillis - previousMillis) >= displaychangeInterval)
{
// сохраняем последний раз, когда мы проверяли интервал
// Serial.println("Интервал 10 секунд!");
previousMillis = currentMillis;
// если это правда, установите значение false
// устанавливаем для отображения обратного отсчета значение true
if (doDisplayStepCount)
{
doDisplayStepCount = false;
doDisplayCountDown = true;
}
else
// отображение обратного отсчета верно, поэтому установите для него значение false
// и устанавливаем для отображения количества шагов значение true
{
doDisplayStepCount = true;
doDisplayCountDown = false;
}
}
}
// отображаем количество шагов только в том случае, если это интервал для этого
if (doDisplayStepCount)
{
displayStepCount();
}
// отображать таймер обратного отсчета только в том случае, если это 10-секундный интервал для этого
if (doDisplayCountDown)
{
displayCountDownTimer();
}
// }
}
}
}
// Отправляем команду очистки дисплея (0x76)
// Это очистит дисплей и сбросит курсор
void clearDisplay()
{
s7s.write(0x76); // Команда очистки дисплея
}
// Включите любую десятичную дробь, ее отсутствие или все десятичные дроби.
// Шесть младших битов в параметре decimals задают десятичное число
// (или двоеточие, или апостроф) вкл. или выкл. 1 означает включение, 0 — выключение.
// [MSB] (X)(X)(Apos)(Двоеточие)(Цифра 4)(Цифра 3)(Цифра2)(Цифра1)
// (0b010000) - только двоеточие
// (0b111111) - все десятичные дроби
void setDecimals(byte decimals)
{
s7s.write(0x77);
s7s.write(decimals);
}
void resetStepCounter()
{
timerbuttonPushCounter = 0;
if (timerStarted)
{
countDownTimer.StopTimer();
timerStarted = false;
}
// очистка
stepsTaken = 0;
clearDisplay();
timerStartValue = 0;
doDisplayHighScore = true;
}
void displayStepCount()
{
setDecimals(0b000000); // Отключаем двоеточие
// Будет использоваться со sprintf для создания строк
sprintf(tempString, "%04d", stepsTaken);
Serial.print("Steps taken: " + String(tempString));
clearDisplay();
s7s.print(tempString);
}
void displayCountDownTimer()
{
// примеры форматов
//sprintf(tempString, "%4d", deciSecond); //Преобразуем deciSecond в строку, настроенную правильно
//sprintf(tempString, "%d", deciSecond); //Преобразуем deciSecond в строку с оставленной поправкой
//sprintf(tempString, "%04d", deciSecond); //Преобразуем deciSecond в строку с ведущими нулями
//sprintf(tempString, "%4d", deciSecond * -1); //Показывает отрицательный знак перед скорректированным вправо числом
unsigned long currentMillis = millis();
int seconds = 0;
int minutes = 0;
// обновляем отображение только по прошествии 1 секунды
// if(currentMillis - previousTimerMillis >= countdownInterval)
// CountUpDownTimer TimeHasChanged() возвращает логическое значение (в секундах)
if (countDownTimer.TimeHasChanged())
{
// предыдущийTimerMillis = currentMillis;
seconds = countDownTimer.ShowSeconds();
Serial.println("Time left: " + seconds);
minutes = countDownTimer.ShowMinutes();
sprintf(tempString, "%02d%02d", minutes, seconds);
clearDisplay();
setDecimals(0b010000); // Включаем двоеточие
Serial.println(tempString);
s7s.print(tempString);
}
}
void EEPROMWriteInt(const int p_address, const int p_value)
{
byte lowByte = ((p_value >> 0) & 0xFF);
byte highByte = ((p_value >> 8) & 0xFF);
EEPROM.write(p_address, lowByte);
EEPROM.write(p_address + 1, highByte);
}
//Эта функция прочитает 2-байтовое целое число из EEPROM по указанному адресу и адресу + 1
int EEPROMReadInt(const int p_address)
{
byte lowByte = EEPROM.read(p_address);
byte highByte = EEPROM.read(p_address + 1);
return ((lowByte << 0) & 0xFF) + ((highByte << 8) & 0xFF00);
}
void WriteHighScore(int address, int value)
{
// int valueRead = EEPROMReadInt(адрес);
//если (значениеRead < значение)
// {
EEPROMWriteInt(address, value);
delay(500);
sprintf(tempString, "%04d", value);
Serial.print("high score: " + String(tempString));
clearDisplay();
s7s.print("-HI-");
delay(500);
clearDisplay();
s7s.print("SCOR");
delay(500);
clearDisplay();
s7s.print(tempString);
delay(500);
// }
}
void displayHighScores()
{
int thirtySecondScore = EEPROMReadInt(thirtySecondAddress);
int threeMinuteScore = EEPROMReadInt(threeMinuteAddress);
sprintf(tempString, "%04d", thirtySecondScore);
Serial.println("thirty second score: " + String(tempString));
sprintf(tempString, "%04d", threeMinuteScore);
Serial.println("Three minute score: " + String(tempString));
clearDisplay(); // Очищаем дисплей, сбрасываем курсор
s7s.println("-HI-");
delay(1000);
clearDisplay();
Serial.println("SCOR");
s7s.print("SCOR");
delay(1000);
clearDisplay();
sprintf(tempString, "%04d", thirtySecondScore);
Serial.println(tempString);
s7s.print(tempString);
delay(1000);
clearDisplay();
sprintf(tempString, "%04d", threeMinuteScore);
Serial.println(tempString);
s7s.print(tempString);
delay(1000);
clearDisplay();
}
float calculateForce(int pinvalue)
{
// Используем показания АЦП для расчета напряжения:
float fsrV = pinvalue * VCC / 1023.0; // 1023 — количество единиц напряжения от 0 до 5 В.
// Используем напряжение и значение статического резистора для
// вычисляем сопротивление FSR:
float fsrR = R_DIV * (VCC / fsrV - 1.0);
// Serial.println("Сопротивление: " + String(fsrR) + " Ом");
// Предполагаемая сила на основе наклонов на рисунке 3
// Спецификация FSR:
float force;
float fsrG = 1.0 / fsrR; // Вычисляем проводимость
// Разбиваем параболическую кривую на два линейных наклона:
force = (fsrG - 0.00075) / 0.00000032639;
return force;
}
@David Green, 👍0
Обсуждение1 ответ
Лучший ответ:
Эта строка:
sprintf(tempString, "%04d", thirtySecondTime);
записывает в tempString 5 символов, четыре цифры и завершающий ноль. Вы дали место только на четверых.
Это:
Serial.println("Timer set to: " + String(tempString));
— это пустая трата ресурсов. Если вы хотите поговорить об использовании памяти, то не используйте класс String и уж точно не используйте его там, где он просто не нужен.
Запишите это как:
Serial.print("Timer set to: ");
Serial.println(tempString);
или еще лучше с точки зрения памяти:
Serial.print(F("Timer set to: "));
Serial.println(tempString);
Спасибо. Я читал об использовании памяти String() и изменю это. Большая часть последовательного вывода будет удалена после того, как я закончу всю отладку. Из комментария @Juraj я провел небольшое исследование и теперь понимаю прекращение действия., @David Green
- Загрузка Arduino Nano дает ошибку: avrdude: stk500_recv(): programmer is not responding
- Асинхронные вызовы функций в скетче ардуино
- Двигатель постоянного тока шумит, но не вращается
- Цикл While не прерывается
- Использовать Arduino Nano V3 для программирования другого Arduino (Pro Mini)?
- как отправить аргумент объектам ESP8266WebServer в функции
- Реализовать связь Visible Light с помощью Arduino
- Скетч мигания ESP8266 не мигает светодиодом
добавьте позицию для завершающего нуля в tempString.
tempString[5]
. теперь 0 записывается sprintf в память после массива, @Juraj