|
Злополучная функция what_day(), Вычисление дня недели |
|
|
|
Jun 6 2008, 07:11
|
Знающий
   
Группа: Свой
Сообщений: 589
Регистрация: 24-04-05
Пользователь №: 4 447

|
Для инициализации 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. Но сделаю это в последнюю очередь. Может быть есть другой способ вычисления дня недели? Какие будут предложения?
|
|
|
|
|
 |
Ответов
(1 - 14)
|
Jun 6 2008, 08:41
|
Знающий
   
Группа: Свой
Сообщений: 589
Регистрация: 24-04-05
Пользователь №: 4 447

|
Цитата(MrYuran @ Jun 6 2008, 11:08)  интересно, а куда она возвращает результат? Так ничего не меняет. Просто вызов самой ф-ции приводит к проблеме. Код int main() {volatile unsigned char day; day = what_day(2008, 6, 6); ..................... P.S. Появилась мысль. Может для этой цели использовать библиотеку <time.h> ? Буду разбираться...
|
|
|
|
|
Jun 6 2008, 09:47
|
Знающий
   
Группа: Свой
Сообщений: 589
Регистрация: 24-04-05
Пользователь №: 4 447

|
Цитата(GetSmart @ Jun 6 2008, 11:52)  Листинг кода посмотрите, в MAP-файле должен указываться раход стека в процедурах what_day, ??div16 (может по-другому называется). И вообще, листинги при всяких неполадках читать очень рекомендуется. По листингу не ясно, сколько расходует стек функция what_day. Может, плохо искал. Если не трудно, взгляните сами. Буду очень признательный. Цитата(GetSmart @ Jun 6 2008, 11:52)  В процедуре 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; } Та же проблема: функция работает, но приводит к тому же глюку. Почему вызов функции в начале программы может переполнять стек. Неужели ей мало 0x100 CSTACK ?
|
|
|
|
|
Jun 6 2008, 10:23
|
Знающий
   
Группа: Свой
Сообщений: 589
Регистрация: 24-04-05
Пользователь №: 4 447

|
Цитата(GetSmart @ Jun 6 2008, 13:02)  Я не очень хорошо разбираюсь в стеках 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 ? Ну так должно хватать...
|
|
|
|
|
Jun 6 2008, 10:53
|

Беспросветный оптимист
     
Группа: Свой
Сообщений: 4 640
Регистрация: 26-12-07
Из: Н.Новгород
Пользователь №: 33 646

|
Насколько я понимаю, 0х8f это не совсем 100, вернее, совсем не 100, а 143. Так что 100 никак не хватит. Не процессор надо пожирнее, а лишний жир с этой функции срезать. Или стек задать поширше. Цитата Неужели ей мало 0x100 CSTACK ? Думаю, что да.
--------------------
Программирование делится на системное и бессистемное. ©Моё :) — а для кого-то БГ — это Bill Gilbert =)
|
|
|
|
|
Jun 6 2008, 11:16
|
Знающий
   
Группа: Свой
Сообщений: 589
Регистрация: 24-04-05
Пользователь №: 4 447

|
Цитата(MrYuran @ Jun 6 2008, 13:53)  0х8f это не совсем 100, вернее, совсем не 100, а 143. Так что 100 никак не хватит. ...0x100 - это 256 байт ОЗУ. Цитата(MrYuran @ Jun 6 2008, 13:53)  Не процессор надо пожирнее, а лишний жир с этой функции срезать. Или стек задать поширше. А что срезать? И CSTACK я увеличивать больше 0x150 не могу. PS. может быть есть более легковесная функция вычисления дня недели? Типа табличным методом... Кто знает?
Сообщение отредактировал alux - Jun 6 2008, 11:42
|
|
|
|
|
Jun 6 2008, 12:08
|
Знающий
   
Группа: Свой
Сообщений: 589
Регистрация: 24-04-05
Пользователь №: 4 447

|
Цитата(vet @ Jun 6 2008, 14:41)  Чип поменять тоже можно попробовать - сталкивался со всякими чудесами вроде сбоя указателя стека на ровном месте, пропадавшими при замене кристалла. В смысле поменять на такой же? Это можно. У меня DIP корпус на панельке. Только мне кажется маловероятным брак чипа. Цитата(rezident @ Jun 6 2008, 14:47)  Совет из области паранойи, но все же попробуйте привести все временные переменные и константы в вашей функции к одному типу - int. Не используйте char при вычислениях. Изначально там были int. Я char поставил для экономии. Проверил. Не помогло (в смысле, если на int все поменять). Да и логики в этом не вижу... Цитата(vet @ Jun 6 2008, 14:41)  ищите ошибку в программе. Программа работает нормально (без этой функции). Я не вижу связи между ней и процессами ОС. Код //--------------------------------------------------------------------------- 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 }
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|