wtorek, 14 lutego 2023

LPC1769 - Timer 0 Delay

Ten post chciałbym poświęcić na opisanie konfiguracji liczników w układzie LPC1769.


Rejestry:


Lista rejestrów powiązana z konfiguracją timera jest następująca:

  • IR - (Interrupt register) - rejest przerwania. Wpis do rejestru czyści przerwanie. Zawiera informacje które przerwanie jest wystawiane.
  • TCR - (Time control register) - kontrola funkcji Timer Counter. Można wyłączać lub czyścić funkcję przez ten rejestr.
  • TC - (Time Counter) - licznik zwiększany w każdym cyklu zegara. Jest sterowany z rejestru TCR.
  • PR - (Prescale Register) - ustawienie wartości preskalera do zwiększania wartości TC
  • PC - (Prescale Counter) - inkrementowany do czasu osiągnięcia wartości z PR. Jeśli do niej dotrze to zwiększany jest licznik TC.
  • MCR - (Match Control Register) - kontroluje resetowanie TC oraz generuje przerwanie gdy zostanie osiągnięta wartość zadana.
  • MR0 - MR3 - (Match Registers) - przechowuje wartości zadane do porównania z licznikiem. 
  • CCR - (Capture Control Registes) - określa jakim zboczem nastąpi ładowanie wartości do CR oraz czy nastąpi wygenerowanie przerwania gdy nastąpi porównanie wartości.
  • CR0 - CR3 - (Capture Register) - ładowanie nowej wartości licznika gdy pojawi się zdefiniowany event na wejściu (CAPn.0).
  • EMR - (External Match Register) - kontroluje zewnętrzne piny przypisanie do porównania z wartością zadaną. 
  • CTCR - (Count Control Register)  - wybiera tryb działania pomiędzy Timer lub Counter mode. 

W bibliotekach struktura przechowująca informację o rejestrach timera wygląda następująco:

  1. typedef struct {   
  2.     __IO uint32_t IR;  
  3.     __IO uint32_t TCR; 
  4.     __IO uint32_t TC;  
  5.     __IO uint32_t PR;  
  6.     __IO uint32_t PC;      
  7.     __IO uint32_t MCR;     
  8.     __IO uint32_t MR[4];   
  9.     __IO uint32_t CCR;         
  10.     __IO uint32_t CR[4];       
  11.     __IO uint32_t EMR;     
  12.     __I  uint32_t RESERVED0[12];
  13.     __IO uint32_t CTCR;            
  14. } LPC_TIMER_T;

Taktowanie zegara dla timera 0 wynosi:

PCLK (12MHz/4):
12MHz/4 = 3MHz

Delay:


Mikrokontroler LPC1769 taktowany jest z zewnętrznego kwarca 12Mhz.

W przypadku konfiguracji Timer'a 0 z wykorzystaniem bibliotek udostępnionych przez producenta:

  1. void Tim0_Blocking_ms_Delay(uint32_t time)
  2. {
  3.     TIM_TIMERCFG_Type TIM_ConfigStruct;
  4.     TIM_MATCHCFG_Type TIM_MatchConfigStruct ;
  5.  
  6.     TIM_ConfigStruct.PrescaleOption = 1;
  7.     TIM_ConfigStruct.PrescaleValue = 1000;
  8.     TIM_MatchConfigStruct.MatchChannel = 0;
  9.     TIM_MatchConfigStruct.IntOnMatch   = TRUE;
  10.     TIM_MatchConfigStruct.ResetOnMatch = FALSE;
  11.     TIM_MatchConfigStruct.StopOnMatch = TRUE;
  12.     TIM_MatchConfigStruct.ExtMatchOutputType = 0;
  13.     TIM_MatchConfigStruct.MatchValue = time;
  14.  
  15.     TIM_Init(LPC_TIMER0, 0, &TIM_ConfigStruct);
  16.     TIM_ConfigMatch(LPC_TIMER0, &TIM_MatchConfigStruct);
  17.     TIM_Cmd(LPC_TIMER0,ENABLE);
  18.     while (!(TIM_GetIntStatus(LPC_TIMER0,0))) { ; };
  19.         TIM_ClearIntPending(LPC_TIMER0,0);
  20. }

Podobnie wygląda konfiguracja dla opóźnień w mikrosekundach:

  1. void Tim0_Blocking_us_Delay(uint32_t time)
  2. {
  3.     TIM_TIMERCFG_Type TIM_ConfigStruct;
  4.     TIM_MATCHCFG_Type TIM_MatchConfigStruct ;
  5.  
  6.     TIM_ConfigStruct.PrescaleOption = 1;
  7.     TIM_ConfigStruct.PrescaleValue  = 1;
  8.     TIM_MatchConfigStruct.MatchChannel = 0;
  9.     TIM_MatchConfigStruct.IntOnMatch   = TRUE;
  10.     TIM_MatchConfigStruct.ResetOnMatch = FALSE;
  11.     TIM_MatchConfigStruct.StopOnMatch  = TRUE;
  12.     TIM_MatchConfigStruct.ExtMatchOutputType = 0;
  13.     TIM_MatchConfigStruct.MatchValue   = time;
  14.  
  15.     TIM_Init(LPC_TIMER0, 0,&TIM_ConfigStruct);
  16.     TIM_ConfigMatch(LPC_TIMER0,&TIM_MatchConfigStruct);
  17.     TIM_Cmd(LPC_TIMER0,ENABLE);
  18.     while ( !(TIM_GetIntStatus(LPC_TIMER0,0))) { ; };
  19.         TIM_ClearIntPending(LPC_TIMER0,0);
  20. }

Poniżej te same funkcje, różnica polega na przeprowadzeniu inicjalizacji bezpośrednio na rejestrach z pominięciem bibliotek producenta. 

  1. void Tim0_Blocking_ms_Delay(uint32_t time)
  2. {
  3.     LPC_SYSCTL->PCONP |= ((uint32_t)(1<<1)) & 0xEFEFF7DE;
  4.     LPC_SYSCTL->PCLKSEL[0] &= (~(0x03 << 2));
  5.  
  6.     LPC_TIMER0->CCR &= ~TIM_CTCR_MODE_MASK;
  7.     LPC_TIMER0->CCR |= 0;
  8.  
  9.     LPC_TIMER0->TC =0;
  10.     LPC_TIMER0->PC =0;
  11.     LPC_TIMER0->PR =0;
  12.  
  13.     uint64_t clkdlycnt;
  14.     clkdlycnt = (uint64_t)SystemCoreClock/4;
  15.     clkdlycnt = (clkdlycnt * 1000) / 1000000;
  16.     LPC_TIMER0->PR = clkdlycnt-1;
  17.     LPC_TIMER0->IR = 0xFFFFFFFF;
  18.    
  19.     LPC_TIMER0->MR[0] = time;
  20.  
  21.     LPC_TIMER0->MCR &=~ ((uint32_t)(7<<0));
  22.     LPC_TIMER0->MCR |=  (1 << 0);
  23.     LPC_TIMER0->MCR |= (1 << 2);
  24.     LPC_TIMER0->EMR = 0;
  25.    
  26.     LPC_TIMER0->TCR |=  1;
  27.     while (!((LPC_TIMER0->IR) & (1 << 0))) { ; };
  28.         LPC_TIMER0->IR |= (1 << (0));
  29. }

Drugi delay jest identyczny, różnica polega jedynie na ustawieniu innej częstotliwości taktowania:

  1. void Tim0_Blocking_us_Delay(uint32_t time)
  2. {
  3.     LPC_SYSCTL->PCONP |= ((uint32_t)(1<<1)) & 0xEFEFF7DE;
  4.     LPC_SYSCTL->PCLKSEL[0] &= (~(0x03 << 2));
  5.  
  6.     LPC_TIMER0->CCR &= ~TIM_CTCR_MODE_MASK;
  7.     LPC_TIMER0->CCR |= 0;
  8.  
  9.     LPC_TIMER0->TC =0;
  10.     LPC_TIMER0->PC =0;
  11.     LPC_TIMER0->PR =0;
  12.  
  13.     uint64_t clkdlycnt;
  14.     clkdlycnt = (uint64_t)SystemCoreClock/4;
  15.     clkdlycnt = (clkdlycnt * 1) / 1000000;
  16.     LPC_TIMER0->PR = clkdlycnt-1;
  17.     LPC_TIMER0->IR = 0xFFFFFFFF;
  18.    
  19.     LPC_TIMER0->MR[0] = time;
  20.  
  21.     LPC_TIMER0->MCR &=~ ((uint32_t)(7<<0));
  22.     LPC_TIMER0->MCR |=  (1 << 0);
  23.     LPC_TIMER0->MCR |= (1 << 2);
  24.     LPC_TIMER0->EMR = 0;
  25.    
  26.     LPC_TIMER0->TCR |=  1;
  27.     while (!((LPC_TIMER0->IR) & (1 << 0))) { ; };
  28.         LPC_TIMER0->IR |= (1 << (0));
  29. }

Przebiegi na analizatorze stanów logicznych zbadam przez zastosowanie prostej funkcji ustawiającej i kasującej stan na linii:

  1. oid TEST_DELAY(void)
  2. {
  3.     LPC_GPIO[0].SET |= 1UL << 20;
  4.     Tim0_Blocking_ms_Delay(400);
  5.     LPC_GPIO[0].CLR |= 1UL << 20;
  6. }

Dla czasu 400ms:


100ms:


1ms:


Teraz funkcja wykonująca opóźnienia w us:

400us:


100us:


10us:


1us (Dla odczytu przebiegu na oscyloskopie wartości były identyczne z tymi przedstawionymi na wykresie):


Dla przebiegu w us czy pojedynczych ms należy pamiętać o wyłączeniu filtrowania, w przeciwnym przypadku na ekranie nie uda się zaobserwować żadnych przebiegów. 

Najmniejsze możliwe do osiągnięcia opóźnienie dla tego timera z pełną częstotliwością taktowania wynosi 7,5us.