Отговори на тема  [ 55 мнения ]  Отиди на страница 1, 2, 3, 4  Следваща
Zilog - Z8 Encore! F083A Series 
Автор Съобщение
Ранг: Минаващ
Ранг: Минаващ

Регистриран на: Съб Ное 22, 2014 6:23 pm
Мнения: 48
Мнение 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:


Пон Ное 19, 2018 4:45 pm
Профил
Ранг: Форумен бог
Ранг: Форумен бог
Аватар

Регистриран на: Сря Яну 26, 2005 1:01 pm
Мнения: 1952
Местоположение: Варна
Мнение Re: Zilog - Z8 Encore! F083A Series
И в двата режима (CAPTURE и CAPTURE RESTART) таймера започва да брои след запис в контролния регистър. И в двата режима трябва да изчакаш минимум две кепчър събития за да измериш времето между тях.
Разликата между двата режима е само в рестартирането при кепчър събитие при CAPTURE RESTART режима.
В определени случаи това ще ти спестява операцията по изваждане на двете "прихванати" таймщампи.

_________________
Най-опасният враг на истината и свободата е мнозинството.


Вто Ное 20, 2018 3:33 am
Профил
Ранг: Минаващ
Ранг: Минаващ

Регистриран на: Съб Ное 22, 2014 6:23 pm
Мнения: 48
Мнение Re: Zilog - Z8 Encore! F083A Series
Благодаря ! Аз като цяло съм прочел документацията и имам представа как работят режимите, но въпросите ми са малко по-различни от това, кой режим, как работи и т.н. :wink:


Вто Ное 20, 2018 2:51 pm
Профил
Ранг: Форумен бог
Ранг: Форумен бог
Аватар

Регистриран на: Сря Яну 26, 2005 1:01 pm
Мнения: 1952
Местоположение: Варна
Мнение 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 но вътъка ми стига до тук :)

_________________
Най-опасният враг на истината и свободата е мнозинството.


Вто Ное 20, 2018 11:57 pm
Профил
Ранг: Форумен бог
Ранг: Форумен бог
Аватар

Регистриран на: Чет Фев 03, 2005 1:21 am
Мнения: 10610
Местоположение: София
Мнение Re: Zilog - Z8 Encore! F083A Series
Zdrav написа:
...1. Възможно е основната нишка да бъде прекъсната точно в момента на "ползване" на променливата Т и да се случи така че за момент да ползва няколко байта от старата стойност и няколко байта от новата. С други думи трябва да защитиш в основната нишка прочитането на Т за да осигуриш atomic операция...

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


Сря Ное 21, 2018 4:06 pm
Профил
Ранг: Форумен бог
Ранг: Форумен бог
Аватар

Регистриран на: Сря Яну 26, 2005 1:01 pm
Мнения: 1952
Местоположение: Варна
Мнение 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
и никога не ползваме директно Т. */


_________________
Най-опасният враг на истината и свободата е мнозинството.


Сря Ное 21, 2018 8:40 pm
Профил
Ранг: Минаващ
Ранг: Минаващ

Регистриран на: Съб Ное 22, 2014 6:23 pm
Мнения: 48
Мнение Re: Zilog - Z8 Encore! F083A Series
Много ти благодаря за съветите и кодът (интелигентно написан, не като моя - лишен от всякаква мисъл). Все още се уча (сам) и ми е доста трудно. А и със сигурност не искам да оставя някой с впечатление, че този код съм го написал аз. Чета една книга и се опитвам да разбера идеята, да правя упражненията и т.н.

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

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

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

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


Сря Ное 21, 2018 8:47 pm
Профил
Ранг: Форумен бог
Ранг: Форумен бог
Аватар

Регистриран на: Сря Яну 26, 2005 1:01 pm
Мнения: 1952
Местоположение: Варна
Мнение 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, но сорс кода ще стане малко по-нечитаем.

_________________
Най-опасният враг на истината и свободата е мнозинството.


Сря Ное 21, 2018 9:43 pm
Профил
Ранг: Минаващ
Ранг: Минаващ

Регистриран на: Съб Ное 22, 2014 6:23 pm
Мнения: 48
Мнение 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 */
      }
   }
}



Благодаря !


Чет Ное 22, 2018 8:56 pm
Профил
Ранг: Форумен бог
Ранг: Форумен бог
Аватар

Регистриран на: Сря Яну 26, 2005 1:01 pm
Мнения: 1952
Местоположение: Варна
Мнение 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 излиза на дисплея със закъснение, когато я увеличиш, е блондински въпрос.

_________________
Най-опасният враг на истината и свободата е мнозинството.


Чет Ное 22, 2018 11:32 pm
Профил
Ранг: Минаващ
Ранг: Минаващ

Регистриран на: Съб Ное 22, 2014 6:23 pm
Мнения: 48
Мнение Re: Zilog - Z8 Encore! F083A Series
Благодаря, отново ! Всъщност изречението "Правих си опити и ако я сложа по-голямо число, стойността на F излиза малко със закъснение на дисплея ?" е съобщително, просто съзнателно или не съм сложил въпросителен знак :mrgreen:

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


Съб Ное 24, 2018 12:54 am
Профил
Ранг: Форумен бог
Ранг: Форумен бог
Аватар

Регистриран на: Сря Яну 26, 2005 1:01 pm
Мнения: 1952
Местоположение: Варна
Мнение 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;
      }
   }
}

_________________
Най-опасният враг на истината и свободата е мнозинството.


Съб Ное 24, 2018 11:21 am
Профил
Ранг: Минаващ
Ранг: Минаващ

Регистриран на: Съб Ное 22, 2014 6:23 pm
Мнения: 48
Мнение 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 !


Съб Ное 24, 2018 10:22 pm
Профил
Ранг: Форумен бог
Ранг: Форумен бог
Аватар

Регистриран на: Сря Яну 26, 2005 1:01 pm
Мнения: 1952
Местоположение: Варна
Мнение Re: Zilog - Z8 Encore! F083A Series
В какъв режим е конфигуриран таймера?

_________________
Най-опасният враг на истината и свободата е мнозинството.


Съб Ное 24, 2018 10:50 pm
Профил
Ранг: Минаващ
Ранг: Минаващ

Регистриран на: Съб Ное 22, 2014 6:23 pm
Мнения: 48
Мнение Re: Zilog - Z8 Encore! F083A Series
Capture/compare :)


Съб Ное 24, 2018 11:18 pm
Профил
Покажи мненията от миналия:  Сортирай по  
Отговори на тема   [ 55 мнения ]  Отиди на страница 1, 2, 3, 4  Следваща

Кой е на линия

Потребители разглеждащи този форум: 0 регистрирани и 3 госта


Вие не можете да пускате нови теми
Вие не можете да отговаряте на теми
Вие не можете да променяте собственото си мнение
Вие не можете да изтривате собствените си мнения
Вие не можете да прикачвате файл

Търсене:
Иди на:  
cron
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group.
Designed by ST Software for PTF.
Хостинг и Домейни