Микроконтролери и електроника
http://mcu-bg.com/mcu_site/

Zilog - Z8 Encore! F083A Series
http://mcu-bg.com/mcu_site/viewtopic.php?f=7&t=16225
Страница 1 от 4

Автор:  Metyyy [ Пон Ное 19, 2018 5:45 pm ]
Заглавие:  Zilog - Z8 Encore! F083A Series

Здравейте !

Бих искал да помоля за помощ, относно начините за измерване на период (честота) на правоъгълен входен сигнал с помощта на таймер. Таймерите в Z8F083A имат три режими на прехващане – режим „прехващане/сравнение”, режим ”прехващане с рестартиране” и класически режим „прехващане”. До колкото разбирам, при capture/compare - таймерът започва да брои едва след като на изводът постъпи активен фронт и при постъпване на следващия активен фронт => прехващане, докато при capture/restart - таймерът започва да брои веднага след като е разрешен и разликата е, че още първият активен фронт на сигнала ще предизвика прекъсване, но изчислената стойност няма да бъде вярна, тъй като все още нямаме изтекъл цял период.

Така, имам 2 въпроса:

Код:

#define FSYS (20000000)
#define FREQ_VALUE_CALCULATION_TIME (25) ----------> Откъде идва тази стойност ? Правих си опити и ако я сложа по-голямо число, стойността на F излиза малко със закъснение на дисплея.

uint32 T; /* Съдържа периода на входния сигнал */

void interrupt Timer0_ISR(void) _At TIMER0
{
   static uint8 n = 0; /* Брояч на препълванията на таймера*/
   
   uint16 Tov; /* Съдържа времето за препълване на таймера */
   
   if(0 == (T0CTL0 & (1 << 0))) /* Прекъсване от препълване? */
   {/* Да */
      n++; /* Увеличаване на брояча на препълванията */
   }
   
   else
   {/* Не,прекъсване от прехващане */
      T = ((uint32)T0PWMH << 8 | T0PWML); /* Копиране на прехванатата стойност > T = T(t3) */
      
      Tov = ((uint16)T0RH << 8 | T0RL); /* Вземане на броячната стойност на таймера, която определя времето му за препълване, т.е. Tov */
      
      T += (uint32)n*Tov; /* Изчисляване на периода Т += n x Tov */
      
      n = 0; /* Нулиране на брояча на препълванията*/
   }
}

int main(void)
{
   uint16 F = 0; /* Честота */
   uint8 i = FREQ_VALUE_CALCULATION_TIME; /* Време за изчисляване на нова стойност на F */
   
   PortConfig();
   InterruptConfig();
   TimerConfig();
   
   while(1)
   {
      if(i != 0)
      {
         SendToDisplay(F);
         i--;
      }
      
      else
      {
         _di(); /* Забраняване на прекъсванията */
         F = FSYS / T; /* Изчисляване на честотата в Hz */
         _ei(); /* Разрешаване на прекъсванията */
         i = FREQ_VALUE_CALCULATION_TIME; /* Презареждане на времето за изчисляване на нова стойност на F */
      }
   }
}



И вторият ми въпрос е свързан със capture/compare: Дали кодът по-долу е правилен, а той трябва да игнорира първото прекъсване по нарастващия фронт на таймера. Прилагам само interrupt-а, тъй като изменението на кодът би следвало да е само тук, а не и в основната програма. Всъщност имам и 3-ти въпрос, дали трябва да получа една и съща честота при capture/compare и capture/restart.

Код:

uint32 T; /* Съдържа периода на входния сигнал */

void interrupt Timer0_ISR(void) _At TIMER0
{
   static uint8 n = 0; /* Брояч на препълванията на таймера */
   static uint8 j = 0; /* Брояч на прехващанията */
   
   uint16 Tov; /* Съдържа времето за препълване на таймера */
   
   if(0 == (T0CTL0 & (1 << 0))) /* Прекъсване от препълване ? */
   {/* Да */
      n++; /* Увеличаване на брояча на препълванията */
      
      if(1 == (T0CTL1 & (1 << 0))) /* Прекъсване от прехващане ? */
      {
         j = 0; /* Нулираме на брояча на прехващанията */
      }
   }
   
   else
   {/* Не,прекъсване от прехващане */
      T = ((uint32)T0PWMH << 8 | T0PWML); /* Копиране на прехванатата стойност > T = T(t3) */
      
      Tov = ((uint16)T0RH << 8 | T0RL); /* Вземане на броячната стойност на таймера, която определя времето му за препълване, т.е. Tov */
      
      T += (uint32)n*Tov; /* Изчисляване на периода Т += n x Tov */
      
      n = 0; /* Нулиране на брояча на препълванията */
   }   
}



Благодаря ! :oops:

Автор:  Zdrav [ Вто Ное 20, 2018 4:33 am ]
Заглавие:  Re: Zilog - Z8 Encore! F083A Series

И в двата режима (CAPTURE и CAPTURE RESTART) таймера започва да брои след запис в контролния регистър. И в двата режима трябва да изчакаш минимум две кепчър събития за да измериш времето между тях.
Разликата между двата режима е само в рестартирането при кепчър събитие при CAPTURE RESTART режима.
В определени случаи това ще ти спестява операцията по изваждане на двете "прихванати" таймщампи.

Автор:  Metyyy [ Вто Ное 20, 2018 3:51 pm ]
Заглавие:  Re: Zilog - Z8 Encore! F083A Series

Благодаря ! Аз като цяло съм прочел документацията и имам представа как работят режимите, но въпросите ми са малко по-различни от това, кой режим, как работи и т.н. :wink:

Автор:  Zdrav [ Сря Ное 21, 2018 12:57 am ]
Заглавие:  Re: Zilog - Z8 Encore! F083A Series

Дааа, пропуснал съм CAPTURE/COMPARE режима.
Но като гледам не ти помага кой знае колко. Ако нулираш j при старт на програмата, в прекъсването просто го установявай в '1'. Това ще ти е флаг че си получил поне едно прехващане. Съответно при влизане в прекъсване от прехващане проверяваш дали (0 != j) и само тогава изчисляваш периода. Но проверката не я влагай в блока от проверката за препълване. Например така:
Код:
volatile uint32 T; /* Съдържа периода на входния сигнал */
volatile uint8 overflow_count = 0; /* Брояч на препълванията на таймера */
volatile uint8 is_capture = 0; /* Флаг за прехващанията */

void interrupt Timer0_ISR(void) _At TIMER0
{   
   uint16 Tov;

   if(0 == (T0CTL0 & (1 << 0))) /* Прекъсване от препълване ? */
   {
      overflow_count++;
   }
   else /* Прекъсване от прехващане! */
   {
      if(0 != is_capture)
      {
         T = (((uint16)T0PWMH << 8) | T0PWML);      
         Tov = (((uint16)T0RH << 8) | T0RL);      
         T += (uint32)overflow_count*Tov;
      }
      overflow_count = 0;
      is_capture = 1;
   }
}



Но този код има няколко други проблема.
1. Възможно е основната нишка да бъде прекъсната точно в момента на "ползване" на променливата Т и да се случи така че за момент да ползва няколко байта от старата стойност и няколко байта от новата. С други думи трябва да защитиш в основната нишка прочитането на Т за да осигуриш atomic операция.
2. Това четене на T0RH и T0RL в прекъсването боде очите. Ако не променяш тази стойност и ако е известна "on build time" защо не я дефинираш като константа? Още повече може да си избереш по-удобна стойност, която да оптимизира умножението.
3. Това процесорче има ли инструкция за умножение? Но дори и да има, умножението в прекъсване може да замениш с по-прости операции.
4. Бих ти препоръчал COMPARE режима, но тогава нищо няма да остане от тръпката да използваш решение до което си стигнал сам.

Кодът би трябвало да работи и в COMPARE/RESTART но вътъка ми стига до тук :)

Автор:  itso.t [ Сря Ное 21, 2018 5:06 pm ]
Заглавие:  Re: Zilog - Z8 Encore! F083A Series

Zdrav написа:
...1. Възможно е основната нишка да бъде прекъсната точно в момента на "ползване" на променливата Т и да се случи така че за момент да ползва няколко байта от старата стойност и няколко байта от новата. С други думи трябва да защитиш в основната нишка прочитането на Т за да осигуриш atomic операция...

Тая част определено е интересна, и би било полезно да се спомене какви са "добрите практики" за избягване на проблема.

Автор:  Zdrav [ Сря Ное 21, 2018 9:40 pm ]
Заглавие:  Re: Zilog - Z8 Encore! F083A Series

itso.t написа:
Тая част определено е интересна, и би било полезно да се спомене какви са "добрите практики" за избягване на проблема.

Добрите практики изискват отделна тема.
В тази тема за да се използват "добрите практики" ще трябва да се пренапише кода и то не само обработчика на прекъсването но и останалата част, която в случая излиза извън темата.
С така написания код и с този подход, две от възможните решения са:
Код:
volatile uint32 T;
uint32 atomic_var_T;
uintXX active IRQs;

active_IRQs = disableIRQ(TIMER0_IRQ);
atomic_var_T = T;
enable_IRQs(active_IRQs);

...
/* тука ползваме atomic_var_T колкото ни душа сака
и никога не ползваме директно Т. */



Код:
volatile uint32 T;
uint32 atomic_var_T;

do{
atomic_var_T = T;
} while(atomic_var_T != T);

...
/* ползваме atomic_var_T
и никога не ползваме директно Т. */


Автор:  Metyyy [ Сря Ное 21, 2018 9:47 pm ]
Заглавие:  Re: Zilog - Z8 Encore! F083A Series

Много ти благодаря за съветите и кодът (интелигентно написан, не като моя - лишен от всякаква мисъл). Все още се уча (сам) и ми е доста трудно. А и със сигурност не искам да оставя някой с впечатление, че този код съм го написал аз. Чета една книга и се опитвам да разбера идеята, да правя упражненията и т.н.

Относно проблемите, които спомена:

1. Въобще си нямам идея за какво говориш (atomic операция). До колко прочетох, това се отнася за всяко нещо, което се изпълнява без прекъсване. Ако е възможно, както спомена itso.t да ми покажеш какво точно имаш в предвид и "добрите практики".

2 - 3. Имаш в предвид - Това : Tov = (((uint16)T0RH << 8) | T0RL), да бъде заменено със: const uint16 Tov = FFFF.

Съжалявам, ако питам/пиша пълни глупости и елементарни неща :)

Автор:  Zdrav [ Сря Ное 21, 2018 10:43 pm ]
Заглавие:  Re: Zilog - Z8 Encore! F083A Series

Metyyy написа:
Съжалявам, ако питам/пиша пълни глупости и елементарни неща :)

Това не го казвай повече, че пречи на мотивацията на тези които биха ти отговорили.

atomic просто означава неделим. Когато 8 битов процесор го натовариш със задачата да обработва 32 битови числа той ще използва последователност от операции(машинни инструкции) и грубо казано ще прочете тази 32 битова променлива на части. По време на изпълнение на отделните операции може да се вмъкне прекъсването от таймера и да обнови стойността на Т. Разбира се това е въпрос на нацелване на точния момент, но се случва доста често :)
Какъв е ефекта?
Ето пример със псевдо код
Код:
да речем в даден момент Т= 0х12345678
прочитам 1-ви байт от Т и го пращам към дисплей/сериен порт (пращаме 0х12)
прочитам 2-ри байт от Т и го пращам към дисплей/сериен порт (пращаме 0х34)
<<< тука се случва прехващане и влизаме в прекъсване>>>
стойността на Т е обновена в прекъсването и новата стойност е: Т = 0х87654321
<<< излизаме от прекъсване>>>
прочитам 3-ри байт от Т и го пращам към дисплей/сериен порт (пращаме 0х43)
прочитам 4-ти байт от Т и го пращам към дисплей/сериен порт (пращаме 0х21)


Така изпратената 32 битова стойност е: 0х12344321
вместо 0х12345678 или 0х87654321

ако процесора е 32 битов той ще прочете от паметта и зареди в регистър на ядрото цялата 32 битова променлива с една atomic операция(инструкция).

И понеже това не се вижда явно когато четеш С код трябва да ползваш "добри практики".

2-3 може да стане и така:
Код:
#define Tov (0xFFFF)

но по-добре да се пипне малко кода и да се избегне умножението.

например:
Код:
volatile uint32 T; /* Съдържа периода на входния сигнал */
volatile uint16 timer_MS = 0; /* старша част на таймера, разширен до 32 бита със софтуерен метод */
volatile uint8 is_capture = 0; /* Флаг за прехващанията */

void init_Timer0()
{
   ...
   T0RH = 0xFF;
   T0RL = 0xFF;
   ...
}

void interrupt Timer0_ISR(void) _At TIMER0
{   
   uint16 T0_cnt;

   if(0 == (T0CTL0 & (1 << 0))) /* Прекъсване от препълване ? */
   {
      timer_MS++;
   }
   else /* Прекъсване от прехващане! */
   {
      if(0 != is_capture)
      {
         T0_cnt = (((uint16)T0PWMH << 8) | T0PWML);
         Т = ((uint32)timer_MS<<16) | T0_cnt;
      }
      timer_MS = 0;
      is_capture = 1;
   }
}



За 8 битово ядро може и да не е оптимално, но поне илюстрира идеята. А ако е необходимо може да се пипне малко и да се оптимизира и за Z8 Encore, но сорс кода ще стане малко по-нечитаем.

Автор:  Metyyy [ Чет Ное 22, 2018 9:56 pm ]
Заглавие:  Re: Zilog - Z8 Encore! F083A Series

Благодаря за подробното обяснение ! Наистина е адски полезно 8O

Финални 2 въпроса:

Код:

void interrupt Timer0_ISR(void) _At TIMER0
{   
   uint16 T0_cnt;

   if(0 == (T0CTL0 & (1 << 0))) /* Прекъсване от препълване ? */
   {
      timer_MS++; ---> Ако разбирам правилно, тук увеличаваме старшата част на таймера, който вече е разширен до 32 бита ?
   }
   else /* Прекъсване от прехващане! */
   {
      if(0 != is_capture)
      {
         T0_cnt = (((uint16)T0PWMH << 8) | T0PWML); ---> Тук прехванатата стойност се записва в тези регистри (PWMH, PWML) и Т0_cnt ?
         Т = ((uint32)timer_MS<<16) | T0_cnt; ---> Тук изчисляваме периода, демек стойността на timer_MS се измества с 16 бита наляво, а за дясната част остава стойността на Т0_cnt и така се получава цял период ?
      }
      timer_MS = 0;
      is_capture = 1;
   }
}





Код:

#define FSYS (20000000)
#define FREQ_VALUE_CALCULATION_TIME (25) ---> Откъде идва тази стойност или аз си я избирам? Правих си опити и ако я сложа по-голямо число, стойността на F излиза малко със закъснение на дисплея?

int main(void)
{
   uint16 F = 0; /* Честота */
   uint8 i = FREQ_VALUE_CALCULATION_TIME; /* Време за изчисляване на нова стойност на F */
   
   PortConfig();
   InterruptConfig();
   TimerConfig();
   
   while(1)
   {
      if(i != 0)
      {
         SendToDisplay(F);
         i--;
      }
     
      else
      {
         _di(); /* Забраняване на прекъсванията */
         F = FSYS / T; /* Изчисляване на честотата в Hz */
         _ei(); /* Разрешаване на прекъсванията */
         i = FREQ_VALUE_CALCULATION_TIME; /* Презареждане на времето за изчисляване на нова стойност на F */
      }
   }
}



Благодаря !

Автор:  Zdrav [ Пет Ное 23, 2018 12:32 am ]
Заглавие:  Re: Zilog - Z8 Encore! F083A Series

Код:
void interrupt Timer0_ISR(void) _At TIMER0
{   
...
    T0_cnt = (((uint16)T0PWMH << 8) | T0PWML); ---> Тук прехванатата стойност се записва в тези регистри (PWMH, PWML) и Т0_cnt ?
...
}


На този ред се прочита записаната в (PWMH, PWML) стойност и се присвоява на променливата T0_cnt. Прехвърля се от едно място на друго за да може да си готов за следващото прехващане.
"Прехващача" на таймера копира (T0H,T0L) в (PWMH, PWML). Т.е. пак прехвърляне от едно място на друго за да имаш стойността на брояча на таймера (T0H,T0L) в момента на прехващането.

Код:
#define FREQ_VALUE_CALCULATION_TIME (25) ---> Откъде идва тази стойност или аз си я избирам? Правих си опити и ако я сложа по-голямо число, стойността на F излиза малко със закъснение на дисплея?


да, ти си я избираш. Защо F излиза на дисплея със закъснение, когато я увеличиш, е блондински въпрос.

Автор:  Metyyy [ Съб Ное 24, 2018 1:54 am ]
Заглавие:  Re: Zilog - Z8 Encore! F083A Series

Благодаря, отново ! Всъщност изречението "Правих си опити и ако я сложа по-голямо число, стойността на F излиза малко със закъснение на дисплея ?" е съобщително, просто съзнателно или не съм сложил въпросителен знак :mrgreen:

Мисля, че темата може да бъде затворена :)

Автор:  Zdrav [ Съб Ное 24, 2018 12:21 pm ]
Заглавие:  Re: Zilog - Z8 Encore! F083A Series

Рано е да ми благодариш.
И... в този форум затворена тема не съм видял.
Ако освен мерене на честота с тази програмка решиш да правиш и нещо друго ще ти се наложи да помислиш и за други неща.
Погледни промените тук:
Код:

#define FSYS (20000000)
#define FREQ_VALUE_REFRESH_TIME (2500)

int main(void)
{
   uint32 atomic_var_T;
   uint16 F = 0; /* Честота */
   uint8 i = FREQ_VALUE_REFRESH_TIME / 4; /* Време за изчакване преди изчисляване на първата стойност на F */
   
   PortConfig();
   InterruptConfig();
   TimerConfig();
   
   while(1)
   {
      if(0 != i)
      {
         i--;
      }     
      else
      {
         _di();
         atomic_var_T = T;
         _ei();

         F = FSYS / atomic_var_T;

         SendToDisplay(F);

         i = FREQ_VALUE_REFRESH_TIME;
      }
   }
}

Автор:  Metyyy [ Съб Ное 24, 2018 11:22 pm ]
Заглавие:  Re: Zilog - Z8 Encore! F083A Series

Прав си, сега искам да измеря коефициента на запълване, обаче нищо не става. С лог. анализатора, получавам едни стойности, а на дисплея съвсем други...какво се случва не знам. Имам закачен потенциометър, с който намалявам и увеличавам "к", но като пусна анализатора, той ми показва съвсем други стойности. Пак ли трябва да забранявам първото прекъсване на таймера...? Вече имам в предвид, твоите забележки относно atomic-a и константата...да, не съм ги приложил, но не мисля, че на това се дължи проблема ми в момента :)

Код:

uint8 k; /* Коефициент на запълване */

void interrupt Timer0_ISR(void) _At TIMER0
{
   static uint8 overflow_count = 0; /* Брояч на препълванията на таймера*/
   
   static uint32 t_h = 0; /* Високо ниво на сигнала */
   static uint32 t_l = 0; /* Ниско ниво на сигнала */
   
   uint32 T; /* Период на сигнала */
   uint16 Tov; /* Съдържа времето за препълване на таймера */
   uint32 Tcap; /* Прехваната стойност */
   
   if(0 == (T0CTL0 & (1 << 0))) /* Прекъсване от препълване? */
   {/* Да */
      overflow_count ++; /* Увеличаване на брояча на препълванията */
   }
   
   else
   {/* Не,прекъсване от прехващане */
      Tcap = ((uint32)T0PWMH << 8 | T0PWML); /* Прочитаме записаната стойност в (PWMH,PWML) и я присвояваме на Tcap */
      Tov = ((uint16)T0RH << 8 | T0RL); /* Прочитаме записаната стойност в (RH, RL) и я присвояваме на Tov */
         
      if(0 == (T0CTL1 & (1 << 6))) /* Прекъсване от нарастващ фронт ? */
      {/* Да */
         t_l = Tcap + overflow_count * Tov; /* Изчисляване на ниското ниво на сигнала */
         T = t_h + t_l; /* Изчисляване на периода */
         k = (t_h * 100) / T; /* Изчисляване на коефициента на запълване */
         
         overflow_count = 0; /* Нулиране на брояча на препълванията*/
         
         T0CTL1 |= 1 << 6; /* Превключване към падащ фронт */
      }
      
      else
      {/* Прекъсване от падащ фронт */
         t_h = Tcap + overflow_count * Tov; /* Изчисляване на високото ниво на сигнала */
         
         overflow_count = 0; /* Нулиране на брояча на препълванията*/
         
         T0CTL1 &= ~(1 << 6); /* Превключване към нарастващ фронт */
      }
   }
}

int main(void)
{
   uint16 DutyCycle = 0; /* Честота */
   uint8 i = DUTY_VALUE_READING_TIME; /* Време за изчитане на нова стойност на DutyCycle */
   
   PortConfig();
   InterruptConfig();
   TimerConfig();
   
   while(1)
   {
      if(i != 0)
      {
         SendToDisplay(DutyCycle);
         i--;
      }
      
      else
      {
         _di(); /* Забраняване на прекъсванията */
         DutyCycle = k; /* Четене на измерения коефициент */
         _ei(); /* Разрешаване на прекъсванията */
         i = DUTY_VALUE_READING_TIME; /* Презареждане на времето за изчитане на нова стойност на DutyCycle */
      }
   }
}



Many thanks !

Автор:  Zdrav [ Съб Ное 24, 2018 11:50 pm ]
Заглавие:  Re: Zilog - Z8 Encore! F083A Series

В какъв режим е конфигуриран таймера?

Автор:  Metyyy [ Нед Ное 25, 2018 12:18 am ]
Заглавие:  Re: Zilog - Z8 Encore! F083A Series

Capture/compare :)

Страница 1 от 4 Часовете са според зоната UTC + 2 часа [ DST ]
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group
http://www.phpbb.com/