Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Злополучная функция what_day()
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > Cредства разработки для МК > IAR
Страницы: 1, 2
alux
Для инициализации 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. Но сделаю это в последнюю очередь. Может быть есть другой способ вычисления дня недели? Какие будут предложения?
MrYuran
интересно, а куда она возвращает результат?
Код
int main()
{
  what_day(2008, 6, 6);
.....................
alux
Цитата(MrYuran @ Jun 6 2008, 11:08) *
интересно, а куда она возвращает результат?

Так ничего не меняет. Просто вызов самой ф-ции приводит к проблеме.
Код
int main()
{volatile unsigned char day;
  day = what_day(2008, 6, 6);
.....................


P.S. Появилась мысль. Может для этой цели использовать библиотеку <time.h> ? Буду разбираться...
GetSmart
Листинг кода посмотрите, в MAP-файле должен указываться раход стека в процедурах what_day, ??div16 (может по-другому называется). И вообще, листинги при всяких неполадках читать очень рекомендуется.

В процедуре what_day куча временных переменных. В стеке они лежат или нет - хз. Всё зависит от мозговитости компилятора.
alux
Цитата(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;
  }

Та же проблема: функция работает, но приводит к тому же глюку. wacko.gif
Почему вызов функции в начале программы может переполнять стек. Неужели ей мало 0x100 CSTACK ?
GetSmart
В листинге нет процедуры ??what_day. Дайте листинг файла, в котором расположена данная процедура (what_day).

Я не очень хорошо разбираюсь в стеках AVR. RSTACK - это стек с указателем в SP только для вызовов процедур? А CSTACK - для данных?
alux
Цитата(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 ? Ну так должно хватать...
GetSmart
Прерывания запретите перед вызовом этой процедуры. После выхода разрешите. Может поможет.
alux
Вызов идет в начале main(). Естественно, прерывания еще не разрешены. Что, получается выход один - менять процессор на пожирнее? Эта проблема точно связана с нехваткой ОЗУ?
MrYuran
Насколько я понимаю, 0х8f это не совсем 100, вернее, совсем не 100, а 143.
Так что 100 никак не хватит.
Не процессор надо пожирнее, а лишний жир с этой функции срезать. Или стек задать поширше.
Цитата
Неужели ей мало 0x100 CSTACK ?

Думаю, что да.
alux
Цитата(MrYuran @ Jun 6 2008, 13:53) *
0х8f это не совсем 100, вернее, совсем не 100, а 143.
Так что 100 никак не хватит.

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

Цитата(MrYuran @ Jun 6 2008, 13:53) *
Не процессор надо пожирнее, а лишний жир с этой функции срезать. Или стек задать поширше.
А что срезать? И CSTACK я увеличивать больше 0x150 не могу.

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

JTAG отладчик и не нужен поскольку в этой функции не используется периферия.
Вполне достаточно симулятора. В простейшем случае, разумеется, только с этой функцией в пустой main.
vet
Коллеги, какая нехватка ОЗУ? три параметра, три переменных, пара математических функций выльются от силы в парочку вложенных вызовов и полдюжины байт стека; даже проверять не буду.
alux, ищите ошибку в программе.
Чип поменять тоже можно попробовать - сталкивался со всякими чудесами вроде сбоя указателя стека на ровном месте, пропадавшими при замене кристалла.
rezident
Совет из области паранойи, но все же попробуйте привести все временные переменные и константы в вашей функции к одному типу - int. Не используйте char при вычислениях.
alux
Цитата(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
}
rezident
Цитата(alux @ Jun 6 2008, 18:08) *
Изначально там были int. Я char поставил для экономии. Проверил. Не помогло (в смысле, если на int все поменять). Да и логики в этом не вижу...
Логика тут простая - ваша экономия мнимая. Компилятор все равно приводит все вычисления к типу int, а временные переменные разместит в регистрах.
alux
Если это хоть как-то наведет на мысли... Глюк проявляется при входе при входе к определенным пунктам меню ("Редактирование параметров"). При этом редактирование и сохранение параметра идет как положено, за исключением того , что вместо кнопки "UP"-смещение на одно знакоместо происходит замена цифрой '6', а вместо "ESC", котороя возвращает в предыдущий п. меню происходит вставка цифры '2'. Некоторые пункты меню работают нормально. Напомню, что все это происходит, если вызвать функцию what_day();
alux
Это выходит за всякие рамки понимания и попахивает чертовщиной...
Написал третью функцию вычисления дня недели:
Код
unsigned char what_day(unsigned int year, unsigned int month, unsigned int day)
{
  unsigned char YEAR[]={0, 2, 3, 4};
  unsigned char MONTH[]={0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5};
  unsigned char a, b, c, e, tmp;
  
  tmp = year - 1996;
  a = tmp & 3;
  b = (tmp >> 2) + (tmp & 0xfc);
  c = MONTH[month-1];
  
  if((a == 0) && (month > 2)) c++;
  a = YEAR[a];
  e = (a + b + c + day) % 7;
  return e;
}

Функцию переписал у Сергея Фролова. В ней нет тяжелых делений. Она работает. Но при вызове все равно приводит к тому глюку! 07.gif

Ради эксперимента попробовал вместо этой функции вызвать какую-нибудь сложную математическую функцию , например решение квадратного уравнения и укруглением результата до 5 знака после запятой.
Код
//_____________________________________________________________________________
double quadratic(double a, double b, double c)
{
  double d, x;
  d=b*b-4.*a*c;                 // Дискриминант
  if(d<0.) return 0;            // Уравнение не имеет корней
  if(d==0.) x=-b/(2.*a);        // Уравнение имеет один корень
  else  x=(-b+sqrt(d))/(2.*a);  // Уравнение имеет два корня,
                        // но нужен только положительный
  return x;
}

//#############################################################################
// round down a number to the decimal place given by power
// e.g round(1.0256,-2) means round to 2nd decimal -> 1.03
//_____________________________________________________________________________
double round_to_pow(double x, int power)
{
   double inc, xnew;
   inc  = pow(10.0, (double)power);
   xnew = inc * (floor(x/inc + 0.5 + SMALL));
   return(xnew);
}

//---------------------------------------------------------------------------
int main()
{
volatile unsigned char day;
  day = (unsigned char)round_to_pow(quadratic(100, 200, 3), -5);
Так все нормально. В чем же может быть дело? Какие еще будут предложения?

PS. Одна деталь. Если убрать из what_day() (обеих вариантов) операцию % 7, то глюк исчезает. Как это объяснить? В дополнение к функции решения кв. уравнения добавил функцию по расчету давления и температуры цифрового датчика давления. Там точно громоздкие вычисления. И все прошло (глюка не вызвало). А здесь простая операция деления по модулю... cranky.gif
sKWO
Цитата(alux @ Jun 6 2008, 11:11) *
Но по какой-то непонятной причине вызов ее приводит к глюкам программы. В частности, при входе в пункт меню невозможно из него выйти, при этом программа реагирует не так как задумано. Должен сказать, что проект использует ОС (scmRTOS) и ОЗУ использовано на 90%. Контроллер Mega324P (2кБ ОЗУ). Отладочными средствами (JTAG) пользоваться не представляется возможным. Это похоже на переполнение стека. Но. Почему это происходит, если вызов достаточно простой функции вставить в начале функции main (до запуска ОС):
Код
int main()
{
  what_day(2008, 6, 6);
.....................

Больше нигде в процессах она не используется. Пробовал менять размеры CSTACK (100...200) и RSTACK (32...64).

Не помогло, не в этом причина . из Вашего мэп
Код
CSTACK               DATA          00000100 - 000001FF         100   dse    0
RSTACK               DATA          00000200 - 0000023F          40   dse    0

Теперь мысль, может бредовая но всё-же- может Вы запретили компилятору какие-то регистры в которых компилятор хранит остаток от деления? не знаю как ИАР но в код вижине остаток хранится в 26 регистре. Пример для КВ
Код
//=========================================================
void prog3(char ch)
{
    ch=ch+2;
    ch=ch/3;
    ch=ch%3;
}
void prog4(char ch)
{
    ch=ch/3;
    ch=ch%3;
}

??? Лишние инструкции
??? Оптимальнее было-бы так -
_prog3:
    LD   R30,Y
    SUBI R30,-LOW(2)
    ST   Y,R30
_prog4:
    LD   R26,Y
    LDI  R30,LOW(3)
    RCALL __DIVB21U
    ST   Y,R30
    LD   R26,Y
    LDI  R30,LOW(3)
    RCALL __MODB21U
    ST   Y,R30
    ADIW R28,1
    RET
Flasher
Недавно в яре натолкнулся под авр использовал функцию % и прога пошла лесом... как только закоментировал строчку- все заработало. В листинге не разбирался- не было времени. Теперь жалею
alux
Цитата(sKWO @ Jun 6 2008, 22:21) *
Не помогло, не в этом причина . из Вашего мэп
Код
CSTACK               DATA          00000100 - 000001FF         100   dse    0
RSTACK               DATA          00000200 - 0000023F          40   dse    0

Не понял. Что Вы имели в виду?

Цитата(sKWO @ Jun 6 2008, 22:21) *
Теперь мысль, может бредовая но всё-же- может Вы запретили компилятору какие-то регистры в которых компилятор хранит остаток от деления?
Регистры я не трогаю.

Заменил в функции операцию % 7 на -= 7:
Код
  day = a + year + c + day;// % 7;
  while(day >= 7)  day -= 7;
  
  return day;

Проблема решилась частично: если вызвать эту функцию из main() , то глюк пропал. Но если вызвать ее из другой функции rtc_set_time(), то глюк имеет место быть... Не знаю что и думать. Варианты исчерпались. Пойду-ка я спать.


PS2. Все! Наконец-то поборол глюк.
Переписал в очередной раз функцию. Прям целая коллекция функций получилась smile.gif
Код
unsigned char what_day(unsigned int year, unsigned int month, unsigned int date)
{
  signed int c=0;
  unsigned char day;
  
  if((date>=1)&&(date<=31)&&(month>=1)&&(month<=12)&&(year>=1582)&&(year<=4903))
  {  
    if(month < 3)
    {
      month += 10;    // Месяц январь или февраль?
      year -= 1;
    }
    else
    {
      month -= 2;     // Остальные месяцы
      
      while(year >= 100)
      {
        c++;          // Вычисляем столетие
        year -= 100;  // Находим год в столетии
      }
      
      day = (unsigned char)((26*month-2)/10 + date + (year>>2) + year + (c>>2) - 2*c);
      while(day >= 7) day -= 7;  // Вычисляем день недели
    }
  }
  return day;
}
Сейчас все работает как положено. Хотя неприятный осадок остался... Нет ответов на вопросы - почему такое произошло?
vet
Посмотрите, какие регистры использует подпрограмма взятия остатка от деления: возможно, ошибка в несохранении компилятором значения регистра, используемого вызывающим кодом.
sKWO
Цитата(alux @ Jun 7 2008, 01:19) *
Не понял. Что Вы имели в виду?

использование функциями CSTACK и RSTACK я всегда смотрю в листинге. файл с расширением .lst.
вот отрывок, удобно
Код
Maximum stack usage in bytes:

     Function           CSTACK RSTACK
     --------           ------ ------
     lcd_EN_toggle          0      2
     lcd_command            1      2
       -> lcd_waitcmd       1      2
       -> lcd_write         1      2
       -> lcd_waitcmd       1      2

а нащёт остатка от деления и рассположения в регистрах надо будет всё же подумать, может попробовать намного пошаманить с инлайн ассемблером ? Сообщите о результатах. Сам тоже попробую
AlexG_changed
Если у вас scmRTOS, то размеры каких стеков вы крутите? Там ведь у каждого процесса свои стеки.

PS: time.h использовал как-то. плохо совместимая с экономией памяти вещь.
Andreas1
Цитата(Flasher @ Jun 6 2008, 23:38) *
Недавно в яре натолкнулся под авр использовал функцию % и прога пошла лесом... как только закоментировал строчку- все заработало. В листинге не разбирался- не было времени. Теперь жалею

Странно... Использовал скопипастленную с сахары функцию
Код
unsigned long DateTimeToLong(TDataTime* pdt){
byte month;
word year;
unsigned long c, ya;
unsigned long res;

month = BCDtoBin(pdt->Month);
year = BCDtoBin(pdt->Year)+2000;

if(month > 2){
month -= 3;
}/*if*/
else {
month += 9;
year--;
}/*else*/

c = (year / 100L);
ya = (year - (100L * c));
res = (146097L * c)/4L + (1461L * ya) /4L + (153L * month + 2L)/5L + BCDtoBin(pdt->Date);
pdt->Day=(res+2)%7;
return (res - 720000L) * 1440L + (BCDtoBin(pdt->Hour) * 60L + BCDtoBin(pdt->Min));
}

Никаких проблем, хотя оптимальности и экономии никакой. IAR AVR 4.30 Значения CSTACK и RSTACK по умолчанию, т.е минимальные.
GetSmart
Цитата(alux)
Проблема решилась частично: если вызвать эту функцию из main() , то глюк пропал. Но если вызвать ее из другой функции rtc_set_time(), то глюк имеет место быть...
Всё-таки не исключено, что стек. В любой другой функции стека меньше по сравнению с main, т.к. они вызываются после main.

Цитата(AlexG)
Если у вас scmRTOS, то размеры каких стеков вы крутите? Там ведь у каждого процесса свои стеки.
Действие происходит ещё до запуска ОС, на этапе инициализаций.

PS. Либо дайте уже нормальный листинг, либо этот телепатический сеанс затянется надолго. Нужен листинг этой "злополучной" функции и карту ОЗУ со стеком и рядом с ними.
alux
Цитата(GetSmart @ Jun 7 2008, 17:00) *
Нужен листинг этой "злополучной" функции и карту ОЗУ со стеком и рядом с ними.

Рано обрадовался... Ночью не доглядел, забыл раскомментировать вызов этой функции. Поэтому решил, что проблема решена. Но только-что обнаружил, если вместо вызова функции вставить ее тело, то проблема исчезает. Выкладываю листинги и мап-файлы обоих вариантов (с вызовом функции и с вставленным телом функции).
Вызов функции what_day() происходит из rtc_set_time(), которая в свою очередь вызывается из rtc_Init().
GetSmart
Вызов функции деления до сих пор присутствует.

Ещё нужен листинг функций PROLOGUE6_L09, S_EC_MUL_L02, US_DIVMOD_L02. Ищется через поиск слова в директории со всеми листингами.
alux
Цитата(GetSmart @ Jun 7 2008, 20:55) *
Вызов функции деления до сих пор присутствует.

Код
day = (unsigned char)((26*month-2)/10 + date + (year>>2) + year + (c>>2) - 2*c);

Выбросить операцию деления я же не могу - без нее не будет вычислять день.

Цитата(GetSmart @ Jun 7 2008, 20:55) *
Ещё нужен листинг функций PROLOGUE6_L09, S_EC_MUL_L02, US_DIVMOD_L02. Ищется через поиск слова в директории со всеми листингами.
Поиск по этим словам в директории list или проекта дает только вызовы этих функций:
Код
//NOTE: This module defines or uses C++ features that are not
//      accessible from assembler code. Assembling this file will
//      not produce an equivalent object file to the one produced
//      by the C++ compiler.

        EXTERN ?EPILOGUE_B6_L09
        EXTERN ?EPILOGUE_B8_L09
        EXTERN ?PROLOGUE6_L09
        EXTERN ?PROLOGUE8_L09
....................
        EXTERN ?US_DIVMOD_L02

..................
        CALL    ?US_DIVMOD_L02
Я не нашел нигде их определения. Судя по замечанию сверху - их ассемблерные инструкции не доступны.
GetSmart
Тогда выкладывайте HEX-файл. Функция деления - она общая для деления и остатка, кстати.

В MAP-файле с глюком указано, что последний адрес в раме = 087B, что вроде бы выходит за пределы чипа. Под рукой есть только описание mega323, у которого тоже 2 КБ и адреса в раме заканчиваются на 085F. Может файл линковки неправильный. Тоже выложите посмотреть.
alux
Цитата(GetSmart @ Jun 8 2008, 00:48) *
Тогда выкладывайте HEX-файл...Может файл линковки неправильный. Тоже выложите посмотреть.

Выкладываю файлы линковки, list, map и hex варианта с глюком. В настройках проекта Configure system using dialogы (not in XCL) стоит галка.
В функции what_day() , которую выкладывал ранее, закралась маленькая ошибка, не имеющая никакого отношения к глюку - после else нужно убрать пару фигурных скобок.
GetSmart
Так и не понял косяк. Со стеком не может быть проблемы точно. В RSTACK требуется всего 2 вызова, то есть 4 байта. В CSTACK тоже минимум - 6 байт. Портит регистры R0,R1,R17-R21,R30,R31 (не восстанавливаются в прологе/эпилоге). Посмотрел получше. R16-R21 можно портить, в них передаются параметры и возвращается результат. Ну а R0,R1,R30,R31 кажется полюбому можно портить. Кстати, CSTACK - это похоже стек данных, а RSTACK - вызовов.

Кажется глюки вообще не от этой злополучной процедуры.

PS. Не всё она правильно считает. 1 января 2000 года = 6 (суббота). 1 января 2004 неправильно посчитала. 1 января 2008 правильно = 2 (вторник). Результат похоже не нужно увеличивать на 1.
alux
Цитата(GetSmart @ Jun 8 2008, 12:49) *
Кажется глюки вообще не от этой злополучной процедуры.
Когда не остается версий, остается одно - валить все на компилятор wink.gif

Я смирился с глюком. Вышел из положения как говорил - вместо вызова функции вставил само тело. Что от этого изменилось - не понятно. Работает (пусть пока) - ну и ладно. Как сказал один умный человек (Билл Гейтс кажется), компьютер (микроконтроллер, компилятор, ...) настолько сложная штука, что не понятно, каким образом это все работает smile.gif

Цитата(GetSmart @ Jun 8 2008, 12:49) *
Не всё она правильно считает. 1 января 2000 года = 6 (суббота). 1 января 2004 неправильно посчитала. 1 января 2008 правильно = 2 (вторник). Результат похоже не нужно увеличивать на 1.
Действительно, 1 января 2004 неправильно посчитало. Алгоритм переписал с
Turbo Pascal
. А единицу нужно прибавить, потому-что у DS1338 Sunday=1, Monday=2 ,... и т.д.
sKWO
Цитата(alux @ Jun 8 2008, 03:21) *
Выкладываю файлы линковки, list, map и hex варианта с глюком. В настройках проекта Configure system using dialogы (not in XCL) стоит галка.
В функции what_day() , которую выкладывал ранее, закралась маленькая ошибка, не имеющая никакого отношения к глюку - после else нужно убрать пару фигурных скобок.

Если я ещё не надоел, то в общем есть предложение переменную day сделать беззнаковой итежер
ну и тоже самое возвращаемое значение беззнаковый итежер функции what_day() .
при вычислении остатка от деления исспользуется макро US_DIVMOD_L02 - файл стандартный библиотеки ИАР I02.s90. Там происходит вот такое
'short' (i.e. 16 bit) unsigned division and modulo
Может поэтому результат возвращается не совсем корректно через беззнаковый чар хотя его размера и достаточно?
GetSmart
Ещё как вариант, нужно сравнить два листинга с процедурой в которой наблюдаются глюки при вызове what_day(), припоминаю что это было в меню. Первый листинг скомпилированный с вызовом процедуры what_day(), в котором наблюдаются глюки. Второй листинг без этой процедуры и без глюков.

Можно даже смело включить в процедуру what_day() нахождение остатка через %. Это та же процедура деления и в ней ничего страшного нет.
alux
Цитата(sKWO @ Jun 8 2008, 22:36) *
Если я ещё не надоел, то в общем есть предложение переменную day сделать беззнаковой итежер
ну и тоже самое возвращаемое значение беззнаковый итежер функции what_day() .
Там же вычисления проводятся с int. К unsigned char day приводится лишь результат. Ну проверил на всякий случай... Не в этом дело.

Цитата(GetSmart @ Jun 9 2008, 00:53) *
Ещё как вариант, нужно сравнить два листинга с процедурой в которой наблюдаются глюки при вызове what_day(), припоминаю что это было в меню. Первый листинг скомпилированный с вызовом процедуры what_day(), в котором наблюдаются глюки. Второй листинг без этой процедуры и без глюков.
Листинги чего? Меню? Дело в том, что программа состоит из десятков файлов. Тем более это все работает под управлением ОС... Вкратце это выглядит так: обработка клавиатуры и получение скан-кода вынесено в высокоприоритетный процесс. Действие, необходимое при нажатии кнопки, зависит от текущего режима: если MainMenu- то навигация по пунктам меню, если EditValue - то редактирование параметров, и т.д....
Код
OS_PROCESS void TKeyScan::Exec()    //TProc1
{
  for(;;)
  {
      PCInt3.Wait();
    
    Sleep(20);        // Задержка 10*2=20мсек для устранения дребезга контактов
    
    scan_key();
  
    if(key_code.scan & KEY_PRESSED)
    {
      key_code.scan &= ~KEY_PRESSED;   // Clear MSB of scan_code (key_pressed)
    
      switch(key_code.scan)      
      {
         case UP:     //k_right
              CurrentMode->Up();  
              break;
              
         case DOWN:   //k_left
              CurrentMode->Down();
              break;
              
         case LEFT:   //k_esc
              CurrentMode->Left();
              break;
              
         case RIGHT:  //k_enter
              CurrentMode->Right();
              break;
              
         default:     // 0...9
              CurrentMode->Numeric();
              break;
      }
    }
  }
}


PS. Функцию вычисления дня недели заменил на предыдущую (Сергея Фролова). Она вычисляет правильно. По крайней мере мне не удалось найти "неправилные" дни. Причем вызов ее не приводит к глюку. Но стоит добавить что-нибудь, например, проверку валидности данных:
Код
if((date>=1)&&(date<=31)&&(month>=1)&&(month<=12)&&(year>=1996)&&(year<=2100))

или хотя бы просто :
Код
i2c_write(year-2000);
... сразу вылазит глюк...
alux
Этот глюк выпил достаточно моей крови... smile3046.gif Решил досконально разобраться с этой проблемой. И выяснил следующее:
каким-то образом портится скан-код нажатой клавиши. За основу программы keymatrix взял апнот Atmel avr243. Проблема исчезает, если убрать #define ALTKEYS (см. avr243). В программе используется следующая структура данных:
Код
union _key_code
{
    unsigned int complete;            // Access all 16 bits
    
    struct
    {
    union
    {
        unsigned char flags;        // Access flag status only
            
        struct
        {
        unsigned char altKey0 : 1;    // Access the flags separately
        unsigned char altKey1 : 1;
        unsigned char altKey2 : 1;
        unsigned char altKey3 : 1;
        unsigned char lckKey0 : 1;
        unsigned char lckKey1 : 1;
        unsigned char lckKey2 : 1;
        };
    };
        
    union
    {
        unsigned char scan;            // Access scancode
            
        struct
        {
            unsigned char col : 3;        // Access column of keypress
        unsigned char row : 3;        // Access row of keypress
                
        unsigned char unused : 2;
        };
    };
    };
};
extern volatile union _key_code key_code;   // Scan result structure
extern volatile unsigned char key_altState; // Current alternation flags
Так как в отдельный момент времени в памяти может находиться только один из указанных при объявлении объединения типов, то при вызове key_processAltKeys(), в которой осуществляется доступ к переменной key_altState, портится key_code.scan нажатой кнопки со всеми вытекающими последствиями. Т.е. изначально проблема была не в функции what_day(), а в драйвере клавиатуры. Внешне это выглядело довольно странно: скан-код портился выборочно при определенных условиях и на завершающей стадии проекта.
Осталось придумать, как решить эту проблему. smile.gif

PS. Насколько я понимаю, этого не должно быть. Ведь эти переменные относятся к разным union, которые являются членами общей структуры, которая в свою очередь является членом объединения. Или я ошибаюсь?
GetSmart
Цитата(alux)
Так как в отдельный момент времени в памяти может находиться только один из указанных при объявлении объединения типов, то при вызове key_processAltKeys(), в которой осуществляется доступ к переменной key_altState, портится key_code.scan нажатой кнопки со всеми вытекающими последствиями.
Вряд ли key_code.scan портится в key_processAltKeys(). В объявлении union _key_code тоже вроде бы всё "чисто". Клавиатура сканируется на двух полных портах C и D и получается 6-битный скан-код. Если на этих портах ещё что-нибудь висит, хотя бы один пин используется не для клавиатуры, то будут глюки. Особенно учитывая, что в процедуре convertKey() индекс в массиве символов используется 5-битный, а скан-код 6-битный. То есть можно залететь в следующую кодовую страницу. Запись в PORTA сначала ~key_altState, а потом сразу же кода символа не очень понятна. Если там светодиоды, то на них будет отображаться именно код символа.

Ещё одна маловероятная идея. В коде:
Код
/*** Global variables ***/
volatile unsigned char key_altState;            // Holding current alternation flags
volatile union _key_code key_code;                // Scan result structure
Поменяйте местами две этих переменных. Объявляйте key_code перед объявлением key_altState. Может что изменится. А вообще, если какая-нить часть проги портит память с этими переменными, то может в этом дело.
alux
Цитата(GetSmart @ Jun 12 2008, 02:15) *
Клавиатура сканируется на двух полных портах C и D и получается 6-битный скан-код. Если на этих портах ещё что-нибудь висит, хотя бы один пин используется не для клавиатуры, то будут глюки. Особенно учитывая, что в процедуре convertKey() индекс в массиве символов используется 5-битный, а скан-код 6-битный. То есть можно залететь в следующую кодовую страницу.

Да, выводы мк использованы все. Для клавиатуры пришлось разнести строки и столбцы в разные порты. Не понял на счет глюка, я же прерывания настроил именно на эти выводы, к которым подключена клавиатура:
Код
// Ports
#define KEYMATRIX_COL_PORT  PORTB        // Command Output Register
#define KEYMATRIX_COL_DDR   DDRB        // Data Direction Register for KeymatrixPort
#define KEYMATRIX_COL_PIN   PINB        // PIN Register for KeymatrixPort

#define KEYMATRIX_ROW_PORT  PORTD        // Command Output Register
#define KEYMATRIX_ROW_DDR   DDRD        // Data Direction Register for KeymatrixPort
#define KEYMATRIX_ROW_PIN   PIND        // PIN Register for KeymatrixPort

// pins PCINT28..31->PCI3
#define ENABLE_PCINT3   {PCIFR |= (1<<PCIF3); PCICR |= (1<<PCIE3);}// Clear interrupt status flag, Enable pin change interrupt PCINT3
#define DISABLE_PCINT3  PCICR &= ~(1<<PCIE3)    // Disable pin change interrupt PCINT3
..........................
void key_Init(void)
{
    /* Init global variables */
    key_altState = 0;        // Clear alternation flags
    key_code.complete = 0;    // Clear scan result

    /* Init ports */
    KEYMATRIX_ROW_DDR &= 0x0f;    // Set row lines to input  
    KEYMATRIX_ROW_PORT |= 0xf0;    // Pull row lines high    
        
    KEYMATRIX_COL_DDR |= 0x0f;  // Set column lines to output  
    KEYMATRIX_COL_PORT &= 0xf0; // Drive all column lines low      
    
    /* Enable external interrupt */
//    PCIFR |= (1<<PCIF3);   //Очистить флаги внешних прерываний
    PCMSK3 = (1<<PCINT31)|(1<<PCINT30)|(1<<PCINT29)|(1<<PCINT28); //Настроить  прерывания на выводах PD4...PD7    
    ENABLE_PCINT3;      // Enable pin change interrupt PCINT3
}

Цитата(GetSmart @ Jun 12 2008, 02:15) *
Запись в PORTA сначала ~key_altState, а потом сразу же кода символа не очень понятна. Если там светодиоды, то на них будет отображаться именно код символа.
Это только для демонстрации скан-кода. В моей программе этого нет.
Цитата(GetSmart @ Jun 12 2008, 02:15) *
Особенно учитывая, что в процедуре convertKey() индекс в массиве символов используется 5-битный, а скан-код 6-битный. То есть можно залететь в следующую кодовую страницу.

Скан-код тоже 5-битный - от 0 до 31. Матрица 4х4.
Цитата(GetSmart @ Jun 12 2008, 02:15) *
Вряд ли key_code.scan портится в key_processAltKeys().

На начальном этапе проекта (до применения ОС) тоже долго не мог понять почему портился скан-код. Тогда случайно обнаружил, что проблема была связана с key_processAltKeys(). Уже не помню, что я тогда изменил (кажется я копировал скан-код в отдельную переменную). Сейчас история повторяется. Действительно, проблема осталась! Сначала после удаления #define ALTKEYS проблема исчезла, о чем я поспешил сообщить. Но после изменения/добавления пунктов меню - снова появилась! Это меня уже достало. cranky.gif
GetSmart
Цитата(alux)
Не понял на счет глюка, я же прерывания настроил именно на эти выводы, к которым подключена клавиатура:

Вне зависимости от того, на что настроены прерывания, в процедуре переполнения таймера 0 формируется 6-битный скан-код. И если на портах C и D помимо клавиатуры что-то подключено (хоть на вход, хоть на выход) скан-коды будут "левые". Особенно, если для клавиатуры на одном порте используется старшая половина полубайта:
Код
    /* Init ports */
    KEYMATRIX_ROW_DDR &= 0x0f;    // Set row lines to input  
    KEYMATRIX_ROW_PORT |= 0xf0;    // Pull row lines high
Как я понял, здесь на битах 4..7 висит клавиатура, а на младших что-то ещё. Прерывание по таймеру сканирует биты от младшего к старшему и найдя еденичный бит в младших битах перестанет дальше сканировать. Даже когда для клавиатуры используются две младших половинки в двух разных портах, то может произойти прерывание по отжатию кнопки, при этом на старших битах порта столбцов будет посторонний нулевой сигнал, то он засчитается за нажатую клавишу. Чтобы всех этих проблем не происходило нужно подправить в файле keymatrix.c процедуру сканирования портов клавиатуры timer0OVFISR().
alux
Цитата(GetSmart @ Jun 12 2008, 14:10) *
Чтобы всех этих проблем не происходило нужно подправить в файле keymatrix.c процедуру сканирования портов клавиатуры timer0OVFISR().

Ну это само собой разумеется. Конечно, я переписал этот keymatrix.c для моего случая:
Код
//#############################################################################
//  Вычисление скан-кода нажатой кнопки
//_____________________________________________________________________________
void scan_key(void)
{
  TCritSect cs;
  
  /* Local variables */
  unsigned char lineResult;    // Resulting column and row lines
  unsigned char tempScan;    // Temporary scancode


  /* Find row of keypress */
  lineResult = KEYMATRIX_ROW_PIN&0xf0;  // Get row lines    
            
  if(lineResult != 0xf0)    // Any row lines low ?
  {
    /* Invert port directions */
    KEYMATRIX_COL_PORT |= 0x0f;    // Drive all col lines high                      
    KEYMATRIX_COL_DDR &= 0xf0;    // Set col lines to input, already pulled up
                                                
    KEYMATRIX_ROW_PORT &= 0x0f; // Disable pull-up on row lines
    KEYMATRIX_ROW_DDR |= 0xf0;  // Set row lines to output, already driven low        

    tempScan = 0;            // Init temp scan code
        
    while(lineResult & 0x10)    // Loop while row line high
    {
      lineResult >>= 1;            // Next row line into LSB
      tempScan += 8;        // Increment row part of scancode
    }
        
    /* Find col of keypress */
    lineResult = KEYMATRIX_COL_PIN&0x0f;  // Get col lines        

    /* Set original port directions */    
    KEYMATRIX_ROW_PORT |= 0xf0; // Drive all row lines high
    KEYMATRIX_ROW_DDR &= 0x0f;    // Set row lines to input, already pulled up,
                                                  
    KEYMATRIX_COL_PORT &= 0xf0; // Disable pull-up on col lines
    KEYMATRIX_COL_DDR |= 0x0f;  // Set col lines to output, alreay driven low            
        

    if(lineResult != 0x0f)    // Any col lines low ?
    {
      while(lineResult & 0x01)  // Loop while col line high
      {
    lineResult >>= 1;       // Next col line into LSB
    tempScan ++;            // Increment col part of scancode
      }
            
      /* Process scancode */
      tempScan |= KEY_PRESSED;  // Set MSB of scan_code (key pressed)
      key_code.scan = tempScan; // Save scancode

      #ifdef ALTKEYS
          key_processAltKeys(); // Process alternation keys if implemented
      #endif            
    }
    else
      key_code.scan = 0;    // Indicate no keys pressed 0x00
  }
  else
    key_code.scan = 0;            // Indicate no keys pressed 0x00
    
    /* Prepare external interrupt */
    ENABLE_PCINT3;        // Reenable pin change interrupt
}
Эта функция вызывается не в прерывании таймера, а в процессе OS_PROCESS void TKeyScan::Exec() с использованием флагов сообщений ОС. Код я приводил в предыдущем посте.

PS. Для эксперимента заменил key_code.scan на обычную глобальную volatile unsigned char scan. Не помогло. Все равно портится те же 4 скан-кода кнопок. sad.gif Т.е. проблема не в использовании union. Версий пока больше нет...

PS2. Все! Наконецто локализовал проблему. Надеюсь в последний раз smile.gif.
Скан-код портит функция convertKey();
Код
char __flash characters[4][32] = {        
                   { '1', '2', '3', 0, 0, 0, 0, 0,            // No alternation
                             '4', '5', '6', 0, 0, 0, 0, 0,
                             '7', '8', '9', 0, 0, 0, 0, 0,
                             '.', '0', '#', 0, 0, 0, 0, 0 },

                           { 'a', 'd', 'g', 0,   'а', 'г', 'є', 'ъ',    // First character
                             'j', 'm', 'p', 0,   'и', 'й', 'м', 'ы',
                             's', 'v', 'y', 0,   'п', 'т', 'х', 0,
                             '*', '*', '#', 0,   'ш', 'ю', 0, 0 },

                           { 'b', 'e', 'h', 0,   'б', 'д', 'ж', 0,    // Second character
                             'k', 'n', 'q', 0,   'і', 'к', 'н', 0,
                             't', 'w', 'z', 0,   'р', 'у', 'ц', 0,
                             '*', '?', '#', 0,   'щ', 'я', 0, 0 },

                           { 'c', 'f', 'i', 0,   'в', 'е', 'з', 0,    // Third character
                             'l', 'o', 'r', 0,   'ї', 'л', 'о', 0,
                             'u', 'x', '-', 0,   'с', 'ф', 'ч', 0,
                             '*', '!', '#', 0,   'ь', 0, 0, 0 }
                         };

/*** Convert scancode to character ***/
char convertKey(void)
{
  char tempChar;

    if(key_code.altKey0)                // First char ?
        tempChar = characters[1][key_code.scan+4];
    else
        if(key_code.altKey1)                    // Second char ?
       tempChar = characters[2][key_code.scan+4];
    else
            if(key_code.altKey2)            // Third char ?
           tempChar = characters[3][key_code.scan+4];
        else                            // No alternation ?
            tempChar = characters[0][key_code.scan];

    if(key_code.altKey3)                // Uppercase if caps lock
        tempChar = toupper(tempChar);
    
    return tempChar;
}
Если заменить многомерный массив одномерным, то все нормально. А почему?
alux
...И вновь продолжается бой. smile.gif
Ребята, извините, пожалуйста, если достал уже этой проблемой. Напомню, что глюк проявляет себя в том, что портит скан-код четырех кнопок. Причем одинаково: кнопка "3" вместо скан-кода 2 дает 1, кнопка "ESC" вместо скан-кода 3 дает 1, кнопка "6" вместо скан-кода 10 дает 9, кнопка "UP" вместо скан-кода 11 дает 10. Может эта информация как-то наведет на мысли.
Глюк появляется при входе в пункт меню ("Редактирование"). Вроде нашел причину: в функции convertKey() если заменить многомерный массив одномерным, то глюк пропадает. Ну, бог с ним, с многомерным массивом. Но следующее выходит за рамки моего понимания: если раскомментировать #define ALTKEYS , т.е. вызвать функцию void key_processAltKeys() , в которой просто XORятся биты флага, снова появляется этот злосчастный глюк.
Код
#ifdef ALTKEYS

void key_processAltKeys()
{
    if     (flags & ALTKEY0) flags ^= ALTKEY0;  
    else if(flags & ALTKEY1) flags ^= ALTKEY1;  
    else if(flags & ALTKEY2) flags ^= ALTKEY2;  
    else if(flags & ALTKEY3) flags ^= ALTKEY3;  
}
#endif
Причем, для чистоты эксперимента переменные scan (скан-код) и flags сделал глобальными volatile unsigned char. Функцию scan_key() приводил в предыдущем посте. Воздействие ОС на глюк исключается,- функция вызывается в критической секции (TCritSect cs) help.gif
sKWO
Цитата(alux @ Jun 13 2008, 12:04) *
Код
    else if(flags & ALTKEY1) flags ^= ALTKEY1;  
    else if(flags & ALTKEY2) flags ^= ALTKEY2;

Так бегло
alux а зачем инвертировать флаг в флаговом регистре ничего при этом не делая и не проверяя сканкод
Выдержка из оригинала
Код
if     ( key_code.scan == ALTKEY0 ) key_altState ^= (1<<0);
    else if( key_code.scan == ALTKEY1 ) key_altState ^= (1<<1);
    else if( key_code.scan == ALTKEY2 ) key_altState ^= (1<<2);
    else if( key_code.scan == ALTKEY3 ) key_altState ^= (1<<3);
    else if( key_code.scan == LCKKEY0 ) key_altState ^= (1<<4);
    else if( key_code.scan == LCKKEY1 ) key_altState ^= (1<<5);
    else if( key_code.scan == LCKKEY2 ) key_altState ^= (1<<6);
    else                                        // Not alternation key ?
    {
        key_altState &= ALTLOCKMASK;                // Clear one-shot key flags
    }

ALTKEY как у Вас определены, може всё завязано на битовых сдвигах?
alux
Код
// Alternation key code
#define ALTKEY   26            // '#' on test keypad

#define ALT0   0x01    
#define ALT1   0x02            
#define ALT2   0x04            
#define ALT3   0x08


Проблема возникает если просто :
Код
void key_processAltKeys()
{
    if(scan == ALTKEY)  flags ^= ALT0;
}
Что здесь не так? Не вижу криминала.

PS. Меня не покидает чувство, что виноват все-таки компилятор (v.5.10A). Где-то на форуме проскакивало сообщение, что компилятор 5.10 не правильно определяет CSTACK >= 0xC0.
Уже качаю IAR v.5.11B.
GetSmart
Цитата(alux @ Jun 13 2008, 14:04) *
Код
#ifdef ALTKEYS

void key_processAltKeys()
{
    if     (flags & ALTKEY0) flags ^= ALTKEY0;  
    else if(flags & ALTKEY1) flags ^= ALTKEY1;  
    else if(flags & ALTKEY2) flags ^= ALTKEY2;  
    else if(flags & ALTKEY3) flags ^= ALTKEY3;  
}
#endif
Это что за ерунда такая? Раньше эта процедура сравнивала скан код нажатой клавиши с кодами альтернативных клавиш и устанавливала бит в key_altState. Даже если key_altState теперь называется flags, то ерунда тут. Исключающее ИЛИ с кодом клавиши? Зачем? В flags лежат коды клавиш?

ЗЫ. Больше не буду искать ошибки, когда на обозрение выкладываются какие-то левые исходники, а потом оказывается что в реале что-то где-то поменяно и вообще хз что скомпилено.
alux
Цитата(GetSmart @ Jun 13 2008, 20:42) *
Раньше эта процедура сравнивала скан код нажатой клавиши с кодами альтернативных клавиш и устанавливала бит в key_altState.
Раньше эта процедура была отключена препроцессором условной компиляцией. После включения #define ALTKEYS возникла проблема, и после этого я начал экспериментировать с флагами, думал, что это как-то связано с объединениями key_code.scan и битовым полем флага. Прошу прощения, если ввел в заблуждение. Просто не знаю где еще искать. Программа scan_key() правильно вычисляет скан-код. Проверял в симуляторе. Но по какой-то причине при определенных условиях, портится скан-код... Почему, не могу понять.

PS. Наконец-то!!!

Сделал следующее:
т.к. у меня матрица 4х4, то сделал скан-код от 0 до 15. Ну и соответственно массив уменьшил в два раза :
Код
char __flash characters[16] = {...};
..........................
    while(lineResult & 0x10)    // Loop while row line high
    {
      lineResult >>= 1;            // Next row line into LSB
      tempScan += 4;        // Increment row part of scancode.     Было tempScan += 8;    
    }
И убрал в функции scan_key()
{
// TCritSect cs;

и глюк пропал. yeah.gif

Наверно это связано с последней строкой в функции scan_key():
Код
    ENABLE_PCINT3;        // Reenable pin change interrupt
defunct
зато теперь у вас

1. целая коллекция рабочих и нерабочих what_day().
2. полная уверенность в компиляторе и в %

biggrin.gif
alux
Вот еще один пример до кучи. Вариация на тему "Вычисление дня недели". Найдено на microchip.com.
Код
__flash const unsigned char Table[13]={0,0,3,3,6,1,4,6,2,5,0,3,5};
unsigned int CurrentYear;
unsigned char Month, Day;

/*********************************************************
* Test Program for the routine GetDayofWeek()
*********************************************************/
void main(void)
{
  char Temp;
  CurrentYear = 1998; //Date : 21 September 1998
  Month = 9;
  Day = 21;
  Temp = GetDayofWeek();// Result stored in Temp
  do{} while(1);
}


/****************************************************************
* GetDayofWeek
* This routine calculate the Day(Sunday, Monday,...Saturday) of
* week when a Date(year, Month, Day) is given.
* Input : Year, Month and Day which in this routine is used
* as global variable.
* Output Variable : 0 to 6(which correspond to Sunday to Saturdaday
* respectively) if the input is acceptable, else a value 8 is return
*****************************************************************/
unsigned char GetDayofWeek(void)
{
  unsigned int TempYear;
  unsigned char AccValue;
  
  if(CheckValidInput(CurrentYear)!= OK) // Return Error if input not Valid  
  return Error;
  
  TempYear = 1990; //Comparation start with year 1990
  AccValue = 0;   //Init AccValue to 0
  
  // If TempYear is a leap year AccValue +2, else AccValue+1
  while(TempYear != CurrentYear)
  {
    AccValue++;
    if(IsLeapYear(TempYear))
    AccValue++;
    TempYear++;
  }
  
  if(Month > 2)
  {
    if(IsLeapYear(TempYear)==1)
    AccValue++;
  }
  
  AccValue += Table[Month];
  AccValue += Day;
  AccValue= AccValue%7;
  
  return(AccValue);
}
/*************************************************************************
* CheckValidInput
* Return a '1' if the input is within the required range. Else
* return a '0'.
*
* Input Variable : 16Bit Unsigned Int
* Output Varible : '1' if input ranges between 1990 & 2099 inclusively
***************************************************************************/
unsigned char CheckValidInput(unsigned int Input)
{
  if(Input >= 1990 && Input <= 2099)
    return OK;    
  else
    return !OK;  
}


/*************************************************************************
* IsLeapYear
* Return a '1' if the input is a leap year. Else return a '0'
*
* Input Variable : 16Bit Unsigned Int
* Output Varible : '1' if is a leap year, else '0'.
***************************************************************************/
unsigned char IsLeapYear(unsigned int Year)
{
  Year=Year&0x0003;
  
  if(Year==0)
    return 1;
  else
    return 0;
}
/*************************************************************************/
sensor_ua
Тогда ещё один вариантwink.gif
Код
//Regra de Zeller
unsigned char GetWDay( int nMDay, int nMonth, int nYear ){
  int nWDay, D, C;
  C = nYear / 100;
  if( nMonth < 3 ) D = ( nYear - 1 ) % 100;
  else D = nYear % 100;
  nMonth = ( ( nMonth + 9 ) % 12 ) + 1;
  nWDay = nMDay + ( ( 13 * nMonth - 1 ) / 5 ) + D + ( D / 4 ) + ( C / 4 ) - 2 * C;
  return( nWDay < 0 ? ( nWDay + 7 ) : ( nWDay % 7 ) );
}
GetSmart
Цитата(sensor_ua)
Код
return( nWDay < 0 ? ( nWDay + 7 ) : ( nWDay % 7 ) );

Можно я упрощу? Вот:
Код
return( ( nWDay + 7 ) % 7  );
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.