Если в коде из #195 перенести метод write в public и добавить шесть строчек (в моём коде ниже) помечены комментарием «/*** GIFT ***/», то можно писать, например, вот так:
class Cl_aaa { //<- некий класс
public:
void go() { //<- метод класса 1
Serial.println("aaa.go()");
}
void ON() { //<- метод класса 2
Serial.println("aaa.ON()");
}
};
typedef void (Cl_aaa::*clDo)();// <- тип переменной метод класса Cl_aaa
Cl_aaa aaa;
//--------------------------
void setup() {
Serial.begin(9600);
Serial.println("Tuc");
clDo Do = &Cl_aaa::ON; //<-- создать переменую и присвоить значение метод класса
(aaa.*Do)(); //<- выполнить функцию записаную в переменной
aaa.go();
}
void loop() {
}
Организовать меню используя один потенциометр вместо энкодера не получится. Но используя потенциометр и кнопку можно организовать ввод пароля или чего-то еще.
/**/
//------дисплей lcd2004_i2c-----------------------------
#include <Wire.h>
// команды
#define LCD_CLEARDISPLAY 0x01
#define LCD_RETURNHOME 0x02
#define LCD_ENTRYMODESET 0x04
#define LCD_DISPLAYCONTROL 0x08
#define LCD_CURSORSHIFT 0x10
#define LCD_FUNCTIONSET 0x20
#define LCD_SETCGRAMADDR 0x40
#define LCD_SETDDRAMADDR 0x80
// флаги для режима ввода дисплея
#define LCD_ENTRYRIGHT 0x00
#define LCD_ENTRYLEFT 0x02
#define LCD_ENTRYSHIFTINCREMENT 0x01
#define LCD_ENTRYSHIFTDECREMENT 0x00
// флаги для управления включением / выключением дисплея
#define LCD_DISPLAYON 0x04
#define LCD_DISPLAYOFF 0x00
#define LCD_CURSORON 0x02
#define LCD_CURSOROFF 0x00
#define LCD_BLINKON 0x01
#define LCD_BLINKOFF 0x00
// флаги для отображения / сдвига курсора
#define LCD_DISPLAYMOVE 0x08
#define LCD_CURSORMOVE 0x00
#define LCD_MOVERIGHT 0x04
#define LCD_MOVELEFT 0x00
// флаги для набора функций
#define LCD_8BITMODE 0x10
#define LCD_4BITMODE 0x00
#define LCD_2LINE 0x08
#define LCD_1LINE 0x00
#define LCD_5x10DOTS 0x04
#define LCD_5x8DOTS 0x00
// флаги для управления подсветкой
#define LCD_BACKLIGHT 0x08
#define LCD_NOBACKLIGHT 0x00
#define En B00000100 // Бит разрешения
#define Rw B00000010 // Чтение / запись бит
#define Rs B00000001 // Бит выбора регистра
const byte custom[8][8] PROGMEM = {
{0, 0, 0, 0, 0, 0, 0, 0},// \0
{31, 31, 31, 0, 0, 0, 0, 0},// \1
{0, 0, 0, 31, 31, 31, 0, 0},// \2
{31, 31, 31, 31, 31, 31, 0, 0},// \3
{0, 0, 0, 0, 0, 0, 31, 31},// \4
{31, 31, 31, 0, 0, 0, 31, 31},// \5
{0, 4, 12, 31, 12, 4, 0, 0},// \6
{31, 31, 31, 31, 31, 31, 31, 31} // \7
};
class Cl_lcd2004_i2c : public Print {
protected:
uint8_t adr, posX, posY;
uint8_t _backlightval;
uint8_t _displayfunction;
uint8_t _displaycontrol;
uint8_t _displaymode;
byte buffer[80];
public:
Cl_lcd2004_i2c(uint8_t a): adr(a) {}
void init() {
Wire.begin();
write4bits(0x03 << 4);
delayMicroseconds(4500); // wait min 4.1ms
write4bits(0x03 << 4);
delayMicroseconds(4500); // wait min 4.1ms
write4bits(0x03 << 4);
delayMicroseconds(150);
write4bits(0x02 << 4);
delay(50);
_backlightval = LCD_NOBACKLIGHT;
expanderWrite(_backlightval); // сбросить расширение и выключить подсветку (бит 8 = 1)
// режим работы дисплея 4-х битный интерфейс ,две строки,5х8 точек
_displayfunction = LCD_4BITMODE | LCD_2LINE | LCD_5x8DOTS;
command(LCD_FUNCTIONSET | _displayfunction);
// режим контроля дисплей вкл, курсор не мигает,дисплей не мигает
_displaycontrol = LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF;
command(LCD_DISPLAYCONTROL | _displaycontrol);
// режим ввода текста в память-начинать слева с движением в право
_displaymode = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT;
command(LCD_ENTRYMODESET | _displaymode);
for (int i = 0; i < 8; i++)
createCharPROGMEM(i, custom[i]);
clear();
}
// подсветку вкл/выкл
void backlight(void) {
_backlightval = LCD_BACKLIGHT;
expanderWrite(0);
}
void noBacklight(void) {
_backlightval = LCD_NOBACKLIGHT;
expanderWrite(0);
}
// очистить буфер
void clear() {
for (byte i = 0; i < 80; i++ )buffer[i] = 0x20;
posX = 0; posY = 0;
}
// отправить информацию из буфера на экран
void show() {
line(0);
for (byte i = 0; i < 20; i++ )out(buffer[i]);
line(1);
for (byte i = 20; i < 40; i++ )out(buffer[i]);
line(2);
for (byte i = 40; i < 60; i++ )out(buffer[i]);
line(3);
for (byte i = 60; i < 80; i++ )out(buffer[i]);
}
void setCursor(byte x, byte y) {
if (x > 20) x = 20;
else posX = x;
if (y > 4) x = 4;
else posY = y;
}
inline size_t write(uint8_t value) {
if (posX < 20 && posY < 4) {
buffer[posY * 20 + posX] = value;
posX++;
}
return 1;
}
void createCharPROGMEM(uint8_t location, const uint8_t *charmap) {
location &= 0x7; // we only have 8 locations 0-7
command(LCD_SETCGRAMADDR | (location << 3));
for (int i = 0; i < 8; i++) {
out(pgm_read_byte_near(charmap + i));
}
}
void createChar(uint8_t location, uint8_t charmap[]) {
location &= 0x7; // we only have 8 locations 0-7
command(LCD_SETCGRAMADDR | (location << 3));
for (int i = 0; i < 8; i++) {
out(charmap[i]);
}
}
private:
/*внутрение функции*/
void line(byte l) {
switch (l) {
case 0: command(LCD_SETDDRAMADDR + 0x00);
break;
case 1: command(LCD_SETDDRAMADDR + 0x40);
break;
case 2: command(LCD_SETDDRAMADDR + 0x14);
break;
case 3: command(LCD_SETDDRAMADDR + 0x54);
break;
}
}
inline void out(uint8_t value) {
send(value, Rs);
}
/*команды низкоуровневого управления*/
inline void command(uint8_t value) {
send(value, 0);
}
void send(uint8_t value, uint8_t mode) {
uint8_t highnib = value & 0xf0;
write4bits((highnib) | mode);
uint8_t lownib = (value << 4) & 0xf0;
write4bits((lownib) | mode);
}
void write4bits(uint8_t value) {
expanderWrite(value);
pulseEnable(value);
}
void expanderWrite(uint8_t _data) {
Wire.beginTransmission(adr);
Wire.write((int)(_data) | _backlightval);
Wire.endTransmission();
}
void pulseEnable(uint8_t _data) {
expanderWrite(_data | En); // En high
delayMicroseconds(1); // enable pulse must be >450ns
expanderWrite(_data & ~En); // En low
delayMicroseconds(50); // commands need > 37us to settle
}
};
typedef void (Cl_lcd2004_i2c::*clDo)(); // <- тип переменной метод класса Cl_lcd2004_i2c
inline Cl_lcd2004_i2c & operator <<(Cl_lcd2004_i2c &s, clDo Do) {
(s.*Do)();
return s;
}
template <typename T> inline Cl_lcd2004_i2c & operator << (Cl_lcd2004_i2c &s, T n) {
s.print(n);
return s;
}
Cl_lcd2004_i2c lcd(0x3F);//0x27
//--------------------------------------------------
class Cl_aaa {
protected:
const byte pin;
unsigned long past;
void read() {
int b = analogRead(pin);
if (b < 90)value = '0';
else if (b < 180)value = '1';
else if (b < 270)value = '2';
else if (b < 360)value = '3';
else if (b < 450)value = '4';
else if (b < 540)value = '5';
else if (b < 630)value = '6';
else if (b < 720)value = '7';
else if (b < 810)value = '8';
else if (b < 900)value = '9';
else value = '\6';
past = millis();
}
public:
char value;
Cl_aaa(byte p): pin(p) {}
void init() {
read();
}
void run() {
if (millis() - past >= 200) read();
}
};
Cl_aaa aaa(/*пин*/A0);
//------- кнопки ---------------------------
typedef void (*pDo)();
class Cl_Btn {
protected:
byte pin;
bool state;
unsigned long past;
void set(bool s) {
state = s;
past = millis();
if (s) Do();
}
public:
Cl_Btn(byte p): pin(p) {}
pDo Do = [] {};
void init() {
pinMode(pin, INPUT_PULLUP);
set(false);
}
void run() {
if (millis() - past >= 100)
switch (state) {
case false:
if (!digitalRead(pin))set(true);
break;
case true:
if (digitalRead(pin))set(false);
//if (millis() - past >= 300)set(false);
break;
}
}
};
Cl_Btn BtnS(/*пин*/2);
//-----------------------------------------------
const byte page0 = 0;
const byte page1a = 10;
const byte page1b = 11;
byte page;
unsigned long past;
String str("ABC");
void goPage(byte p) {
page = p;
past = millis();
switch (page) {
case page0:
break;
case page1a:
case page1b:
{ lcd << &Cl_lcd2004_i2c::clear << str ;
if (page == page1a) lcd << aaa.value;
else lcd << '\7';
lcd << &Cl_lcd2004_i2c::show; // вывод сообщения на экран
}
BtnS.Do = [] {
switch (aaa.value) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
str += aaa.value;
break;
case '\6':
str = "";
break;
}
}; // конец лямда
break;//page
}
}
void menu_init() {
goPage( page1a);
}
void menu_run() {
if (page == page1a && millis() - past >= 300)goPage( page1b);
if (page == page1b && millis() - past >= 300)goPage( page1a);
}
//-----------------------------------------------
void setup() {
lcd << &Cl_lcd2004_i2c::init << &Cl_lcd2004_i2c::backlight;// инициализация
aaa.init();
BtnS.init();
menu_init();
}
void loop() {
aaa.run();
BtnS.run();
menu_run();
}
Не правда ли что метод Do похож на виртуальную функцию. Но фишка не в этом. Вот к примеру пишем мы графическую библиотеку. И для установки пикселя в байте в зависимости от цвета нужно производить или логическое сложение или логическое умножение. А если много то этот анализ проводить для каждого пикселя. А этим способом мы можем банально прописать нужный "карандаш" однократно и дальше не заморачиваться в попиксельной работе.
А этим способом мы можем банально прописать нужный "карандаш" однократно и дальше не заморачиваться в попиксельной работе.
А лямбды-то зачем? Чем не угодил обычный указатель на функцию?
Я вот смотрю на Ваши лямбды и никак не возьму в толк для чего Вы их тянете туда, где достаточно обычного указателя? Это примерно как ставить микроконтроллер для того, чтобы сделать плавное выключение лампочки - работать, конечно, будет, но ведь там достаточно конденсатора!
Главная фишка лямбд в том, что они тянут за собой внешний контекст и являются функциями высшего порядка (вернее с их помощью такие функции строятся - та же свёртка). Вы же никогда этого (зазвата внешнего контекста) не используете, функций высших порядков не строите, а без этого лямбда не даёт никаких преимуществ по сравнению с обычной, именованной функцией и указателем на неё. Ну, вообще никаких.
Есть глобальные переменные,есть локальные, есть даже статик переменные. Но в Си есть глобальные функции, а вот с локальными или статик функциями не очень . Но есть некий эрзац . Лямда это один из вариантов эрзаца.
Ну, не знаю, освойте функторы - те же локальные функции, но с ними можно и как с переменными работать (параметром передавать, операции выполнять). Лямбда собственно тоже функтор, но она ещё много чего - это огромная пушка для воробья - локальной функции.
О. Хочу спросить уважаемого ЕвгенияП. Если у меня одна функция в качестве параметров требует указатель на другую функцию, чтоб её потом вызвать, а та функция короткая в одну строку, можно ли передать её лямбдой, или это тоже из пушки пабабачкам?
Главная фишка лямбд в том, что они тянут за собой внешний контекст
правильно называется "замыкание" и это действительно одна из двух главных фишек лямбда-выражений в этом языке. Другая - то, что они (лямбда-выражения) являются функторами. Без этих двух фишек (а ты их не используешь) лямбда-выражения теряют смысл и не дают никаких выгод по сравнению с указателями на функции.
(Хотя, многие говорят, что это на самом деле одна фишка, а не две - первая. т.к. замыкание, по определению, функция первого класса, стало быть замыкание можно рассматривать как функтор)
требует указатель на другую функцию, чтоб её потом вызвать, а та функция короткая в одну строку, можно ли передать её лямбдой, или это тоже из пушки пабабачкам?
Делай как тебе удобно и не парься. С точки зрения кода особой разницы нет, как уже писали и я, и петрович, если не использовать замыкания, то лямбда ничем особым не отличается от указателя на функцию.
Просто лямбды - гораздо более мощный механизм, чем указатели на функции, а раз Пух явно интересуется, хочется помочь ему освоить его на более глубоком уровне, а то сейчас он похоже, просто пихает лямбды ради того, чтобы пихать лямбды.
Делай как тебе удобно и не парься. С точки зрения кода особой разницы нет, как уже писали и я, и петрович, если не использовать замыкания, то лямбда ничем особым не отличается от указателя на функцию.
Просто лямбды - гораздо более мощный механизм, чем указатели на функции, а раз Пух явно интересуется, хочется помочь ему освоить его на более глубоком уровне, а то сейчас он похоже, просто пихает лямбды ради того, чтобы пихать лямбды.
Мда. Пора читать, полупьяный щуря глаз, про функторы и замыкания. А то так дураком и помру.
Дед, это штучки из функционального программирования, оно те правда надо?
А если реально хочется поиграться всякими лямбдами, замыканиями и прочими свёртками, то уж лучше делать это не на С++, где они притянуты через задницу, а на каком-нибудь языке, где они естественны. Например, возьми Haskell. Классный язык и там всё это естественно и натурально, он собственно для этого и создавался. Особенно мне нравится как в википедии определили его (языка) класс: "Класс языка: функциональный, ленивый, модульный". Как видишь, что-то родное в нём есть :)
Делай как тебе удобно и не парься. С точки зрения кода особой разницы нет, как уже писали и я, и петрович, если не использовать замыкания, то лямбда ничем особым не отличается от указателя на функцию.
Просто лямбды - гораздо более мощный механизм, чем указатели на функции, а раз Пух явно интересуется, хочется помочь ему освоить его на более глубоком уровне, а то сейчас он похоже, просто пихает лямбды ради того, чтобы пихать лямбды.
Мда. Пора читать, полупьяный щуря глаз, про функторы и замыкания. А то так дураком и помру.
Не торописЯ. Это новодел в плюсах. Надо обождать лет десять пока устаканится, концепция выпрямится и компиляторы допиляют до какогото единообразия в понимании стандарта. Я еще в 1.6.5 попробовал, наплевался и расслабился. Так шо наливай. Без замыкания 30 лет писали и ниче. Конечно лямдами нужно пользоваться т.к. менше имен придумывать. Главное внутр [] ничего не пиши! Вон как квон делает: классы, но без наследования, лямбды, но без замыкания. У него и вотка без спирту!
Продолжу. Вот к примеру надо написать библиотеку под lcd5110.(Знаю написана). Но как там решается проблема различия кода на аппаратном SPI и программном. Но ведь можно решить так. И нет плясок с бубном.
Отстаньте от него, у него свое виденье. Он же четко указал, обращать внимание на строку 10. Наверно const приветствовать аплодисментами. В остальное селедку заворачивал, index зарезервировал для будущих применений.
Поясните, пожалуйста? Почему надо на это обращать внимание? Потому, что сделано так, чтобы могло быть только в правой части присваивания? Чтобы нельзя было экземпляру присвоить значение как обычному bool? Это-то я понял. А вот из каких соображений это было сделано, поясните пожалуйста.
удастся заслушать начальника транспортного цеха? (энкодер), или как говорила экономист, когда мы автоматизировали одно предприятие, моя работа не поддаётся автоматизации )))
Мне энкодер как устройство регулировки давно перестало нравится. Глючное устройство. Где-то в моих темах валяется этот компонент. Так что заменить кнопки энкодером не трудно для разумного человека.
Продолжу. Если же надо к примеру использовать 10 однородных устройств и 3 комплектами настроек.
/**/
#include "qwonelib.h"
//-------------------
struct data_t { // некая пользовательская структура
byte bData;
int iData;
float fData;
};
byte index = 0;
const byte max_index = 10;
data_t EEMEM adr1[max_index]; // размещение ее в EEPROM
byte setting_index = 0;
const byte max_setting_index = 3;
void setting(byte in) {
static const PROGMEM data_t adr2[3] = { // набор констант для пользовательской функции
{100, 1000, 10.10} // 0 комплект настроек
, {101, 1001, 10.11} // 1 комплект настроек
, {102, 1002, 10.12} // 2 комплект настроек
};
(eeRef <byte>) (&adr1[in].bData) = (pgmRef<byte>) (&adr2[setting_index].bData); // загрузка байтовой части
(eeRef <int>) (&adr1[in].iData) = (pgmRef<int>) (&adr2[setting_index].iData); // загрузка интовой части
(eeRef <float>)(&adr1[in].fData) = (pgmRef<float>)(&adr2[setting_index].fData); // загрузка флоат части
}
//---кнопки-----------------------------
typedef void (*pDo)();
class Cl_btn {
protected:
byte pin;
bool state;
unsigned long past;
void set(bool s) {
state = s;
past = millis();
if (s == true) Do();
}
public:
Cl_btn(byte p): pin(p) {}
pDo Do = [] {};
void init() {
pinMode(pin, INPUT_PULLUP);
set(false);
}
void run() {
if (millis() - past >= 100)
switch (state) {
case false:
if (!digitalRead(pin))set(true);
break;
case true:
if (digitalRead(pin))set(false);
if (millis() - past >= 300)set(false);
break;
}
}
};
Cl_btn BtnS(/*пин*/2); //кнопка селект
Cl_btn BtnU(/*пин*/3); //кнопка верх
Cl_btn BtnD(/*пин*/4); //кнопка вниз
//--------------- меню---------------------------------------------
template <typename T> inline Print & operator << (Print &s, T n) {
s.print(n);
return s;
}
const byte page0 = 0; //ноказ настроек
const byte page0a = 5;// поменять индекс устройства
const byte page1 = 10;//изменение байтовой части
const byte page2 = 20;//изменение интовой части
const byte page3 = 30;//изменение флоат части
const byte page4 = 40; //сброс настроек
byte page;
unsigned long past;
void goPage(byte p) {
past = millis();
page = p;
switch (page) {
case page0:
BtnS.Do = [] {goPage(page0a);};
BtnU.Do = [] {};
BtnD.Do = [] {};
Serial << "info:\n";
Serial << "byte(" << index << ") =" << (eeRef <byte>) (&adr1[index].bData) << '\n'; // вывод байтовой части
Serial << "int(" << index << ") =" << (eeRef <int>) (&adr1[index].iData) << '\n'; // вывод интовой части
Serial << "float(" << index << ")=" << (eeRef <float>)(&adr1[index].fData) << '\n'; // вывод флоат части
break;
case page0a:
BtnS.Do = [] {goPage(page1);};
BtnU.Do = [] {
if (index < (max_index - 1))index++;
goPage(page0a);
};
BtnD.Do = [] {
if (index > 0)index--;
goPage(page0a);
};
Serial << "set index:" << index << "\n";
break;
case page1:
BtnS.Do = [] {goPage(page2);};
BtnU.Do = [] {
if ((eeRef <byte>) (&adr1[index].bData) <= 200)(eeRef <byte>) (&adr1[index].bData) += 10;
goPage(page1);
};
BtnD.Do = [] {
if ((eeRef <byte>) (&adr1[index].bData) >= 50)(eeRef <byte>) (&adr1[index].bData) -= 10;
goPage(page1);
};
Serial << "set byte(" << index << ")=";
Serial << (eeRef <byte>) (&adr1[index].bData) << '\n'; // вывод байтовой части
break;
case page2:
BtnS.Do = [] {goPage(page3);};
BtnU.Do = [] {
if ((eeRef <int>) (&adr1[index].iData) <= 2000)(eeRef <int>) (&adr1[index].iData) += 100;
goPage(page2);
};
BtnD.Do = [] {
if ((eeRef <int>) (&adr1[index].iData) >= 500)(eeRef <int>) (&adr1[index].iData) -= 100;
goPage(page2);
};
Serial << "set int(" << index << ")=";
Serial << (eeRef <int>) (&adr1[index].iData) << '\n'; // вывод интовой части
break;
case page3:
BtnS.Do = [] {goPage(page4);};
BtnU.Do = [] {
if ((eeRef <float>) (&adr1[index].fData) <= 20.30)(eeRef <float>) (&adr1[index].fData) += 1.01;
goPage(page3);
};
BtnD.Do = [] {
if ((eeRef <float>) (&adr1[index].fData) >= 5.05)(eeRef <float>) (&adr1[index].fData) -= 1.01;
goPage(page3);
};
Serial << "set float(" << index << ")=";
Serial << (eeRef <float>)(&adr1[index].fData) << '\n'; // вывод флоат части
break;
case page4:
BtnS.Do = [] {goPage(page0);};
BtnU.Do = [] {
if (setting_index < (max_setting_index - 1))setting_index++;
setting(index);
goPage(page4);
};
BtnD.Do = [] {
if (setting_index > 0)setting_index--;
setting(index);
goPage(page4);
};
Serial << "setting(" << setting_index << ")\n";
Serial << "byte(" << index << ") =" << (eeRef <byte>) (&adr1[index].bData) << '\n'; // вывод байтовой части
Serial << "int(" << index << ") =" << (eeRef <int>) (&adr1[index].iData) << '\n'; // вывод интовой части
Serial << "float(" << index << ")=" << (eeRef <float>)(&adr1[index].fData) << '\n'; // вывод флоат части
break;
}
}
void menu_init() {
Serial.begin(9600);
goPage(page0);
}
void menu_run() {}
//----------------------------------------
void setup() {
BtnS.init();
BtnU.init();
BtnD.init();
menu_init();
}
void loop() {
BtnS.run();
BtnU.run();
BtnD.run();
menu_run();
}
Мне энкодер как устройство регулировки давно перестало нравится. Глючное устройство.
перебирал библиотеки от разных авторов, даже на библиотеку Гайвера набрёл, нашёл ту, что подошла идеально, автор пишет, если ваш энкодер даёт глюки на моей библиотеке, то он настолько плох и раздолбан, что просто его выкиньте, эксперименты провожу с энкодером в чистом виде, ни резисторов ни конденсаторов в обвязке нет, пины вверх не притягиваю, видимо всё реализовано в библиотеке, твой код кнопок использую )))
Все верно, не знаю что за либа, но знаю что при правильной реализации все ок. И категорически, не при каких обстоятельствах, не вешать кондеры. С ними какбы дааже работает сразу лучше, но то только сразу. Они быстро ушатают контакт до состояния мусора. Их разряды окисляют и без того не самые стойкие контакты. После этого останется только снимать осцилограммы и публиковать их тут с нытьем "ну как с таким работать".
Все верно, не знаю что за либа, но знаю что при правильной реализации все ок. И категорически, не при каких обстоятельствах, не вешать кондеры. С ними какбы дааже работает сразу лучше, но то только сразу. Они быстро ушатают контакт до состояния мусора. Их разряды окисляют и без того не самые стойкие контакты. После этого останется только снимать осцилограммы и публиковать их тут с нытьем "ну как с таким работать".
У меня на YAESU FT-897 куча энкодеров, 20 лет полёт нормальный )))
Так отож. Китайские энкодеры пашут везде нормально, даже у самих китайцев, даже в автомагнитолах в суровых условиях. Везде кроме многих участников этого форума ))
Сброшу мои наметки для работы EEROMом
В ней работа без классов! Ну как так можна! Да еще в такой теме.
Исправте немедленно!
Пух, держи подарок!
Если в коде из #195 перенести метод write в public и добавить шесть строчек (в моём коде ниже) помечены комментарием «/*** GIFT ***/», то можно писать, например, вот так:
int temp = 366; printf ("TEMP:%3d,%dC", temp / 10, temp % 10);По-моему удобно. Дарю!
Вот код. Он, собственно твой, я только перенёс функцию и вставил шесть строк:
/**/ template <typename T> inline Print & operator << (Print &s, T n) { s.print(n); return s; } //------дисплей lcd2004_i2c----------------------------- // даташит <"<a href="https://www.sparkfun.com/datasheets/LCD/ADM1602K-NSW-FBS-3.3v.pdf" rel="nofollow">https://www.sparkfun.com/datasheets/LCD/ADM1602K-NSW-FBS-3.3v.pdf</a>"><a href="https://www.sparkfun.com/datasheets/LCD/HD44780.pdf" rel="nofollow">https://www.sparkfun.com/datasheets/LCD/HD44780.pdf</a> // на русском <"<a href="http://www.melt.aha.ru/pdf/mt-16s2h.pdf" rel="nofollow">http://www.melt.aha.ru/pdf/mt-16s2h.pdf</a>"> #include <Wire.h> // команды #define LCD_CLEARDISPLAY 0x01 #define LCD_RETURNHOME 0x02 #define LCD_ENTRYMODESET 0x04 #define LCD_DISPLAYCONTROL 0x08 #define LCD_CURSORSHIFT 0x10 #define LCD_FUNCTIONSET 0x20 #define LCD_SETCGRAMADDR 0x40 #define LCD_SETDDRAMADDR 0x80 // флаги для режима ввода дисплея #define LCD_ENTRYRIGHT 0x00 #define LCD_ENTRYLEFT 0x02 #define LCD_ENTRYSHIFTINCREMENT 0x01 #define LCD_ENTRYSHIFTDECREMENT 0x00 // флаги для управления включением / выключением дисплея #define LCD_DISPLAYON 0x04 #define LCD_DISPLAYOFF 0x00 #define LCD_CURSORON 0x02 #define LCD_CURSOROFF 0x00 #define LCD_BLINKON 0x01 #define LCD_BLINKOFF 0x00 // флаги для отображения / сдвига курсора #define LCD_DISPLAYMOVE 0x08 #define LCD_CURSORMOVE 0x00 #define LCD_MOVERIGHT 0x04 #define LCD_MOVELEFT 0x00 // флаги для набора функций #define LCD_8BITMODE 0x10 #define LCD_4BITMODE 0x00 #define LCD_2LINE 0x08 #define LCD_1LINE 0x00 #define LCD_5x10DOTS 0x04 #define LCD_5x8DOTS 0x00 // флаги для управления подсветкой #define LCD_BACKLIGHT 0x08 #define LCD_NOBACKLIGHT 0x00 #define En B00000100 // Бит разрешения #define Rw B00000010 // Чтение / запись бит #define Rs B00000001 // Бит выбора регистра class Cl_lcd1602_i2c : public Print { protected: uint8_t adr, posX, posY; uint8_t _backlightval; uint8_t _displayfunction; uint8_t _displaycontrol; uint8_t _displaymode; byte buffer[80]; public: Cl_lcd1602_i2c(uint8_t a): adr(a) {} void init() { Wire.begin(); write4bits(0x03 << 4); delayMicroseconds(4500); // wait min 4.1ms write4bits(0x03 << 4); delayMicroseconds(4500); // wait min 4.1ms write4bits(0x03 << 4); delayMicroseconds(150); write4bits(0x02 << 4); delay(50); _backlightval = LCD_NOBACKLIGHT; expanderWrite(_backlightval); // сбросить расширение и выключить подсветку (бит 8 = 1) // режим работы дисплея 4-х битный интерфейс ,две строки,5х8 точек _displayfunction = LCD_4BITMODE | LCD_2LINE | LCD_5x8DOTS; command(LCD_FUNCTIONSET | _displayfunction); // режим контроля дисплей вкл, курсор не мигает,дисплей не мигает _displaycontrol = LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF; command(LCD_DISPLAYCONTROL | _displaycontrol); // режим ввода текста в память-начинать слева с движением в право _displaymode = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT; command(LCD_ENTRYMODESET | _displaymode); clear(); } // подсветку вкл/выкл void backlight(void) { _backlightval = LCD_BACKLIGHT; expanderWrite(0); } void noBacklight(void) { _backlightval = LCD_NOBACKLIGHT; expanderWrite(0); } // очистить буфер void clear() { for (byte i = 0; i < 80; i++ )buffer[i] = 0x20; posX = 0; posY = 0; } // отправить информацию из буфера на экран void show() { line(0); for (byte i = 0; i < 20; i++ )out(buffer[i]); line(1); for (byte i = 20; i < 40; i++ )out(buffer[i]); line(2); for (byte i = 40; i < 60; i++ )out(buffer[i]); line(3); for (byte i = 60; i < 80; i++ )out(buffer[i]); } void setCursor(byte x, byte y) { if (x > 20) x = 20; else posX = x; if (y > 4) x = 4; else posY = y; } inline size_t write(uint8_t value) { if (posX < 20 && posY < 4) { buffer[posY * 20 + posX] = value; posX++; } return 1; } private: /*внутрение функции*/ void line(byte l) { switch (l) { case 0: command(LCD_SETDDRAMADDR + 0x00); break; case 1: command(LCD_SETDDRAMADDR + 0x40); break; case 2: command(LCD_SETDDRAMADDR + 0x14); break; case 3: command(LCD_SETDDRAMADDR + 0x54); break; } } inline void out(uint8_t value) { send(value, Rs); } /*команды низкоуровневого управления*/ inline void command(uint8_t value) { send(value, 0); } void send(uint8_t value, uint8_t mode) { uint8_t highnib = value & 0xf0; write4bits((highnib) | mode); uint8_t lownib = (value << 4) & 0xf0; write4bits((lownib) | mode); } void write4bits(uint8_t value) { expanderWrite(value); pulseEnable(value); } void expanderWrite(uint8_t _data) { Wire.beginTransmission(adr); Wire.write((int)(_data) | _backlightval); Wire.endTransmission(); } void pulseEnable(uint8_t _data) { expanderWrite(_data | En); // En high delayMicroseconds(1); // enable pulse must be >450ns expanderWrite(_data & ~En); // En low delayMicroseconds(50); // commands need > 37us to settle } }; Cl_lcd1602_i2c lcd(0x3F);//0x27 //-------------------------------------------------- static int lcd_fputchar(const char ch, FILE *stream) { /*** GIFT ***/ lcd.write(ch); /*** GIFT ***/ return 1; /*** GIFT ***/ } /*** GIFT ***/ static FILE *lcd_stream = fdevopen(lcd_fputchar, NULL); /*** GIFT ***/ void setup() { stdout = lcd_stream; /*** GIFT ***/ lcd.init(); lcd.backlight(); lcd.clear(); lcd.setCursor(0, 0); int temp = 366; printf("TEMP:%3d,%dC", temp / 10, temp % 10); lcd.show(); } void loop() { }Опа! Спасибо за плюсик. Пусть кто хочет, думает, что эти плюсики ни на что не влияют, а мне приятно, что ты поблагодарил. Спасибо, Пух.
работа с указателями на методы класса
class Cl_aaa { //<- некий класс public: void go() { //<- метод класса 1 Serial.println("aaa.go()"); } void ON() { //<- метод класса 2 Serial.println("aaa.ON()"); } }; typedef void (Cl_aaa::*clDo)();// <- тип переменной метод класса Cl_aaa Cl_aaa aaa; //-------------------------- void setup() { Serial.begin(9600); Serial.println("Tuc"); clDo Do = &Cl_aaa::ON; //<-- создать переменую и присвоить значение метод класса (aaa.*Do)(); //<- выполнить функцию записаную в переменной aaa.go(); } void loop() { }/**/ //------дисплей lcd2004_i2c----------------------------- #include <Wire.h> // команды #define LCD_CLEARDISPLAY 0x01 #define LCD_RETURNHOME 0x02 #define LCD_ENTRYMODESET 0x04 #define LCD_DISPLAYCONTROL 0x08 #define LCD_CURSORSHIFT 0x10 #define LCD_FUNCTIONSET 0x20 #define LCD_SETCGRAMADDR 0x40 #define LCD_SETDDRAMADDR 0x80 // флаги для режима ввода дисплея #define LCD_ENTRYRIGHT 0x00 #define LCD_ENTRYLEFT 0x02 #define LCD_ENTRYSHIFTINCREMENT 0x01 #define LCD_ENTRYSHIFTDECREMENT 0x00 // флаги для управления включением / выключением дисплея #define LCD_DISPLAYON 0x04 #define LCD_DISPLAYOFF 0x00 #define LCD_CURSORON 0x02 #define LCD_CURSOROFF 0x00 #define LCD_BLINKON 0x01 #define LCD_BLINKOFF 0x00 // флаги для отображения / сдвига курсора #define LCD_DISPLAYMOVE 0x08 #define LCD_CURSORMOVE 0x00 #define LCD_MOVERIGHT 0x04 #define LCD_MOVELEFT 0x00 // флаги для набора функций #define LCD_8BITMODE 0x10 #define LCD_4BITMODE 0x00 #define LCD_2LINE 0x08 #define LCD_1LINE 0x00 #define LCD_5x10DOTS 0x04 #define LCD_5x8DOTS 0x00 // флаги для управления подсветкой #define LCD_BACKLIGHT 0x08 #define LCD_NOBACKLIGHT 0x00 #define En B00000100 // Бит разрешения #define Rw B00000010 // Чтение / запись бит #define Rs B00000001 // Бит выбора регистра class Cl_lcd2004_i2c : public Print { protected: uint8_t adr, posX, posY; uint8_t _backlightval; uint8_t _displayfunction; uint8_t _displaycontrol; uint8_t _displaymode; byte buffer[80]; public: Cl_lcd2004_i2c(uint8_t a): adr(a) {} void init() { Wire.begin(); write4bits(0x03 << 4); delayMicroseconds(4500); // wait min 4.1ms write4bits(0x03 << 4); delayMicroseconds(4500); // wait min 4.1ms write4bits(0x03 << 4); delayMicroseconds(150); write4bits(0x02 << 4); delay(50); _backlightval = LCD_NOBACKLIGHT; expanderWrite(_backlightval); // сбросить расширение и выключить подсветку (бит 8 = 1) // режим работы дисплея 4-х битный интерфейс ,две строки,5х8 точек _displayfunction = LCD_4BITMODE | LCD_2LINE | LCD_5x8DOTS; command(LCD_FUNCTIONSET | _displayfunction); // режим контроля дисплей вкл, курсор не мигает,дисплей не мигает _displaycontrol = LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF; command(LCD_DISPLAYCONTROL | _displaycontrol); // режим ввода текста в память-начинать слева с движением в право _displaymode = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT; command(LCD_ENTRYMODESET | _displaymode); clear(); } // подсветку вкл/выкл void backlight(void) { _backlightval = LCD_BACKLIGHT; expanderWrite(0); } void noBacklight(void) { _backlightval = LCD_NOBACKLIGHT; expanderWrite(0); } // очистить буфер void clear() { for (byte i = 0; i < 80; i++ )buffer[i] = 0x20; posX = 0; posY = 0; } // отправить информацию из буфера на экран void show() { line(0); for (byte i = 0; i < 20; i++ )out(buffer[i]); line(1); for (byte i = 20; i < 40; i++ )out(buffer[i]); line(2); for (byte i = 40; i < 60; i++ )out(buffer[i]); line(3); for (byte i = 60; i < 80; i++ )out(buffer[i]); } void setCursor(byte x, byte y) { if (x > 20) x = 20; else posX = x; if (y > 4) x = 4; else posY = y; } inline size_t write(uint8_t value) { if (posX < 20 && posY < 4) { buffer[posY * 20 + posX] = value; posX++; } return 1; } private: /*внутрение функции*/ void line(byte l) { switch (l) { case 0: command(LCD_SETDDRAMADDR + 0x00); break; case 1: command(LCD_SETDDRAMADDR + 0x40); break; case 2: command(LCD_SETDDRAMADDR + 0x14); break; case 3: command(LCD_SETDDRAMADDR + 0x54); break; } } inline void out(uint8_t value) { send(value, Rs); } /*команды низкоуровневого управления*/ inline void command(uint8_t value) { send(value, 0); } void send(uint8_t value, uint8_t mode) { uint8_t highnib = value & 0xf0; write4bits((highnib) | mode); uint8_t lownib = (value << 4) & 0xf0; write4bits((lownib) | mode); } void write4bits(uint8_t value) { expanderWrite(value); pulseEnable(value); } void expanderWrite(uint8_t _data) { Wire.beginTransmission(adr); Wire.write((int)(_data) | _backlightval); Wire.endTransmission(); } void pulseEnable(uint8_t _data) { expanderWrite(_data | En); // En high delayMicroseconds(1); // enable pulse must be >450ns expanderWrite(_data & ~En); // En low delayMicroseconds(50); // commands need > 37us to settle } }; typedef void (Cl_lcd2004_i2c::*clDo)(); // <- тип переменной метод класса Cl_lcd2004_i2c inline Cl_lcd2004_i2c & operator <<(Cl_lcd2004_i2c &s, clDo Do) { (s.*Do)(); return s; } template <typename T> inline Cl_lcd2004_i2c & operator << (Cl_lcd2004_i2c &s, T n) { s.print(n); return s; } Cl_lcd2004_i2c lcd(0x3F);//0x27 //-------------------------------------------------- void setup() { lcd << &Cl_lcd2004_i2c::init << &Cl_lcd2004_i2c::backlight;// инициализация lcd << &Cl_lcd2004_i2c::clear << (int)366 << &Cl_lcd2004_i2c::show; // вывод сообщения на экран } void loop() { }Организовать меню используя один потенциометр вместо энкодера не получится. Но используя потенциометр и кнопку можно организовать ввод пароля или чего-то еще.
/**/ //------дисплей lcd2004_i2c----------------------------- #include <Wire.h> // команды #define LCD_CLEARDISPLAY 0x01 #define LCD_RETURNHOME 0x02 #define LCD_ENTRYMODESET 0x04 #define LCD_DISPLAYCONTROL 0x08 #define LCD_CURSORSHIFT 0x10 #define LCD_FUNCTIONSET 0x20 #define LCD_SETCGRAMADDR 0x40 #define LCD_SETDDRAMADDR 0x80 // флаги для режима ввода дисплея #define LCD_ENTRYRIGHT 0x00 #define LCD_ENTRYLEFT 0x02 #define LCD_ENTRYSHIFTINCREMENT 0x01 #define LCD_ENTRYSHIFTDECREMENT 0x00 // флаги для управления включением / выключением дисплея #define LCD_DISPLAYON 0x04 #define LCD_DISPLAYOFF 0x00 #define LCD_CURSORON 0x02 #define LCD_CURSOROFF 0x00 #define LCD_BLINKON 0x01 #define LCD_BLINKOFF 0x00 // флаги для отображения / сдвига курсора #define LCD_DISPLAYMOVE 0x08 #define LCD_CURSORMOVE 0x00 #define LCD_MOVERIGHT 0x04 #define LCD_MOVELEFT 0x00 // флаги для набора функций #define LCD_8BITMODE 0x10 #define LCD_4BITMODE 0x00 #define LCD_2LINE 0x08 #define LCD_1LINE 0x00 #define LCD_5x10DOTS 0x04 #define LCD_5x8DOTS 0x00 // флаги для управления подсветкой #define LCD_BACKLIGHT 0x08 #define LCD_NOBACKLIGHT 0x00 #define En B00000100 // Бит разрешения #define Rw B00000010 // Чтение / запись бит #define Rs B00000001 // Бит выбора регистра const byte custom[8][8] PROGMEM = { {0, 0, 0, 0, 0, 0, 0, 0},// \0 {31, 31, 31, 0, 0, 0, 0, 0},// \1 {0, 0, 0, 31, 31, 31, 0, 0},// \2 {31, 31, 31, 31, 31, 31, 0, 0},// \3 {0, 0, 0, 0, 0, 0, 31, 31},// \4 {31, 31, 31, 0, 0, 0, 31, 31},// \5 {0, 4, 12, 31, 12, 4, 0, 0},// \6 {31, 31, 31, 31, 31, 31, 31, 31} // \7 }; class Cl_lcd2004_i2c : public Print { protected: uint8_t adr, posX, posY; uint8_t _backlightval; uint8_t _displayfunction; uint8_t _displaycontrol; uint8_t _displaymode; byte buffer[80]; public: Cl_lcd2004_i2c(uint8_t a): adr(a) {} void init() { Wire.begin(); write4bits(0x03 << 4); delayMicroseconds(4500); // wait min 4.1ms write4bits(0x03 << 4); delayMicroseconds(4500); // wait min 4.1ms write4bits(0x03 << 4); delayMicroseconds(150); write4bits(0x02 << 4); delay(50); _backlightval = LCD_NOBACKLIGHT; expanderWrite(_backlightval); // сбросить расширение и выключить подсветку (бит 8 = 1) // режим работы дисплея 4-х битный интерфейс ,две строки,5х8 точек _displayfunction = LCD_4BITMODE | LCD_2LINE | LCD_5x8DOTS; command(LCD_FUNCTIONSET | _displayfunction); // режим контроля дисплей вкл, курсор не мигает,дисплей не мигает _displaycontrol = LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF; command(LCD_DISPLAYCONTROL | _displaycontrol); // режим ввода текста в память-начинать слева с движением в право _displaymode = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT; command(LCD_ENTRYMODESET | _displaymode); for (int i = 0; i < 8; i++) createCharPROGMEM(i, custom[i]); clear(); } // подсветку вкл/выкл void backlight(void) { _backlightval = LCD_BACKLIGHT; expanderWrite(0); } void noBacklight(void) { _backlightval = LCD_NOBACKLIGHT; expanderWrite(0); } // очистить буфер void clear() { for (byte i = 0; i < 80; i++ )buffer[i] = 0x20; posX = 0; posY = 0; } // отправить информацию из буфера на экран void show() { line(0); for (byte i = 0; i < 20; i++ )out(buffer[i]); line(1); for (byte i = 20; i < 40; i++ )out(buffer[i]); line(2); for (byte i = 40; i < 60; i++ )out(buffer[i]); line(3); for (byte i = 60; i < 80; i++ )out(buffer[i]); } void setCursor(byte x, byte y) { if (x > 20) x = 20; else posX = x; if (y > 4) x = 4; else posY = y; } inline size_t write(uint8_t value) { if (posX < 20 && posY < 4) { buffer[posY * 20 + posX] = value; posX++; } return 1; } void createCharPROGMEM(uint8_t location, const uint8_t *charmap) { location &= 0x7; // we only have 8 locations 0-7 command(LCD_SETCGRAMADDR | (location << 3)); for (int i = 0; i < 8; i++) { out(pgm_read_byte_near(charmap + i)); } } void createChar(uint8_t location, uint8_t charmap[]) { location &= 0x7; // we only have 8 locations 0-7 command(LCD_SETCGRAMADDR | (location << 3)); for (int i = 0; i < 8; i++) { out(charmap[i]); } } private: /*внутрение функции*/ void line(byte l) { switch (l) { case 0: command(LCD_SETDDRAMADDR + 0x00); break; case 1: command(LCD_SETDDRAMADDR + 0x40); break; case 2: command(LCD_SETDDRAMADDR + 0x14); break; case 3: command(LCD_SETDDRAMADDR + 0x54); break; } } inline void out(uint8_t value) { send(value, Rs); } /*команды низкоуровневого управления*/ inline void command(uint8_t value) { send(value, 0); } void send(uint8_t value, uint8_t mode) { uint8_t highnib = value & 0xf0; write4bits((highnib) | mode); uint8_t lownib = (value << 4) & 0xf0; write4bits((lownib) | mode); } void write4bits(uint8_t value) { expanderWrite(value); pulseEnable(value); } void expanderWrite(uint8_t _data) { Wire.beginTransmission(adr); Wire.write((int)(_data) | _backlightval); Wire.endTransmission(); } void pulseEnable(uint8_t _data) { expanderWrite(_data | En); // En high delayMicroseconds(1); // enable pulse must be >450ns expanderWrite(_data & ~En); // En low delayMicroseconds(50); // commands need > 37us to settle } }; typedef void (Cl_lcd2004_i2c::*clDo)(); // <- тип переменной метод класса Cl_lcd2004_i2c inline Cl_lcd2004_i2c & operator <<(Cl_lcd2004_i2c &s, clDo Do) { (s.*Do)(); return s; } template <typename T> inline Cl_lcd2004_i2c & operator << (Cl_lcd2004_i2c &s, T n) { s.print(n); return s; } Cl_lcd2004_i2c lcd(0x3F);//0x27 //-------------------------------------------------- class Cl_aaa { protected: const byte pin; unsigned long past; void read() { int b = analogRead(pin); if (b < 90)value = '0'; else if (b < 180)value = '1'; else if (b < 270)value = '2'; else if (b < 360)value = '3'; else if (b < 450)value = '4'; else if (b < 540)value = '5'; else if (b < 630)value = '6'; else if (b < 720)value = '7'; else if (b < 810)value = '8'; else if (b < 900)value = '9'; else value = '\6'; past = millis(); } public: char value; Cl_aaa(byte p): pin(p) {} void init() { read(); } void run() { if (millis() - past >= 200) read(); } }; Cl_aaa aaa(/*пин*/A0); //------- кнопки --------------------------- typedef void (*pDo)(); class Cl_Btn { protected: byte pin; bool state; unsigned long past; void set(bool s) { state = s; past = millis(); if (s) Do(); } public: Cl_Btn(byte p): pin(p) {} pDo Do = [] {}; void init() { pinMode(pin, INPUT_PULLUP); set(false); } void run() { if (millis() - past >= 100) switch (state) { case false: if (!digitalRead(pin))set(true); break; case true: if (digitalRead(pin))set(false); //if (millis() - past >= 300)set(false); break; } } }; Cl_Btn BtnS(/*пин*/2); //----------------------------------------------- const byte page0 = 0; const byte page1a = 10; const byte page1b = 11; byte page; unsigned long past; String str("ABC"); void goPage(byte p) { page = p; past = millis(); switch (page) { case page0: break; case page1a: case page1b: { lcd << &Cl_lcd2004_i2c::clear << str ; if (page == page1a) lcd << aaa.value; else lcd << '\7'; lcd << &Cl_lcd2004_i2c::show; // вывод сообщения на экран } BtnS.Do = [] { switch (aaa.value) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': str += aaa.value; break; case '\6': str = ""; break; } }; // конец лямда break;//page } } void menu_init() { goPage( page1a); } void menu_run() { if (page == page1a && millis() - past >= 300)goPage( page1b); if (page == page1b && millis() - past >= 300)goPage( page1a); } //----------------------------------------------- void setup() { lcd << &Cl_lcd2004_i2c::init << &Cl_lcd2004_i2c::backlight;// инициализация aaa.init(); BtnS.init(); menu_init(); } void loop() { aaa.run(); BtnS.run(); menu_run(); }Подключил реле
/**/ //------дисплей lcd2004_i2c----------------------------- #include <Wire.h> // команды #define LCD_CLEARDISPLAY 0x01 #define LCD_RETURNHOME 0x02 #define LCD_ENTRYMODESET 0x04 #define LCD_DISPLAYCONTROL 0x08 #define LCD_CURSORSHIFT 0x10 #define LCD_FUNCTIONSET 0x20 #define LCD_SETCGRAMADDR 0x40 #define LCD_SETDDRAMADDR 0x80 // флаги для режима ввода дисплея #define LCD_ENTRYRIGHT 0x00 #define LCD_ENTRYLEFT 0x02 #define LCD_ENTRYSHIFTINCREMENT 0x01 #define LCD_ENTRYSHIFTDECREMENT 0x00 // флаги для управления включением / выключением дисплея #define LCD_DISPLAYON 0x04 #define LCD_DISPLAYOFF 0x00 #define LCD_CURSORON 0x02 #define LCD_CURSOROFF 0x00 #define LCD_BLINKON 0x01 #define LCD_BLINKOFF 0x00 // флаги для отображения / сдвига курсора #define LCD_DISPLAYMOVE 0x08 #define LCD_CURSORMOVE 0x00 #define LCD_MOVERIGHT 0x04 #define LCD_MOVELEFT 0x00 // флаги для набора функций #define LCD_8BITMODE 0x10 #define LCD_4BITMODE 0x00 #define LCD_2LINE 0x08 #define LCD_1LINE 0x00 #define LCD_5x10DOTS 0x04 #define LCD_5x8DOTS 0x00 // флаги для управления подсветкой #define LCD_BACKLIGHT 0x08 #define LCD_NOBACKLIGHT 0x00 #define En B00000100 // Бит разрешения #define Rw B00000010 // Чтение / запись бит #define Rs B00000001 // Бит выбора регистра const byte custom[8][8] PROGMEM = { {0, 0, 0, 0, 0, 0, 0, 0},// \0 {31, 31, 31, 0, 0, 0, 0, 0},// \1 {0, 0, 0, 31, 31, 31, 0, 0},// \2 {31, 31, 31, 31, 31, 31, 0, 0},// \3 {0, 0, 0, 0, 0, 0, 31, 31},// \4 {21, 10, 21, 10, 21, 10, 21, 0},// \5 {0, 4, 12, 31, 12, 4, 0, 0},// \6 {31, 31, 31, 31, 31, 31, 31, 31} // \7 }; class Cl_lcd2004_i2c : public Print { protected: uint8_t adr, posX, posY; uint8_t _backlightval; uint8_t _displayfunction; uint8_t _displaycontrol; uint8_t _displaymode; byte buffer[80]; public: Cl_lcd2004_i2c(uint8_t a): adr(a) {} void init() { Wire.begin(); write4bits(0x03 << 4); delayMicroseconds(4500); // wait min 4.1ms write4bits(0x03 << 4); delayMicroseconds(4500); // wait min 4.1ms write4bits(0x03 << 4); delayMicroseconds(150); write4bits(0x02 << 4); delay(50); _backlightval = LCD_NOBACKLIGHT; expanderWrite(_backlightval); // сбросить расширение и выключить подсветку (бит 8 = 1) // режим работы дисплея 4-х битный интерфейс ,две строки,5х8 точек _displayfunction = LCD_4BITMODE | LCD_2LINE | LCD_5x8DOTS; command(LCD_FUNCTIONSET | _displayfunction); // режим контроля дисплей вкл, курсор не мигает,дисплей не мигает _displaycontrol = LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF; command(LCD_DISPLAYCONTROL | _displaycontrol); // режим ввода текста в память-начинать слева с движением в право _displaymode = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT; command(LCD_ENTRYMODESET | _displaymode); for (int i = 0; i < 8; i++) createCharPROGMEM(i, custom[i]); clear(); } // подсветку вкл/выкл void backlight(void) { _backlightval = LCD_BACKLIGHT; expanderWrite(0); } void noBacklight(void) { _backlightval = LCD_NOBACKLIGHT; expanderWrite(0); } // очистить буфер void clear() { for (byte i = 0; i < 80; i++ )buffer[i] = 0x20; posX = 0; posY = 0; } // отправить информацию из буфера на экран void show() { line(0); for (byte i = 0; i < 20; i++ )out(buffer[i]); line(1); for (byte i = 20; i < 40; i++ )out(buffer[i]); line(2); for (byte i = 40; i < 60; i++ )out(buffer[i]); line(3); for (byte i = 60; i < 80; i++ )out(buffer[i]); } void setCursor(byte x, byte y) { if (x > 20) x = 20; else posX = x; if (y > 4) x = 4; else posY = y; } inline size_t write(uint8_t value) { if (posX < 20 && posY < 4) { buffer[posY * 20 + posX] = value; posX++; } return 1; } void createCharPROGMEM(uint8_t location, const uint8_t *charmap) { location &= 0x7; // we only have 8 locations 0-7 command(LCD_SETCGRAMADDR | (location << 3)); for (int i = 0; i < 8; i++) { out(pgm_read_byte_near(charmap + i)); } } void createChar(uint8_t location, uint8_t charmap[]) { location &= 0x7; // we only have 8 locations 0-7 command(LCD_SETCGRAMADDR | (location << 3)); for (int i = 0; i < 8; i++) { out(charmap[i]); } } private: /*внутрение функции*/ void line(byte l) { switch (l) { case 0: command(LCD_SETDDRAMADDR + 0x00); break; case 1: command(LCD_SETDDRAMADDR + 0x40); break; case 2: command(LCD_SETDDRAMADDR + 0x14); break; case 3: command(LCD_SETDDRAMADDR + 0x54); break; } } inline void out(uint8_t value) { send(value, Rs); } /*команды низкоуровневого управления*/ inline void command(uint8_t value) { send(value, 0); } void send(uint8_t value, uint8_t mode) { uint8_t highnib = value & 0xf0; write4bits((highnib) | mode); uint8_t lownib = (value << 4) & 0xf0; write4bits((lownib) | mode); } void write4bits(uint8_t value) { expanderWrite(value); pulseEnable(value); } void expanderWrite(uint8_t _data) { Wire.beginTransmission(adr); Wire.write((int)(_data) | _backlightval); Wire.endTransmission(); } void pulseEnable(uint8_t _data) { expanderWrite(_data | En); // En high delayMicroseconds(1); // enable pulse must be >450ns expanderWrite(_data & ~En); // En low delayMicroseconds(50); // commands need > 37us to settle } }; typedef void (Cl_lcd2004_i2c::*clDo)(); // <- тип переменной метод класса Cl_lcd2004_i2c inline Cl_lcd2004_i2c & operator <<(Cl_lcd2004_i2c &s, clDo Do) { (s.*Do)(); return s; } template <typename T> inline Cl_lcd2004_i2c & operator << (Cl_lcd2004_i2c &s, T n) { s.print(n); return s; } Cl_lcd2004_i2c lcd(0x3F);//0x27 //-----реле--------------------------------------------- class Cl_relay { protected: const byte pin; unsigned long past; static const byte sOFF = 0; static const byte sON = 1; byte state; void set(byte s) { past = millis(); state = s; switch (state) { case sOFF : digitalWrite(pin, LOW); break; case sON : digitalWrite(pin, HIGH); break; } } public: Cl_relay(byte p): pin(p) {} void init() { pinMode(pin,OUTPUT); set(sOFF); } void run() { if (state == sON && millis() - past >= 1000)set(sOFF); } void ON() { set(sON); } void OFF() { set(sOFF); } }; Cl_relay relay(/*пин*/12);//<- подключено реле //-----потенциометр--------------------------------------------- class Cl_aaa { protected: const byte pin; unsigned long past; void read() { int b = analogRead(pin); if (b < 90)value = '0'; else if (b < 180)value = '1'; else if (b < 270)value = '2'; else if (b < 360)value = '3'; else if (b < 450)value = '4'; else if (b < 540)value = '5'; else if (b < 630)value = '6'; else if (b < 720)value = '7'; else if (b < 810)value = '8'; else if (b < 900)value = '9'; else value = '\6'; past = millis(); } public: char value; Cl_aaa(byte p): pin(p) {} void init() { read(); } void run() { if (millis() - past >= 200) read(); } }; Cl_aaa aaa(/*пин*/A0);//<- подключен потенциометр //------- кнопки --------------------------- typedef void (*pDo)(); class Cl_Btn { protected: byte pin; bool state; unsigned long past; void set(bool s) { state = s; past = millis(); if (s) Do(); } public: Cl_Btn(byte p): pin(p) {} pDo Do = [] {}; void init() { pinMode(pin, INPUT_PULLUP); set(false); } void run() { if (millis() - past >= 100) switch (state) { case false: if (!digitalRead(pin))set(true); break; case true: if (digitalRead(pin))set(false); //if (millis() - past >= 300)set(false); break; } } }; Cl_Btn BtnS(/*пин*/2); //----------------------------------------------- const byte page0 = 0; const byte page1a = 10; const byte page1b = 11; byte page; unsigned long past; String str("");//<- буфер ввода String parol("12345");//<- пароль void goPage(byte p) { page = p; past = millis(); switch (page) { case page0: break; case page1a: case page1b: { lcd << &Cl_lcd2004_i2c::clear << str ; if (page == page1a) lcd << aaa.value; else lcd << '\5';//<-вид курсора lcd << &Cl_lcd2004_i2c::show; // вывод сообщения на экран } BtnS.Do = [] { switch (aaa.value) { case '0'://<- команда ввод цифры,но можно и сделать буквы case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': str += aaa.value; if (str==parol)relay.ON(); break; case '\6'://<- команда делете str = ""; break; } }; // конец лямда break;//page } } void menu_init() { goPage( page1a); } void menu_run() { if (page == page1a && millis() - past >= 300)goPage( page1b); if (page == page1b && millis() - past >= 300)goPage( page1a); } //----------------------------------------------- void setup() { lcd << &Cl_lcd2004_i2c::init << &Cl_lcd2004_i2c::backlight;// инициализация aaa.init(); relay.init(); BtnS.init(); menu_init(); } void loop() { aaa.run(); relay.run(); BtnS.run(); menu_run(); }проведем эксперимент.
typedef void (*pDo)(); class cl_G { protected: pDo Do = [] {}; public: void set() { Do = [] {Serial.print("\n Set");}; } void reset() { Do = [] {Serial.print("\n Reset");}; } void print() { Do(); } }; cl_G G; void setup() { Serial.begin(9600); G.set(); G.print(); G.reset(); G.print(); } void loop() { }Работает . Упростим скетч.
typedef void (*pDo)(); class cl_G { public: pDo Do = [] {}; void set() { Do = [] {Serial.print("\n Set");}; } void reset() { Do = [] {Serial.print("\n Reset");}; } }; cl_G G; void setup() { Serial.begin(9600); G.set(); G.Do(); G.reset(); G.Do(); } void loop() { }Не правда ли что метод Do похож на виртуальную функцию. Но фишка не в этом. Вот к примеру пишем мы графическую библиотеку. И для установки пикселя в байте в зависимости от цвета нужно производить или логическое сложение или логическое умножение. А если много то этот анализ проводить для каждого пикселя. А этим способом мы можем банально прописать нужный "карандаш" однократно и дальше не заморачиваться в попиксельной работе.
Я вот смотрю на Ваши лямбды и никак не возьму в толк для чего Вы их тянете туда, где достаточно обычного указателя? Это примерно как ставить микроконтроллер для того, чтобы сделать плавное выключение лампочки - работать, конечно, будет, но ведь там достаточно конденсатора!
Главная фишка лямбд в том, что они тянут за собой внешний контекст и являются функциями высшего порядка (вернее с их помощью такие функции строятся - та же свёртка). Вы же никогда этого (зазвата внешнего контекста) не используете, функций высших порядков не строите, а без этого лямбда не даёт никаких преимуществ по сравнению с обычной, именованной функцией и указателем на неё. Ну, вообще никаких.
Есть глобальные переменные,есть локальные, есть даже статик переменные. Но в Си есть глобальные функции, а вот с локальными или статик функциями не очень . Но есть некий эрзац . Лямда это один из вариантов эрзаца.
Ну, не знаю, освойте функторы - те же локальные функции, но с ними можно и как с переменными работать (параметром передавать, операции выполнять). Лямбда собственно тоже функтор, но она ещё много чего - это огромная пушка для воробья - локальной функции.
О. Хочу спросить уважаемого ЕвгенияП. Если у меня одна функция в качестве параметров требует указатель на другую функцию, чтоб её потом вызвать, а та функция короткая в одну строку, можно ли передать её лямбдой, или это тоже из пушки пабабачкам?
hTimerSeconds = TimerList.Add(1000, [](){ SendMessage(msg_SecondTick);}или сиравно, лучше делать правильную, обычную функцию и не плодить лишних сучностей?
qwone, если захочешь разобраться, то вот это
Главная фишка лямбд в том, что они тянут за собой внешний контекст
правильно называется "замыкание" и это действительно одна из двух главных фишек лямбда-выражений в этом языке. Другая - то, что они (лямбда-выражения) являются функторами. Без этих двух фишек (а ты их не используешь) лямбда-выражения теряют смысл и не дают никаких выгод по сравнению с указателями на функции.
(Хотя, многие говорят, что это на самом деле одна фишка, а не две - первая. т.к. замыкание, по определению, функция первого класса, стало быть замыкание можно рассматривать как функтор)
DetSimen, можно я отвечу.
требует указатель на другую функцию, чтоб её потом вызвать, а та функция короткая в одну строку, можно ли передать её лямбдой, или это тоже из пушки пабабачкам?
Делай как тебе удобно и не парься. С точки зрения кода особой разницы нет, как уже писали и я, и петрович, если не использовать замыкания, то лямбда ничем особым не отличается от указателя на функцию.
Просто лямбды - гораздо более мощный механизм, чем указатели на функции, а раз Пух явно интересуется, хочется помочь ему освоить его на более глубоком уровне, а то сейчас он похоже, просто пихает лямбды ради того, чтобы пихать лямбды.
Делай как тебе удобно и не парься. С точки зрения кода особой разницы нет, как уже писали и я, и петрович, если не использовать замыкания, то лямбда ничем особым не отличается от указателя на функцию.
Просто лямбды - гораздо более мощный механизм, чем указатели на функции, а раз Пух явно интересуется, хочется помочь ему освоить его на более глубоком уровне, а то сейчас он похоже, просто пихает лямбды ради того, чтобы пихать лямбды.
Мда. Пора читать, полупьяный щуря глаз, про функторы и замыкания. А то так дураком и помру.
Дед, это штучки из функционального программирования, оно те правда надо?
А если реально хочется поиграться всякими лямбдами, замыканиями и прочими свёртками, то уж лучше делать это не на С++, где они притянуты через задницу, а на каком-нибудь языке, где они естественны. Например, возьми Haskell. Классный язык и там всё это естественно и натурально, он собственно для этого и создавался. Особенно мне нравится как в википедии определили его (языка) класс: "Класс языка: функциональный, ленивый, модульный". Как видишь, что-то родное в нём есть :)
Делай как тебе удобно и не парься. С точки зрения кода особой разницы нет, как уже писали и я, и петрович, если не использовать замыкания, то лямбда ничем особым не отличается от указателя на функцию.
Просто лямбды - гораздо более мощный механизм, чем указатели на функции, а раз Пух явно интересуется, хочется помочь ему освоить его на более глубоком уровне, а то сейчас он похоже, просто пихает лямбды ради того, чтобы пихать лямбды.
Мда. Пора читать, полупьяный щуря глаз, про функторы и замыкания. А то так дураком и помру.
Не торописЯ. Это новодел в плюсах. Надо обождать лет десять пока устаканится, концепция выпрямится и компиляторы допиляют до какогото единообразия в понимании стандарта. Я еще в 1.6.5 попробовал, наплевался и расслабился. Так шо наливай. Без замыкания 30 лет писали и ниче. Конечно лямдами нужно пользоваться т.к. менше имен придумывать. Главное внутр [] ничего не пиши! Вон как квон делает: классы, но без наследования, лямбды, но без замыкания. У него и вотка без спирту!
Ну, оч. убедительно. Не буду на старости лет память засорять. :)
Лямбдами буду пользоваться по месту, для коротких функций.
Продолжу. Вот к примеру надо написать библиотеку под lcd5110.(Знаю написана). Но как там решается проблема различия кода на аппаратном SPI и программном. Но ведь можно решить так. И нет плясок с бубном.
/**/ typedef void (*pDob)(byte c); class PCD8544 { protected: int8_t _SCLK, _DIN, _DC, _CS, _RST; public: pDob command; PCD8544(int8_t SCLK, int8_t DIN, int8_t DC, int8_t CS, int8_t RST) : _SCLK(SCLK), _DIN(DIN), _DC(DC), _CS(CS), _RST(RST) { command = [](byte c) { Serial.print("\n shiftOut:"); Serial.print(c); }; } PCD8544( int8_t DC, int8_t CS, int8_t RST) : _SCLK(-1), _DIN(-1), _DC(DC), _CS(CS), _RST(RST) { command = [](byte c) { Serial.print("\n SPI:"); Serial.print(c); }; } }; PCD8544 lcd1(/*SCLK*/3,/*DIN*/ 4,/*D/C*/ 5,/*CS*/ 6,/*RST*/7); PCD8544 lcd2(/*D/C*/ 5,/*CS*/ 6,/*RST*/7); void setup() { Serial.begin(9600); lcd1.command(5); lcd2.command(5); } void loop() { }ПС: Для тех кто в танке . https://github.com/adafruit/Adafruit-PCD8544-Nokia-5110-LCD-library/blob/master/Adafruit_PCD8544.cpp
bool Adafruit_PCD8544::isHardwareSPI() { return (_din == -1 && _sclk == -1); }/**/ // уровень данных class aaa_t { protected: public: int a; aaa_t(int _a): a(_a) {} } ; // уровень канальный class chanel_t { protected: public: void init() { Serial.begin(9600); } void show(aaa_t data) { Serial.print(data.a); } }; chanel_t ch; //----------------------------------------------- // уровень прикладной void setup() { ch.init(); { aaa_t a(5); ch.show(a); } } void loop() { }/**/ // уровень данных class aaa_t { protected: public: int a; aaa_t(int _a): a(_a) {} } ; // уровень канальный class chanel_t { protected: public: void init() { Serial.begin(9600); } void show(aaa_t data) { Serial.print(data.a); } }; chanel_t ch; //----------------------------------------------- // уровень прикладной void setup() { ch.init(); { aaa_t a(5); ch.show(a); } } void loop() { }Пух, сегодня в соседней теме народ говорил, что ООП-программы можно писать и без слова class.
Но ты уникум! Ты умудряешься писать не-ООПные (и даже анти-ООПные) программы с использованием слова class :-)
Заготовка меню для экрана lcd5110 и библиотек <Adafruit_GFX.h><Adafruit_PCD8544.h>
/**/ //---кнопки----------------------------- typedef void (*pDo)(); class Cl_btn { protected: byte pin; bool state; unsigned long past; void set(bool s) { state = s; past = millis(); if (s == true) Do(); } public: Cl_btn(byte p): pin(p) {} pDo Do = [] {}; void init() { pinMode(pin, INPUT_PULLUP); set(false); } void run() { if (millis() - past >= 100) switch (state) { case false: if (!digitalRead(pin))set(true); break; case true: if (digitalRead(pin))set(false); if (millis() - past >= 300)set(false); break; } } }; Cl_btn BtnS(/*пин*/2); //кнопка селект Cl_btn BtnU(/*пин*/3); //кнопка верх //--------------------------------------------------------------------------- #include <Adafruit_GFX.h> #include <Adafruit_PCD8544.h> Adafruit_PCD8544 display(/*DC*/ 10,/*cs*/9,/*rst*/8); //*Din*/ 11,/*clk*/ 13 //Adafruit_PCD8544 display = Adafruit_PCD8544(/*clk*/ 13,/*Din*/ 11,/*DC*/ 10,/*cs*/ 9,/*rst*/8); //--------------- меню--------------------------------------------- const byte page0 = 0; const byte page1 = 10; const byte page2 = 20; const byte page3 = 30; byte page; unsigned long past; void goPage(byte p) { past = millis(); page = p; display.clearDisplay(); switch (page) { case page0: { display.print("page 0"); } BtnS.Do = [] {goPage(page1);}; BtnU.Do = [] {}; break; case page1: { display.print( "page 1"); } BtnS.Do = [] {goPage(page2);}; BtnU.Do = [] {}; break; case page2: { display.print("page 2"); } BtnS.Do = [] {goPage(page3);}; BtnU.Do = [] {}; break; case page3: { display.print("page 3"); } BtnS.Do = [] {goPage(page0);}; BtnU.Do = [] {}; break; } display.display(); } void menu_init() { display.begin(); display.setTextSize(2); // установка размера шрифта display.setTextColor(BLACK); // установка цвета текста display.setRotation(2); // перевернуть изображение display.setContrast(50); // установка контраста goPage(page0); } void menu_run() {} void setup() { BtnS.init(); BtnU.init(); menu_init(); } void loop() { BtnS.run(); BtnU.run(); menu_run(); }/**/ //---кнопки----------------------------- typedef void (*pDo)(); class Cl_btn { protected: byte pin; bool state; unsigned long past; void set(bool s) { state = s; past = millis(); if (s == true) Do(); } public: Cl_btn(byte p): pin(p) {} pDo Do = [] {}; void init() { pinMode(pin, INPUT_PULLUP); set(false); } void run() { if (millis() - past >= 100) switch (state) { case false: if (!digitalRead(pin))set(true); break; case true: if (digitalRead(pin))set(false); if (millis() - past >= 300)set(false); break; } } }; Cl_btn BtnS(/*пин*/2); //кнопка селект Cl_btn BtnU(/*пин*/3); //кнопка верх //--------------------------------------------------------------------------- #include <Adafruit_GFX.h> #include <Adafruit_PCD8544.h> Adafruit_PCD8544 display(/*DC*/ 10,/*cs*/9,/*rst*/8); //*Din*/ 11,/*clk*/ 13 //Adafruit_PCD8544 display = Adafruit_PCD8544(/*clk*/ 13,/*Din*/ 11,/*DC*/ 10,/*cs*/ 9,/*rst*/8); //--------------- меню--------------------------------------------- const byte page0 = 0; const byte page1 = 10; const byte page2 = 20; const byte page3 = 30; byte page; unsigned long past; void goPage(byte p) { past = millis(); page = p; display.clearDisplay(); switch (page) { case page0: { display.setTextSize(2); // установка размера шрифта display.setTextColor(BLACK); // установка цвета текста display.print("page 0"); Adafruit_GFX_Button btn; char label_1[] = "<0+>"; char label_2[] = "<0->"; btn.initButton(&display, // *gfx 20, 40, 24, 8, // x,y,w,h, BLACK, BLACK, //outline, fill, WHITE, label_1 , 1); //textcolor, label,textsize btn.drawButton(); btn.initButton(&display, // *gfx 50, 40, 24, 8, // x,y,w,h, BLACK, BLACK, //outline, fill, WHITE, label_2 , 1); //textcolor, label,textsize btn.drawButton(); } BtnS.Do = [] {goPage(page1);}; BtnU.Do = [] {}; break; case page1: { display.setTextSize(2); // установка размера шрифта display.setTextColor(BLACK); // установка цвета текста display.print( "page 1"); Adafruit_GFX_Button btn; char label_1[] = "<1+>"; char label_2[] = "<1->"; btn.initButton(&display, // *gfx 20, 40, 24, 8, // x,y,w,h, BLACK, BLACK, //outline, fill, WHITE, label_1 , 1); //textcolor, label,textsize btn.drawButton(); btn.initButton(&display, // *gfx 50, 40, 24, 8, // x,y,w,h, BLACK, BLACK, //outline, fill, WHITE, label_2 , 1); //textcolor, label,textsize btn.drawButton(); } BtnS.Do = [] {goPage(page2);}; BtnU.Do = [] {}; break; case page2: { display.setTextSize(2); // установка размера шрифта display.setTextColor(BLACK); // установка цвета текста display.print("page 2"); Adafruit_GFX_Button btn; char label_1[] = "<2+>"; char label_2[] = "<2->"; btn.initButton(&display, // *gfx 20, 40, 24, 8, // x,y,w,h, BLACK, BLACK, //outline, fill, WHITE, label_1 , 1); //textcolor, label,textsize btn.drawButton(); btn.initButton(&display, // *gfx 50, 40, 24, 8, // x,y,w,h, BLACK, BLACK, //outline, fill, WHITE, label_2 , 1); //textcolor, label,textsize btn.drawButton(); } BtnS.Do = [] {goPage(page3);}; BtnU.Do = [] {}; break; case page3: { display.setTextSize(2); // установка размера шрифта display.setTextColor(BLACK); // установка цвета текста display.print("page 3"); Adafruit_GFX_Button btn; char label_1[] = "<3+>"; char label_2[] = "<3->"; btn.initButton(&display, // *gfx 20, 40, 24, 8, // x,y,w,h, BLACK, BLACK, //outline, fill, WHITE, label_1 , 1); //textcolor, label,textsize btn.drawButton(); btn.initButton(&display, // *gfx 50, 40, 24, 8, // x,y,w,h, BLACK, BLACK, //outline, fill, WHITE, label_2 , 1); //textcolor, label,textsize btn.drawButton(); } BtnS.Do = [] {goPage(page0);}; BtnU.Do = [] {}; break; } display.display(); } void menu_init() { display.begin(); display.setRotation(2); // перевернуть изображение display.setContrast(50); // установка контраста goPage(page0); } void menu_run() {} void setup() { BtnS.init(); BtnU.init(); menu_init(); } void loop() { BtnS.run(); BtnU.run(); menu_run(); }const uint8_t adr[] PROGMEM = { 8, 7, 8, 5, // h,w,h1,w1 1, 2, 3, 4, 5 // первое изображение }; void aaa(const uint8_t * adr_obj) { int h = pgm_read_byte(adr_obj); int w = pgm_read_byte(adr_obj + 1); Serial.println(h); Serial.println(w); } class bbb { protected: uint8_t h, w, h1, w1; uint8_t * buffer; public: bbb(const uint8_t * adr_obj) : h (pgm_read_byte(adr_obj)), w (pgm_read_byte(adr_obj + 1)), h1(pgm_read_byte(adr_obj + 2)), w1(pgm_read_byte(adr_obj + 3)) {} bbb(uint8_t _h, uint8_t _w) : h (_h), w (_w) {} }; void setup() { Serial.begin(9600); aaa(adr); bbb a(adr); } void loop() { }Посмотрим на это https://github.com/PaulStoffregen/EEPROM/blob/master/EEPROM.h, а особенно на структуру EERef
А потом напишем это.
#include <inttypes.h> #include <avr/eeprom.h> #include <avr/io.h> //.h ------------------- struct EERef { int index; //Index of current EEPROM cell. EERef( const int index ) : index( index ) {} //Access/read members. uint8_t operator*() const { return eeprom_read_byte( (uint8_t*) index ); } operator uint8_t() const { // <- да здесь 1 конст выкинут return **this; } //Assignment/write members. EERef &operator=( const EERef &ref ) { return *this = *ref; } EERef &operator=( uint8_t in ) { return eeprom_write_byte( (uint8_t*) index, in ), *this; } EERef &operator +=( uint8_t in ) { return *this = **this + in; } EERef &operator -=( uint8_t in ) { return *this = **this - in; } EERef &operator *=( uint8_t in ) { return *this = **this * in; } EERef &operator /=( uint8_t in ) { return *this = **this / in; } EERef &operator ^=( uint8_t in ) { return *this = **this ^ in; } EERef &operator %=( uint8_t in ) { return *this = **this % in; } EERef &operator &=( uint8_t in ) { return *this = **this & in; } EERef &operator |=( uint8_t in ) { return *this = **this | in; } EERef &operator <<=( uint8_t in ) { return *this = **this << in; } EERef &operator >>=( uint8_t in ) { return *this = **this >> in; } EERef &update( uint8_t in ) { return in != *this ? *this = in : *this; } /** Prefix increment/decrement **/ EERef& operator++() { return *this += 1; } EERef& operator--() { return *this -= 1; } /** Postfix increment/decrement **/ uint8_t operator++ (int) { uint8_t ret = **this; return ++(*this), ret; } uint8_t operator-- (int) { uint8_t ret = **this; return --(*this), ret; } }; //------------------- uint8_t EEMEM adr; EERef aaa(adr); void setup() { aaa = 235; Serial.begin(9600); Serial.println(aaa); ++aaa; Serial.println(aaa); } void loop() { }Думаю понятно. То есть если напишем некий шаблон этого класса, то можем обращаться к структуре помещенную в EEPROM, как к структуре в ОЗУ.
небольшой пустячок
struct aaa { int index; aaa(const int index): index(index) {} operator int() const { // <-- обратите внимание на это return index; } }; void setup() { aaa a(345); Serial.begin(9600); Serial.println(a); // <-- а потом сюда. } void loop() { }Осталось сюда воткнуть лямду и будет полный писец.
Да тут и без лямбды уже...
class Relay { protected: bool state; void set(bool s) { state = s; } public: int index; Relay(): state(0) {} operator bool () const { // <-- обратите внимание на это return state; } void ON() { state = 1; } void OFF() { state = 0; } }; void setup() { Serial.begin(9600); Relay R1; Serial.println(R1);// выведено 0 R1.ON(); Serial.println(R1);// выведено 1 R1.OFF(); Serial.println(R1);// выведено 0 } void loop() { }где энкодер?
А восьмая строка зачем?
Отстаньте от него, у него свое виденье. Он же четко указал, обращать внимание на строку 10. Наверно const приветствовать аплодисментами. В остальное селедку заворачивал, index зарезервировал для будущих применений.
да там вапще всё трэш, угар и ржака. :)
Вы как маленький... Разве не помните, что "иллюзионисты" обычно отвлекают ваше внимание на что-то нелогичное, а тем временем бумажник из кармана прут?
/**/ //.h ------------------- #include <avr/eeprom.h> template <typename TT> struct EERef { uint8_t* index; EERef( const TT *_index) { index = (uint8_t*)_index; } TT operator*() const { TT data; uint8_t* p = (uint8_t*)&data; for (uint8_t i = 0; i < sizeof(TT); i++) p[i] = eeprom_read_byte(index + i); return data; } operator TT() const { return **this; } //Assignment/write members. EERef &operator=( const EERef &ref ) { return *this = *ref; } EERef &operator=( TT data ) { uint8_t* p = (uint8_t*)&data; for (uint8_t i = 0; i < sizeof(TT); i++) eeprom_write_byte(index + i , p[i] ); return *this; } EERef &operator +=( TT data ) { return *this = **this + data; } EERef &operator -=( TT data ) { return *this = **this - data; } EERef &operator *=( TT data ) { return *this = **this * data; } EERef &operator /=( TT data ) { return *this = **this / data; } EERef &operator ^=( TT data ) { return *this = **this ^ data; } EERef &operator %=( TT data ) { return *this = **this % data; } EERef &operator &=( TT data ) { return *this = **this & data; } EERef &operator |=( TT data ) { return *this = **this | data; } EERef &operator <<=( TT data ) { return *this = **this << data; } EERef &operator >>=( TT data ) { return *this = **this >> data; } EERef &update( TT data ) { return data != *this ? *this = data : *this; } /** Prefix datacrement/decrement **/ EERef& operator++() { return *this += 1; } EERef& operator--() { return *this -= 1; } /** Postfix datacrement/decrement **/ TT operator++ (int) { TT ret = **this; return ++(*this), ret; } TT operator-- (int) { TT ret = **this; return --(*this), ret; } }; //------------------- float EEMEM adr[10]; EERef<float> aaa(&adr[3]); void setup() { aaa = -12.50; Serial.begin(9600); Serial.println(aaa); aaa += 3.14; Serial.println(aaa); } void loop() { }продолжаем выступление "иллюзиониста"
Красиво конечно, очень прошу, объясни простыми словами зачем это нужно и как это работает?
продолжаем выступление "иллюзиониста"
Если я ничего не путаю, то в строке №26 запись без проверки - может не стоит? update как-то более щадящая конструкция.
/**/ #include "qwonelib.h" //------------------- struct data_t { int iData; float fData; }; data_t EEMEM adr1; void setting() { static const PROGMEM data_t adr2 = {-9556, 13.14}; { eeRef <int> aaa1(&adr1.iData); pgmRef<int> aaa2(&adr2.iData); aaa1 = aaa2; } { eeRef <float> aaa1(&adr1.fData); pgmRef<float> aaa2(&adr2.fData); aaa1 = aaa2; } } void setup() { setting(); Serial.begin(9600); { eeRef <int> aaa1(&adr1.iData); Serial.println(aaa1); } { eeRef <float> aaa1(&adr1.fData); Serial.println(aaa1); } } void loop() { }файл qwonelib.h
/**/ #ifndef QWONELIB_H #define QWONELIB_H #include <avr/eeprom.h> template <typename TT> struct eeRef { uint8_t* adr; eeRef( const TT *_adr): adr((uint8_t*)_adr) {} void setNewAdr(const TT *_adr) { adr = (uint8_t*)_adr; } TT operator*() const { TT data; uint8_t* p = (uint8_t*)&data; for (uint8_t i = 0; i < sizeof(TT); i++) p[i] = eeprom_read_byte(adr + i); return data; } operator TT() const { return **this; } //Assignment/write members. eeRef &operator=( const eeRef &ref ) { return *this = *ref; } eeRef &operator=( TT data ) { uint8_t* p = (uint8_t*)&data; for (uint8_t i = 0; i < sizeof(TT); i++) if ( p[i] != eeprom_read_byte(adr + i)) eeprom_write_byte(adr + i, p[i]); return *this; } eeRef &operator +=( TT data ) { return *this = **this + data; } eeRef &operator -=( TT data ) { return *this = **this - data; } eeRef &operator *=( TT data ) { return *this = **this * data; } eeRef &operator /=( TT data ) { return *this = **this / data; } eeRef &operator ^=( TT data ) { return *this = **this ^ data; } eeRef &operator %=( TT data ) { return *this = **this % data; } eeRef &operator &=( TT data ) { return *this = **this & data; } eeRef &operator |=( TT data ) { return *this = **this | data; } eeRef &operator <<=( TT data ) { return *this = **this << data; } eeRef &operator >>=( TT data ) { return *this = **this >> data; } eeRef &update( TT data ) { return data != *this ? *this = data : *this; } /** Prefix datacrement/decrement **/ eeRef& operator++() { return *this += 1; } eeRef& operator--() { return *this -= 1; } /** Postfix datacrement/decrement **/ TT operator++ (int) { TT ret = **this; return ++(*this), ret; } TT operator-- (int) { TT ret = **this; return --(*this), ret; } }; #include <avr/pgmspace.h> template <typename TT> struct pgmRef { uint8_t* adr; pgmRef( const TT *_adr): adr((uint8_t*)_adr) {} void setNewAdr(const TT *_adr) { adr = (uint8_t*)_adr; } TT operator*() const { TT data; uint8_t* p = (uint8_t*)&data; for (uint8_t i = 0; i < sizeof(TT); i++) p[i] = pgm_read_byte(adr + i); return data; } operator TT() const { return **this; } }; #endifТот же скетч в упакованном виде, хотя define можно ее сделать компактнее и удобочитаемей
/**/ #include "qwonelib.h" //------------------- struct data_t { // некая пользовательская структура byte bData; int iData; float fData; }; data_t EEMEM adr1; // размещение ее в EEPROM void setting() { static const PROGMEM data_t adr2 = { 99, -9556, 13.14}; // набор констант для пользовательской функции (eeRef <byte>) (&adr1.bData) = (pgmRef<byte>) (&adr2.bData); // загрузка байтовой части (eeRef <int>) (&adr1.iData) = (pgmRef<int>) (&adr2.iData); // загрузка интовой части (eeRef <float>)(&adr1.fData) = (pgmRef<float>)(&adr2.fData); // загрузка флоат части } //---------------------------------------- template <typename T> inline Print & operator << (Print &s, T n) { s.print(n); return s; } //---------------------------------------- void setup() { setting(); Serial.begin(9600); Serial << (eeRef <byte>) (&adr1.bData) << '\n'; // вывод байтовой части Serial << (eeRef <int>) (&adr1.iData) << '\n'; // вывод интовой части Serial << (eeRef <float>)(&adr1.fData) << '\n'; // вывод флоат части } void loop() { }qwonelib.h
/*qwonelib.h*/ #ifndef QWONELIB_H #define QWONELIB_H #include <avr/pgmspace.h> template <typename TT> struct pgmRef { uint8_t* adr; pgmRef( const TT *_adr): adr((uint8_t*)_adr) {} void setNewAdr(const TT *_adr) { adr = (uint8_t*)_adr; } TT operator*() const { TT data; uint8_t* p = (uint8_t*)&data; for (uint8_t i = 0; i < sizeof(TT); i++) p[i] = pgm_read_byte(adr + i); return data; } operator TT() const { return **this; } }; #include <avr/eeprom.h> template <typename TT> struct eeRef { uint8_t* adr; eeRef( const TT *_adr): adr((uint8_t*)_adr) {} void setNewAdr(const TT *_adr) { adr = (uint8_t*)_adr; } TT operator*() const { TT data; uint8_t* p = (uint8_t*)&data; for (uint8_t i = 0; i < sizeof(TT); i++) p[i] = eeprom_read_byte(adr + i); return data; } operator TT() const { return **this; } //Assignment/write members. eeRef &operator=( const eeRef &ref ) { return *this = *ref; } eeRef &operator=( TT data ) { uint8_t* p = (uint8_t*)&data; for (uint8_t i = 0; i < sizeof(TT); i++) if ( p[i] != eeprom_read_byte(adr + i)) eeprom_write_byte(adr + i, p[i]); return *this; } eeRef &operator +=( TT data ) { return *this = **this + data; } eeRef &operator -=( TT data ) { return *this = **this - data; } eeRef &operator *=( TT data ) { return *this = **this * data; } eeRef &operator /=( TT data ) { return *this = **this / data; } eeRef &operator ^=( TT data ) { return *this = **this ^ data; } eeRef &operator %=( TT data ) { return *this = **this % data; } eeRef &operator &=( TT data ) { return *this = **this & data; } eeRef &operator |=( TT data ) { return *this = **this | data; } eeRef &operator <<=( TT data ) { return *this = **this << data; } eeRef &operator >>=( TT data ) { return *this = **this >> data; } eeRef &update( TT data ) { return data != *this ? *this = data : *this; } /** Prefix datacrement/decrement **/ eeRef& operator++() { return *this += 1; } eeRef& operator--() { return *this -= 1; } /** Postfix datacrement/decrement **/ TT operator++ (int) { TT ret = **this; return ++(*this), ret; } TT operator-- (int) { TT ret = **this; return --(*this), ret; } }; #endifНу и наконец прикрутим это все к меню, если потом понадобится прикрутить это меню к чему-то другому.
/**/ #include "qwonelib.h" //------------------- struct data_t { // некая пользовательская структура byte bData; int iData; float fData; }; data_t EEMEM adr1; // размещение ее в EEPROM void setting() { static const PROGMEM data_t adr2 = { 100, 1000, 10.10}; // набор констант для пользовательской функции (eeRef <byte>) (&adr1.bData) = (pgmRef<byte>) (&adr2.bData); // загрузка байтовой части (eeRef <int>) (&adr1.iData) = (pgmRef<int>) (&adr2.iData); // загрузка интовой части (eeRef <float>)(&adr1.fData) = (pgmRef<float>)(&adr2.fData); // загрузка флоат части } //---кнопки----------------------------- typedef void (*pDo)(); class Cl_btn { protected: byte pin; bool state; unsigned long past; void set(bool s) { state = s; past = millis(); if (s == true) Do(); } public: Cl_btn(byte p): pin(p) {} pDo Do = [] {}; void init() { pinMode(pin, INPUT_PULLUP); set(false); } void run() { if (millis() - past >= 100) switch (state) { case false: if (!digitalRead(pin))set(true); break; case true: if (digitalRead(pin))set(false); if (millis() - past >= 300)set(false); break; } } }; Cl_btn BtnS(/*пин*/2); //кнопка селект Cl_btn BtnU(/*пин*/3); //кнопка верх Cl_btn BtnD(/*пин*/4); //кнопка вниз //--------------- меню--------------------------------------------- template <typename T> inline Print & operator << (Print &s, T n) { s.print(n); return s; } const byte page0 = 0; //ноказ настроек const byte page1 = 10;//изменение байтовой части const byte page2 = 20;//изменение интовой части const byte page3 = 30;//изменение флоат части const byte page4 = 40; //сброс настроек byte page; unsigned long past; void goPage(byte p) { past = millis(); page = p; switch (page) { case page0: BtnS.Do = [] {goPage(page1);}; BtnU.Do = [] {}; BtnD.Do = [] {}; Serial << "info\n"; Serial << "byte= " << (eeRef <byte>) (&adr1.bData) << '\n'; // вывод байтовой части Serial << "int= " << (eeRef <int>) (&adr1.iData) << '\n'; // вывод интовой части Serial << "float= " << (eeRef <float>)(&adr1.fData) << '\n'; // вывод флоат части break; case page1: BtnS.Do = [] {goPage(page2);}; BtnU.Do = [] { if ((eeRef <byte>) (&adr1.bData) <= 200)(eeRef <byte>) (&adr1.bData) += 10; goPage(page1); }; BtnD.Do = [] { if ((eeRef <byte>) (&adr1.bData) >= 50)(eeRef <byte>) (&adr1.bData) -= 10; goPage(page1); }; Serial << "set byte\n"; Serial << (eeRef <byte>) (&adr1.bData) << '\n'; // вывод байтовой части break; case page2: BtnS.Do = [] {goPage(page3);}; BtnU.Do = [] { if ((eeRef <int>) (&adr1.iData) <= 2000)(eeRef <int>) (&adr1.iData) += 100; goPage(page2); }; BtnD.Do = [] { if ((eeRef <int>) (&adr1.iData) >= 500)(eeRef <int>) (&adr1.iData) -= 100; goPage(page2); }; Serial << "set int\n"; Serial << (eeRef <int>) (&adr1.iData) << '\n'; // вывод интовой части break; case page3: BtnS.Do = [] {goPage(page4);}; BtnU.Do = [] { if ((eeRef <float>) (&adr1.fData) <= 20.30)(eeRef <float>) (&adr1.fData) += 1.01; goPage(page3); }; BtnD.Do = [] { if ((eeRef <float>) (&adr1.fData) >= 5.05)(eeRef <float>) (&adr1.fData) -= 1.01; goPage(page3); }; Serial << "set float\n"; Serial << (eeRef <float>)(&adr1.fData) << '\n'; // вывод флоат части break; case page4: BtnS.Do = [] {goPage(page0);}; BtnU.Do = [] {setting(); goPage(page4);}; BtnD.Do = [] {setting(); goPage(page4);}; Serial << "setting\n"; Serial << "byte= " << (eeRef <byte>) (&adr1.bData) << '\n'; // вывод байтовой части Serial << "int= " << (eeRef <int>) (&adr1.iData) << '\n'; // вывод интовой части Serial << "float= " << (eeRef <float>)(&adr1.fData) << '\n'; // вывод флоат части break; } } void menu_init() { Serial.begin(9600); goPage(page0); } void menu_run() {} //---------------------------------------- void setup() { BtnS.init(); BtnU.init(); BtnD.init(); menu_init(); } void loop() { BtnS.run(); BtnU.run(); BtnD.run(); menu_run(); }qwonelib.h
/*qwonelib.h*/ #ifndef QWONELIB_H #define QWONELIB_H #include <avr/pgmspace.h> template <typename TT> struct pgmRef { uint8_t* adr; pgmRef( const TT *_adr): adr((uint8_t*)_adr) {} void setNewAdr(const TT *_adr) { adr = (uint8_t*)_adr; } TT operator*() const { TT data; uint8_t* p = (uint8_t*)&data; for (uint8_t i = 0; i < sizeof(TT); i++) p[i] = pgm_read_byte(adr + i); return data; } operator TT() const { return **this; } }; #include <avr/eeprom.h> template <typename TT> struct eeRef { uint8_t* adr; eeRef( const TT *_adr): adr((uint8_t*)_adr) {} void setNewAdr(const TT *_adr) { adr = (uint8_t*)_adr; } TT operator*() const { TT data; uint8_t* p = (uint8_t*)&data; for (uint8_t i = 0; i < sizeof(TT); i++) p[i] = eeprom_read_byte(adr + i); return data; } operator TT() const { return **this; } //Assignment/write members. eeRef &operator=( const eeRef &ref ) { return *this = *ref; } eeRef &operator=( TT data ) { uint8_t* p = (uint8_t*)&data; for (uint8_t i = 0; i < sizeof(TT); i++) if ( p[i] != eeprom_read_byte(adr + i)) eeprom_write_byte(adr + i, p[i]); return *this; } eeRef &operator +=( TT data ) { return *this = **this + data; } eeRef &operator -=( TT data ) { return *this = **this - data; } eeRef &operator *=( TT data ) { return *this = **this * data; } eeRef &operator /=( TT data ) { return *this = **this / data; } eeRef &operator ^=( TT data ) { return *this = **this ^ data; } eeRef &operator %=( TT data ) { return *this = **this % data; } eeRef &operator &=( TT data ) { return *this = **this & data; } eeRef &operator |=( TT data ) { return *this = **this | data; } eeRef &operator <<=( TT data ) { return *this = **this << data; } eeRef &operator >>=( TT data ) { return *this = **this >> data; } eeRef &update( TT data ) { return data != *this ? *this = data : *this; } /** Prefix datacrement/decrement **/ eeRef& operator++() { return *this += 1; } eeRef& operator--() { return *this -= 1; } /** Postfix datacrement/decrement **/ TT operator++ (int) { TT ret = **this; return ++(*this), ret; } TT operator-- (int) { TT ret = **this; return --(*this), ret; } }; #endifудастся заслушать начальника транспортного цеха? (энкодер), или как говорила экономист, когда мы автоматизировали одно предприятие, моя работа не поддаётся автоматизации )))
Мне энкодер как устройство регулировки давно перестало нравится. Глючное устройство. Где-то в моих темах валяется этот компонент. Так что заменить кнопки энкодером не трудно для разумного человека.
Продолжу. Если же надо к примеру использовать 10 однородных устройств и 3 комплектами настроек.
/**/ #include "qwonelib.h" //------------------- struct data_t { // некая пользовательская структура byte bData; int iData; float fData; }; byte index = 0; const byte max_index = 10; data_t EEMEM adr1[max_index]; // размещение ее в EEPROM byte setting_index = 0; const byte max_setting_index = 3; void setting(byte in) { static const PROGMEM data_t adr2[3] = { // набор констант для пользовательской функции {100, 1000, 10.10} // 0 комплект настроек , {101, 1001, 10.11} // 1 комплект настроек , {102, 1002, 10.12} // 2 комплект настроек }; (eeRef <byte>) (&adr1[in].bData) = (pgmRef<byte>) (&adr2[setting_index].bData); // загрузка байтовой части (eeRef <int>) (&adr1[in].iData) = (pgmRef<int>) (&adr2[setting_index].iData); // загрузка интовой части (eeRef <float>)(&adr1[in].fData) = (pgmRef<float>)(&adr2[setting_index].fData); // загрузка флоат части } //---кнопки----------------------------- typedef void (*pDo)(); class Cl_btn { protected: byte pin; bool state; unsigned long past; void set(bool s) { state = s; past = millis(); if (s == true) Do(); } public: Cl_btn(byte p): pin(p) {} pDo Do = [] {}; void init() { pinMode(pin, INPUT_PULLUP); set(false); } void run() { if (millis() - past >= 100) switch (state) { case false: if (!digitalRead(pin))set(true); break; case true: if (digitalRead(pin))set(false); if (millis() - past >= 300)set(false); break; } } }; Cl_btn BtnS(/*пин*/2); //кнопка селект Cl_btn BtnU(/*пин*/3); //кнопка верх Cl_btn BtnD(/*пин*/4); //кнопка вниз //--------------- меню--------------------------------------------- template <typename T> inline Print & operator << (Print &s, T n) { s.print(n); return s; } const byte page0 = 0; //ноказ настроек const byte page0a = 5;// поменять индекс устройства const byte page1 = 10;//изменение байтовой части const byte page2 = 20;//изменение интовой части const byte page3 = 30;//изменение флоат части const byte page4 = 40; //сброс настроек byte page; unsigned long past; void goPage(byte p) { past = millis(); page = p; switch (page) { case page0: BtnS.Do = [] {goPage(page0a);}; BtnU.Do = [] {}; BtnD.Do = [] {}; Serial << "info:\n"; Serial << "byte(" << index << ") =" << (eeRef <byte>) (&adr1[index].bData) << '\n'; // вывод байтовой части Serial << "int(" << index << ") =" << (eeRef <int>) (&adr1[index].iData) << '\n'; // вывод интовой части Serial << "float(" << index << ")=" << (eeRef <float>)(&adr1[index].fData) << '\n'; // вывод флоат части break; case page0a: BtnS.Do = [] {goPage(page1);}; BtnU.Do = [] { if (index < (max_index - 1))index++; goPage(page0a); }; BtnD.Do = [] { if (index > 0)index--; goPage(page0a); }; Serial << "set index:" << index << "\n"; break; case page1: BtnS.Do = [] {goPage(page2);}; BtnU.Do = [] { if ((eeRef <byte>) (&adr1[index].bData) <= 200)(eeRef <byte>) (&adr1[index].bData) += 10; goPage(page1); }; BtnD.Do = [] { if ((eeRef <byte>) (&adr1[index].bData) >= 50)(eeRef <byte>) (&adr1[index].bData) -= 10; goPage(page1); }; Serial << "set byte(" << index << ")="; Serial << (eeRef <byte>) (&adr1[index].bData) << '\n'; // вывод байтовой части break; case page2: BtnS.Do = [] {goPage(page3);}; BtnU.Do = [] { if ((eeRef <int>) (&adr1[index].iData) <= 2000)(eeRef <int>) (&adr1[index].iData) += 100; goPage(page2); }; BtnD.Do = [] { if ((eeRef <int>) (&adr1[index].iData) >= 500)(eeRef <int>) (&adr1[index].iData) -= 100; goPage(page2); }; Serial << "set int(" << index << ")="; Serial << (eeRef <int>) (&adr1[index].iData) << '\n'; // вывод интовой части break; case page3: BtnS.Do = [] {goPage(page4);}; BtnU.Do = [] { if ((eeRef <float>) (&adr1[index].fData) <= 20.30)(eeRef <float>) (&adr1[index].fData) += 1.01; goPage(page3); }; BtnD.Do = [] { if ((eeRef <float>) (&adr1[index].fData) >= 5.05)(eeRef <float>) (&adr1[index].fData) -= 1.01; goPage(page3); }; Serial << "set float(" << index << ")="; Serial << (eeRef <float>)(&adr1[index].fData) << '\n'; // вывод флоат части break; case page4: BtnS.Do = [] {goPage(page0);}; BtnU.Do = [] { if (setting_index < (max_setting_index - 1))setting_index++; setting(index); goPage(page4); }; BtnD.Do = [] { if (setting_index > 0)setting_index--; setting(index); goPage(page4); }; Serial << "setting(" << setting_index << ")\n"; Serial << "byte(" << index << ") =" << (eeRef <byte>) (&adr1[index].bData) << '\n'; // вывод байтовой части Serial << "int(" << index << ") =" << (eeRef <int>) (&adr1[index].iData) << '\n'; // вывод интовой части Serial << "float(" << index << ")=" << (eeRef <float>)(&adr1[index].fData) << '\n'; // вывод флоат части break; } } void menu_init() { Serial.begin(9600); goPage(page0); } void menu_run() {} //---------------------------------------- void setup() { BtnS.init(); BtnU.init(); BtnD.init(); menu_init(); } void loop() { BtnS.run(); BtnU.run(); BtnD.run(); menu_run(); }qwonelib.h
/*qwonelib.h*/ #ifndef QWONELIB_H #define QWONELIB_H #include <avr/pgmspace.h> template <typename TT> struct pgmRef { uint8_t* adr; pgmRef( const TT *_adr): adr((uint8_t*)_adr) {} void setNewAdr(const TT *_adr) { adr = (uint8_t*)_adr; } TT operator*() const { TT data; uint8_t* p = (uint8_t*)&data; for (uint8_t i = 0; i < sizeof(TT); i++) p[i] = pgm_read_byte(adr + i); return data; } operator TT() const { return **this; } }; #include <avr/eeprom.h> template <typename TT> struct eeRef { uint8_t* adr; eeRef( const TT *_adr): adr((uint8_t*)_adr) {} void setNewAdr(const TT *_adr) { adr = (uint8_t*)_adr; } TT operator*() const { TT data; uint8_t* p = (uint8_t*)&data; for (uint8_t i = 0; i < sizeof(TT); i++) p[i] = eeprom_read_byte(adr + i); return data; } operator TT() const { return **this; } //Assignment/write members. eeRef &operator=( const eeRef &ref ) { return *this = *ref; } eeRef &operator=( TT data ) { uint8_t* p = (uint8_t*)&data; for (uint8_t i = 0; i < sizeof(TT); i++) if ( p[i] != eeprom_read_byte(adr + i)) eeprom_write_byte(adr + i, p[i]); return *this; } eeRef &operator +=( TT data ) { return *this = **this + data; } eeRef &operator -=( TT data ) { return *this = **this - data; } eeRef &operator *=( TT data ) { return *this = **this * data; } eeRef &operator /=( TT data ) { return *this = **this / data; } eeRef &operator ^=( TT data ) { return *this = **this ^ data; } eeRef &operator %=( TT data ) { return *this = **this % data; } eeRef &operator &=( TT data ) { return *this = **this & data; } eeRef &operator |=( TT data ) { return *this = **this | data; } eeRef &operator <<=( TT data ) { return *this = **this << data; } eeRef &operator >>=( TT data ) { return *this = **this >> data; } eeRef &update( TT data ) { return data != *this ? *this = data : *this; } /** Prefix datacrement/decrement **/ eeRef& operator++() { return *this += 1; } eeRef& operator--() { return *this -= 1; } /** Postfix datacrement/decrement **/ TT operator++ (int) { TT ret = **this; return ++(*this), ret; } TT operator-- (int) { TT ret = **this; return --(*this), ret; } }; #endifМне энкодер как устройство регулировки давно перестало нравится. Глючное устройство.
)))
главное код работы с ним безглючный ))))
Мне энкодер как устройство регулировки давно перестало нравится. Глючное устройство.
перебирал библиотеки от разных авторов, даже на библиотеку Гайвера набрёл, нашёл ту, что подошла идеально, автор пишет, если ваш энкодер даёт глюки на моей библиотеке, то он настолько плох и раздолбан, что просто его выкиньте, эксперименты провожу с энкодером в чистом виде, ни резисторов ни конденсаторов в обвязке нет, пины вверх не притягиваю, видимо всё реализовано в библиотеке, твой код кнопок использую )))
Все верно, не знаю что за либа, но знаю что при правильной реализации все ок. И категорически, не при каких обстоятельствах, не вешать кондеры. С ними какбы дааже работает сразу лучше, но то только сразу. Они быстро ушатают контакт до состояния мусора. Их разряды окисляют и без того не самые стойкие контакты. После этого останется только снимать осцилограммы и публиковать их тут с нытьем "ну как с таким работать".
Все верно, не знаю что за либа, но знаю что при правильной реализации все ок. И категорически, не при каких обстоятельствах, не вешать кондеры. С ними какбы дааже работает сразу лучше, но то только сразу. Они быстро ушатают контакт до состояния мусора. Их разряды окисляют и без того не самые стойкие контакты. После этого останется только снимать осцилограммы и публиковать их тут с нытьем "ну как с таким работать".
У меня на YAESU FT-897 куча энкодеров, 20 лет полёт нормальный )))
Так отож. Китайские энкодеры пашут везде нормально, даже у самих китайцев, даже в автомагнитолах в суровых условиях. Везде кроме многих участников этого форума ))
Прикрутим экран lcd2004 через I2C
/**/ #include "qwonelib.h" //------------------- struct data_t { // некая пользовательская структура byte bData; int iData; float fData; }; byte index = 0; const byte max_index = 10; data_t EEMEM adr1[max_index]; // размещение ее в EEPROM byte setting_index = 0; const byte max_setting_index = 3; void setting(byte in) { static const PROGMEM data_t adr2[3] = { // набор констант для пользовательской функции {100, 1000, 10.10} // 0 комплект настроек , {101, 1001, 10.11} // 1 комплект настроек , {102, 1002, 10.12} // 2 комплект настроек }; (eeRef <byte>) (&adr1[in].bData) = (pgmRef<byte>) (&adr2[setting_index].bData); // загрузка байтовой части (eeRef <int>) (&adr1[in].iData) = (pgmRef<int>) (&adr2[setting_index].iData); // загрузка интовой части (eeRef <float>)(&adr1[in].fData) = (pgmRef<float>)(&adr2[setting_index].fData); // загрузка флоат части } //---кнопки----------------------------- typedef void (*pDo)(); class Cl_btn { protected: byte pin; bool state; unsigned long past; void set(bool s) { state = s; past = millis(); if (s == true) Do(); } public: Cl_btn(byte p): pin(p) {} pDo Do = [] {}; void init() { pinMode(pin, INPUT_PULLUP); set(false); } void run() { if (millis() - past >= 100) switch (state) { case false: if (!digitalRead(pin))set(true); break; case true: if (digitalRead(pin))set(false); if (millis() - past >= 300)set(false); break; } } }; Cl_btn BtnS(/*пин*/2); //кнопка селект Cl_btn BtnU(/*пин*/3); //кнопка верх Cl_btn BtnD(/*пин*/4); //кнопка вниз //--------------- меню--------------------------------------------- #include "lcd2004_i2c.h" /*template <typename T> inline Print & operator << (Print &s, T n) { s.print(n); return s; }*/ const byte page0 = 0; //ноказ настроек const byte page1 = 10;//поменять индекс устройства const byte page2 = 20;//изменение байтовой части const byte page3 = 30;//изменение интовой части const byte page4 = 40;//изменение флоат части const byte page5 = 50; //сброс настроек byte page; unsigned long past; void goPage(byte p) { past = millis(); page = p; lcd.clear(); switch (page) { case page0: BtnS.Do = [] {goPage(page1);}; BtnU.Do = [] {}; BtnD.Do = [] {}; lcd << "info:"; lcd.setCursor(0, 1); lcd << "byte(" << index << ") =" << (eeRef <byte>) (&adr1[index].bData); // вывод байтовой части lcd.setCursor(0, 2); lcd << "int(" << index << ") =" << (eeRef <int>) (&adr1[index].iData); // вывод интовой части lcd.setCursor(0, 3); lcd << "float(" << index << ")=" << (eeRef <float>)(&adr1[index].fData); // вывод флоат части break; case page1: BtnS.Do = [] {goPage(page2);}; BtnU.Do = [] { if (index < (max_index - 1))index++; goPage(page1); }; BtnD.Do = [] { if (index > 0)index--; goPage(page1); }; lcd << "set index:" << index; break; case page2: BtnS.Do = [] {goPage(page3);}; BtnU.Do = [] { if ((eeRef <byte>) (&adr1[index].bData) <= 200)(eeRef <byte>) (&adr1[index].bData) += 10; goPage(page2); }; BtnD.Do = [] { if ((eeRef <byte>) (&adr1[index].bData) >= 50)(eeRef <byte>) (&adr1[index].bData) -= 10; goPage(page2); }; lcd << "set byte(" << index << ")=" << (eeRef <byte>) (&adr1[index].bData); // вывод байтовой части break; case page3: BtnS.Do = [] {goPage(page4);}; BtnU.Do = [] { if ((eeRef <int>) (&adr1[index].iData) <= 2000)(eeRef <int>) (&adr1[index].iData) += 100; goPage(page3); }; BtnD.Do = [] { if ((eeRef <int>) (&adr1[index].iData) >= 500)(eeRef <int>) (&adr1[index].iData) -= 100; goPage(page3); }; lcd << "set int(" << index << ")=" << (eeRef <int>) (&adr1[index].iData); // вывод интовой части break; case page4: BtnS.Do = [] {goPage(page5);}; BtnU.Do = [] { if ((eeRef <float>) (&adr1[index].fData) <= 20.30)(eeRef <float>) (&adr1[index].fData) += 1.01; goPage(page4); }; BtnD.Do = [] { if ((eeRef <float>) (&adr1[index].fData) >= 5.05)(eeRef <float>) (&adr1[index].fData) -= 1.01; goPage(page4); }; lcd << "set float(" << index << ")=" << (eeRef <float>)(&adr1[index].fData); // вывод флоат части break; case page5: BtnS.Do = [] {goPage(page0);}; BtnU.Do = [] { if (setting_index < (max_setting_index - 1))setting_index++; setting(index); goPage(page5); }; BtnD.Do = [] { if (setting_index > 0)setting_index--; setting(index); goPage(page5); }; lcd << "setting(" << setting_index << ")"; lcd.setCursor(0, 1); lcd << "byte(" << index << ") =" << (eeRef <byte>) (&adr1[index].bData) ; // вывод байтовой части lcd.setCursor(0, 2); lcd << "int(" << index << ") =" << (eeRef <int>) (&adr1[index].iData) ; // вывод интовой части lcd.setCursor(0, 3); lcd << "float(" << index << ")=" << (eeRef <float>)(&adr1[index].fData) ; // вывод флоат части break; } lcd.show(); } void menu_init() { lcd.init(); lcd.backlight(); goPage(page0); } void menu_run() {} //---------------------------------------- void setup() { BtnS.init(); BtnU.init(); BtnD.init(); menu_init(); } void loop() { BtnS.run(); BtnU.run(); BtnD.run(); menu_run(); }qwonelib.h
/*qwonelib.h*/ #ifndef QWONELIB_H #define QWONELIB_H #include <avr/pgmspace.h> template <typename TT> struct pgmRef { uint8_t* adr; pgmRef( const TT *_adr): adr((uint8_t*)_adr) {} void setNewAdr(const TT *_adr) { adr = (uint8_t*)_adr; } TT operator*() const { TT data; uint8_t* p = (uint8_t*)&data; for (uint8_t i = 0; i < sizeof(TT); i++) p[i] = pgm_read_byte(adr + i); return data; } operator TT() const { return **this; } }; #include <avr/eeprom.h> template <typename TT> struct eeRef { uint8_t* adr; eeRef( const TT *_adr): adr((uint8_t*)_adr) {} void setNewAdr(const TT *_adr) { adr = (uint8_t*)_adr; } TT operator*() const { TT data; uint8_t* p = (uint8_t*)&data; for (uint8_t i = 0; i < sizeof(TT); i++) p[i] = eeprom_read_byte(adr + i); return data; } operator TT() const { return **this; } //Assignment/write members. eeRef &operator=( const eeRef &ref ) { return *this = *ref; } eeRef &operator=( TT data ) { uint8_t* p = (uint8_t*)&data; for (uint8_t i = 0; i < sizeof(TT); i++) if ( p[i] != eeprom_read_byte(adr + i)) eeprom_write_byte(adr + i, p[i]); return *this; } eeRef &operator +=( TT data ) { return *this = **this + data; } eeRef &operator -=( TT data ) { return *this = **this - data; } eeRef &operator *=( TT data ) { return *this = **this * data; } eeRef &operator /=( TT data ) { return *this = **this / data; } eeRef &operator ^=( TT data ) { return *this = **this ^ data; } eeRef &operator %=( TT data ) { return *this = **this % data; } eeRef &operator &=( TT data ) { return *this = **this & data; } eeRef &operator |=( TT data ) { return *this = **this | data; } eeRef &operator <<=( TT data ) { return *this = **this << data; } eeRef &operator >>=( TT data ) { return *this = **this >> data; } eeRef &update( TT data ) { return data != *this ? *this = data : *this; } /** Prefix datacrement/decrement **/ eeRef& operator++() { return *this += 1; } eeRef& operator--() { return *this -= 1; } /** Postfix datacrement/decrement **/ TT operator++ (int) { TT ret = **this; return ++(*this), ret; } TT operator-- (int) { TT ret = **this; return --(*this), ret; } }; #endiflcd2004_i2c.h
/*lcd2004_i2c.h*/ #ifndef LCD2004_I2C_H #define LCD2004_I2C_H #include <Wire.h> // команды #define LCD_CLEARDISPLAY 0x01 #define LCD_RETURNHOME 0x02 #define LCD_ENTRYMODESET 0x04 #define LCD_DISPLAYCONTROL 0x08 #define LCD_CURSORSHIFT 0x10 #define LCD_FUNCTIONSET 0x20 #define LCD_SETCGRAMADDR 0x40 #define LCD_SETDDRAMADDR 0x80 // флаги для режима ввода дисплея #define LCD_ENTRYRIGHT 0x00 #define LCD_ENTRYLEFT 0x02 #define LCD_ENTRYSHIFTINCREMENT 0x01 #define LCD_ENTRYSHIFTDECREMENT 0x00 // флаги для управления включением / выключением дисплея #define LCD_DISPLAYON 0x04 #define LCD_DISPLAYOFF 0x00 #define LCD_CURSORON 0x02 #define LCD_CURSOROFF 0x00 #define LCD_BLINKON 0x01 #define LCD_BLINKOFF 0x00 // флаги для отображения / сдвига курсора #define LCD_DISPLAYMOVE 0x08 #define LCD_CURSORMOVE 0x00 #define LCD_MOVERIGHT 0x04 #define LCD_MOVELEFT 0x00 // флаги для набора функций #define LCD_8BITMODE 0x10 #define LCD_4BITMODE 0x00 #define LCD_2LINE 0x08 #define LCD_1LINE 0x00 #define LCD_5x10DOTS 0x04 #define LCD_5x8DOTS 0x00 // флаги для управления подсветкой #define LCD_BACKLIGHT 0x08 #define LCD_NOBACKLIGHT 0x00 #define En B00000100 // Бит разрешения #define Rw B00000010 // Чтение / запись бит #define Rs B00000001 // Бит выбора регистра class Cl_lcd2004_i2c : public Print { protected: uint8_t adr, posX, posY; uint8_t _backlightval; uint8_t _displayfunction; uint8_t _displaycontrol; uint8_t _displaymode; byte buffer[80]; public: Cl_lcd2004_i2c(uint8_t a): adr(a) {} void init() { Wire.begin(); write4bits(0x03 << 4); delayMicroseconds(4500); // wait min 4.1ms write4bits(0x03 << 4); delayMicroseconds(4500); // wait min 4.1ms write4bits(0x03 << 4); delayMicroseconds(150); write4bits(0x02 << 4); delay(50); _backlightval = LCD_NOBACKLIGHT; expanderWrite(_backlightval); // сбросить расширение и выключить подсветку (бит 8 = 1) // режим работы дисплея 4-х битный интерфейс ,две строки,5х8 точек _displayfunction = LCD_4BITMODE | LCD_2LINE | LCD_5x8DOTS; command(LCD_FUNCTIONSET | _displayfunction); // режим контроля дисплей вкл, курсор не мигает,дисплей не мигает _displaycontrol = LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF; command(LCD_DISPLAYCONTROL | _displaycontrol); // режим ввода текста в память-начинать слева с движением в право _displaymode = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT; command(LCD_ENTRYMODESET | _displaymode); clear(); } // подсветку вкл/выкл void backlight(void) { _backlightval = LCD_BACKLIGHT; expanderWrite(0); } void noBacklight(void) { _backlightval = LCD_NOBACKLIGHT; expanderWrite(0); } // очистить буфер void clear() { for (byte i = 0; i < 80; i++ )buffer[i] = 0x20; posX = 0; posY = 0; } // отправить информацию из буфера на экран void show() { line(0); for (byte i = 0; i < 20; i++ )out(buffer[i]); line(1); for (byte i = 20; i < 40; i++ )out(buffer[i]); line(2); for (byte i = 40; i < 60; i++ )out(buffer[i]); line(3); for (byte i = 60; i < 80; i++ )out(buffer[i]); } void setCursor(byte x, byte y) { if (x > 20) x = 20; else posX = x; if (y > 4) x = 4; else posY = y; } inline size_t write(uint8_t value) { if (posX < 20 && posY < 4) { buffer[posY * 20 + posX] = value; posX++; } return 1; } private: /*внутрение функции*/ void line(byte l) { switch (l) { case 0: command(LCD_SETDDRAMADDR + 0x00); break; case 1: command(LCD_SETDDRAMADDR + 0x40); break; case 2: command(LCD_SETDDRAMADDR + 0x14); break; case 3: command(LCD_SETDDRAMADDR + 0x54); break; } } inline void out(uint8_t value) { send(value, Rs); } /*команды низкоуровневого управления*/ inline void command(uint8_t value) { send(value, 0); } void send(uint8_t value, uint8_t mode) { uint8_t highnib = value & 0xf0; write4bits((highnib) | mode); uint8_t lownib = (value << 4) & 0xf0; write4bits((lownib) | mode); } void write4bits(uint8_t value) { expanderWrite(value); pulseEnable(value); } void expanderWrite(uint8_t _data) { Wire.beginTransmission(adr); Wire.write((int)(_data) | _backlightval); Wire.endTransmission(); } void pulseEnable(uint8_t _data) { expanderWrite(_data | En); // En high delayMicroseconds(1); // enable pulse must be >450ns expanderWrite(_data & ~En); // En low delayMicroseconds(50); // commands need > 37us to settle } }; typedef void (Cl_lcd2004_i2c::*clDo)(); // <- тип переменной метод класса Cl_lcd2004_i2c inline Cl_lcd2004_i2c & operator <<(Cl_lcd2004_i2c &s, clDo Do) { (s.*Do)(); return s; } template <typename T> inline Cl_lcd2004_i2c & operator << (Cl_lcd2004_i2c &s, T n) { s.print(n); return s; } //.cpp---------------------------- Cl_lcd2004_i2c lcd(0x3F);//0x27 #endifСделаем гашение экрана, когда долго не работаем с кнопками
/**/ #include "qwonelib.h" //------------------- struct data_t { // некая пользовательская структура byte bData; int iData; float fData; }; byte index = 0; const byte max_index = 10; data_t EEMEM adr1[max_index]; // размещение ее в EEPROM byte setting_index = 0; const byte max_setting_index = 5; void setting(byte in) { static const PROGMEM data_t adr2[max_setting_index] = { // набор констант для пользовательской функции {100, 1000, 10.10} // 0 комплект настроек , {101, 1001, 10.11} // 1 комплект настроек , {102, 1002, 10.12} // 2 комплект настроек , {103, 1003, 10.13} // 3 комплект настроек , {104, 1004, 10.14} // 4 комплект настроек }; (eeRef <byte>) (&adr1[in].bData) = (pgmRef<byte>) (&adr2[setting_index].bData); // загрузка байтовой части (eeRef <int>) (&adr1[in].iData) = (pgmRef<int>) (&adr2[setting_index].iData); // загрузка интовой части (eeRef <float>)(&adr1[in].fData) = (pgmRef<float>)(&adr2[setting_index].fData); // загрузка флоат части } //---кнопки----------------------------- typedef void (*pDo)(); class Cl_btn { protected: byte pin; bool state; unsigned long past; void set(bool s) { state = s; past = millis(); if (s == true) Do(); } public: Cl_btn(byte p): pin(p) {} pDo Do = [] {}; void init() { pinMode(pin, INPUT_PULLUP); set(false); } void run() { if (millis() - past >= 100) switch (state) { case false: if (!digitalRead(pin))set(true); break; case true: if (digitalRead(pin))set(false); if (millis() - past >= 300)set(false); break; } } }; Cl_btn BtnS(/*пин*/2); //кнопка селект Cl_btn BtnU(/*пин*/3); //кнопка верх Cl_btn BtnD(/*пин*/4); //кнопка вниз //--------------- меню--------------------------------------------- #include "lcd2004_i2c.h" const byte page0 = 0;//гашение экрана const byte page1 = 10;//ноказ настроек const byte page2 = 20;//поменять индекс устройства const byte page3 = 30;//изменение байтовой части const byte page4 = 40;//изменение интовой части const byte page5 = 50;//изменение флоат части const byte page6 = 60; //сброс настроек byte page; unsigned long past; void goPage(byte p) { past = millis(); page = p; qGTX display(LCD_W, LCD_H); switch (page) { case page0: BtnS.Do = [] {goPage(page1);}; BtnU.Do = [] {goPage(page1);}; BtnD.Do = [] {goPage(page1);}; lcd.noBacklight(); break; case page1: BtnS.Do = [] {goPage(page2);}; BtnU.Do = [] {}; BtnD.Do = [] {}; lcd.backlight(); display << xy_t(0, 0) << F("info:"); display << xy_t(0, 1) << F(" byte(") << index << F(")=") << (eeRef <byte>) (&adr1[index].bData); // вывод байтовой части display << xy_t(0, 2) << F(" int(") << index << F(")=") << (eeRef <int>) (&adr1[index].iData); // вывод интовой части display << xy_t(0, 3) << F("float(") << index << F(")=") << (eeRef <float>)(&adr1[index].fData); // вывод флоат части break; case page2: BtnS.Do = [] {goPage(page3);}; BtnU.Do = [] { if (index < (max_index - 1))index++; goPage(page2); }; BtnD.Do = [] { if (index > 0)index--; goPage(page2); }; display << xy_t(0, 0) << F("set index:") << index; break; case page3: BtnS.Do = [] {goPage(page4);}; BtnU.Do = [] { if ((eeRef <byte>) (&adr1[index].bData) <= 200)(eeRef <byte>) (&adr1[index].bData) += 10; goPage(page3); }; BtnD.Do = [] { if ((eeRef <byte>) (&adr1[index].bData) >= 50)(eeRef <byte>) (&adr1[index].bData) -= 10; goPage(page3); }; display << xy_t(0, 0) << F("set byte(") << index << F(")=") << (eeRef <byte>) (&adr1[index].bData); // вывод байтовой части break; case page4: BtnS.Do = [] {goPage(page5);}; BtnU.Do = [] { if ((eeRef <int>) (&adr1[index].iData) <= 2000)(eeRef <int>) (&adr1[index].iData) += 100; goPage(page4); }; BtnD.Do = [] { if ((eeRef <int>) (&adr1[index].iData) >= 500)(eeRef <int>) (&adr1[index].iData) -= 100; goPage(page4); }; display << xy_t(0, 0) << F("set int(") << index << ")=" << (eeRef <int>) (&adr1[index].iData); // вывод интовой части break; case page5: BtnS.Do = [] {goPage(page6);}; BtnU.Do = [] { if ((eeRef <float>) (&adr1[index].fData) <= 20.30)(eeRef <float>) (&adr1[index].fData) += 1.01; goPage(page5); }; BtnD.Do = [] { if ((eeRef <float>) (&adr1[index].fData) >= 5.05)(eeRef <float>) (&adr1[index].fData) -= 1.01; goPage(page5); }; display << xy_t(0, 0) << F("set float(") << index << F(")=") << (eeRef <float>)(&adr1[index].fData); // вывод флоат части break; case page6: BtnS.Do = [] {goPage(page1);}; BtnU.Do = [] { if (setting_index < (max_setting_index - 1))setting_index++; setting(index); goPage(page6); }; BtnD.Do = [] { if (setting_index > 0)setting_index--; setting(index); goPage(page6); }; display << xy_t(0, 0) << F("setting(") << setting_index << F(")"); display << xy_t(0, 1) << F(" byte(") << index << F(")=") << (eeRef <byte>) (&adr1[index].bData) ; // вывод байтовой части display << xy_t(0, 2) << F(" int(") << index << F(")=") << (eeRef <int>) (&adr1[index].iData) ; // вывод интовой части display << xy_t(0, 3) << F("float(") << index << F(")=") << (eeRef <float>)(&adr1[index].fData) ; // вывод флоат части break; } lcd.show(display); } void menu_init() { lcd.init(); goPage(page1); } void menu_run() { switch (page) { case page0: break; default: if (millis() - past >= 10000)goPage(page0);// если долго не щелкают по кнопкам то погасить экран break; } } //---------------------------------------- void setup() { BtnS.init(); BtnU.init(); BtnD.init(); menu_init(); } void loop() { BtnS.run(); BtnU.run(); BtnD.run(); menu_run(); }qwonelib.h
/*qwonelib.h*/ #ifndef QWONELIB_H #define QWONELIB_H #include <avr/pgmspace.h> template <typename TT> struct pgmRef { uint8_t* adr; pgmRef( const TT *_adr): adr((uint8_t*)_adr) {} void setNewAdr(const TT *_adr) { adr = (uint8_t*)_adr; } TT operator*() const { TT data; uint8_t* p = (uint8_t*)&data; for (uint8_t i = 0; i < sizeof(TT); i++) p[i] = pgm_read_byte(adr + i); return data; } operator TT() const { return **this; } }; #include <avr/eeprom.h> template <typename TT> struct eeRef { uint8_t* adr; eeRef( const TT *_adr): adr((uint8_t*)_adr) {} void setNewAdr(const TT *_adr) { adr = (uint8_t*)_adr; } TT operator*() const { TT data; uint8_t* p = (uint8_t*)&data; for (uint8_t i = 0; i < sizeof(TT); i++) p[i] = eeprom_read_byte(adr + i); return data; } operator TT() const { return **this; } //Assignment/write members. eeRef &operator=( const eeRef &ref ) { return *this = *ref; } eeRef &operator=( TT data ) { uint8_t* p = (uint8_t*)&data; for (uint8_t i = 0; i < sizeof(TT); i++) if ( p[i] != eeprom_read_byte(adr + i)) eeprom_write_byte(adr + i, p[i]); return *this; } eeRef &operator +=( TT data ) { return *this = **this + data; } eeRef &operator -=( TT data ) { return *this = **this - data; } eeRef &operator *=( TT data ) { return *this = **this * data; } eeRef &operator /=( TT data ) { return *this = **this / data; } eeRef &operator ^=( TT data ) { return *this = **this ^ data; } eeRef &operator %=( TT data ) { return *this = **this % data; } eeRef &operator &=( TT data ) { return *this = **this & data; } eeRef &operator |=( TT data ) { return *this = **this | data; } eeRef &operator <<=( TT data ) { return *this = **this << data; } eeRef &operator >>=( TT data ) { return *this = **this >> data; } eeRef &update( TT data ) { return data != *this ? *this = data : *this; } /** Prefix datacrement/decrement **/ eeRef& operator++() { return *this += 1; } eeRef& operator--() { return *this -= 1; } /** Postfix datacrement/decrement **/ TT operator++ (int) { TT ret = **this; return ++(*this), ret; } TT operator-- (int) { TT ret = **this; return --(*this), ret; } }; #endiflcd2004_i2c.h
/*lcd2004_i2c.h*/ //.h---------------------------- #ifndef LCD2004_I2C_H #define LCD2004_I2C_H //--класс обработки данных ------------------------------------------ class Cl_lcd2004_i2c; struct xy_t { uint8_t x, y; explicit xy_t(int _x, int _y) : x(_x), y(_y) {} }; class qGTX: public Print { friend class Cl_lcd2004_i2c; protected: uint8_t *buffer; uint8_t W, H, posX, posY; public: qGTX(uint8_t w, uint8_t h) { if (w == 0) w = 1; if (h == 0) h = 1; W = w; H = h; buffer = new uint8_t[W * H]; clear(); } ~qGTX() { delete[] buffer; } inline size_t write(uint8_t value) { if (posX < W && posY < H) { buffer[posY * W + posX] = value; posX++; } return 1; } void setCursor(byte x, byte y) { if (x > W) x = W; else posX = x; if (y > H) x = H; else posY = y; } void clear() { for (byte i = 0; i < W * H; i++) buffer[i] = 0x20; posX = 0; posY = 0; } }; template <typename T> qGTX & operator << (qGTX &s,const T n) { s.print(n); return s; } template <> qGTX & operator << <struct xy_t>(qGTX &s, const struct xy_t n) { s.setCursor(n.x, n.y); return s; } //-- канальный класс Cl_lcd2004_i2c----------------------------------------- #include <Wire.h> // команды #define LCD_CLEARDISPLAY 0x01 #define LCD_RETURNHOME 0x02 #define LCD_ENTRYMODESET 0x04 #define LCD_DISPLAYCONTROL 0x08 #define LCD_CURSORSHIFT 0x10 #define LCD_FUNCTIONSET 0x20 #define LCD_SETCGRAMADDR 0x40 #define LCD_SETDDRAMADDR 0x80 // флаги для режима ввода дисплея #define LCD_ENTRYRIGHT 0x00 #define LCD_ENTRYLEFT 0x02 #define LCD_ENTRYSHIFTINCREMENT 0x01 #define LCD_ENTRYSHIFTDECREMENT 0x00 // флаги для управления включением / выключением дисплея #define LCD_DISPLAYON 0x04 #define LCD_DISPLAYOFF 0x00 #define LCD_CURSORON 0x02 #define LCD_CURSOROFF 0x00 #define LCD_BLINKON 0x01 #define LCD_BLINKOFF 0x00 // флаги для отображения / сдвига курсора #define LCD_DISPLAYMOVE 0x08 #define LCD_CURSORMOVE 0x00 #define LCD_MOVERIGHT 0x04 #define LCD_MOVELEFT 0x00 // флаги для набора функций #define LCD_8BITMODE 0x10 #define LCD_4BITMODE 0x00 #define LCD_2LINE 0x08 #define LCD_1LINE 0x00 #define LCD_5x10DOTS 0x04 #define LCD_5x8DOTS 0x00 // флаги для управления подсветкой #define LCD_BACKLIGHT 0x08 #define LCD_NOBACKLIGHT 0x00 // размеры экрана #define LCD_W 20 #define LCD_H 4 #define En B00000100 // Бит разрешения #define Rw B00000010 // Чтение / запись бит #define Rs B00000001 // Бит выбора регистра class Cl_lcd2004_i2c { protected: uint8_t adr; uint8_t _backlightval; uint8_t _displayfunction; uint8_t _displaycontrol; uint8_t _displaymode; public: Cl_lcd2004_i2c(uint8_t a): adr(a) {} void init() { Wire.begin(); write4bits(0x03 << 4); delayMicroseconds(4500); // wait min 4.1ms write4bits(0x03 << 4); delayMicroseconds(4500); // wait min 4.1ms write4bits(0x03 << 4); delayMicroseconds(150); write4bits(0x02 << 4); delay(50); _backlightval = LCD_NOBACKLIGHT; expanderWrite(_backlightval); // сбросить расширение и выключить подсветку (бит 8 = 1) // режим работы дисплея 4-х битный интерфейс ,две строки,5х8 точек _displayfunction = LCD_4BITMODE | LCD_2LINE | LCD_5x8DOTS; command(LCD_FUNCTIONSET | _displayfunction); // режим контроля дисплей вкл, курсор не мигает,дисплей не мигает _displaycontrol = LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF; command(LCD_DISPLAYCONTROL | _displaycontrol); // режим ввода текста в память-начинать слева с движением в право _displaymode = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT; command(LCD_ENTRYMODESET | _displaymode); } // подсветку вкл/выкл void backlight(void) { _backlightval = LCD_BACKLIGHT; expanderWrite(0); } void noBacklight(void) { _backlightval = LCD_NOBACKLIGHT; expanderWrite(0); } // отправить информацию из qGTX на экран void show(const qGTX &obj) { line(0); for (byte i = 0; i < obj.W; i++ )out(obj.buffer[i]); line(1); for (byte i = obj.W; i < 2 * obj.W; i++ )out(obj.buffer[i]); line(2); for (byte i = 2 * obj.W; i < 3 * obj.W; i++ )out(obj.buffer[i]); line(3); for (byte i = 3 * obj.W; i < 4 * obj.W; i++ )out(obj.buffer[i]); } private: /*внутрение функции*/ void line(byte l) { switch (l) { case 0: command(LCD_SETDDRAMADDR + 0x00); break; case 1: command(LCD_SETDDRAMADDR + 0x40); break; case 2: command(LCD_SETDDRAMADDR + 0x14); break; case 3: command(LCD_SETDDRAMADDR + 0x54); break; } } inline void out(uint8_t value) { send(value, Rs); } /*команды низкоуровневого управления*/ inline void command(uint8_t value) { send(value, 0); } void send(uint8_t value, uint8_t mode) { uint8_t highnib = value & 0xf0; write4bits((highnib) | mode); uint8_t lownib = (value << 4) & 0xf0; write4bits((lownib) | mode); } void write4bits(uint8_t value) { expanderWrite(value); pulseEnable(value); } void expanderWrite(uint8_t _data) { Wire.beginTransmission(adr); Wire.write((int)(_data | _backlightval)); Wire.endTransmission(); } void pulseEnable(uint8_t _data) { expanderWrite(_data | En); // En high delayMicroseconds(1); // enable pulse must be >450ns expanderWrite(_data & ~En); // En low delayMicroseconds(50); // commands need > 37us to settle } }; //.cpp---------------------------- //qGTX display(20, 4); Cl_lcd2004_i2c lcd(0x3F);//0x27 #endifработает )))