Если человек не умеет управлять автомобилем без тормозов, на лысой резине, без печки и света, и на льду, то он, конечно, водитель, но не очень профессиональный, согласитесь?
а, кто не умеет трахаться втроём стоя в гамаке на лыжах, тот непрофессиональный любовник, согласитесь(с)?
И всё же, господа, помогите практическим советом. Как решить вопрос перемещение курсора?
Нужна переменная, отвечающая за позицию курсора в текущем меню/подменю. Как будет выглядеть кусок кода для следующей ситуации:
Верхняя строка ("хлебные крошки") выводится в зависимости от переменной меню/подменю, нижняя строка выводится от той же переменной и переменной позиции курсора (функция?).
MENU>SUBMENU1
*ON OFF AUTO
При нажатии кнопки "вправо" получаем комманду на изменения переменной, отвечающей за позиционирование курсора:
MENU>SUBMENU1
ON *OFF AUTO
Для включения или отключения (ON/OFF при нажатии кнопки "select") предполагаю, что нужно использовать функцию, которая от трех переменных (позиция в дереве меню, позиция курсора в текущем меню, нажатие кнопки "select") будет менять сигнал HIGH/LOW на логическом выходе ардуины, причем на том, на котором висит управление из текущего подменю. Я понимаю логику, и вижу как должна работать программа. Даже блок-схему набросать могу. Но с реализацие в коде для ардуино проблемы. Практики, кто написал не одну сотню(тысячу) строк скетчей, сбросьте кусок как реализовать вывод вышеприведенной схемы работы перемещения и выбора в меню.
P.S. Пока для меня самое понятное исполнение меню через switch/case. Но вот вопрос, оптимально ли это с точки зрения грамотного кода и нагрузки на процессор?
Вот код, который на просторах интернета ближе всего моей задумке. Самому с нуля написать такое мне проблематично, но подредактировать чужой я смогу. Тут навигация "двухпозиционная" энкодером. Мне же нужна четырехпозиционная под аналоговый шилд с помощью 4 кнопок и кнопки "выбор". Задача добавить две кнопки "влево" вправо" и организовать горизонтальное перемещение курсора.
/*
MENU ROUTINE
*/
void basicMenu(){
byte topItemDisplayed = 0; // stores menu item displayed at top of LCD screen
byte cursorPosition = 0; // where cursor is on screen, from 0 --> totalRows.
// redraw = 0 - don't redraw
// redraw = 1 - redraw cursor
// redraw = 2 - redraw list
byte redraw = MOVELIST; // triggers whether menu is redrawn after cursor move.
byte i=0; // temp variable for loops.
byte totalMenuItems = 0; //a while loop below will set this to the # of menu items.
// Put the menu items here. Remember, the first item will have a 'position' of 0.
char* menuItems[]={
"Set Timer 1",
"Set Timer 2",
"Set Probe 1 Temp",
"Set Probe 2 Temp",
"Probe 1 Controller",
"Probe 2 Controller",
"Advanced Settings",
"",
};
while (menuItems[totalMenuItems] != ""){
totalMenuItems++; // count how many items are in list.
}
totalMenuItems--; //subtract 1 so we know total items in array.
lcd.clear(); // clear the screen so we can paint the menu.
boolean stillSelecting = true; // set because user is still selecting.
timeoutTime = millis() + menuTimeout; // set initial timeout limit.
do // loop while waiting for user to select.
{
/*
IF YOU WANT OTHER CODE GOING ON IN THE BACKGROUND
WHILE WAITING FOR THE USER TO DO SOMETHING, PUT IT HERE
*/
/*
my code uses a rotary encoder for input.
You should obviously change the code to meet your needs.
For a button, you could do something like this, but note that
it does not have ANY debouncing and will scroll for as long as
the button is being pushed. This is not a button tutorial,
so you should look elsewhere on how to implement that. Just
remember that ALL of the code between the corresponding 'case'
and 'break' should be moved to each button push routine.
buttonState = digitalRead(buttonPin);
if (buttonState == HIGH) {
AND THEN PUT THE CORRESPONDING CODE
FROM BELOW HERE
}
*/
switch(read_encoder())
{ // analyze encoder response. Default is 0.
case 1: // ENCODER ROTATED UP. EQUIVALENT OF 'UP' BUTTON PUSHED
timeoutTime = millis()+menuTimeout; // reset timeout timer
// if cursor is at top and menu is NOT at top
// move menu up one.
if(cursorPosition == 0 && topItemDisplayed > 0) // Cursor is at top of LCD, and there are higher menu items still to be displayed.
{
topItemDisplayed--; // move top menu item displayed up one.
redraw = MOVELIST; // redraw the entire menu
}
// if cursor not at top, move it up one.
if(cursorPosition>0)
{
cursorPosition--; // move cursor up one.
redraw = MOVECURSOR; // redraw just cursor.
}
break;
case 2: // ENCODER ROTATED UP. EQUIVALENT OF 'DOWN' BUTTON PUSHED
timeoutTime = millis()+menuTimeout; // reset timeout timer
// this sees if there are menu items below the bottom of the LCD screen & sees if cursor is at bottom of LCD
if((topItemDisplayed + (totalRows-1)) < totalMenuItems && cursorPosition == (totalRows-1))
{
topItemDisplayed++; // move menu down one
redraw = MOVELIST; // redraw entire menu
}
if(cursorPosition<(totalRows-1)) // cursor is not at bottom of LCD, so move it down one.
{
cursorPosition++; // move cursor down one
redraw = MOVECURSOR; // redraw just cursor.
}
break;
case 4: // ENCODER BUTTON PUSHED FOR SHORT PERIOD & RELEASED.
// EQUIVALENT TO 'SELECT' OR 'OKAY' BEING PUSHED
timeoutTime = millis()+menuTimeout; // reset timeout timer
switch(topItemDisplayed + cursorPosition) // adding these values together = where on menuItems cursor is.
{
// put code to be run when specific item is selected in place of the Serial.print filler.
// the Serial.print code can be removed, but DO NOT change the case & break structure.
// (Obviously, you should have as many case instances as you do menu items.)
case 0: // menu item 1 selected
Serial.print("Menu item ");
Serial.print(topItemDisplayed + cursorPosition);
Serial.print(" selected - ");
Serial.println(menuItems[topItemDisplayed + cursorPosition]);
// there is no menuSubMenu1() function. BUT, to have nested menus,
// copy this function(i.e. all of basicMenu) into a new function named
// whatever you want for your sub menu items and repeat.
// menuSubMenu1();
break;
case 1: // menu item 2 selected
Serial.print("Menu item ");
Serial.print(topItemDisplayed + cursorPosition);
Serial.print(" selected - ");
Serial.println(menuItems[topItemDisplayed + cursorPosition]);
break;
case 2: // menu item 3 selected
Serial.print("Menu item ");
Serial.print(topItemDisplayed + cursorPosition);
Serial.print(" selected - ");
Serial.println(menuItems[topItemDisplayed + cursorPosition]);
break;
case 3: // menu item 4 selected
Serial.print("Menu item ");
Serial.print(topItemDisplayed + cursorPosition);
Serial.print(" selected - ");
Serial.println(menuItems[topItemDisplayed + cursorPosition]);
break;
case 4: // menu item 5 selected
Serial.print("Menu item ");
Serial.print(topItemDisplayed + cursorPosition);
Serial.print(" selected - ");
Serial.println(menuItems[topItemDisplayed + cursorPosition]);
break;
case 5: // menu item 6 selected
Serial.print("Menu item ");
Serial.print(topItemDisplayed + cursorPosition);
Serial.print(" selected - ");
Serial.println(menuItems[topItemDisplayed + cursorPosition]);
break;
case 6: // menu item 7 selected
Serial.print("Menu item ");
Serial.print(topItemDisplayed + cursorPosition);
Serial.print(" selected - ");
Serial.println(menuItems[topItemDisplayed + cursorPosition]);
break;
// add as many "case #:" as items you have. You could put
// line separators in menuList and leave out the
// corresponding case, which would mean that nothing
// would be triggered when user selected the line separator.
}
break;
case 8: // encoder button was pushed for long time. This corresponds to "Back" or "Cancel" being pushed.
stillSelecting = false;
Serial.println("Button held for a long time");
break;
}
switch(redraw){ // checks if menu should be redrawn at all.
case MOVECURSOR: // Only the cursor needs to be moved.
redraw = false; // reset flag.
if (cursorPosition > totalMenuItems) // keeps cursor from moving beyond menu items.
cursorPosition = totalMenuItems;
for(i = 0; i < (totalRows); i++){ // loop through all of the lines on the LCD
lcd.setCursor(0,i);
lcd.print(" "); // and erase the previously displayed cursor
lcd.setCursor((totalCols-1), i);
lcd.print(" ");
}
lcd.setCursor(0,cursorPosition); // go to LCD line where new cursor should be & display it.
lcd.print(">");
lcd.setCursor((totalCols-1), cursorPosition);
lcd.print("<");
break; // MOVECURSOR break.
case MOVELIST: // the entire menu needs to be redrawn
redraw=MOVECURSOR; // redraw cursor after clearing LCD and printing menu.
lcd.clear(); // clear screen so it can be repainted.
if(totalMenuItems>((totalRows-1))){ // if there are more menu items than LCD rows, then cycle through menu items.
for (i = 0; i < (totalRows); i++){
lcd.setCursor(1,i);
lcd.print(menuItems[topItemDisplayed + i]);
}
}
else{ // if menu has less items than LCD rows, display all available menu items.
for (i = 0; i < totalMenuItems+1; i++){
lcd.setCursor(1,i);
lcd.print(menuItems[topItemDisplayed + i]);
}
}
break; // MOVELIST break
}
if (timeoutTime<millis()){ // user hasn't done anything in awhile
stillSelecting = false; // tell loop to bail out.
/*
in my main code, I had a function that
displayed a default screen on the LCD, so
I would put that function here, and it would
bail out to the default screen.
defaultScreen();
*/
}
}
while (stillSelecting == true); //
}
Самому с нуля написать такое мне проблематично, но подредактировать чужой я смогу.
Ну, если можете - редактируйте. А не можете - закажите кому-нибудь в разделе "Ищу исполнителя". Я готов помочь тому, кто пишет сам, но не готов писать за кого-то.
Самому с нуля написать такое мне проблематично, но подредактировать чужой я смогу.
Ну, если можете - редактируйте. А не можете - закажите кому-нибудь в разделе "Ищу исполнителя". Я готов помочь тому, кто пишет сам, но не готов писать за кого-то.
Я всего лишь просил оптимальное ли решение с применением case для построения пунктов меню, и помочь с механникой горизонтального перемещения по меню, а не писать готовый скетч за меня. Есть идей, желательно с парой строк кода, чтобы понять механнику - буду благодарен. Если бы у меня был готовый работоспособный код и мне нужно было бы совета, я выложил его в первом посте. А по поводу готовых решений - да их куча, но ни один не подходит в полной мере для моей задумки.
Я не понял, какое из моих слов Вам непонятно. Если бы Вы писали свой собственный код, я бы Вам помог. Если нет - обратитесь в "Ищу исполнителя".
Вы же не пишете собственного кода, а хотите модифицировать чужой, к тому ещё и безграмотно написанный. На вопросы по этому коду типа "а для чего вот это" Вы ответить не сможете. Совета типа "добавьте такие-то проверки" без "пары строк кода" Вы не вспримете, т.к. оригинального кода не понимаете. Получится не сотрудничество, а работа за Вас. Оно мне надо?
Я готов помогать и много помогаю тем, кто делает сам. И никак по-другому.
Вот так и рассказывай, как первокласнику брать интеграл. d_p, вы не готовы к написанию меню. Да как пользователь может вы знаете как делать меню, а вот саму механику нет. Энкодер меняйте на три клавиши (left,right,select) и все. А вот up и down это включить и отключить подсветку. Да и забейте про курсор. У вас не так много экрана и количество клавиш , что бы городить еще и это.
void viev(int n) { // в зависимости от n в нужном месте будет *
lcd.setCursor(0, 0) ;
lcd.print("MENU>SUBMENU1");
lcd.setCursor(0, 1) ;
if (n == 1) lcd.print("*");
else lcd.print(" ");
lcd.print("ON");
if (n == 2) lcd.print("*");
else lcd.print(" ");
lcd.print("OFF");
if (n == 3) lcd.print("*");
else lcd.print(" ");
lcd.print("AUTO");
}
Ну не хотят люди головой думать. Все нашарика хочется. Даже если и думать не надо.
Ну не хотят люди головой думать. Все нашарика хочется. Даже если и думать не надо.
так он звёздочку курсором обзывает, а ты и курсор и звёздочку заюзал...
Он то что нужно использовал: lcd.setCursor() позиция курсора (невидимого) для вывода на дисплей. Мне не нужен lcd.Cursor(), вместо него использую звёздочку.
void viev(int n) { // в зависимости от n в нужном месте будет *
lcd.setCursor(0, 0) ;
lcd.print("MENU>SUBMENU1");
lcd.setCursor(0, 1) ;
if (n == 1) lcd.print("*");
else lcd.print(" ");
lcd.print("ON");
if (n == 2) lcd.print("*");
else lcd.print(" ");
lcd.print("OFF");
if (n == 3) lcd.print("*");
else lcd.print(" ");
lcd.print("AUTO");
}
Ну не хотят люди головой думать. Все нашарика хочется. Даже если и думать не надо.
Похоже. Только строки - в массив, допустим ArrStr, и тогда заменятся строкт кода 5-13 на for(byte i=0;i<LenArrStr;i++) { if(n==i) lcd.print("*"); else lcd.ptint(" ");lcd.print(ArrStr[i]);} Если ArrStr и LenArrStr (это количество строк в ArrStr) передавать как параметры, то функция становится универсальной для любого подменю, правда нужно еще придумать что делать с заглавием.
Я не правильно написал - правильно наверно эту строчку
if (!key_old&&key ) return key;
понимать так: если значение key_old равно нулю, а значение key не равно нулю, то вернуть это значение! Верно?
И кстати дальше там идет строка return 0;
if (!key_old&&key ) return key;
return 0;
А зачем возвращать из этой функции ноль? Если например условие истинно и функция возвращает значение key, оно тут же перезаписывается нулем? Или я неправильно понял?
Вот опять вы ничего не поняли. Там есть 5(пять !!) кнопок. А значит 6(шесть!!!) состояний, интересующих программиста. когда нажата одна из кнопок и когда не нажата ни одна из них. Опять же интересует переход от не нажата ни одна к нажатию одной из них. Тогда появляется значение 1,2,3,4,5 - а иначе 0. ПС: сейчас бы я написал программу меню совершенно иначе. :)
if (/*если раньше было не нажато ни одной*/!key_old&&/*а сейчас нажато*/key ) return /*то вернуть код этой кнопки */key;
Новый код выглядит намного лучше!
Но вот заметил, что в старом варианте Вашего кода переменная, в которую передается значение из millis(); объявлена со словом static
static uint32_t
а в новом, без него! То есть unsigned long ? Перед ней слово static писать не нужно?
В новом это глобальная переменая и объявляется в начале. Так как она нужна внутри классов. А в старом это локальная, но постояная, а не вновь создаваемая при каждом проходе. это про переменную со значением millis().
ПС:
/* свободные 0,1,2,3,11,12,13,A1,A2,A3,A4,A5,A6,A7
кнопки -> A0 (button sellect)
S1 =Right 800-1023
S2 =Up 0-199
S3 =Down 200-399
S4 =Left 400-599
S5 = Select 600-799
нет нажатия 800-1023
экран -> D4 (DB4)
-> D5 (DB5)
-> D6 (DB6)
-> D7 (DB7)
-> D8 (RS)
-> D9 (Enable)
-> D10 (LCD Backlight Control)
*/
uint32_t mill ;
//------------------------------
//#5 устройство
class Cl_device {
protected:
uint32_t past = 0 ;
public:
Cl_device() {};
/*иницирование*/
void init() {}
/*работа*/
void run() {
if (mill - past >= 1000) {/*1 раз в сек*/
past = mill ;
/*код работы*/
}
}
};
//#8 дисплей
#include <LiquidCrystal.h>
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
char* scrn0[] = {"Main "};
char* scrn1[] = {" .. ", "Load ", "Save ", "Regime", "Sub1 ", "Sub2 ", "var1.1", "var1.2", "var1.3", ""};
char* scrn2[] = {" .. ", "var2.1", "var2.2", "var2.3", ""};
char* scrn3[] = {" .. ", "var3.1", "var3.2", "var3.3", ""};
char* scrn4[] = {" .. ", "Regime1", "Regime2", "Regime3", "Regime4", ""};
uint8_t N_scrn = 0; // номер экрана для показа
uint8_t N_ln = 0; // номер строки для показа
void Display_setup() {
lcd.begin(16, 2);
}
void scrn0_viev() {
lcd.print(scrn0[0]);
}
void scrn1_viev() {
lcd.print(scrn1[N_ln]);
lcd.setCursor(0, 1);
lcd.print(scrn1[N_ln + 1]);
}
void scrn2_viev() {
lcd.print(scrn2[N_ln]);
lcd.setCursor(0, 1);
lcd.print(scrn2[N_ln + 1]);
}
void scrn3_viev() {
lcd.print(scrn3[N_ln]);
lcd.setCursor(0, 1);
lcd.print(scrn3[N_ln + 1]);
}
void scrn4_viev() {
lcd.print(scrn4[N_ln]);
lcd.setCursor(0, 1);
lcd.print(scrn4[N_ln + 1]);
}
void Display() {
lcd.clear();
switch (N_scrn) {
case 0:
scrn0_viev();
break;
case 1:
scrn1_viev();
break;
case 2:
scrn2_viev();
break;
case 3:
scrn3_viev();
break;
case 4:
scrn4_viev();
break;
}
}
//#9
int const keyboard_pin = 0; // A0 - нога к которой присоединена клавиатура
uint16_t key_data; // значение уровня сигнала на ноге клавиатуры
uint8_t key, key_old; // значение последнего и прошлого состояние на ноге в коде кнопки
void Controls_setup() {
key = keys();
}
uint8_t keys() { // первичный опрос
key_data = analogRead (keyboard_pin);
if (key_data < 60) return 1;
else if (key_data < 200) return 2;
else if (key_data < 400) return 3;
else if (key_data < 600) return 4;
else if (key_data < 800) return 5;
else return 0 ;
}
uint8_t keyboard() { // получение кода кода только что нажатой клавиши
key_old = key;
key = keys();
if (!key_old && key ) return key;
return 0;
}
uint8_t Controls() { // анализ нажатых клавиш и выработка действия
switch (keyboard()) {
case 1: N_scrn++ ; // Right
N_ln = 0;
break;
case 2: N_ln-- ; // Up
break;
case 3: N_ln++ ; // Down
break;
case 4: N_scrn--; //Left
N_ln = 0;
break;
case 5: ; //Select
break;
}
}
//---------------------------
Cl_device device;
// --------- main -------------
void setup() {
device.init();
//#8 вывод на дисплей
Display_setup();
//#9 опрос кнопок и выдача команд на выполнение
Controls_setup();
}
void loop() {
mill = millis() ;
//#5 1000 миллисекунд устройство
device.run();
//#8 1000 миллисекунд. Вывод на дисплей
static uint32_t past8 = 0 ;
if (mill - past8 >= 1000) {
past8 = mill;
Display();
}
//#9 200 миллисекунд. Опрос кнопок и выдача команд на выполнение
static uint32_t past9 = 0 ;
if (mill - past9 >= 200) {
past9 = mill;
Controls();
}
}
/*Скетч использует 2556 байт (8%) памяти устройства. Всего доступно 30720 байт.
Глобальные переменные используют 259 байт (12%) динамической памяти, оставляя 1789 байт для локальных переменных. Максимум: 2048 байт.
*/
Я бы сделал первой строкой (её номер 104)
if (key_data >1000 ) return 0;
чтобы зазря не бегать по всем ифам
если чаще всего результат - это
ненажатая кнопка
Бесполезно Пуха программированью учить. Он в классе вон использует глобальную переменную модуля. Которая, кроме как в классе, больше нигде использоваться не должна. Самаучка, чо уж тут...
Я завожу в классе переменную lasttime, инициализирую в конструктре нулем. При обращении к классу читается текущий millis. Некоторые датчики часто читать нельзя, поэтому заводится еще одна переменная, delta. Если при обращении к классу прошло времени меньше дельты, отдается последнее значение, если больше - датчик реально читается. В первый раз, ри обращении к классу, так как lasttime==0 установлен в конструкторе, датчик реально читается.
Это дело вкуса. А на вкус и цвет все фломатреры разные. Вот скетч по qwone-эталону
/**/
unsigned long mill;// переменная для millis()
typedef void (*pDo)() ;// тип -функция обработчик
//------Cl_Led----------------------
// класс светодиод
class Cl_Led {
protected:
const byte pin;
bool led;
unsigned long past, time;
byte state; //0 выкл/ 1 вкл / 2 мигать
/*установить в состояние*/
void stand(byte state_) {
past = mill;
state = state_;
switch (state) {
case 0: // выкл
digitalWrite(pin, led = LOW);
break;
case 1: // вкл
digitalWrite(pin, led = HIGH);
break;
case 2:// мигать
digitalWrite(pin, led = !led);
break;
case 3:// короткое выключение
digitalWrite(pin, led = LOW);
break;
case 4:// короткое включение
digitalWrite(pin, led = HIGH);
break;
}
}
public:
/*конструктор*/
Cl_Led(byte pin_): pin(pin_) {}
/*инициализация-вставить в setup()*/
void init() {
pinMode(pin, OUTPUT);
OFF();
}
/*работа-вставить в loop()*/
void run() {
if (state == 2 && mill - past >= time)stand(2);
if (state == 3 && mill - past >= time)stand(1);
if (state == 4 && mill - past >= time)stand(0);
}
/*включить*/
void ON() {
stand(1);
}
/*коротко включить*/
void ON(unsigned long time_) {
time = time_;
stand(4);
}
/*выключить*/
void OFF() {
stand(0);
}
/*коротко выключить*/
void OFF(unsigned long time_) {
time = time_;
stand(3);
}
/*мигать*/
void blink(unsigned long time_ = 200) {
time = time_;
stand(2);
}
};
//------Cl_Btn----------------------
// класс кнопка
class Cl_Btn {
protected:
const byte pin;
pDo Do;//обработчик
bool bounce = 0;
bool btn = 1, oldBtn;
unsigned long past;
public:
/*конструктор*/
Cl_Btn(byte pin_, pDo Do_): pin(pin_), Do(Do_) {}
/*инициализация-вставить в setup()*/
void init() {
pinMode(pin, INPUT_PULLUP);
}
/*работа-вставить в loop()*/
void run() {
bool newBtn = digitalRead(pin);
if (!bounce && newBtn != btn) {
bounce = 1;
past = mill;
}
if (bounce && mill - past >= 10) {
bounce = 0 ;
oldBtn = btn;
btn = newBtn;
if (!btn && oldBtn) Do();
}
}
};
//-----компоновка----------------------
Cl_Led Led(/*пин*/13);
void DoBtn1() {
Led.ON(200);
Serial.println("DoBtn1");
}
void DoBtn2() {
Led.OFF(200);
Serial.println("DoBtn2");
}
void DoBtn3() {
Led.blink();
Serial.println("DoBtn3");
}
Cl_Btn Btn1(/*пин*/2,/*обработчик*/DoBtn1);
Cl_Btn Btn2(/*пин*/3,/*обработчик*/DoBtn2);
Cl_Btn Btn3(/*пин*/4,/*обработчик*/DoBtn3);
//-----main----------------------
void setup() {
Serial.begin(9600);
Led.init();
Btn1.init();
Btn2.init();
Btn3.init();
}
void loop() {
mill = millis();
Led.run();
Btn1.run();
Btn2.run();
Btn3.run();
}
/*Скетч использует 2858 байт (9%) памяти устройства. Всего доступно 30720 байт.
Глобальные переменные используют 251 байт (12%) динамической памяти, оставляя 1797 байт для локальных переменных. Максимум: 2048 байт.
*/
002
unsigned longmill;// переменная для millis()
и дальше
128
voidloop() {
129
mill = millis();
130
Led.run();
131
Btn1.run();
132
Btn2.run();
133
Btn3.run();
Зачем переменная mill. Для того что бы millis() не дергать часто. Раз millis() - глобальная функция, то и переменная обслуживающая ее глобальна. И не надо городить посредников в представителе каждого класса.
Зачем переменная mill. Для того что бы millis() не дергать часто. Раз millis() - глобальная функция, то и переменная обслуживающая ее глобальна. И не надо городить посредников в представителе каждого класса.
Процессор "изнашивается". Время для выполнения millis() больше чем извлечение из переменной. И если 10 раз выполнить millis(), то на столько времени "изностися" процессор.
Использование глобальной переменной в классе - дело не вкуса, а непрофессионального проектирования программы. Отвыкайте от этого. DetSimen дело говорит.
Я завожу в классе переменную lasttime, инициализирую в конструктре нулем. При обращении к классу читается текущий millis. Некоторые датчики часто читать нельзя, поэтому заводится еще одна переменная, delta. Если при обращении к классу прошло времени меньше дельты, отдается последнее значение, если больше - датчик реально читается. В первый раз, ри обращении к классу, так как lasttime==0 установлен в конструкторе, датчик реально читается.
Буду дома - выкладу код для примера.
Да, если Вам не сложно, приведите пожалуйста пример кода!
Ох, осторожнее коллега! Как бы не нарваться. Посмотрите п. 9 вот здесь! Хотя мне там больше всего нравится налог - п.7. Святые сёстры знали толк в правильных налогах :)))))
const int sensor 3; // сенсор подключен к этому выводу
int ReadSensor() { // функция чтения сенсора через заданный интервал времени
static uint32_t lasttime = 0; // переменная для запоминания времени
static uint32_t millis = 0; // для считывания времени из функции
const uint32_t delta =; 1000; // константа для разницы значений (одна секунда)
millis = millis(); // считывание значения из функции "таймера"
if (millis - lasttime >= delta) { // если прошло достаточно времени...
val = analogRead(sensor); // прочесть датчик
lasttime = millis; // передать значение для следующего сравнения
}
return val; // вернуть значение, сохраненное в переменной
}
Да действительно в коде была целая куча ошибок! Однако я все их исправил и он уже успешнно компилируется! Вот что получилось в итоге:
const int sensor = 3; // сенсор подключен к этому выводу
int ReadSensor() { // функция чтения сенсора через заданный интервал времени
static uint32_t lasttime = 0; // переменная для запоминания времени
static uint32_t mill = 0; // для считывания времени из функции
const uint32_t delta = 1000; // константа для разницы значений (одна секунда)
int val;
mill = millis(); // считывание значения из функции "таймера"
if (mill - lasttime >= delta) { // если прошло достаточно времени...
val = analogRead(sensor); // прочесть датчик
lasttime = mill; // передать значение для следующего сравнения
}
return val; // вернуть значение, сохраненное в переменной
}
void setup() {
pinMode(sensor, INPUT);
}
void loop() {
ReadSensor();
}
Теперь ведь правильно? Единственный вопрос: как сделать так, чтобы датчик читался ещё в начале запуска, а не только через заданный промежуток времени! Подскажите пожалуйста, что нужно изменить?
Не знал, но только что погуглил и нашел! Ничего страшного - я не занимаюсь проектами, у которых "сроки горят", а просто пытаюсь разобраться в элементарных (по крайней мере на первый вгляд) вещах.
sadman41 пишет:
А поделитесь вы с другом функцией - откуда он узнает, почему компилятор ругается на необъявленную переменную sensor?
Да! Вы абсолютно правы! я как-то над этим не подумал.
Херня все это . Идеальная программа это программа которая кормит всех, и даже тех кто ищет ошибки в программе. И как только из программы удалят последнюю ошибку, то эту программу отправят на свалку и начнут писать новую идеальную программу ;).
Расшифруйте пожалуйста, что это значит?
Две черточки это логическое ИЛИ?
то есть !lasttime ИЛИ mill - lasttime >= delta
что означает !lasttime с этим знаком "!"? Это эквивалентно lasttime == 0;
Верно?
Расшифруйте пожалуйста, что это значит?
Две черточки это логическое ИЛИ?
то есть !lasttime ИЛИ mill - lasttime >= delta
что означает !lasttime с этим знаком "!"? Это эквивалентно lasttime == 0;
Верно?
Если человек не умеет управлять автомобилем без тормозов, на лысой резине, без печки и света, и на льду, то он, конечно, водитель, но не очень профессиональный, согласитесь?
а, кто не умеет трахаться втроём стоя в гамаке на лыжах, тот непрофессиональный любовник, согласитесь(с)?
И всё же, господа, помогите практическим советом. Как решить вопрос перемещение курсора?
Нужна переменная, отвечающая за позицию курсора в текущем меню/подменю. Как будет выглядеть кусок кода для следующей ситуации:
Верхняя строка ("хлебные крошки") выводится в зависимости от переменной меню/подменю, нижняя строка выводится от той же переменной и переменной позиции курсора (функция?).
MENU>SUBMENU1
*ON OFF AUTO
При нажатии кнопки "вправо" получаем комманду на изменения переменной, отвечающей за позиционирование курсора:
MENU>SUBMENU1
ON *OFF AUTO
Для включения или отключения (ON/OFF при нажатии кнопки "select") предполагаю, что нужно использовать функцию, которая от трех переменных (позиция в дереве меню, позиция курсора в текущем меню, нажатие кнопки "select") будет менять сигнал HIGH/LOW на логическом выходе ардуины, причем на том, на котором висит управление из текущего подменю. Я понимаю логику, и вижу как должна работать программа. Даже блок-схему набросать могу. Но с реализацие в коде для ардуино проблемы. Практики, кто написал не одну сотню(тысячу) строк скетчей, сбросьте кусок как реализовать вывод вышеприведенной схемы работы перемещения и выбора в меню.
P.S. Пока для меня самое понятное исполнение меню через switch/case. Но вот вопрос, оптимально ли это с точки зрения грамотного кода и нагрузки на процессор?
И всё же, господа, помогите практическим советом. Как решить вопрос перемещение курсора?
оптимально ли это с точки зрения грамотного кода и нагрузки на процессор?
Пересмотрел всю тему и не нашёл ни единой строчки кода от Вас.
Поэтому абсолютно непонятно: что именно "это"?
Приведите свой код, посмотрим.
Вот код, который на просторах интернета ближе всего моей задумке. Самому с нуля написать такое мне проблематично, но подредактировать чужой я смогу. Тут навигация "двухпозиционная" энкодером. Мне же нужна четырехпозиционная под аналоговый шилд с помощью 4 кнопок и кнопки "выбор". Задача добавить две кнопки "влево" вправо" и организовать горизонтальное перемещение курсора.
Самому с нуля написать такое мне проблематично, но подредактировать чужой я смогу.
Ну, если можете - редактируйте. А не можете - закажите кому-нибудь в разделе "Ищу исполнителя". Я готов помочь тому, кто пишет сам, но не готов писать за кого-то.
Самому с нуля написать такое мне проблематично, но подредактировать чужой я смогу.
Ну, если можете - редактируйте. А не можете - закажите кому-нибудь в разделе "Ищу исполнителя". Я готов помочь тому, кто пишет сам, но не готов писать за кого-то.
Я всего лишь просил оптимальное ли решение с применением case для построения пунктов меню, и помочь с механникой горизонтального перемещения по меню, а не писать готовый скетч за меня. Есть идей, желательно с парой строк кода, чтобы понять механнику - буду благодарен. Если бы у меня был готовый работоспособный код и мне нужно было бы совета, я выложил его в первом посте. А по поводу готовых решений - да их куча, но ни один не подходит в полной мере для моей задумки.
Я не понял, какое из моих слов Вам непонятно. Если бы Вы писали свой собственный код, я бы Вам помог. Если нет - обратитесь в "Ищу исполнителя".
Вы же не пишете собственного кода, а хотите модифицировать чужой, к тому ещё и безграмотно написанный. На вопросы по этому коду типа "а для чего вот это" Вы ответить не сможете. Совета типа "добавьте такие-то проверки" без "пары строк кода" Вы не вспримете, т.к. оригинального кода не понимаете. Получится не сотрудничество, а работа за Вас. Оно мне надо?
Я готов помогать и много помогаю тем, кто делает сам. И никак по-другому.
Вот так и рассказывай, как первокласнику брать интеграл. d_p, вы не готовы к написанию меню. Да как пользователь может вы знаете как делать меню, а вот саму механику нет. Энкодер меняйте на три клавиши (left,right,select) и все. А вот up и down это включить и отключить подсветку. Да и забейте про курсор. У вас не так много экрана и количество клавиш , что бы городить еще и это.
Как решить вопрос перемещение курсора?
блин. в который раз читаю вопрошания про курсор и не могу понять, что вопрошающий желает и что подразумевает под курсором.
алё! гараж, курсор - это выделенное красным прямоугольником, печатается lcd.cursor();
Ну не хотят люди головой думать. Все нашарика хочется. Даже если и думать не надо.
Ну не хотят люди головой думать. Все нашарика хочется. Даже если и думать не надо.
так он звёздочку курсором обзывает, а ты и курсор и звёздочку заюзал...
Ну не хотят люди головой думать. Все нашарика хочется. Даже если и думать не надо.
так он звёздочку курсором обзывает, а ты и курсор и звёздочку заюзал...
Он то что нужно использовал: lcd.setCursor() позиция курсора (невидимого) для вывода на дисплей. Мне не нужен lcd.Cursor(), вместо него использую звёздочку.
qwone спасибо
Ну не хотят люди головой думать. Все нашарика хочется. Даже если и думать не надо.
Похоже. Только строки - в массив, допустим ArrStr, и тогда заменятся строкт кода 5-13 на for(byte i=0;i<LenArrStr;i++) { if(n==i) lcd.print("*"); else lcd.ptint(" ");lcd.print(ArrStr[i]);} Если ArrStr и LenArrStr (это количество строк в ArrStr) передавать как параметры, то функция становится универсальной для любого подменю, правда нужно еще придумать что делать с заглавием.
Помогите пожалуйста понять, что означает этот кусочек кода:
Предполагаю, что это защита от дребезга кнопок? Опрос клавиатуры выполняется несколько раз. Вижу, что до этого в коде есть такая функция
однако в ней нету return key;
Как тогда она может передать значение?
и ещё мне совсем непонятно, что означает
Здесь сравниваются логические значения? FALSE и TRUE? Буквально, если key_old равно FALSE и key равно TRUE, тогда вернуть значение key? Верно?
#7 где вы там увидели true и false ?
Я не правильно написал - правильно наверно эту строчку
понимать так: если значение key_old равно нулю, а значение key не равно нулю, то вернуть это значение! Верно?
И кстати дальше там идет строка return 0;
А зачем возвращать из этой функции ноль? Если например условие истинно и функция возвращает значение key, оно тут же перезаписывается нулем? Или я неправильно понял?
Вот опять вы ничего не поняли. Там есть 5(пять !!) кнопок. А значит 6(шесть!!!) состояний, интересующих программиста. когда нажата одна из кнопок и когда не нажата ни одна из них. Опять же интересует переход от не нажата ни одна к нажатию одной из них. Тогда появляется значение 1,2,3,4,5 - а иначе 0. ПС: сейчас бы я написал программу меню совершенно иначе. :)
Я наконец-то понял! Спасибо!
Ещё есть вопрос по вот этому кучочку кода
можно ли его заменить на такой
Подскажите пожалуйста, кто разбирается. Заранее спасибо!
Это мой старый стиль. Такчто код поновее был бы таким
ПС: Ну а теперешний он больше такой.#1
Новый код выглядит намного лучше!
Но вот заметил, что в старом варианте Вашего кода переменная, в которую передается значение из millis(); объявлена со словом static
а в новом, без него! То есть unsigned long ? Перед ней слово static писать не нужно?
В новом это глобальная переменая и объявляется в начале. Так как она нужна внутри классов. А в старом это локальная, но постояная, а не вновь создаваемая при каждом проходе. это про переменную со значением millis().
ПС:
Я бы сделал первой строкой (её номер 104)
if (key_data >1000 ) return 0;
чтобы зазря не бегать по всем ифам
если чаще всего результат - это
ненажатая кнопка
Бесполезно Пуха программированью учить. Он в классе вон использует глобальную переменную модуля. Которая, кроме как в классе, больше нигде использоваться не должна. Самаучка, чо уж тут...
И где же эта глобальная переменная? Покажите конкретно строку!
018
uint32_t mill ;
обращение
029
void
run() {
030
if
(mill - past >= 1000) {
/*1 раз в сек*/
031
past = mill ;
032
/*код работы*/
033
}
034
}
нельзя так делать. все должно быть скрыто в классе.
да. действительно это глобальная переменная!
А как тогда правильно? Создать в классе ещё переменную (приватную) и присваивать ей значение глобальной?
Я завожу в классе переменную lasttime, инициализирую в конструктре нулем. При обращении к классу читается текущий millis. Некоторые датчики часто читать нельзя, поэтому заводится еще одна переменная, delta. Если при обращении к классу прошло времени меньше дельты, отдается последнее значение, если больше - датчик реально читается. В первый раз, ри обращении к классу, так как lasttime==0 установлен в конструкторе, датчик реально читается.
Буду дома - выкладу код для примера.
Это дело вкуса. А на вкус и цвет все фломатреры разные. Вот скетч по qwone-эталону
002
unsigned
long
mill;
// переменная для millis()
и дальше
128
void
loop
() {
129
mill = millis();
130
Led.run();
131
Btn1.run();
132
Btn2.run();
133
Btn3.run();
Зачем переменная mill. Для того что бы millis() не дергать часто. Раз millis() - глобальная функция, то и переменная обслуживающая ее глобальна. И не надо городить посредников в представителе каждого класса.
Зачем переменная mill. Для того что бы millis() не дергать часто. Раз millis() - глобальная функция, то и переменная обслуживающая ее глобальна. И не надо городить посредников в представителе каждого класса.
А что, millis() изнашивается?
Представляю, что творит с процессором loop(). А ISR(TIMER0_OVF_vect) и вовсе надо за геноцид привлечь.
Хотя, я же понимаю, что вы шутите. Действительно - кому нужно протухшее значение millis() в какой-то там глобальной переменной.
Cкетч на тему LCD Key Shield .#11 Ну и теперешняя версия
Это дело вкуса.
Использование глобальной переменной в классе - дело не вкуса, а непрофессионального проектирования программы. Отвыкайте от этого. DetSimen дело говорит.
Я завожу в классе переменную lasttime, инициализирую в конструктре нулем. При обращении к классу читается текущий millis. Некоторые датчики часто читать нельзя, поэтому заводится еще одна переменная, delta. Если при обращении к классу прошло времени меньше дельты, отдается последнее значение, если больше - датчик реально читается. В первый раз, ри обращении к классу, так как lasttime==0 установлен в конструкторе, датчик реально читается.
Буду дома - выкладу код для примера.
Да, если Вам не сложно, приведите пожалуйста пример кода!
для этого еще пратрезветь надо.
для этого еще пратрезветь надо.
Ох, осторожнее коллега! Как бы не нарваться. Посмотрите п. 9 вот здесь! Хотя мне там больше всего нравится налог - п.7. Святые сёстры знали толк в правильных налогах :)))))
батогами мня уже били.
Посмотрите пожалуйста, правильно ли я написал?
Это не будет скомпилированно.
static
uint32_t millis -
не обязательно должен быть static, да и вообще может быть исключен.Посмотрите пожалуйста, правильно ли я написал?
Простите, за личный вопрос, но какой Вы конфессии, что Ваша религия не позволяет Вам запустить компилятор и посмотреть как он это скомпилирует?
Да действительно в коде была целая куча ошибок! Однако я все их исправил и он уже успешнно компилируется! Вот что получилось в итоге:
Теперь ведь правильно? Единственный вопрос: как сделать так, чтобы датчик читался ещё в начале запуска, а не только через заданный промежуток времени! Подскажите пожалуйста, что нужно изменить?
.
целая куча ошибок! Однако я все их исправил
Вы Закон Брукса знаете?
Теперь ведь правильно?
И опять откуда-то с глобального уровня в функцию падает
int
sensor.
Функция вам нужна для реентерабельности или для красоты?А поделитесь вы с другом функцией - откуда он узнает, почему компилятор ругается на необъявленную переменную sensor?
целая куча ошибок! Однако я все их исправил
Вы Закон Брукса знаете?
А поделитесь вы с другом функцией - откуда он узнает, почему компилятор ругается на необъявленную переменную sensor?
Вы не о том. Причём тут название книги?
Закон Брукса имеет прямое отношение к Вашему заявлению
целая куча ошибок! Однако я все их исправил
Он формулируется так: "Всякая найденная в программе последняя ошибка, на самом деле является предпоследней"
Запомните на всю оставшуюся жизнь.
Эту фразу приписывают многим, но Кнут говорил, что оригинально она принадлежит Бруксу.
Херня все это . Идеальная программа это программа которая кормит всех, и даже тех кто ищет ошибки в программе. И как только из программы удалят последнюю ошибку, то эту программу отправят на свалку и начнут писать новую идеальную программу ;).
Две черточки это логическое ИЛИ?
то есть !lasttime ИЛИ mill - lasttime >= delta
что означает !lasttime с этим знаком "!"? Это эквивалентно lasttime == 0;
Верно?
Две черточки это логическое ИЛИ?
то есть !lasttime ИЛИ mill - lasttime >= delta
что означает !lasttime с этим знаком "!"? Это эквивалентно lasttime == 0;
Верно?