Создание библиотек в среде Arduino

michail36
Offline
Зарегистрирован: 31.07.2015

При написании программы для Arduino UNO всвязи в большим количеством функций возникла необходимость вынести их в отдельный файл или создать библиотеку ( в идеале).

Нашел две темы на вашем форуме, ссылки привожу: http://arduino.ru/forum/programmirovanie/kak-vynesti-funktsii-v-otdelnyi-fail-podklyuchaemyi-k-proektu, http://arduino.ru/Hacking/LibraryTutorial

Однако создать файлы с расширением "h" или "cpp" в среде Arduino невозможно.

Более того эти файлы и не открываются в среде Arduino. Можно, конечно, вынести их в текстовый редактор, например, WordPad или Блокнот, но как эти файлы компилировать, да и очень неудобно писать программу.  Убедительная просьба, к тем, кто сталкивался с такой задачей, подскажите, что нужно сделать без установки Visual Studio.
ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

michail36 пишет:

Однако создать файлы с расширением "h" или "cpp" в среде Arduino невозможно.

 
Кто Вам это сказал?
 
michail36 пишет:
Более того эти файлы и не открываются в среде Arduino. 
 
А это Вам кто сказал? Плюньте ему в рожу!
ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

1. Открываете любой скетч

2. В меню выбираете Эскиз|Добавить файл

3. Идетё к папке в которой лежит открытый скетч

4. Дойдя до папки, просто набираете имя файла (.cpp или .h) и файл создаётся и остаётся открытым у Вас в дополнительной вкладке.

Как отладите, переносите библиотеку в папку libraries или просто оставляете многофайловый скетч - я постоянно так делаю

michail36
Offline
Зарегистрирован: 31.07.2015

Евгений, за совет спасибо. Попробовал. Работает. Буду разбираться дальше. 

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Тут правда, есть одна неприятность: среда почему-то открывает сразу все файлы, лежащие в текущей папаке. Если их больше 3-4 пар, то все вкладки в окошке уже не помещаются. Выбрать их, коначно, можно из ниспадающего списка, но IDE не показывает, с каким именно файлом в данный момет работаешь (если он не входит в число тех нескольких первых, которые помещаются на экране).

Gippopotam
Gippopotam аватар
Offline
Зарегистрирован: 12.09.2014

andriano пишет:

Тут правда, есть одна неприятность: среда почему-то открывает сразу все файлы, лежащие в текущей папаке. Если их больше 3-4 пар, то все вкладки в окошке уже не помещаются. Выбрать их, коначно, можно из ниспадающего списка, но IDE не показывает, с каким именно файлом в данный момет работаешь (если он не входит в число тех нескольких первых, которые помещаются на экране).

это не неприятность а фишка, причем очень удобная.

насчет вкладок - кто запрещает растянуть окно пошире, или вообще развернуть?

kisoft
kisoft аватар
Offline
Зарегистрирован: 13.11.2012

А еще можно дисплей купить пошире ;)

Клапауций 321
Offline
Зарегистрирован: 17.12.2015

kisoft пишет:

А еще можно дисплей купить пошире ;)

три

Iwan73
Offline
Зарегистрирован: 02.11.2013

для ассемблерных портянок переворачиваем дисплей на 90 градусов,для ардуины добавляем 3 дисплея, ага?

ура,в 1.6.7 наконец появилось сворачивание блоков,может и вкладки сдвигаются,не пробовал,23 дюйма пока хватает и поработать и поиграться

sandr4
sandr4 аватар
Offline
Зарегистрирован: 11.02.2015

У меня вооще 80см по диогонали телевизор в место монитора. :-)

alexnik100
Offline
Зарегистрирован: 21.12.2015

Привет коллеги!

Подскажите где скачать библиотеку "ethernetudp.h"?

Заранее спасибо.

 

 

Jeka_M
Jeka_M аватар
Offline
Зарегистрирован: 06.07.2014

Ничего скачивать не надо. Это файл из библиотеки Ethernet, которая изначально есть в Arduino IDE.

michail36
Offline
Зарегистрирован: 31.07.2015

Господа, в продолжение темы.

Выяснилась одна неприятность.

В файлы "cpp" и "h" перенес функции из основной программы.

Первое с чем стокнулся. Глобальные переменные - назначение входов/выходов контроллера, объявленные

 в основной программе,  функции в "cpp" файле не видят. Хотя по моему мнению они должны быть видны для всего проекта. 

Второе. В исходной программе была подключена библиотека Ultrasonic.h. Так как ее нет в составе библиотек Arduino,  пришлось загружать ее в среду через zip файл. В Include Library она оказалась в разделе "Contributed libraries". Принимаю на веру такое подключение сторонних библиотек.

В исходной программе библиотека подключилась и работает. Но "cpp" файл ее не видит. Включил в "cpp" файл

#include <Ultrasonic.h>. Теперь ругается компилятор: 

sketch\lib_Robot_truck.cpp:1:24: fatal error: Ultrasonic.h: No such file or directory
 #include <Ultrasonic.h>

Что я делаю не так???

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

michail36 пишет:

Первое с чем стокнулся. Глобальные переменные - назначение входов/выходов контроллера, объявленные  в основной программе,  функции в "cpp" файле не видят. Хотя по моему мнению они должны быть видны для всего проекта. 

А Вы их там объявили? Если нет, то с какого перепугу они будут видны? Глобальную переменную, определённую в одном файле, в другом необходимо объявить с аттрибутом extern

Про библиотеку. Победить безусловно можно, но я не слишком много балуюсь со сторонникими библиотеками, поэтому заочно дать рецепт не могу. Будь проблема у меня. я бы поэкспериментировал и победил бы.

Есть "лом", против которого не приёма - забудьте про zip и разместие её честно вместе со всеми системными библиотеками - должно сработать.

Gres
Gres аватар
Offline
Зарегистрирован: 26.03.2013

Если подняться вверх этой страницы, то Вы увидите ссылку: "Программирование", перейдя по ней, увидите другую ссылку: "Написание библиотеки для Arduino".

Ну а вообще, можно скачать любую, желательно простенькую библиотеку и посмотреть, как там все организовано.

alexnik100
Offline
Зарегистрирован: 21.12.2015

Спасибо за ответ!

И еще вопросы, если не затруднит.

Я провел небольшое исследование по совместимости сред разработки и различных скетчей для Ping:

http://arduino.ru/forum/programmirovanie/platformy-arduino

Платформы Arduino.

Что посоветуете?

С уважением, alexnik100

 

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Gippopotam пишет:

это не неприятность а фишка, причем очень удобная.

насчет вкладок - кто запрещает растянуть окно пошире, или вообще развернуть?

Интересно, наскоько нужно распахнуть окно, чтобы в нем помещались названия 60 файлов. При том, что осмысленные названия, как правило, не короче 15-20 символов.

А еще если синхронно программируешь две Ардуины, и нужно два окошка IDE и две консоди COM-порта...

В общем, для серьезных проектов фишка ОЧЕНЬ неудобная.

Алек97
Offline
Зарегистрирован: 21.08.2018

Здравствуйте, ранее не создал собственных библиотек в среде,  Создал папку "common" и перенес туда все реализации h*,c*. После перенес ее в хранилище сторонних библиотек (Мои документы\Arduino\libraries). Но среда сильно ругается, подскажите как решить

Файл с расширением .ino

#include "common.h"

#include <LiquidCrystal.h>
LiquidCrystal lcd(7, 6, 5, 4, 3, 2);

//#######################################################################################################################
uint8_t pageNum = 0;          // счетчик нажатий кнопки

enum switchVariants : byte  {  // Определения для переключателя пунктов меню;
  MAIN_MENU, MENU_MANUAL, MENU_AUTO, MENU_SETUP, MENU_DEBUG//, EXITSAVE
};
switchVariants switchPointer = MAIN_MENU; // С чего начнем цикл;

enum manuState : byte {  // Определения для переключателя ручного режима;
  EDIT_PARAM, HEAT_ZONE, COOL_ZONE, STOP_PROCESS, EXIT_PROCESS
};
manuState mode = EDIT_PARAM; // С чего начнем цикл;

static SELECTION menu_m0[]={
{MENU_MANUAL , MAIN_MENU},
};
static SELECTION menu_m1[]={
{MENU_AUTO, MAIN_MENU}, 
};
static SELECTION menu_m2[]={
{MENU_SETUP, MAIN_MENU} 
};
static SELECTION menu_m3[]={
{MENU_DEBUG, MAIN_MENU}
};

static PAGE_MENU menu[] = {
  menu_m0, //Меню 1
  menu_m1, //Меню 2
  menu_m2, //Меню 3
  menu_m3  //Меню 4
};

const uint8_t MN000[] PROGMEM="REWORK v05\0";
//меню 1
const uint8_t MN100[] PROGMEM="SETTINGS\0";
//подпункт меню
const uint8_t MN101[] PROGMEM="STEPS:\0";
const uint8_t MN102[] PROGMEM="DWELL:\0";
const uint8_t MN103[] PROGMEM="PWR:\0";
const uint8_t MN104[] PROGMEM="RAMP:\0";
const uint8_t MN105[] PROGMEM="TARGET:\0";
//меню 2
const uint8_t MN200[] PROGMEM="MANUAL\0";
//подпункт меню 2
const uint8_t MN201[] PROGMEM="EDIT\0";
const uint8_t MN202[] PROGMEM="HEAT\0";
//меню 3
const uint8_t MN300[] PROGMEM="AUTO\0";
//подпункт меню 3
const uint8_t MN301[] PROGMEM="TEMP\0";
//меню 4
const uint8_t MN400[] PROGMEM="DEBUG\0";
//подпункт меню 4
const uint8_t MN401[] PROGMEM="COM\0";
//Массивы указателей на строки меню, хранящиеся на флэш
const uint8_t *MENU[] ={
    MN100,  //menu 1 string
    MN200,  //menu 2 string
    MN300,  //menu 3 string
    MN400   //menu 4 string
};
const uint8_t *SUBMENU[] ={
    MN101, MN102, MN103, MN104, MN105,  //подпункты меню 1
    MN201, MN202,                       //подпункты меню 2
    MN301,                              //подпункты меню 3
    MN401,                              //подпункты меню 4
};

//Структура меню
//[0] -Number of level 0 menu items
//[1]...[n] number of second level menu items
//Eg. MSTR2[1] shows that menu item 1 has 3 subMenus
const uint8_t MSTR2[] PROGMEM ={
  4,  // количество пунктов меню
  5,  //Количество подпунктов в пункте меню 1
  2,  //Количество подпунктов в пункте меню 2
  1,  //Количество подпунктов в пункте меню 3
  1   //Количество подпунктов в пункте меню 4
  }; 
//Прототипы функций
//Инициализация Timer2 
void init_timer2(void);
//Начальное меню
//void menu_Init(void);
//Назначаем порты для кнопок и светодиодов
void ports_Init(void);
//Функции для каждого пункта меню
void func101(void);
void func102(void);
void func103(void);
void func104(void);
void func105(void);
void func201(void);
void func202(void);
void func301(void);
void func401(void);

//#define NULL_FUNC  (void*)0

//Массив указателей на функции во флэш
const FuncPtr FuncPtrTable[] PROGMEM=
{   
  func101, func102, func103, func104, func105,  //functions for subMenus of menu 1
  func201, func202,     //functions for subMenus of menu 2
  func301,             //functions for subMenus of menu 3
  func401              //functions for subMenus of menu 4
};
//subMenu and Function table pointer update
uint8_t MFIndex(uint8_t, uint8_t);
//------------------------------------------

//#######################################################################################################################

//current editing step pointer
uint8_t editStep = 0;

struct reflowStation : public Printable {
        uint8_t rampRateStep = 0; //
        uint8_t temperatureStep = 0; //
        uint8_t dwellTimerStep = 0; //
        uint8_t pwr_TOP = 60;     //максимальная мощность верхнего нагревателя
        uint8_t pwr_BOTTOM = 90;  //максимальная мощность нижнего нагревателя           

  size_t printTo(Print& p) const { 
    size_t res;
    
    if (MN.subMenu == 1) res = p.print(pwr_BOTTOM);
    else if (MN.subMenu == 2) res = p.print(pwr_TOP);
    else if (MN.subMenu == 3) res = p.print(rampRateStep);
    else if (MN.subMenu == 4) res = p.print(temperatureStep);
    else if (MN.subMenu == 5) res = p.print(dwellTimerStep);
    return res;
  }
} profile;

//##################################################################################


void setup() {
  //Serial.begin(9600);
  lcd.begin(20, 4);
  lcd.home();
  //Приветствие
  CopyStringtoLCD(MN000, 3, 1);
  _delay_ms(100);
  lcd.clear();
  //Настройка таймера 2
  ports_Init();
  init_timer2(); // каждые 100 Гц
  sei(); // разрешаем прерывания глобально interrupts(); 
}

void loop() {
  
  char BtnMask = BtnGet();

  /*
  lcd.clear();
  //Display menu item
  CopyStringtoLCD(MENU[MN.menu-1], 0, 0 );
  //Display subMenu item
  CopyStringtoLCD(SUBMENU[MFIndex(MN.menu, MN.subMenu)], 0, 1 );
  //Assign function to function pointer
  FPtr=(FuncPtr)pgm_read_word(&FuncPtrTable[MFIndex(MN.menu, MN.subMenu)]);
  */

  if (BtnMask == BTN_SHRT_OK)      
  { 
       lcd.clear();
       static uint8_t i = 0;
       if (!i) {
         switchPointer = (switchVariants) menu[pageNum].m[0].ent_f;
         i=1; 
         menu_Init(&pageNum); //Initial menu and initial function
       }
       //switchPointer = current_menu;
       //last_item=current_poz;
       //f = menu[pageNum].m[0].esc_f;
       else {
         switchPointer = (switchVariants) menu[pageNum].m[0].esc_f;
         i=0;
       }
    
  }//обработка короткого нажатия ВВЕРХ
  
  if (BtnMask == BTN_SHRT_CANSEL)   
  { 
       
  }//обработка короткого нажатия ВЛЕВО
    
  switch (switchPointer)  // Все делаем в одном операторе и одной функции;
  {
    case MAIN_MENU:  /***************** Главное меню ***************/
    
      startMenu(); //формирование меню
      
      if (BtnMask == BTN_SHRT_RIGHT) //обработка короткого нажатия ВПРАВО
      {
         pageNum++;  // с каждым нажатием pageNum++ пока pageNum<4
         if (pageNum > 3) pageNum = 0;
      }
      
      if (BtnMask == BTN_SHRT_LEFT)  
      { 
         //pageNum--;
         //if (pageNum <= 0) pageNum = 0;  
      }//обработка короткого нажатия ВЛЕВО
  
      //if (BtnMask == BTN_LONG_UP)     { }//обработка длинного нажатия ВВЕРХ
      //if (BtnMask == BTN_LONG_DN)     {}//обработка длинного нажатия ВНИЗ
      //if (BtnMask == BTN_LONG_RIGHT)     {}//обработка длинного нажатия ВПРАВО
      //if (BtnMask == BTN_LONG_LEFT)     {}//обработка длинного нажатия ВЛЕВО
    break;
    
    case MENU_MANUAL: /***************** Ручной режим ***************/
    
      if (BtnMask == BTN_SHRT_RIGHT) //обработка короткого нажатия ВПРАВО
      {       
         if (MN.subMenu<pgm_read_byte(&MSTR2[MN.mMenu])) MN.subMenu++;
         else MN.subMenu=1;
         alexMenu();
      }
      
      if (BtnMask == BTN_SHRT_UP)     
      { 
        if (MN.subMenu == 1) profile.pwr_BOTTOM++;
        else if (MN.subMenu == 2) profile.pwr_TOP+=5;
        else if (MN.subMenu == 3) profile.temperatureStep+=5;
        else if (MN.subMenu == 4) profile.temperatureStep+=1;
        else if (MN.subMenu == 5) profile.dwellTimerStep+=1;
      }//обработка короткого нажатия ВВЕРХ
      
      if (BtnMask == BTN_SHRT_DN) 
      {
        
      }
      
      lcd.setCursor(0,2);
      lcd.print(profile);
      
      
      switch (mode) { // Выбор режима работы ручного режима
        case EDIT_PARAM: // Режим редактирования

        break;

        case HEAT_ZONE:

        break;
        
        case COOL_ZONE:

        break;
        
        case STOP_PROCESS:
          
        break;
      }
          
          
    break;
    
    case MENU_AUTO: /***************** Автоматический режим ***************/

    break;
    
    case MENU_SETUP: /***************** Настройки ***************/
      
    break;
    
    case MENU_DEBUG: /***************** Настройки ***************/
    
    break;
  }
}

//####################################################################################################################### 
//-----------------------------------------------------------------------------------------------------------------------
void ports_Init(void)
{    
    BTN_DDR &= ~(BTN_LINE_UP| BTN_LINE_DN| BTN_LINE_OK| BTN_LINE_LEFT| BTN_LINE_RIGHT| BTN_LINE_CANSEL);    // кнопки на ввод
    BTN_PORT |= (BTN_LINE_UP| BTN_LINE_DN| BTN_LINE_OK| BTN_LINE_LEFT| BTN_LINE_RIGHT| BTN_LINE_CANSEL);    //подтяжка вкл
}
//-----------------------------------------------------------------------------------------------------------------------

void CopyStringtoLCD(const uint8_t *FlashLoc, uint8_t x, uint8_t y)
{
  uint8_t i;
  lcd.setCursor(x,y);
  for(i=0;(uint8_t)pgm_read_byte(&FlashLoc[i]);i++)
  {
    lcd.write((uint8_t)pgm_read_byte(&FlashLoc[i]));
  }
}
//------------------------------------------

void menu_Init(uint8_t *page)
{
  MN.mMenu = *page+1;
  MN.subMenu = 1;  
  lcd.clear();
  CopyStringtoLCD(MENU[MN.mMenu-1], 0, 0 );
  CopyStringtoLCD(SUBMENU[MFIndex(MN.mMenu, MN.subMenu)], 0, 1 );
  //FPtr=(FuncPtr)pgm_read_word(&FuncPtrTable[0]);
}
//------------------------------------------
void alexMenu(void) {
  lcd.clear();
  //Display menu item
  CopyStringtoLCD(MENU[MN.mMenu-1], 0, 0 );
  //Display subMenu item
  CopyStringtoLCD(SUBMENU[MFIndex(MN.mMenu, MN.subMenu)], 0, 1 );
  //Assign function to function pointer
  //FPtr=(FuncPtr)pgm_read_word(&FuncPtrTable[MFIndex(MN.mMenu, MN.subMenu)]);
}
//------------------------------------------
uint8_t MFIndex(uint8_t mn, uint8_t sb)
{
  uint8_t p=0;//points to menu in table of function pointer 
  for(uint8_t i=0; i<(mn-1); i++)
  {
    p=p+pgm_read_byte(&MSTR2[i+1]);
  }
  p=p+sb-1;
  return p;
}

//------------------------------------------
void func101(void)
{

}
void func102(void)
{

}
void func103(void)
{

}
void func104(void)
{

}
void func105(void)
{

}
void func201(void)
{

}
void func202(void)
{

}
void func301(void)
{

}
void func302(void)
{

}
void func401(void)
{

}

void setString(unsigned char *bcount) {
  static uint8_t pos_y_curs = 4;
  
  lcd.setCursor(0, *bcount);
  lcd.write(62);
  if (bcount == 0) *bcount = pos_y_curs;
  lcd.setCursor(0, *bcount - 1);
  lcd.write(32);
}

void startMenu(){
  
  setString(&pageNum); // включаем отображение стрелки выбора меню
  
  lcd.setCursor(1, 0); // 1 строка
  lcd.print(F("PY HO ")); // ручной режим
  lcd.setCursor(1, 1); // 2 строка
  lcd.print(F("ABTOMAT  ECK")); // автоматический режим
  lcd.setCursor(1, 2); // 3 строка
  lcd.print(F("HACTPO K ")); // настройки
  lcd.setCursor(1, 3); // 4 строка
  lcd.print(F("OT A KA")); // отладка
}

Файлы библиотеки

common.h

#ifndef COMMON_H
#define COMMON_H

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <util/delay.h>

#include "pin_config.h"
#include "menu_config.h"
#include "def.h"
#include "buttonLib.h"

#endif

buttonLib.h

#ifndef buttonLib_h
#define buttonLib_h

void BtnExe (void);

#endif

buttonLib.c



#include "buttonLib.h"

void BtnExe (void)
{    
    static unsigned char BtnLockBit = 0;             //защелка (защита от дребезга)
    static unsigned char BtnLockCount = 0;            //счетчик защелки (защита от дребезга)
    static unsigned char BtnLongCount = 0;            //счетчик длинного нажатия
    static unsigned char BtnLastState= 0;            //последнее состояние кнопок перед отпусканием
 
    char mask = 0;
    /*if (! (BTN_PIN & BTN_LINE_UP))        mask = BTN_SHRT_UP;
    if (! (BTN_PIN & BTN_LINE_DN))        mask = BTN_SHRT_DN;
    if (! (BTN_PIN & BTN_LINE_LEFT))      mask = BTN_SHRT_LEFT;
    if (! (BTN_PIN & BTN_LINE_RIGHT))     mask = BTN_SHRT_RIGHT;*/
    
    if (! (BTN_PIN & BTN_LINE_DN))        mask = BTN_SHRT_DN;
    if (! (BTN_PIN & BTN_LINE_UP))        mask = BTN_SHRT_UP;
    if (! (BTN_PIN & BTN_LINE_OK))        mask = BTN_SHRT_OK;
    if (! (BTN_PIN & BTN_LINE_LEFT))      mask = BTN_SHRT_LEFT;
    if (! (BTN_PIN & BTN_LINE_RIGHT))     mask = BTN_SHRT_RIGHT;
    if (! (BTN_PIN & BTN_LINE_CANSEL))    mask = BTN_SHRT_CANSEL;
    
    if (mask){                                    //опрос состояния кнопки
        if (BtnLockCount < (BTN_LOCK_TIME/10)){    //клавиша нажата
            BtnLockCount++;
            return;                                //защелка еще не дощитала - возврат
        }
        BtnLastState = mask;
        BtnLockBit =1;                            //нажатие зафиксировано                
        //if (BtnLongCount >= (BTN_LONG_TIME/10))                                
        //    return;                                //возврат, т.к. счетчик длинн нажат досчитал до максимума еще раньше        
        //if (++BtnLongCount >= (BTN_LONG_TIME/10))
        //    BtnFlags |= (BtnLastState<<4);        //счетчик досчитал до максимума - устанавливаем биты длинного нажатия 
    }            
    else{                                        //клавиша отжата            
        if (BtnLockCount){
            BtnLockCount --;
            return;                                //защелка еще не обнулилась - возврат
        }
        if (! BtnLockBit)                        //СТАТИЧЕСКИЙ ВОЗВРАТ
            return;                                
        BtnLockBit =0;                            //отжатие зафиксировано
        if (BtnLongCount < (BTN_LONG_TIME/10))
            BtnFlags |= BtnLastState;            //установка бита короткого нажатия
        //BtnLongCount = 0;                        //сброс счетчика длительности нажатия
    }
}    

def.h

#ifndef DEF_H
#define DEF_H

//#include "common.h"

#define PERIOD_PWM 100UL           // периодичность вывода температуры
#define PERIOD_DURATION 1000UL     // периодичность 
#define PERIOD_BLINK 200UL         // периодичность
#define TIME_ALARM 3000UL          // время в режиме ТРЕВОГА
#define TIME_WAIT 2000UL           // ожидание для подтверждения возврата в меню

//Параметры регулятора
#define P_MIN 0.0       //минимум П составляющей - не < 0
#define P_MAX 100.0     //максимум П составляющей - не > 100
#define I_MIN 0.0       //минимум И составляющей
#define I_MAX 30.0      //максимум И составляющей
#define d_ctl 100      //зона пропорциональности ust-d_ctl
#define OUT_MIN 0       //минимальный выходной %
#define OUT_MAX 100     //максимальный выходной %

#endif

menu_config.h

#ifndef MENU_CONFIG_H
#define MENU_CONFIG_H

//#include "main.h"

//------------------------------------------
typedef void (*FuncPtr)(void);
//указатель на функции
FuncPtr FPtr;
//Структура описывает текущее состояние меню и подменю
struct Menu_State{
   uint8_t mMenu;//1,2,3,4
   uint8_t subMenu;//1,2,3
}MN;

typedef struct _selection
{
  unsigned char ent_f : 4; //Флаг входа 4 бита — обычно ID меню в которое надо войти
  unsigned char esc_f : 4; //Флаг выхода 4 бита — обычно ID меню в которое надо вернуться
}SELECTION;

typedef struct _menu {
  //unsigned char id; //Номер меню/подменю
  //unsigned char num_selections; //Количество Punktов данного меню/подменю
  SELECTION *m; //Указатель намассив Punktов данного меню/подменю
} PAGE_MENU;

#endif

pin_config.h

#ifndef PIN_CONFIG_H
#define PIN_CONFIG_H

//#include "commmon.h"

//настройка параметров работы функций
#define BTN_LOCK_TIME         30           /*время обработки дребезга в милисекундах (10-100)*/
#define BTN_LONG_TIME         1000         /*время фиксации длинного нажатия в милисекундах (1000 - 2500)*/
 
//настройки портов
#define BTN_PORT        PORTB                    /*порт чтения кнопок*/
#define BTN_DDR         DDRB
#define BTN_PIN         PINB 

#define BTN_LINE_DN     (1<<0)                /*пины чтения кнопок*/
#define BTN_LINE_UP     (1<<1)
#define BTN_LINE_OK     (1<<2)
#define BTN_LINE_LEFT   (1<<3)
#define BTN_LINE_RIGHT  (1<<4)
#define BTN_LINE_CANSEL (1<<5)


//глобальные переменные
volatile uint8_t BtnFlags;                         //байт флагов нажатия кнопки
    //#define BTN_LONG_UP      (1<<4)                /*бит длинного нажатия кнопки up*/ 
    //#define BTN_LONG_DN      (1<<5)                /*бит длинного нажатия кнопки dn*/ 
    //#define BTN_LONG_LEFT    (1<<6)                /*бит длинного нажатия кнопки left*/ 
    //#define BTN_LONG_RIGHT   (1<<7)                /*бит длинного нажатия кнопки right*/ 
    
    #define BTN_SHRT_DN      (1<<0)                /*бит короткого нажатия кнопки up*/ 
    #define BTN_SHRT_UP      (1<<1)                /*бит короткого нажатия кнопки dn*/ 
    #define BTN_SHRT_OK      (1<<2)                /*бит короткого нажатия кнопки up*/ 
    #define BTN_SHRT_LEFT    (1<<3)                /*бит короткого нажатия кнопки left*/ 
    #define BTN_SHRT_RIGHT   (1<<4)                /*бит короткого нажатия кнопки right*/ 
    #define BTN_SHRT_CANSEL  (1<<5)                /*бит короткого нажатия кнопки right*/

//##############################################################################################################
	
#define RELAY_PORT        PORTC                    /*порт чтения кнопок*/	
#define RELAY_BTM PC0  //назначаем пин "НИЖНЕГО" нагревателя
#define RELAY_TOP PC1  //назначаем пин "ВЕРХНЕГО" нагревателя 	

#define PIN_BUZZER 3 // назначаем пин для пьезодинамика

#endif	

timer.h

#ifndef TIMER_H
#define TIMER_H
 
void init_timer2(void);

#endif

timer.c

#include "timer.h"

void init_timer2(void)
{
  TCCR2B=0; // таймер2 остановлен
  TCNT2=0; //счётный регистр обнулён
  OCR2B=0;
  TIFR2&=0xff; //отчистить флаги прерываний
  TCCR2A = (1<<WGM21);    // Режим CTC (сброс по совпадению)
  TCCR2B = (1<<CS20)|(1<<CS21)|(1<<CS22); // CLK/1024
  OCR2A = 155;
  TIMSK2 = (1<<OCIE2A);  // Разрешить прерывание по совпадению
}

ISR(TIMER2_COMPA_vect)
{
    //BtnExe();
}

 

b707
Offline
Зарегистрирован: 26.05.2017

положите все эти файлы в одну папку со скетчем

Алек97
Offline
Зарегистрирован: 21.08.2018

Выдало множество ошибок











Build options changed, rebuilding all
Using library LiquidCrystal in folder: D:\Саша\1.6.0\arduino-1.6.0\libraries\LiquidCrystal 

D:\Саша\1.6.0\arduino-1.6.0/hardware/tools/avr/bin/avr-gcc -c -g -Os -w -ffunction-sections -fdata-sections -MMD -mmcu=atmega328p -DF_CPU=16000000L -DARDUINO=10600 -DARDUINO_AVR_UNO -DARDUINO_ARCH_AVR -ID:\Саша\1.6.0\arduino-1.6.0\hardware\arduino\avr\cores\arduino -ID:\Саша\1.6.0\arduino-1.6.0\hardware\arduino\avr\variants\standard -ID:\Саша\1.6.0\arduino-1.6.0\libraries\LiquidCrystal\src C:\DOCUME~1\9335~1\LOCALS~1\Temp\build7163073385139009280.tmp\buttonLib.c -o C:\DOCUME~1\9335~1\LOCALS~1\Temp\build7163073385139009280.tmp\buttonLib.c.o 
C:\DOCUME~1\9335~1\LOCALS~1\Temp\build7163073385139009280.tmp\buttonLib.c: In function 'BtnExe':
C:\DOCUME~1\9335~1\LOCALS~1\Temp\build7163073385139009280.tmp\buttonLib.c:16:12: error: 'BTN_PIN' undeclared (first use in this function)
     if (! (BTN_PIN & BTN_LINE_DN))        mask = BTN_SHRT_DN;
            ^
C:\DOCUME~1\9335~1\LOCALS~1\Temp\build7163073385139009280.tmp\buttonLib.c:16:12: note: each undeclared identifier is reported only once for each function it appears in
C:\DOCUME~1\9335~1\LOCALS~1\Temp\build7163073385139009280.tmp\buttonLib.c:16:22: error: 'BTN_LINE_DN' undeclared (first use in this function)
     if (! (BTN_PIN & BTN_LINE_DN))        mask = BTN_SHRT_DN;
                      ^
C:\DOCUME~1\9335~1\LOCALS~1\Temp\build7163073385139009280.tmp\buttonLib.c:16:50: error: 'BTN_SHRT_DN' undeclared (first use in this function)
     if (! (BTN_PIN & BTN_LINE_DN))        mask = BTN_SHRT_DN;
                                                  ^
C:\DOCUME~1\9335~1\LOCALS~1\Temp\build7163073385139009280.tmp\buttonLib.c:17:22: error: 'BTN_LINE_UP' undeclared (first use in this function)
     if (! (BTN_PIN & BTN_LINE_UP))        mask = BTN_SHRT_UP;
                      ^
C:\DOCUME~1\9335~1\LOCALS~1\Temp\build7163073385139009280.tmp\buttonLib.c:17:50: error: 'BTN_SHRT_UP' undeclared (first use in this function)
     if (! (BTN_PIN & BTN_LINE_UP))        mask = BTN_SHRT_UP;
                                                  ^
C:\DOCUME~1\9335~1\LOCALS~1\Temp\build7163073385139009280.tmp\buttonLib.c:18:22: error: 'BTN_LINE_OK' undeclared (first use in this function)
     if (! (BTN_PIN & BTN_LINE_OK))        mask = BTN_SHRT_OK;
                      ^
C:\DOCUME~1\9335~1\LOCALS~1\Temp\build7163073385139009280.tmp\buttonLib.c:18:50: error: 'BTN_SHRT_OK' undeclared (first use in this function)
     if (! (BTN_PIN & BTN_LINE_OK))        mask = BTN_SHRT_OK;
                                                  ^
C:\DOCUME~1\9335~1\LOCALS~1\Temp\build7163073385139009280.tmp\buttonLib.c:19:22: error: 'BTN_LINE_LEFT' undeclared (first use in this function)
     if (! (BTN_PIN & BTN_LINE_LEFT))      mask = BTN_SHRT_LEFT;
                      ^
C:\DOCUME~1\9335~1\LOCALS~1\Temp\build7163073385139009280.tmp\buttonLib.c:19:50: error: 'BTN_SHRT_LEFT' undeclared (first use in this function)
     if (! (BTN_PIN & BTN_LINE_LEFT))      mask = BTN_SHRT_LEFT;
                                                  ^
C:\DOCUME~1\9335~1\LOCALS~1\Temp\build7163073385139009280.tmp\buttonLib.c:20:22: error: 'BTN_LINE_RIGHT' undeclared (first use in this function)
     if (! (BTN_PIN & BTN_LINE_RIGHT))     mask = BTN_SHRT_RIGHT;
                      ^
C:\DOCUME~1\9335~1\LOCALS~1\Temp\build7163073385139009280.tmp\buttonLib.c:20:50: error: 'BTN_SHRT_RIGHT' undeclared (first use in this function)
     if (! (BTN_PIN & BTN_LINE_RIGHT))     mask = BTN_SHRT_RIGHT;
                                                  ^
C:\DOCUME~1\9335~1\LOCALS~1\Temp\build7163073385139009280.tmp\buttonLib.c:21:22: error: 'BTN_LINE_CANSEL' undeclared (first use in this function)
     if (! (BTN_PIN & BTN_LINE_CANSEL))    mask = BTN_SHRT_CANSEL;
                      ^
C:\DOCUME~1\9335~1\LOCALS~1\Temp\build7163073385139009280.tmp\buttonLib.c:21:50: error: 'BTN_SHRT_CANSEL' undeclared (first use in this function)
     if (! (BTN_PIN & BTN_LINE_CANSEL))    mask = BTN_SHRT_CANSEL;
                                                  ^
C:\DOCUME~1\9335~1\LOCALS~1\Temp\build7163073385139009280.tmp\buttonLib.c:24:29: error: 'BTN_LOCK_TIME' undeclared (first use in this function)
         if (BtnLockCount < (BTN_LOCK_TIME/10)){    //клавиша нажата
                             ^
C:\DOCUME~1\9335~1\LOCALS~1\Temp\build7163073385139009280.tmp\buttonLib.c:43:29: error: 'BTN_LONG_TIME' undeclared (first use in this function)
         if (BtnLongCount < (BTN_LONG_TIME/10))
                             ^
C:\DOCUME~1\9335~1\LOCALS~1\Temp\build7163073385139009280.tmp\buttonLib.c:44:13: error: 'BtnFlags' undeclared (first use in this function)
             BtnFlags |= BtnLastState;            //установка бита короткого нажатия
             ^
Ошибка компиляции.

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Какой тайный смысл Вы видите в строке

#include "common.h"

?

скажу Вам по секрету, она ищет файл common.h в той же попке, что и скетч.

Если у Вас все файлы лежат папке common, а та, в свою очередь в arduino\libraries, так Вы так и пишите 

#include <common/common.h>

Алек97
Offline
Зарегистрирован: 21.08.2018

ЕвгенийП пишет:

Какой тайный смысл Вы видите в строке

#include "common.h"

?

скажу Вам по секрету, она ищет файл common.h в той же попке, что и скетч.

Если у Вас все файлы лежат папке common, а та, в свою очередь в arduino\libraries, так Вы так и пишите 

#include <common/common.h>

Спасибо за достаточно подробный ответ. Теперь мне стало понятно почему следует поместить файлы в папке со скетчем

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

А мне нет. Если эти файлы предполагается использовать во многих скетчах, то поместить их надо как раз в общую папку, просто ссылаться на них надо правильно.

Алек97
Offline
Зарегистрирован: 21.08.2018

Я собираюсь их использовать только в текущем скетче. Поэтому все заголовочные файлы и скетч будут лежать в одной папке

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Нормально. Можете в подпапке скетча (чтобы под ногами не путались), тогда укажете

#include "podpapka/common.h"

Алек97
Offline
Зарегистрирован: 21.08.2018

За хороший совет благодарю но оставил как есть 

Посоветуйте как мне решить часть ошибок

C:\TEMP\build6137302794757910183.tmp\sketch.cpp.o:(.bss.MN+0x0): multiple definition of `MN'
C:\TEMP\build6137302794757910183.tmp\buttonLib.cpp.o:(.bss.MN+0x0): first defined here
C:\TEMP\build6137302794757910183.tmp\sketch.cpp.o: In function `ports_Init()':
C:\Program Files\Arduino/sketch.ino:169: multiple definition of `FPtr'
C:\TEMP\build6137302794757910183.tmp\buttonLib.cpp.o:C:\TEMP\build6137302794757910183.tmp/buttonLib.cpp:17: first defined here
C:\TEMP\build6137302794757910183.tmp\sketch.cpp.o: In function `ports_Init()':
C:\Program Files\Arduino/sketch.ino:169: multiple definition of `BtnFlags'
C:\TEMP\build6137302794757910183.tmp\buttonLib.cpp.o:C:\TEMP\build6137302794757910183.tmp/buttonLib.cpp:17: first defined here
C:\TEMP\build6137302794757910183.tmp\timer.cpp.o:(.bss.MN+0x0): multiple definition of `MN'
C:\TEMP\build6137302794757910183.tmp\buttonLib.cpp.o:C:\TEMP\build6137302794757910183.tmp/buttonLib.cpp:17: first defined here
C:\TEMP\build6137302794757910183.tmp\timer.cpp.o: In function `init_timer2()':
C:\TEMP\build6137302794757910183.tmp/timer.cpp:17: multiple definition of `FPtr'
C:\TEMP\build6137302794757910183.tmp\buttonLib.cpp.o:C:\TEMP\build6137302794757910183.tmp/buttonLib.cpp:17: first defined here
C:\TEMP\build6137302794757910183.tmp\timer.cpp.o: In function `init_timer2()':
C:\TEMP\build6137302794757910183.tmp/timer.cpp:17: multiple definition of `BtnFlags'
C:\TEMP\build6137302794757910183.tmp\buttonLib.cpp.o:C:\TEMP\build6137302794757910183.tmp/buttonLib.cpp:17: first defined here
collect2.exe: error: ld returned 1 exit status
Ошибка компиляции.

Файл с расширением .ino

#include "common.h"

#include <LiquidCrystal.h>
LiquidCrystal lcd(7, 6, 5, 4, 3, 2);

//#######################################################################################################################
uint8_t pageNum = 0;          // счетчик нажатий кнопки

//unsigned char current_menu=0; //Переменная указывает на текущее меню
//unsigned char current_poz=0;  //Переменная указывает на текущий Punkt меню/подменю
//char last_item;
unsigned char f;

//#######################################################################################################################

enum switchVariants : byte  {  // Определения для переключателя пунктов меню;
  MAIN_MENU, MENU_MANUAL, MENU_AUTO, MENU_SETUP, MENU_DEBUG//, EXITSAVE
};
switchVariants switchPointer = MAIN_MENU; // С чего начнем цикл;

enum manuState : byte {  // Определения для переключателя ручного режима;
  EDIT_PARAM, HEAT_ZONE, COOL_ZONE, STOP_PROCESS, EXIT_PROCESS
};
manuState mode = EDIT_PARAM; // С чего начнем цикл;

static SELECTION menu_m0[]={
{MENU_MANUAL , MAIN_MENU},
};
static SELECTION menu_m1[]={
{MENU_AUTO, MAIN_MENU}, 
};
static SELECTION menu_m2[]={
{MENU_SETUP, MAIN_MENU} 
};
static SELECTION menu_m3[]={
{MENU_DEBUG, MAIN_MENU}
};

static PAGE_MENU menu[] = {
  menu_m0, //Меню 1
  menu_m1, //Меню 2
  menu_m2, //Меню 3
  menu_m3  //Меню 4
};

const uint8_t MN000[] PROGMEM="REWORK v05\0";
//меню 1
const uint8_t MN100[] PROGMEM="SETTINGS\0";
//подпункт меню
const uint8_t MN101[] PROGMEM="STEPS:\0";
const uint8_t MN102[] PROGMEM="DWELL:\0";
const uint8_t MN103[] PROGMEM="PWR:\0";
const uint8_t MN104[] PROGMEM="RAMP:\0";
const uint8_t MN105[] PROGMEM="TARGET:\0";
//меню 2
const uint8_t MN200[] PROGMEM="MANUAL\0";
//подпункт меню 2
const uint8_t MN201[] PROGMEM="EDIT\0";
const uint8_t MN202[] PROGMEM="HEAT\0";
//меню 3
const uint8_t MN300[] PROGMEM="AUTO\0";
//подпункт меню 3
const uint8_t MN301[] PROGMEM="TEMP\0";
//меню 4
const uint8_t MN400[] PROGMEM="DEBUG\0";
//подпункт меню 4
const uint8_t MN401[] PROGMEM="COM\0";
//Массивы указателей на строки меню, хранящиеся на флэш
const uint8_t *MENU[] ={
    MN100,  //menu 1 string
    MN200,  //menu 2 string
    MN300,  //menu 3 string
    MN400   //menu 4 string
};
const uint8_t *SUBMENU[] ={
    MN101, MN102, MN103, MN104, MN105,  //подпункты меню 1
    MN201, MN202,                       //подпункты меню 2
    MN301,                              //подпункты меню 3
    MN401,                              //подпункты меню 4
};

//Структура меню
//[0] -Number of level 0 menu items
//[1]...[n] number of second level menu items
//Eg. MSTR2[1] shows that menu item 1 has 3 subMenus
const uint8_t MSTR2[] PROGMEM ={
  4,  // количество пунктов меню
  5,  //Количество подпунктов в пункте меню 1
  2,  //Количество подпунктов в пункте меню 2
  1,  //Количество подпунктов в пункте меню 3
  1   //Количество подпунктов в пункте меню 4
  }; 
//Прототипы функций
//Инициализация Timer2 
void init_timer2(void);
//Начальное меню
//void menu_Init(void);
//Назначаем порты для кнопок и светодиодов
void ports_Init(void);
//Функции для каждого пункта меню
void func101(void);
void func102(void);
void func103(void);
void func104(void);
void func105(void);
void func201(void);
void func202(void);
void func301(void);
void func401(void);

//#define NULL_FUNC  (void*)0

//Массив указателей на функции во флэш
const FuncPtr FuncPtrTable[] PROGMEM=
{   
  func101, func102, func103, func104, func105,  //functions for subMenus of menu 1
  func201, func202,     //functions for subMenus of menu 2
  func301,             //functions for subMenus of menu 3
  func401              //functions for subMenus of menu 4
};
//subMenu and Function table pointer update
uint8_t MFIndex(uint8_t, uint8_t);
//------------------------------------------

//#######################################################################################################################

//current editing step pointer
uint8_t editStep = 0;

/*struct reflowStation : public Printable {
        uint8_t rampRateStep = 0; //
        uint8_t temperatureStep = 0; //
        uint8_t dwellTimerStep = 0; //
        uint8_t pwr_TOP = 60;     //максимальная мощность верхнего нагревателя
        uint8_t pwr_BOTTOM = 90;  //максимальная мощность нижнего нагревателя
        //int Setpoint2;  
        //float kp1, kd1, ki1; // коэффициенты пид "НИЖНЕГО" нагревателя
        //float kp2, kd2, ki2; // коэффициенты пид "ВЕРХНЕГО" нагревателя              

  size_t printTo(Print& p) const { 
    size_t res;
    
    if (MN.subMenu == 1) res = p.print(pwr_BOTTOM);
    else if (MN.subMenu == 2) res = p.print(pwr_TOP);
    else if (MN.subMenu == 3) res = p.print(rampRateStep);
    else if (MN.subMenu == 4) res = p.print(temperatureStep);
    else if (MN.subMenu == 5) res = p.print(dwellTimerStep);
    return res;
  }
} profile;*/

//##################################################################################


void setup() {
  //Serial.begin(9600);
  lcd.begin(20, 4);
  lcd.home();
  //Приветствие
  CopyStringtoLCD(MN000, 3, 1);
  _delay_ms(100);
  lcd.clear();
  //Настройка таймера 2
  ports_Init();
  init_timer2(); // каждые 100 Гц
  sei(); // разрешаем прерывания глобально interrupts(); 
}

void loop() {
  
  char BtnMask = BtnGet();

  /*
  lcd.clear();
  //Display menu item
  CopyStringtoLCD(MENU[MN.menu-1], 0, 0 );
  //Display subMenu item
  CopyStringtoLCD(SUBMENU[MFIndex(MN.menu, MN.subMenu)], 0, 1 );
  //Assign function to function pointer
  FPtr=(FuncPtr)pgm_read_word(&FuncPtrTable[MFIndex(MN.menu, MN.subMenu)]);
  */

  if (BtnMask == BTN_SHRT_OK)      
  { 
       lcd.clear();
       static uint8_t i = 0;
       if (!i) {
         switchPointer = (switchVariants) menu[pageNum].m[0].ent_f;
         i=1; 
         menu_Init(&pageNum); //Initial menu and initial function
       }
       //switchPointer = current_menu;
       //last_item=current_poz;
       //f = menu[pageNum].m[0].esc_f;
       else {
         switchPointer = (switchVariants) menu[pageNum].m[0].esc_f;
         i=0;
       }
    
  }//обработка короткого нажатия ВВЕРХ
  
  if (BtnMask == BTN_SHRT_CANSEL)   
  { 
       
  }//обработка короткого нажатия ВЛЕВО
    
  switch (switchPointer)  // Все делаем в одном операторе и одной функции;
  {
    case MAIN_MENU:  /***************** Главное меню ***************/
    
      startMenu(); //формирование меню
      
      if (BtnMask == BTN_SHRT_RIGHT) //обработка короткого нажатия ВПРАВО
      {
         pageNum++;  // с каждым нажатием pageNum++ пока pageNum<4
         if (pageNum > 3) pageNum = 0;
      }
      
      if (BtnMask == BTN_SHRT_LEFT)  
      { 
         //pageNum--;
         //if (pageNum <= 0) pageNum = 0;  
      }//обработка короткого нажатия ВЛЕВО
  
      //if (BtnMask == BTN_LONG_UP)     { }//обработка длинного нажатия ВВЕРХ
      //if (BtnMask == BTN_LONG_DN)     {}//обработка длинного нажатия ВНИЗ
      //if (BtnMask == BTN_LONG_RIGHT)     {}//обработка длинного нажатия ВПРАВО
      //if (BtnMask == BTN_LONG_LEFT)     {}//обработка длинного нажатия ВЛЕВО
    break;
    
    case MENU_MANUAL: /***************** Ручной режим ***************/
    
      if (BtnMask == BTN_SHRT_RIGHT) //обработка короткого нажатия ВПРАВО
      {       
         if (MN.subMenu<pgm_read_byte(&MSTR2[MN.mMenu])) MN.subMenu++;
         else MN.subMenu=1;
         alexMenu();
      }
      
      if (BtnMask == BTN_SHRT_UP)     
      { 
        /*if (MN.subMenu == 1) profile.pwr_BOTTOM++;
        else if (MN.subMenu == 2) profile.pwr_TOP+=5;
        else if (MN.subMenu == 3) profile.temperatureStep+=5;
        else if (MN.subMenu == 4) profile.temperatureStep+=1;
        else if (MN.subMenu == 5) profile.dwellTimerStep+=1;*/
      }//обработка короткого нажатия ВВЕРХ
      
      if (BtnMask == BTN_SHRT_DN) 
      {
        
      }
      
      //lcd.setCursor(0,2);
      //lcd.print(profile);
      
      
      switch (mode) { // Выбор режима работы ручного режима
        case EDIT_PARAM: // Режим редактирования

        break;

        case HEAT_ZONE:

        break;
        
        case COOL_ZONE:

        break;
        
        case STOP_PROCESS:
          
        break;
      }
          
          
    break;
    
    case MENU_AUTO: /***************** Автоматический режим ***************/

    break;
    
    case MENU_SETUP: /***************** Настройки ***************/
      
    break;
    
    case MENU_DEBUG: /***************** Настройки ***************/
    
    break;
  }
}

//####################################################################################################################### 
//-----------------------------------------------------------------------------------------------------------------------
//функция настройки библиотеки работы с кнопками
void ports_Init(void)
{    
    BTN_DDR &= ~(BTN_LINE_UP| BTN_LINE_DN| BTN_LINE_OK| BTN_LINE_LEFT| BTN_LINE_RIGHT| BTN_LINE_CANSEL);    // кнопки на ввод
    BTN_PORT |= (BTN_LINE_UP| BTN_LINE_DN| BTN_LINE_OK| BTN_LINE_LEFT| BTN_LINE_RIGHT| BTN_LINE_CANSEL);    //подтяжка вкл
}
//-----------------------------------------------------------------------------------------------------------------------

void CopyStringtoLCD(const uint8_t *FlashLoc, uint8_t x, uint8_t y)
{
  uint8_t i;
  lcd.setCursor(x,y);
  for(i=0;(uint8_t)pgm_read_byte(&FlashLoc[i]);i++)
  {
    lcd.write((uint8_t)pgm_read_byte(&FlashLoc[i]));
  }
}
//------------------------------------------

void menu_Init(uint8_t *page)
{
  MN.mMenu = *page+1;
  MN.subMenu = 1;  
  lcd.clear();
  CopyStringtoLCD(MENU[MN.mMenu-1], 0, 0 );
  CopyStringtoLCD(SUBMENU[MFIndex(MN.mMenu, MN.subMenu)], 0, 1 );
  //FPtr=(FuncPtr)pgm_read_word(&FuncPtrTable[0]);
}
//------------------------------------------
void alexMenu(void) {
  lcd.clear();
  //Display menu item
  CopyStringtoLCD(MENU[MN.mMenu-1], 0, 0 );
  //Display subMenu item
  CopyStringtoLCD(SUBMENU[MFIndex(MN.mMenu, MN.subMenu)], 0, 1 );
  //Assign function to function pointer
  //FPtr=(FuncPtr)pgm_read_word(&FuncPtrTable[MFIndex(MN.mMenu, MN.subMenu)]);
}
//------------------------------------------
uint8_t MFIndex(uint8_t mn, uint8_t sb)
{
  uint8_t p=0;//points to menu in table of function pointer 
  for(uint8_t i=0; i<(mn-1); i++)
  {
    p=p+pgm_read_byte(&MSTR2[i+1]);
  }
  p=p+sb-1;
  return p;
}

//------------------------------------------
void func101(void)
{

}
void func102(void)
{

}
void func103(void)
{

}
void func104(void)
{

}
void func105(void)
{

}
void func201(void)
{

}
void func202(void)
{

}
void func301(void)
{

}
void func302(void)
{

}
void func401(void)
{

}

void setString(unsigned char *bcount) {
  static uint8_t pos_y_curs = 4;
  
  lcd.setCursor(0, *bcount);
  lcd.write(62);
  if (bcount == 0) *bcount = pos_y_curs;
  lcd.setCursor(0, *bcount - 1);
  lcd.write(32);
}

void startMenu(){
  
  setString(&pageNum); // включаем отображение стрелки выбора меню
  
  lcd.setCursor(1, 0); // 1 строка
  lcd.print(F("PY HO ")); // ручной режим
  lcd.setCursor(1, 1); // 2 строка
  lcd.print(F("ABTOMAT  ECK")); // автоматический режим
  lcd.setCursor(1, 2); // 3 строка
  lcd.print(F("HACTPO K ")); // настройки
  lcd.setCursor(1, 3); // 4 строка
  lcd.print(F("OT A KA")); // отладка
}

/*void ButtonClick(unsigned char *ButtonId) {
    if (*ButtonId == 0)  switchPointer = MENU_MANUAL;   // Клик [Menu] Выход из меню
    else if (*ButtonId == 1) switchPointer = MENU_AUTO;  //MenuCurent--;   // Клик [Prev] Позицию ниже
    else if (*ButtonId == 2) switchPointer = MENU_SETUP; //MenuCurent++;   // Клик [Next] Позиция выше
    else if (*ButtonId == 3) switchPointer = MENU_DEBUG;
}*/

buttonLib.c

#include "buttonLib.h"
#include "pin_config.h"

void BtnExe (void)
{    
    static unsigned char BtnLockBit = 0;             //защелка (защита от дребезга)
    static unsigned char BtnLockCount = 0;            //счетчик защелки (защита от дребезга)
    static unsigned char BtnLongCount = 0;            //счетчик длинного нажатия
    static unsigned char BtnLastState= 0;            //последнее состояние кнопок перед отпусканием
 
    char mask = 0;
    /*if (! (BTN_PIN & BTN_LINE_UP))        mask = BTN_SHRT_UP;
    if (! (BTN_PIN & BTN_LINE_DN))        mask = BTN_SHRT_DN;
    if (! (BTN_PIN & BTN_LINE_LEFT))      mask = BTN_SHRT_LEFT;
    if (! (BTN_PIN & BTN_LINE_RIGHT))     mask = BTN_SHRT_RIGHT;*/
    
    if (! (BTN_PIN & BTN_LINE_DN))        mask = BTN_SHRT_DN;
    if (! (BTN_PIN & BTN_LINE_UP))        mask = BTN_SHRT_UP;
    if (! (BTN_PIN & BTN_LINE_OK))        mask = BTN_SHRT_OK;
    if (! (BTN_PIN & BTN_LINE_LEFT))      mask = BTN_SHRT_LEFT;
    if (! (BTN_PIN & BTN_LINE_RIGHT))     mask = BTN_SHRT_RIGHT;
    if (! (BTN_PIN & BTN_LINE_CANSEL))    mask = BTN_SHRT_CANSEL;
    
    if (mask){                                    //опрос состояния кнопки
        if (BtnLockCount < (BTN_LOCK_TIME/10)){    //клавиша нажата
            BtnLockCount++;
            return;                                //защелка еще не дощитала - возврат
        }
        BtnLastState = mask;
        BtnLockBit =1;                            //нажатие зафиксировано                
        //if (BtnLongCount >= (BTN_LONG_TIME/10))                                
        //    return;                                //возврат, т.к. счетчик длинн нажат досчитал до максимума еще раньше        
        //if (++BtnLongCount >= (BTN_LONG_TIME/10))
        //    BtnFlags |= (BtnLastState<<4);        //счетчик досчитал до максимума - устанавливаем биты длинного нажатия 
    }            
    else{                                        //клавиша отжата            
        if (BtnLockCount){
            BtnLockCount --;
            return;                                //защелка еще не обнулилась - возврат
        }
        if (! BtnLockBit)                        //СТАТИЧЕСКИЙ ВОЗВРАТ
            return;                                
        BtnLockBit =0;                            //отжатие зафиксировано
        if (BtnLongCount < (BTN_LONG_TIME/10))
            BtnFlags |= BtnLastState;            //установка бита короткого нажатия
        //BtnLongCount = 0;                        //сброс счетчика длительности нажатия
    }
}    

//функция чтения данных о нажатии кнопок
char BtnGet (void)
{
    cli();
    char temp = BtnFlags;
    BtnFlags = 0;
    sei();
    return temp;
}

buttonLib.h

#ifndef buttonLib_h
#define buttonLib_h

void BtnExe (void);
char BtnGet (void);

#endif

common.h

#ifndef COMMON_H
#define COMMON_H

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <util/delay.h>

#include "pin_config.h"
#include "menu_config.h"
#include "def.h"
#include "timer.h"
#include "buttonLib.h"

#endif

def.h

#ifndef DEF_H
#define DEF_H

//#include "common.h"

#define PERIOD_PWM 100UL           // периодичность вывода температуры
#define PERIOD_DURATION 1000UL     // периодичность 
#define PERIOD_BLINK 200UL         // периодичность
#define TIME_ALARM 3000UL          // время в режиме ТРЕВОГА
#define TIME_WAIT 2000UL           // ожидание для подтверждения возврата в меню

//Параметры регулятора
#define P_MIN 0.0       //минимум П составляющей - не < 0
#define P_MAX 100.0     //максимум П составляющей - не > 100
#define I_MIN 0.0       //минимум И составляющей
#define I_MAX 30.0      //максимум И составляющей
#define d_ctl 100      //зона пропорциональности ust-d_ctl
#define OUT_MIN 0       //минимальный выходной %
#define OUT_MAX 100     //максимальный выходной %

#endif

menu_config.h

#ifndef MENU_CONFIG_H
#define MENU_CONFIG_H

//#include "main.h"

//------------------------------------------
typedef void (*FuncPtr)(void);
//указатель на функции
FuncPtr FPtr;
//Структура описывает текущее состояние меню и подменю
struct Menu_State{
   uint8_t mMenu;//1,2,3,4
   uint8_t subMenu;//1,2,3
}MN;

typedef struct _selection
{
  unsigned char ent_f : 4; //Флаг входа 4 бита — обычно ID меню в которое надо войти
  unsigned char esc_f : 4; //Флаг выхода 4 бита — обычно ID меню в которое надо вернуться
}SELECTION;

typedef struct _menu {
  //unsigned char id; //Номер меню/подменю
  //unsigned char num_selections; //Количество Punktов данного меню/подменю
  SELECTION *m; //Указатель намассив Punktов данного меню/подменю
} PAGE_MENU;

#endif

pin_config.h

#ifndef PIN_CONFIG_H
#define PIN_CONFIG_H

#include "common.h"

//настройка параметров работы функций
#define BTN_LOCK_TIME         30           /*время обработки дребезга в милисекундах (10-100)*/
#define BTN_LONG_TIME         1000         /*время фиксации длинного нажатия в милисекундах (1000 - 2500)*/
 
//настройки портов
#define BTN_PORT        PORTB                    /*порт чтения кнопок*/
#define BTN_DDR         DDRB
#define BTN_PIN         PINB 

#define BTN_LINE_DN     (1<<0)                /*пины чтения кнопок*/
#define BTN_LINE_UP     (1<<1)
#define BTN_LINE_OK     (1<<2)
#define BTN_LINE_LEFT   (1<<3)
#define BTN_LINE_RIGHT  (1<<4)
#define BTN_LINE_CANSEL (1<<5)


//глобальные переменные
volatile uint8_t BtnFlags;                         //байт флагов нажатия кнопки
    //#define BTN_LONG_UP      (1<<4)                /*бит длинного нажатия кнопки up*/ 
    //#define BTN_LONG_DN      (1<<5)                /*бит длинного нажатия кнопки dn*/ 
    //#define BTN_LONG_LEFT    (1<<6)                /*бит длинного нажатия кнопки left*/ 
    //#define BTN_LONG_RIGHT   (1<<7)                /*бит длинного нажатия кнопки right*/ 
    
    #define BTN_SHRT_DN      (1<<0)                /*бит короткого нажатия кнопки up*/ 
    #define BTN_SHRT_UP      (1<<1)                /*бит короткого нажатия кнопки dn*/ 
    #define BTN_SHRT_OK      (1<<2)                /*бит короткого нажатия кнопки up*/ 
    #define BTN_SHRT_LEFT    (1<<3)                /*бит короткого нажатия кнопки left*/ 
    #define BTN_SHRT_RIGHT   (1<<4)                /*бит короткого нажатия кнопки right*/ 
    #define BTN_SHRT_CANSEL  (1<<5)                /*бит короткого нажатия кнопки right*/

//##############################################################################################################
	
#define RELAY_PORT        PORTC                    /*порт чтения кнопок*/	
#define RELAY_BTM PC0  //назначаем пин "НИЖНЕГО" нагревателя
#define RELAY_TOP PC1  //назначаем пин "ВЕРХНЕГО" нагревателя 	

#define PIN_BUZZER 3 // назначаем пин для пьезодинамика

#endif
	

timer.c

#include "timer.h"
#include "common.h"

void init_timer2(void)
{
  TCCR2B=0; // таймер2 остановлен
  TCNT2=0; //счётный регистр обнулён
  OCR2B=0;
  TIFR2&=0xff; //отчистить флаги прерываний
  TCCR2A = (1<<WGM21);    // Режим CTC (сброс по совпадению)
  TCCR2B = (1<<CS20)|(1<<CS21)|(1<<CS22); // CLK/1024
  OCR2A = 155;
  TIMSK2 = (1<<OCIE2A);  // Разрешить прерывание по совпадению
}

ISR(TIMER2_COMPA_vect)
{
    BtnExe();
}

timer.h

#ifndef TIMER_H
#define TIMER_H
 
void init_timer2(void);

#endif

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015
Ну, Вы ведь не пишете что Вы хотели сделать, остаётся только догадываться.
 
Я так полагаю, Вы хотели, чтобы переменные BtnFlags, MN и FPtr были общими для всех файлов, так? 
 
Ну, так тогда надо так и писать. А Вы что пишете? (на примере только BtnFlags, с  остальными всё тоже самое)
 
Вы объявили её в файле pin_config.h (стр. №24). Затем этот файл "pin_config.h" Вы включили (include'ами) и в основную программу (sketch.ino) , и в файл "buttonLib.c". В результате у Вас получилось две переменных BtnFlags. Одна описана "sketch.ino", а другая - в "buttonLib.c". Их реально две, под них выделена разная память, а имена у них одинаковые. Вот система и сходит с ума - какую испольховать-то?
 
Если Вам нужно, чтобы переменная BtnFlags была одна на все файлы, Вы должны описать её один раз в каком-нибудь конечном файле (в смысле в файле, который никуда никакими инклюдами не включается), например в том же "buttonLib.c". А во включаемом файле описать её с использованием ключевого слова extern. Тогда это будет не описание, а просто указание, что переменная такого-то типа, с таким-то именем где-то описана и использовать надо именно её.
 
Т.е.
1) в начало строки №24 файла "pin_config.h" добавляете слово extern.
2) в Файле "buttonLib.c" (или в любом другом .ino / .c файле, но только в одном!) добавляете описание этой переменной точно так, как она сейчас описана в "pin_config.h" (без "extern")
 
И с этой переменной проблема снимается.
 
У Вас есть ещё одна ошибка, которую Вы пока не видите, но которая Вам боком выйдет. Если Вы не имеете на то веских причин, не используйте смесь языков. Ваш .ino файл - это опрограмма на С++. Вот и остальные делайте на С++ (используйте расширение .cpp, а не .с). Если Вы используете расширение .с - это программа на С. В этом случае надо специальным образом описывать все функции и переменные, чтобы С++-ный код из Вашего sketch.ino их адекватно понимал. Вы этого не делаете (видимо, не знаете как), вот и не используйте языковую смесь, пишите на одном языке. Какой смысл Вам тут два языка задействовать?
Алек97
Offline
Зарегистрирован: 21.08.2018

ЕвгенийП пишет:

Ну, Вы ведь не пишете что Вы хотели сделать, остаётся только догадываться.
 
 
Огромное вам Спасибо, рассказали все на пальцах. Теперь на все выши вопросы отвечу и подробно объясню свою задумку.
 
Занимаюсь какое-то время изучением программироввания, недостаточно хорошо знаю язык, больше учусь когда находится свободное от работы время, отлаживая и где - то по новому переписывая программу меню.
Старый ник - buonanotteMasha.
 
Так вот поменял стратегию напичания меню на новую и когда понял что функций будет достаточно много решил вынести это в отдельные файлы проекта.
 
ЕвгенийП пишет:
Я так полагаю, Вы хотели, чтобы переменные BtnFlags, MN и FPtr были общими для всех файлов, так?Ну, так тогда надо так и писать. А Вы что пишете? (на примере только BtnFlags, с  остальными всё тоже самое)Вы объявили её в файле pin_config.h (стр. №24). Затем этот файл "pin_config.h" Вы включили (include'ами) и в основную программу (sketch.ino) , и в файл "buttonLib.c". В результате у Вас получилось две переменных BtnFlags. Одна описана "sketch.ino", а другая - в "buttonLib.c". Их реально две, под них выделена разная память, а имена у них одинаковые. Вот система и сходит с ума - какую испольховать-то?Если Вам нужно, чтобы переменная BtnFlags была одна на все файлы, Вы должны описать её один раз в каком-нибудь конечном файле (в смысле в файле, который никуда никакими инклюдами не включается), например в том же "buttonLib.c". А во включаемом файле описать её с использованием ключевого слова extern. Тогда это будет не описание, а просто указание, что переменная такого-то типа, с таким-то именем где-то описана и использовать надо именно её.Т.е.1) в начало строки №24 файла "pin_config.h" добавляете слово extern.2) в Файле "buttonLib.c" (или в любом другом .ino / .c файле, но только в одном!) добавляете описание этой переменной точно так, как она сейчас описана в "pin_config.h" (без "extern")И с этой переменной проблема снимается.
 
Да хотелось бы чтобы эти переменные были глобальными для всех файлов. Теперь с ваших слов стало ясно.
Ранее уже читал про модификатор extern, но нигде не применял и не работал с ним. К вечеру освобожусь попробую, о результатах сообщу
ЕвгенийП пишет:
У Вас есть ещё одна ошибка, которую Вы пока не видите, но которая Вам боком выйдет. Если Вы не имеете на то веских причин, не используйте смесь языков. Ваш .ino файл - это опрограмма на С++. Вот и остальные делайте на С++ (используйте расширение .cpp, а не .с). Если Вы используете расширение .с - это программа на С. В этом случае надо специальным образом описывать все функции и переменные, чтобы С++-ный код из Вашего sketch.ino их адекватно понимал. Вы этого не делаете (видимо, не знаете как), вот и не используйте языковую смесь, пишите на одном языке. Какой смысл Вам тут два языка задействовать?

Да после первого поста я не поправил на сайте хотя у себя все файлы переделал с правильным расширением .срр,  по причине того что вчера ради интереса проглядывал прошивку для 3D принтера Marlin для среды, и там все заголовочные файлы .h, .срр.

Алек97
Offline
Зарегистрирован: 21.08.2018

Здравствуте, чем больше капаюсь, тем больше ошибаюсь. ЕвгенийП не затруднит вас мне еще указать правильное решение

sketch.ino:29:3: error: 'SELECTION' does not name a type
sketch.ino:44:8: error: 'SELECTION' does not name a type
sketch.ino:47:8: error: 'SELECTION' does not name a type
sketch.ino:50:8: error: 'SELECTION' does not name a type
sketch.ino:53:8: error: 'SELECTION' does not name a type
sketch.ino:57:8: error: 'PAGE_MENU' does not name a type
sketch.ino: In function 'void loop()':
sketch.ino:206:43: error: 'menu' was not declared in this scope
sketch.ino:214:43: error: 'menu' was not declared in this scope
Ошибка компиляции.

sketch.ino

#include "common.h"

#include <LiquidCrystal.h>
LiquidCrystal lcd(7, 6, 5, 4, 3, 2);

//#######################################################################################################################
uint8_t pageNum = 0;          // счетчик нажатий кнопки
FuncPtr FPtr;
//unsigned char current_menu=0; //Переменная указывает на текущее меню
//unsigned char current_poz=0;  //Переменная указывает на текущий Punkt меню/подменю
//char last_item;
unsigned char f;

//Структура описывает текущее состояние меню и подменю
struct Menu_State{
   uint8_t mMenu;//1,2,3,4
   uint8_t subMenu;//1,2,3
} MN;

struct _selection
{
  unsigned char ent_f : 4; //Флаг входа 4 бита — обычно ID меню в которое надо войти
  unsigned char esc_f : 4; //Флаг выхода 4 бита — обычно ID меню в которое надо вернуться
} SELECTION;

struct _menu {
  //unsigned char id; //Номер меню/подменю
  //unsigned char num_selections; //Количество Punktов данного меню/подменю
  SELECTION *m; //Указатель намассив Punktов данного меню/подменю
} PAGE_MENU;

//#######################################################################################################################

enum switchVariants : byte  {  // Определения для переключателя пунктов меню;
  MAIN_MENU, MENU_MANUAL, MENU_AUTO, MENU_SETUP, MENU_DEBUG//, EXITSAVE
};
switchVariants switchPointer = MAIN_MENU; // С чего начнем цикл;

enum manuState : byte {  // Определения для переключателя ручного режима;
  EDIT_PARAM, HEAT_ZONE, COOL_ZONE, STOP_PROCESS, EXIT_PROCESS
};
manuState mode = EDIT_PARAM; // С чего начнем цикл;

static SELECTION menu_m0[]={
{MENU_MANUAL , MAIN_MENU},
};
static SELECTION menu_m1[]={
{MENU_AUTO, MAIN_MENU}, 
};
static SELECTION menu_m2[]={
{MENU_SETUP, MAIN_MENU} 
};
static SELECTION menu_m3[]={
{MENU_DEBUG, MAIN_MENU}
};

static PAGE_MENU menu[] = {
  menu_m0, //Меню 1
  menu_m1, //Меню 2
  menu_m2, //Меню 3
  menu_m3  //Меню 4
};

const uint8_t MN000[] PROGMEM="REWORK v05\0";
//меню 1
const uint8_t MN100[] PROGMEM="SETTINGS\0";
//подпункт меню
const uint8_t MN101[] PROGMEM="STEPS:\0";
const uint8_t MN102[] PROGMEM="DWELL:\0";
const uint8_t MN103[] PROGMEM="PWR:\0";
const uint8_t MN104[] PROGMEM="RAMP:\0";
const uint8_t MN105[] PROGMEM="TARGET:\0";
//меню 2
const uint8_t MN200[] PROGMEM="MANUAL\0";
//подпункт меню 2
const uint8_t MN201[] PROGMEM="EDIT\0";
const uint8_t MN202[] PROGMEM="HEAT\0";
//меню 3
const uint8_t MN300[] PROGMEM="AUTO\0";
//подпункт меню 3
const uint8_t MN301[] PROGMEM="TEMP\0";
//меню 4
const uint8_t MN400[] PROGMEM="DEBUG\0";
//подпункт меню 4
const uint8_t MN401[] PROGMEM="COM\0";
//Массивы указателей на строки меню, хранящиеся на флэш
const uint8_t *MENU[] ={
    MN100,  //menu 1 string
    MN200,  //menu 2 string
    MN300,  //menu 3 string
    MN400   //menu 4 string
};
const uint8_t *SUBMENU[] ={
    MN101, MN102, MN103, MN104, MN105,  //подпункты меню 1
    MN201, MN202,                       //подпункты меню 2
    MN301,                              //подпункты меню 3
    MN401,                              //подпункты меню 4
};

//Структура меню
//[0] -Number of level 0 menu items
//[1]...[n] number of second level menu items
//Eg. MSTR2[1] shows that menu item 1 has 3 subMenus
const uint8_t MSTR2[] PROGMEM ={
  4,  // количество пунктов меню
  5,  //Количество подпунктов в пункте меню 1
  2,  //Количество подпунктов в пункте меню 2
  1,  //Количество подпунктов в пункте меню 3
  1   //Количество подпунктов в пункте меню 4
  }; 
//Прототипы функций
//Инициализация Timer2 
void init_timer2(void);
//Начальное меню
//void menu_Init(void);
//Назначаем порты для кнопок и светодиодов
void ports_Init(void);
//Функции для каждого пункта меню
void func101(void);
void func102(void);
void func103(void);
void func104(void);
void func105(void);
void func201(void);
void func202(void);
void func301(void);
void func401(void);

//#define NULL_FUNC  (void*)0

//Массив указателей на функции во флэш
const FuncPtr FuncPtrTable[] PROGMEM=
{   
  func101, func102, func103, func104, func105,  //functions for subMenus of menu 1
  func201, func202,     //functions for subMenus of menu 2
  func301,             //functions for subMenus of menu 3
  func401              //functions for subMenus of menu 4
};
//subMenu and Function table pointer update
uint8_t MFIndex(uint8_t, uint8_t);
//------------------------------------------

//#######################################################################################################################

//current editing step pointer
uint8_t editStep = 0;

struct reflowStation : public Printable {
        uint8_t rampRateStep = 0; //
        uint8_t temperatureStep = 0; //
        uint8_t dwellTimerStep = 0; //
        uint8_t pwr_TOP = 60;     //максимальная мощность верхнего нагревателя
        uint8_t pwr_BOTTOM = 90;  //максимальная мощность нижнего нагревателя
        //int Setpoint2;  
        //float kp1, kd1, ki1; // коэффициенты пид "НИЖНЕГО" нагревателя
        //float kp2, kd2, ki2; // коэффициенты пид "ВЕРХНЕГО" нагревателя              

  size_t printTo(Print& p) const { 
    size_t res;
    
    if (MN.subMenu == 1) res = p.print(pwr_BOTTOM);
    else if (MN.subMenu == 2) res = p.print(pwr_TOP);
    else if (MN.subMenu == 3) res = p.print(rampRateStep);
    else if (MN.subMenu == 4) res = p.print(temperatureStep);
    else if (MN.subMenu == 5) res = p.print(dwellTimerStep);
    return res;
  }
} profile;

//##################################################################################


void setup() {
  //Serial.begin(9600);
  lcd.begin(20, 4);
  lcd.home();
  //Приветствие
  CopyStringtoLCD(MN000, 3, 1);
  _delay_ms(100);
  lcd.clear();
  //Настройка таймера 2
  ports_Init();
  init_timer2(); // каждые 100 Гц
  sei(); // разрешаем прерывания глобально interrupts(); 
}

void loop() {
  
  char BtnMask = BtnGet();

  /*
  lcd.clear();
  //Display menu item
  CopyStringtoLCD(MENU[MN.menu-1], 0, 0 );
  //Display subMenu item
  CopyStringtoLCD(SUBMENU[MFIndex(MN.menu, MN.subMenu)], 0, 1 );
  //Assign function to function pointer
  FPtr=(FuncPtr)pgm_read_word(&FuncPtrTable[MFIndex(MN.menu, MN.subMenu)]);
  */

  if (BtnMask == BTN_SHRT_OK)      
  { 
       lcd.clear();
       static uint8_t i = 0;
       if (!i) {
         switchPointer = (switchVariants) menu[pageNum].m[0].ent_f;
         i=1; 
         menu_Init(&pageNum); //Initial menu and initial function
       }
       //switchPointer = current_menu;
       //last_item=current_poz;
       //f = menu[pageNum].m[0].esc_f;
       else {
         switchPointer = (switchVariants) menu[pageNum].m[0].esc_f;
         i=0;
       }
    
  }//обработка короткого нажатия ВВЕРХ
  
  if (BtnMask == BTN_SHRT_CANSEL)   
  { 
       
  }//обработка короткого нажатия ВЛЕВО
    
  switch (switchPointer)  // Все делаем в одном операторе и одной функции;
  {
    case MAIN_MENU:  /***************** Главное меню ***************/
    
      startMenu(); //формирование меню
      
      if (BtnMask == BTN_SHRT_RIGHT) //обработка короткого нажатия ВПРАВО
      {
         pageNum++;  // с каждым нажатием pageNum++ пока pageNum<4
         if (pageNum > 3) pageNum = 0;
      }
      
      if (BtnMask == BTN_SHRT_LEFT)  
      { 
         //pageNum--;
         //if (pageNum <= 0) pageNum = 0;  
      }//обработка короткого нажатия ВЛЕВО
  
      //if (BtnMask == BTN_LONG_UP)     { }//обработка длинного нажатия ВВЕРХ
      //if (BtnMask == BTN_LONG_DN)     {}//обработка длинного нажатия ВНИЗ
      //if (BtnMask == BTN_LONG_RIGHT)     {}//обработка длинного нажатия ВПРАВО
      //if (BtnMask == BTN_LONG_LEFT)     {}//обработка длинного нажатия ВЛЕВО
    break;
    
    case MENU_MANUAL: /***************** Ручной режим ***************/
    
      if (BtnMask == BTN_SHRT_RIGHT) //обработка короткого нажатия ВПРАВО
      {       
         if (MN.subMenu<pgm_read_byte(&MSTR2[MN.mMenu])) MN.subMenu++;
         else MN.subMenu=1;
         alexMenu();
      }
      
      if (BtnMask == BTN_SHRT_UP)     
      { 
        if (MN.subMenu == 1) profile.pwr_BOTTOM++;
        else if (MN.subMenu == 2) profile.pwr_TOP+=5;
        else if (MN.subMenu == 3) profile.temperatureStep+=5;
        else if (MN.subMenu == 4) profile.temperatureStep+=1;
        else if (MN.subMenu == 5) profile.dwellTimerStep+=1;
      }//обработка короткого нажатия ВВЕРХ
      
      if (BtnMask == BTN_SHRT_DN) 
      {
        
      }
      
      lcd.setCursor(0,2);
      lcd.print(profile);
      
      
      switch (mode) { // Выбор режима работы ручного режима
        case EDIT_PARAM: // Режим редактирования

        break;

        case HEAT_ZONE:

        break;
        
        case COOL_ZONE:

        break;
        
        case STOP_PROCESS:
          
        break;
      }
          
          
    break;
    
    case MENU_AUTO: /***************** Автоматический режим ***************/

    break;
    
    case MENU_SETUP: /***************** Настройки ***************/
      
    break;
    
    case MENU_DEBUG: /***************** Настройки ***************/
    
    break;
  }
}

//####################################################################################################################### 
//-----------------------------------------------------------------------------------------------------------------------
//функция настройки библиотеки работы с кнопками
void ports_Init(void)
{    
    BTN_DDR &= ~(BTN_LINE_UP| BTN_LINE_DN| BTN_LINE_OK| BTN_LINE_LEFT| BTN_LINE_RIGHT| BTN_LINE_CANSEL);    // кнопки на ввод
    BTN_PORT |= (BTN_LINE_UP| BTN_LINE_DN| BTN_LINE_OK| BTN_LINE_LEFT| BTN_LINE_RIGHT| BTN_LINE_CANSEL);    //подтяжка вкл
}
//-----------------------------------------------------------------------------------------------------------------------

void CopyStringtoLCD(const uint8_t *FlashLoc, uint8_t x, uint8_t y)
{
  uint8_t i;
  lcd.setCursor(x,y);
  for(i=0;(uint8_t)pgm_read_byte(&FlashLoc[i]);i++)
  {
    lcd.write((uint8_t)pgm_read_byte(&FlashLoc[i]));
  }
}
//------------------------------------------

void menu_Init(uint8_t *page)
{
  MN.mMenu = *page+1;
  MN.subMenu = 1;  
  lcd.clear();
  CopyStringtoLCD(MENU[MN.mMenu-1], 0, 0 );
  CopyStringtoLCD(SUBMENU[MFIndex(MN.mMenu, MN.subMenu)], 0, 1 );
  //FPtr=(FuncPtr)pgm_read_word(&FuncPtrTable[0]);
}
//------------------------------------------
void alexMenu(void) {
  lcd.clear();
  //Display menu item
  CopyStringtoLCD(MENU[MN.mMenu-1], 0, 0 );
  //Display subMenu item
  CopyStringtoLCD(SUBMENU[MFIndex(MN.mMenu, MN.subMenu)], 0, 1 );
  //Assign function to function pointer
  //FPtr=(FuncPtr)pgm_read_word(&FuncPtrTable[MFIndex(MN.mMenu, MN.subMenu)]);
}
//------------------------------------------
uint8_t MFIndex(uint8_t mn, uint8_t sb)
{
  uint8_t p=0;//points to menu in table of function pointer 
  for(uint8_t i=0; i<(mn-1); i++)
  {
    p=p+pgm_read_byte(&MSTR2[i+1]);
  }
  p=p+sb-1;
  return p;
}

//------------------------------------------
void func101(void)
{

}
void func102(void)
{

}
void func103(void)
{

}
void func104(void)
{

}
void func105(void)
{

}
void func201(void)
{

}
void func202(void)
{

}
void func301(void)
{

}
void func302(void)
{

}
void func401(void)
{

}

void setString(unsigned char *bcount) {
  static uint8_t pos_y_curs = 4;
  
  lcd.setCursor(0, *bcount);
  lcd.write(62);
  if (bcount == 0) *bcount = pos_y_curs;
  lcd.setCursor(0, *bcount - 1);
  lcd.write(32);
}

void startMenu(){
  
  setString(&pageNum); // включаем отображение стрелки выбора меню
  
  lcd.setCursor(1, 0); // 1 строка
  lcd.print(F("PY HO ")); // ручной режим
  lcd.setCursor(1, 1); // 2 строка
  lcd.print(F("ABTOMAT  ECK")); // автоматический режим
  lcd.setCursor(1, 2); // 3 строка
  lcd.print(F("HACTPO K ")); // настройки
  lcd.setCursor(1, 3); // 4 строка
  lcd.print(F("OT A KA")); // отладка
}

buttonLib.cpp

#include "buttonLib.h"
#include "pin_config.h"

volatile uint8_t BtnFlags;

void BtnExe (void)
{    
    static unsigned char BtnLockBit = 0;             //защелка (защита от дребезга)
    static unsigned char BtnLockCount = 0;            //счетчик защелки (защита от дребезга)
    static unsigned char BtnLongCount = 0;            //счетчик длинного нажатия
    static unsigned char BtnLastState= 0;            //последнее состояние кнопок перед отпусканием
 
    char mask = 0;
    /*if (! (BTN_PIN & BTN_LINE_UP))        mask = BTN_SHRT_UP;
    if (! (BTN_PIN & BTN_LINE_DN))        mask = BTN_SHRT_DN;
    if (! (BTN_PIN & BTN_LINE_LEFT))      mask = BTN_SHRT_LEFT;
    if (! (BTN_PIN & BTN_LINE_RIGHT))     mask = BTN_SHRT_RIGHT;*/
    
    if (! (BTN_PIN & BTN_LINE_DN))        mask = BTN_SHRT_DN;
    if (! (BTN_PIN & BTN_LINE_UP))        mask = BTN_SHRT_UP;
    if (! (BTN_PIN & BTN_LINE_OK))        mask = BTN_SHRT_OK;
    if (! (BTN_PIN & BTN_LINE_LEFT))      mask = BTN_SHRT_LEFT;
    if (! (BTN_PIN & BTN_LINE_RIGHT))     mask = BTN_SHRT_RIGHT;
    if (! (BTN_PIN & BTN_LINE_CANSEL))    mask = BTN_SHRT_CANSEL;
    
    if (mask){                                    //опрос состояния кнопки
        if (BtnLockCount < (BTN_LOCK_TIME/10)){    //клавиша нажата
            BtnLockCount++;
            return;                                //защелка еще не дощитала - возврат
        }
        BtnLastState = mask;
        BtnLockBit =1;                            //нажатие зафиксировано                
        //if (BtnLongCount >= (BTN_LONG_TIME/10))                                
        //    return;                                //возврат, т.к. счетчик длинн нажат досчитал до максимума еще раньше        
        //if (++BtnLongCount >= (BTN_LONG_TIME/10))
        //    BtnFlags |= (BtnLastState<<4);        //счетчик досчитал до максимума - устанавливаем биты длинного нажатия 
    }            
    else{                                        //клавиша отжата            
        if (BtnLockCount){
            BtnLockCount --;
            return;                                //защелка еще не обнулилась - возврат
        }
        if (! BtnLockBit)                        //СТАТИЧЕСКИЙ ВОЗВРАТ
            return;                                
        BtnLockBit =0;                            //отжатие зафиксировано
        if (BtnLongCount < (BTN_LONG_TIME/10))
            BtnFlags |= BtnLastState;            //установка бита короткого нажатия
        //BtnLongCount = 0;                        //сброс счетчика длительности нажатия
    }
}    

//функция чтения данных о нажатии кнопок
char BtnGet (void)
{
    cli();
    char temp = BtnFlags;
    BtnFlags = 0;
    sei();
    return temp;
}

menu_config.h

#ifndef MENU_CONFIG_H
#define MENU_CONFIG_H

//#include "main.h"
extern struct Menu_State MN;;
//------------------------------------------
extern struct _selection SELECTION;
//------------------------------------------
extern struct _menu PAGE_MENU;
//------------------------------------------
typedef void (*FuncPtr)(void);
//указатель на функции
extern FuncPtr FPtr;

#endif

pin_config.h

#ifndef PIN_CONFIG_H
#define PIN_CONFIG_H

#include "common.h"

//настройка параметров работы функций
#define BTN_LOCK_TIME         30           /*время обработки дребезга в милисекундах (10-100)*/
#define BTN_LONG_TIME         1000         /*время фиксации длинного нажатия в милисекундах (1000 - 2500)*/
 
//настройки портов
#define BTN_PORT        PORTB                    /*порт чтения кнопок*/
#define BTN_DDR         DDRB
#define BTN_PIN         PINB 

#define BTN_LINE_DN     (1<<0)                /*пины чтения кнопок*/
#define BTN_LINE_UP     (1<<1)
#define BTN_LINE_OK     (1<<2)
#define BTN_LINE_LEFT   (1<<3)
#define BTN_LINE_RIGHT  (1<<4)
#define BTN_LINE_CANSEL (1<<5)


//глобальные переменные
extern volatile uint8_t BtnFlags;                         //байт флагов нажатия кнопки
    //#define BTN_LONG_UP      (1<<4)                /*бит длинного нажатия кнопки up*/ 
    //#define BTN_LONG_DN      (1<<5)                /*бит длинного нажатия кнопки dn*/ 
    //#define BTN_LONG_LEFT    (1<<6)                /*бит длинного нажатия кнопки left*/ 
    //#define BTN_LONG_RIGHT   (1<<7)                /*бит длинного нажатия кнопки right*/ 
    
    #define BTN_SHRT_DN      (1<<0)                /*бит короткого нажатия кнопки up*/ 
    #define BTN_SHRT_UP      (1<<1)                /*бит короткого нажатия кнопки dn*/ 
    #define BTN_SHRT_OK      (1<<2)                /*бит короткого нажатия кнопки up*/ 
    #define BTN_SHRT_LEFT    (1<<3)                /*бит короткого нажатия кнопки left*/ 
    #define BTN_SHRT_RIGHT   (1<<4)                /*бит короткого нажатия кнопки right*/ 
    #define BTN_SHRT_CANSEL  (1<<5)                /*бит короткого нажатия кнопки right*/

//##############################################################################################################
	
#define RELAY_PORT        PORTC                    /*порт чтения кнопок*/	
#define RELAY_BTM PC0  //назначаем пин "НИЖНЕГО" нагревателя
#define RELAY_TOP PC1  //назначаем пин "ВЕРХНЕГО" нагревателя 	

#define PIN_BUZZER 3 // назначаем пин для пьезодинамика

#endif
	

 

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

Алек97, Вы конечно занимаетесь меню не просто так а для подключения к какому-то устройству, которое будет сидеть в том же МК. Допустим у Вас уже сидит это устройство в МК и работает. И вам надо 1- отображать текущие данные и 2 менять режимы работы оперативно. Так вот для этого система меню не подходит. Потому что надо постоянно бегать по пунктам меню и вносить изменения. Причем во время бегания по пунктам меню вы уже не можете видеть оперативную информацию и пользоваться кнопками для оперативного управлиния, так с помощью их вы бегаете по пунктам меню.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Вы опять не пишете, что хотели сделать, опять придётся догадываться, хотя на этот раз это нелегко, т.к. Вы в разных местах делаете разные вещи. А что хотели – непонятно, потому и непонятно в каком из мест правильно, а в каком ошибка.

Вот из этих сток (файл "sxetch.h", стоки №№20-30):

struct _selection
{
  unsigned char ent_f : 4; //Флаг входа 4 бита — обычно ID меню в которое надо войти
  unsigned char esc_f : 4; //Флаг выхода 4 бита — обычно ID меню в которое надо вернуться
} SELECTION;

struct _menu {
  //unsigned char id; //Номер меню/подменю
  //unsigned char num_selections; //Количество Punktов данного меню/подменю
  SELECTION *m; //Указатель намассив Punktов данного меню/подменю
} PAGE_MENU;

я предположил, что Вы, наверное хотели создать тип данных с именем SELECTION и использовать его как тип данных (для объявления переменных, как в строке №29 и позже, в строка №№ 44, 47, 50 и 53.

И я уже было собираюсь говорить об ошибке в строке №20

Но потом Вы пишете в файле "menu_config.h" строку №7

extern struct _selection SELECTION;

и окончательно сбиваете меня с толку. Если Вы хотите так, то строка №20 у Вас правильная, а ошибки как раз в строках №№ 29, 44, 47, 50 и 53.

Вот и пойми Вас - новичков.

В обще так, то как сейчас написаны строки 20-24, означает следующее:

1.
Объявлен тип данных с именем "struct _selection" или просто "_selection. Можете использовать и так и так. Разница, конечно, есть, на этом этапе изучения будем считать, что её нет. Работать будет и так и эдак.
2.
Объявлена переменная типа struct _selection с именем SELECTION.

Если Вы так и хотели, то у Вас всё правильно в строке №7 файла ""menu_config.h", а вот в строках №№ 29, 44, 47, 50 и 53 Вы при объявлении переменных зачем-то вместо типа данных struct _selection используете имя переменной SELECTION - Вот Вам и говорят, что такого типа нет.

Если же Вы хотели, чтобы SELECTION был типом данных, то в строке 20 в самом начале нужно было написать слово typedef. Тогда строки  №№ 29, 44, 47, 50 и 53 были бы правильными, а вот строка №7 файла ""menu_config.h" была бы ошибочной, т.к. в ней Вы определяете внешнюю переменную с именем, которое совпадает с существующим типом данных.

Как-то так. Хотите внятнее, спрашивайте внятнее - пишите, что хотели сделать.

Алек97
Offline
Зарегистрирован: 21.08.2018

qwone пишет:

Алек97, Вы конечно занимаетесь меню не просто так а для подключения к какому-то устройству, которое будет сидеть в том же МК. Допустим у Вас уже сидит это устройство в МК и работает. И вам надо 1- отображать текущие данные и 2 менять режимы работы оперативно. Так вот для этого система меню не подходит. Потому что надо постоянно бегать по пунктам меню и вносить изменения. Причем во время бегания по пунктам меню вы уже не можете видеть оперативную информацию и пользоваться кнопками для оперативного управлиния, так с помощью их вы бегаете по пунктам меню.

В моем случае бегать по пунктам меню не нужно, достаточно задать начальные настройки либо выбрать готовый профиль из EEPROM. Меню состоит из четырех пунктов, поьзоваться в основном придется максимум двумя, удобно.

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

Алек97 пишет:

В моем случае бегать по пунктам меню не нужно, достаточно задать начальные настройки либо выбрать готовый профиль из EEPROM. Меню состоит из четырех пунктов, поьзоваться в основном придется максимум двумя, удобно.

Разберем по пунктам - Задание профиля. У вас должна структура -тип профиль и две функции:1- запись в профиля в ЕЕPROM и чтение профиля из EEPROMa. А так же желательно чтение профиля из PROGMEM - пригодится для сброса в начальные установка.

  Теперь по меню -там тоже просто . Создать тип структура - строка меню и функции работы с этой строкой :вывод на экран этой строки, увеличить на шаг данные этой строки, уменьшить на шаг данные этой строки , запуск данных ....

  То есть все это отработка работы компонентов нижнего уровня. Вот когда они будут работать четко, то можно и собирать их в кучу. А так ковыряться в вашей простыне и догадываться что не так работает сложновато. Можно это задумано для сложности чтения другими программистами.

Алек97
Offline
Зарегистрирован: 21.08.2018

qwone пишет:

  Теперь по меню -там тоже просто . Создать тип структура - строка меню и функции работы с этой строкой :вывод на экран этой строки, увеличить на шаг данные этой строки, уменьшить на шаг данные этой строки , запуск данных ....

  То есть все это отработка работы компонентов нижнего уровня. Вот когда они будут работать четко, то можно и собирать их в кучу. А так ковыряться в вашей простыне и догадываться что не так работает сложновато. Можно это задумано для сложности чтения другими программистами.

По поводу вывода строк проблем нет, идея создания отдельной структуры для записи-чтения из EEPROM хорошая не спорю. 

Скорее усложняю жизнь себе тем, что до конца не вникнув в суть задачи чего-то требую от других. Да недостаток знаний и не четко сформулированная мысль заставляет видеть во мне не годного программиста. За это прошу прощения в том числе и у ЕвгенияП. 

Евгений, ваши рассуждения прочел и пока не тороплюсь делать какие - то выводы. Пока не разберусь в сути построения программы вашей помощи просить не буду. Если появится действительно стоящий вопрос который будет понятен не только мне но и форуму, тогда я постараюсь все максимально четко передать. Спасибо