Operácie

Meranie dĺžky impulzu 2: Rozdiel medzi revíziami

Z SensorWiki

(1. Neznámy signál a základný program)
Riadok 100: Riadok 100:
 
ISR (TIMER0_OVF_vect)     
 
ISR (TIMER0_OVF_vect)     
 
{   
 
{   
   timer0ext++;       // pomocne pocitadlo
+
   timer0ext++;               // pomocne pocitadlo
  
   if (timer0ext>20)
+
   if (timer0ext>20)         // 20x bude Log. 1
 
       set_bit(PORTB,LED1);
 
       set_bit(PORTB,LED1);
 
 
   if (timer0ext>64) // po 61 preruseniach zmeni stav
+
   if (timer0ext>64)         // 44x (=64-20) bude log.0
 
     {
 
     {
  timer0ext = 0;
+
  timer0ext = 0;     // a zaroven zacne cely cyklus znova
       clear_bit(PORTB,LED1); //predtym len toggle a zmenilo to 50:50
+
       clear_bit(PORTB,LED1);  
 
  }  
 
  }  
 
}
 
}
Riadok 151: Riadok 151:
  
 
Tento program preložte, nahrajte do procesora a pripojte sa cez sériový terminál. LED dióda na doske by mala začať blikať
 
Tento program preložte, nahrajte do procesora a pripojte sa cez sériový terminál. LED dióda na doske by mala začať blikať
cca 1x za sekundu a vo výpise by ste mali vidieť, že na vstupe je stále log. 0.  
+
cca 1x za sekundu a vo výpise by ste mali vidieť, že na vstupe je stále log. 0.
 
 
  
 
== 2. Meranie frekvencie ==
 
== 2. Meranie frekvencie ==

Verzia zo dňa a času 12:07, 17. máj 2021


MIPS pulseDemoScope01.png
Meranie impulzov na osciloskope.

V tomto návode vytvoríme program na meranie krok za krokom.


1. Neznámy signál a základný program

Najprv si vytvorte nový projekt, ktorý bude pozostávať z nasledovných súborov.

#include <stdio.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

#include "hardware.h"
#include "uart.h"

FILE uart_stream = FDEV_SETUP_STREAM(uart_putc, uart_getc, _FDEV_SETUP_RW);


int main(void)
{   
   hw_init();              // tu je skryte nastavenie vystupu D13 (PD5) na nejaku frekvenciu a plnenie
   
   uart_init(57600);       // konfiguracia seriovej linky na rychlost 57600 Bd

   stdout = stdin = &uart_stream;
   
   printf("Ready to start...\n\n");   
 
    /* 
     * v nekonecnej slucke budeme do terminalu vypisovat
     * hodnotu na vstupe PB0 (na doske oznaceny ako D8)
     * vypis bude 10x za sekundu (preto 100ms) 
     */

   while(1)  
    {
     unsigned char inputValue =0;
	 
     if bit_is_set(PINB,PB0)
       inputValue = 1;
     else
       inputValue = 0;
	 
     printf("Input D8: %u\r",inputValue); 
     _delay_ms(100);

    }

 return(0); 
}
#include <stdio.h>
#include <avr/io.h>
#include <avr/interrupt.h>

#define LED1  PB5          // Arduino D13 - zabudovana dioda 
#define RCinput PB3        // Arduino D8

#define set_bit(ADDRESS,BIT) (ADDRESS |= (1<<BIT))
#define clear_bit(ADDRESS,BIT) (ADDRESS &= ~(1<<BIT))
#define toggle_bit(ADDRESS,BIT) (ADDRESS ^= (1<<BIT))

void hw_init(void);
void timer0_init(void);
#include <stdio.h>
#include <avr/io.h>
#include <avr/interrupt.h>

#define LED1  PB5             // Arduino D13 - zabudovana dioda 
#define inputPulse PB0        // Arduino D8

#define set_bit(ADDRESS,BIT) (ADDRESS |= (1<<BIT))
#define clear_bit(ADDRESS,BIT) (ADDRESS &= ~(1<<BIT))
#define toggle_bit(ADDRESS,BIT) (ADDRESS ^= (1<<BIT))

unsigned volatile static int timer0ext = 0;


ISR (TIMER0_OVF_vect)     
{  
   timer0ext++;	              // pomocne pocitadlo

   if (timer0ext>20)          // 20x bude Log. 1
       set_bit(PORTB,LED1);
	
   if (timer0ext>64)          // 44x (=64-20) bude log.0 
     {
	   timer0ext = 0;     // a zaroven zacne cely cyklus znova
       clear_bit(PORTB,LED1); 
	  }	 
}

void timer0_init(void)  /* CTC match mode */
{
    /* Timer 0 Normal mode with clock = I/O clock / 1024 */    
	TCCR0A = 0x00;                 // Mode 0: normal
	TCCR0B = (1<<CS02)|(1<<CS00);  // Clock :1024  
	 TCNT0 = 0x00;                 // 8-bit Counter reset
	 TIFR0 = (1 << TOV0);          // Clear interrupt flag 
	TIMSK0 = (1 << TOIE0);         // Enable Overflow Interrupt 
	  sei();					   // Enable interrupts 
	
}

void hw_init(void)
{
   cli();                     // zakaz vsetky prerusenia

   set_bit(DDRB,LED1);        // set pin LED1 as output
   clear_bit(DDRB,inputPulse);     // set RC input as input
   timer0_init();
}
 /* použite vlastný z predošlého cvičenia */
 /* použite vlastný z predošlého cvičenia */



Tento program preložte, nahrajte do procesora a pripojte sa cez sériový terminál. LED dióda na doske by mala začať blikať cca 1x za sekundu a vo výpise by ste mali vidieť, že na vstupe je stále log. 0.

2. Meranie frekvencie

Chceme najskôr zmerať, s akou frekvenciou sa mení výstup s LED diódou (teda PB5, na doske D13). Na to použijeme to najpresnejšie počítadlo, ktoré na procesore máme k dispozícii, t.j. 16-bitové počítadlo T1. Necháme ho len samovoľne počítať s nejakou frekvenciou a vždy, keď príde nábežná hrana, odchytíme aktuálny stav počítadla TCNT1 do záchytného (capture) registra ICR1 (Input Capture Register). Toto odchytenie by sme síce mohli spraviť aj softvérovo, teda kontrolovať stav na vstupe PB5 a pri zmene sa pozrieť do TCNT1 a odpamätať si aktuálny stav. Ale to je nepohodlné a nepresné, preto na to využijeme možnosť spraviť to automaticky - počítadlo T1 to umožňuje spraviť signálom na vstupe PB0. Preto musíte prepojiť káblikom výstup D13 (PB5) so vstupom D8 (PB0).

V demonštračnom programe by ste už mali vidieť meniaci sa stav na vstupe.

Ready to measure...

Input D8: 0 
Input D8: 0 
Input D8: 1 
Input D8: 1 


Ďalším krokom bude nakonfigurovanie počítadla T1 tak, aby jednak samostatne počítalo od 0 po 65 535 a zároveň aby sa aktuálny stav počítadla odchytil do registra ICR1. Pri konfigurácii sa dá vybrať, či sa odchytávanie uskutoční pri nábežnej, alebo dobežnej hrane.


MIPS pulseDemoTimer01.png
Bloková schéma počítadla T1 v režime zachytávania impulzov.

Zvolíme spúšťanie ICR nábežnou hranou, počítadlo bude počítať s frekvenciou 16 MHz:1024 (prescaler 1024), a do výpisu si pridáme aj stavy všetkých zúčastnených registrov

/* Doplnime definiciu funkcie, ktora inicializuje T1 */

void timer1_init(void)
{
    TCCR1A  = 0x00;                 // Mode 0: normal
    TCCR1B  = (1<<CS02)|(1<<CS00);  // Clock :1024  
    TCCR1B |= (1<<ICES1);           // Capture on rising edge
    TCNT1   = 0x0000;               // 16-bit Counter reset
	
}

/* ktoru potom pridame na koniec inicializacie, teda
   niekam pred while(1)                               */

  timer1_init();

/* a napokon rozsirime funkciu na vypis hodnot  */

printf("Input D8: %u TCNT1: %u ICR: %u\r",inputValue,TCNT1,ICR1);

Mali by ste dostať výstup nejako podobný tomuto

Input D8: 1 TCNT1: 24695 ICR: 18688
Input D8: 1 TCNT1: 26354 ICR: 18688
Input D8: 1 TCNT1: 28013 ICR: 18688
Input D8: 1 TCNT1: 29672 ICR: 18688
Input D8: 1 TCNT1: 31331 ICR: 18688
Input D8: 1 TCNT1: 32990 ICR: 18688
Input D8: 1 TCNT1: 34649 ICR: 18688
Input D8: 0 TCNT1: 36308 ICR: 35328
Input D8: 0 TCNT1: 37967 ICR: 35328
Input D8: 0 TCNT1: 39626 ICR: 35328
Input D8: 1 TCNT1: 41285 ICR: 35328
Input D8: 1 TCNT1: 42943 ICR: 35328
Input D8: 1 TCNT1: 44602 ICR: 35328
Input D8: 1 TCNT1: 46261 ICR: 35328
Input D8: 1 TCNT1: 47920 ICR: 35328
Input D8: 1 TCNT1: 49579 ICR: 35328
Input D8: 1 TCNT1: 51238 ICR: 35328
Input D8: 0 TCNT1: 52897 ICR: 51968
Input D8: 0 TCNT1: 54556 ICR: 51968
Input D8: 0 TCNT1: 56215 ICR: 51968


Vo výpise vidíme, že keď sa zmení stav na vstupe D8 tak sa aktuálny stav počítadla TCNT1 prepíše do ICR. Nám teraz stačí odpočítať dve po sebe idúce hodnoty ICR a získame tak časový rozdiel medzi dvoma nábežnými hranami, čo je vlastne perióda signálu na vstupe. Ak by počítadlo T1 inkrementovalo svoj stav vždy presne po 1ms, tak by sme mali po odčítaní priamo čas v milisekundách. Lenže počítadlo T1 inkrementuje svoj stav vždy po čase (1/16 000 000:1024) = 64 us (mikrosekund). Preto nami nameraná dĺžka impulzu je

51968 - 35328 = 16640
alebo
35328 - 18688 = 16640    
teda
16640x64 = 1 064 960 us = 1064,960 ms = 1,06496 s

Tento výpočet by už mohol robiť aj mikroprocesor, ale nejdeme si teraz komplikovať život počítaním v plávajúcej desatinnej čiarke. Ale program môžeme vylepšiť aspoň o automatické vypočítavanie rozdielu. Stačí, ak zároveň s odchytením registra vyvoláme aj prerušenie a v jeho obsluhe porovnáme aktuálnu hodnotu s predošlou.


3. Meranie frekvencie s prerušením

Ak chceme merať aj striedu, musíme meranie realizovať s prerušením a v obsluhe prerušenia preklopiť konfiguračný bit, ktorý rozhoduje o prepise TCNT do ICR registra. Je to bit ICES1, ktorý je v TCCR1B.6.

#include <avr/interrupt.h>  

volatile int newTick = 0;   // The variable for interrupt should be declared as a volatile one!
                  
ISR(TIMER1_CAPT_vect)       // Timer 1 Capture Interrupt Service Routine
{
  TCCR1B = ??               // toggle Edge Select bit
  newTick = ICR1;
}; 

main()
{

   DDRB = ??                // Set ICR - Port B, pin0  as INPUT
 TCCR1B = ??                // T1 clk = F_CPU : 1024, falling edge pin ICP1,
 TCCR1A = ??                // T1 in timer mode !! Note: if You omit this, TCNT1 will be only 8-bit !!
  TCNT1 = 0x0000;           // initialize the counter (16-bit! Low+High bytes)
  TIFR1 = ??                // (1<<ICF1);   if a 1 is written to a ICF1 bit
                            //              - the ICF1 bit will be cleared
                      
 TIMSK1 = ??                // Enable ICR interrupt
  sei();                    // Enable ALL interrupts                      

  ....                      // TODO: display measured value somewhere
  
}


Ukážka pre Arduino využíva zabudovaný príkaz PulseIn:

#define SWITCH 8                // select the pin for Switch
unsigned long duration;

void setup()
{
 pinMode(SWITCH, INPUT);        // this pin is an INPUT
 Serial.begin(9600);
 Serial.println("PulsIn test:");
}

void loop()                      // endless loop
{
 duration = pulseIn(SWITCH, HIGH);
 Serial.print(" T1 = ");
 Serial.print(duration,DEC);
 Serial.print(" [us]");

 duration = pulseIn(SWITCH, LOW);
 Serial.print(" T0 = ");
 Serial.print(duration,DEC);
 Serial.println(" [us]");
}


Literatúra