Перейти к содержанию
    

Злополучная функция what_day()

Для инициализации Real Time Clock возникла необходимость вычисления дня недели по дате. Функция достаточно простая:

//----------------------------------------------------------------------------
// Вычисления дня недели по дате
// Все деления целочисленные (остаток отбрасывается).
// Результат: 0 — воскресенье, 1 — понедельник и т. д.
//----------------------------------------------------------------------------
unsigned char what_day(unsigned int year, unsigned char month, unsigned char date)  
{
  unsigned char a = (14 - month) / 12; 
  unsigned int y = year - a;         
  unsigned char m = month + 12*a - 2; 
  
  return (7000 + (date + y + y/4 - y/100 + y/400 + (31*m)/12 ))% 7;   
}

 

Но по какой-то непонятной причине вызов ее приводит к глюкам программы. В частности, при входе в пункт меню невозможно из него выйти, при этом программа реагирует не так как задумано. Должен сказать, что проект использует ОС (scmRTOS) и ОЗУ использовано на 90%. Контроллер Mega324P (2кБ ОЗУ). Отладочными средствами (JTAG) пользоваться не представляется возможным. Это похоже на переполнение стека. Но. Почему это происходит, если вызов достаточно простой функции вставить в начале функции main (до запуска ОС):

int main()
{
  what_day(2008, 6, 6);
.....................

Больше нигде в процессах она не используется. Пробовал менять размеры CSTACK (100...200) и RSTACK (32...64). Не помогло. Думаю менять контроллер на AT90USB1287. Но сделаю это в последнюю очередь. Может быть есть другой способ вычисления дня недели? Какие будут предложения?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

интересно, а куда она возвращает результат?

int main()
{
  what_day(2008, 6, 6);
.....................

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

интересно, а куда она возвращает результат?

Так ничего не меняет. Просто вызов самой ф-ции приводит к проблеме.

int main()
{volatile unsigned char day;
  day = what_day(2008, 6, 6);
.....................

 

P.S. Появилась мысль. Может для этой цели использовать библиотеку <time.h> ? Буду разбираться...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Листинг кода посмотрите, в MAP-файле должен указываться раход стека в процедурах what_day, ??div16 (может по-другому называется). И вообще, листинги при всяких неполадках читать очень рекомендуется.

 

В процедуре what_day куча временных переменных. В стеке они лежат или нет - хз. Всё зависит от мозговитости компилятора.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Листинг кода посмотрите, в MAP-файле должен указываться раход стека в процедурах what_day, ??div16 (может по-другому называется). И вообще, листинги при всяких неполадках читать очень рекомендуется.

По листингу не ясно, сколько расходует стек функция what_day. Может, плохо искал. Если не трудно, взгляните сами. Буду очень признательный.

 

В процедуре what_day куча временных переменных. В стеке они лежат или нет - хз.

Так разве после возврата из ф-ции стек не освобождается? В одном из процессов ОС у меня применяются вычисления двойной точности. И ничего, справляется. Хватает 100 байт для процесса.

 

P.s. Нашел еще одну функцию вычисления дня недели:

enum Month {January=1,February,March,April,May,June,July,August,
            September,October,November,December};
struct Date
{
  float day;
  Month month;
  int year;
};

signed int DayOfWeek(Date date) 
{
    float F;
    if (date.month<March)
      F=365*date.year+date.day+31*(date.month-1)+
        (signed int)((date.year-1)/4)-(signed int)(3*(signed int)((date.year-1)/100+1)/4);
    else
      F=365*date.year+date.day+31*(date.month-1)-(signed int)(0.4*date.month+2.3)+
        (signed int)(date.year/4)-(signed int)(3*(signed int)(date.year/100+1)/4);
    return  (signed int)F-7*(signed int)(F/7)-1;
}

........................
int main()
{volatile unsigned char day;
  Date Today={5,June,2008};
  
    switch (DayOfWeek(Today))
  {
       case -1: day=1;break;  // printf("Sunday\n");break;
       case 0:  day=2;break;  //printf("Monday\n");break;
       case 1:  day=3;break;  //printf("Tuesday\n");break;
       case 2:  day=4;break;  //printf("Wednesday\n");break;
       case 3:  day=5;break;  //printf("Thursday\n");break;
       case 4:  day=6;break;  //printf("Friday\n");break;
       case 5:  day=7;break;  //printf("Saturday\n");break;
  }

Та же проблема: функция работает, но приводит к тому же глюку. :wacko:

Почему вызов функции в начале программы может переполнять стек. Неужели ей мало 0x100 CSTACK ?

device_list.rar

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

В листинге нет процедуры ??what_day. Дайте листинг файла, в котором расположена данная процедура (what_day).

 

Я не очень хорошо разбираюсь в стеках AVR. RSTACK - это стек с указателем в SP только для вызовов процедур? А CSTACK - для данных?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Я не очень хорошо разбираюсь в стеках AVR. RSTACK - это стек с указателем в SP только для вызовов процедур? А CSTACK - для данных?
RSCTAK - стек возвратов. CSTACK - стек данных.

Вот новый map-файл. В опциях Linker поставил все галки для генерации map-файла. Если пользоваться поиском компилятора (Edit->Find (F3)) , можно быстро найти нужное место:

  02    what_day(unsigned int, unsigned char, unsigned char)
        | Stack used (prev) :  0000008F 00000038
        | + function block  :  00000008 00000004

Если я правильно понимаю, то вызов этой ф-ции потребляет CSTACK=0x8f и RSTACK=0x38 ? Ну так должно хватать...

device_map.rar

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Прерывания запретите перед вызовом этой процедуры. После выхода разрешите. Может поможет.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Вызов идет в начале main(). Естественно, прерывания еще не разрешены. Что, получается выход один - менять процессор на пожирнее? Эта проблема точно связана с нехваткой ОЗУ?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Насколько я понимаю, 0х8f это не совсем 100, вернее, совсем не 100, а 143.

Так что 100 никак не хватит.

Не процессор надо пожирнее, а лишний жир с этой функции срезать. Или стек задать поширше.

Неужели ей мало 0x100 CSTACK ?

Думаю, что да.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

0х8f это не совсем 100, вернее, совсем не 100, а 143.

Так что 100 никак не хватит.

...0x100 - это 256 байт ОЗУ.

 

Не процессор надо пожирнее, а лишний жир с этой функции срезать. Или стек задать поширше.
А что срезать? И CSTACK я увеличивать больше 0x150 не могу.

 

PS. может быть есть более легковесная функция вычисления дня недели? Типа табличным методом... Кто знает?

Изменено пользователем alux

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Отладочными средствами (JTAG) пользоваться не представляется возможным.

JTAG отладчик и не нужен поскольку в этой функции не используется периферия.

Вполне достаточно симулятора. В простейшем случае, разумеется, только с этой функцией в пустой main.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Коллеги, какая нехватка ОЗУ? три параметра, три переменных, пара математических функций выльются от силы в парочку вложенных вызовов и полдюжины байт стека; даже проверять не буду.

alux, ищите ошибку в программе.

Чип поменять тоже можно попробовать - сталкивался со всякими чудесами вроде сбоя указателя стека на ровном месте, пропадавшими при замене кристалла.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Совет из области паранойи, но все же попробуйте привести все временные переменные и константы в вашей функции к одному типу - int. Не используйте char при вычислениях.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Чип поменять тоже можно попробовать - сталкивался со всякими чудесами вроде сбоя указателя стека на ровном месте, пропадавшими при замене кристалла.

В смысле поменять на такой же? Это можно. У меня DIP корпус на панельке. Только мне кажется маловероятным брак чипа.

 

Совет из области паранойи, но все же попробуйте привести все временные переменные и константы в вашей функции к одному типу - int. Не используйте char при вычислениях.
Изначально там были int. Я char поставил для экономии. Проверил. Не помогло (в смысле, если на int все поменять). Да и логики в этом не вижу...

 

ищите ошибку в программе.
Программа работает нормально (без этой функции). Я не вижу связи между ней и процессами ОС.

//---------------------------------------------------------------------------
int main()
{
volatile unsigned char day;
  day = what_day(2008, 6, 16);

  // Initialise variables from EEPROM
  area = ee_area;         // Площадь поперечного сечения газохода, m^2
  Kt = ee_Kt;             // Коэффициент напорности трубки
  Y0 = ee_Y0;             // Плотность газа при 0 градусов, kg/m^3
  rate = ee_rate;         // Частота обновления данных, Hz
  nmax = ee_nmax;         // Коэффициент усреднения данных
  
  // Initialise
  uart_Init(baud_select);
  i2c_Init(100);          // set TWI bit rate to 100KHz    
  spi_Init();             // Master mode, clock = f/16, select clock phase positive-going in middle of data
  key_Init();
  pca9557_Init();
  lcd_Init();
  menu_Init(); 
  hp03_Init();
  ds75_Init();
  rtc_Init(); 

  SLEEP_ENABLE;
  SELECT_IDLE;
  
__enable_interrupt();    // set the Global Interrupt Enable Bit 
  ad7799_Init();
  
  //beep();                 // Инициализация прошла успешно 
  
  TCCR0B = 0x03;          // Start System Timer f_clk/64
  TIMSK0 |= (1 << TOIE0); // Разрешить прерывания Timer0 по переполнению (OVF)
                          // Период переполнения при f_clk=7.3728 Mhz 2.222 ms
  OS::Run();              //                     при f_clk=20 Mhz 0.8192 ms
}

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...