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:
- typedef struct {
- __IO uint32_t IR;
- __IO uint32_t TCR;
- __IO uint32_t TC;
- __IO uint32_t PR;
- __IO uint32_t PC;
- __IO uint32_t MCR;
- __IO uint32_t MR[4];
- __IO uint32_t CCR;
- __IO uint32_t CR[4];
- __IO uint32_t EMR;
- __I uint32_t RESERVED0[12];
- __IO uint32_t CTCR;
- } 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:
- void Tim0_Blocking_ms_Delay(uint32_t time)
- {
- TIM_TIMERCFG_Type TIM_ConfigStruct;
- TIM_MATCHCFG_Type TIM_MatchConfigStruct ;
- TIM_ConfigStruct.PrescaleOption = 1;
- TIM_ConfigStruct.PrescaleValue = 1000;
- TIM_MatchConfigStruct.MatchChannel = 0;
- TIM_MatchConfigStruct.IntOnMatch = TRUE;
- TIM_MatchConfigStruct.ResetOnMatch = FALSE;
- TIM_MatchConfigStruct.StopOnMatch = TRUE;
- TIM_MatchConfigStruct.ExtMatchOutputType = 0;
- TIM_MatchConfigStruct.MatchValue = time;
- TIM_Init(LPC_TIMER0, 0, &TIM_ConfigStruct);
- TIM_ConfigMatch(LPC_TIMER0, &TIM_MatchConfigStruct);
- TIM_Cmd(LPC_TIMER0,ENABLE);
- while (!(TIM_GetIntStatus(LPC_TIMER0,0))) { ; };
- TIM_ClearIntPending(LPC_TIMER0,0);
- }
Podobnie wygląda konfiguracja dla opóźnień w mikrosekundach:
- void Tim0_Blocking_us_Delay(uint32_t time)
- {
- TIM_TIMERCFG_Type TIM_ConfigStruct;
- TIM_MATCHCFG_Type TIM_MatchConfigStruct ;
- TIM_ConfigStruct.PrescaleOption = 1;
- TIM_ConfigStruct.PrescaleValue = 1;
- TIM_MatchConfigStruct.MatchChannel = 0;
- TIM_MatchConfigStruct.IntOnMatch = TRUE;
- TIM_MatchConfigStruct.ResetOnMatch = FALSE;
- TIM_MatchConfigStruct.StopOnMatch = TRUE;
- TIM_MatchConfigStruct.ExtMatchOutputType = 0;
- TIM_MatchConfigStruct.MatchValue = time;
- TIM_Init(LPC_TIMER0, 0,&TIM_ConfigStruct);
- TIM_ConfigMatch(LPC_TIMER0,&TIM_MatchConfigStruct);
- TIM_Cmd(LPC_TIMER0,ENABLE);
- while ( !(TIM_GetIntStatus(LPC_TIMER0,0))) { ; };
- TIM_ClearIntPending(LPC_TIMER0,0);
- }
Poniżej te same funkcje, różnica polega na przeprowadzeniu inicjalizacji bezpośrednio na rejestrach z pominięciem bibliotek producenta.
- void Tim0_Blocking_ms_Delay(uint32_t time)
- {
- LPC_SYSCTL->PCONP |= ((uint32_t)(1<<1)) & 0xEFEFF7DE;
- LPC_SYSCTL->PCLKSEL[0] &= (~(0x03 << 2));
- LPC_TIMER0->CCR &= ~TIM_CTCR_MODE_MASK;
- LPC_TIMER0->CCR |= 0;
- LPC_TIMER0->TC =0;
- LPC_TIMER0->PC =0;
- LPC_TIMER0->PR =0;
- uint64_t clkdlycnt;
- clkdlycnt = (uint64_t)SystemCoreClock/4;
- clkdlycnt = (clkdlycnt * 1000) / 1000000;
- LPC_TIMER0->PR = clkdlycnt-1;
- LPC_TIMER0->IR = 0xFFFFFFFF;
- LPC_TIMER0->MR[0] = time;
- LPC_TIMER0->MCR &=~ ((uint32_t)(7<<0));
- LPC_TIMER0->MCR |= (1 << 0);
- LPC_TIMER0->MCR |= (1 << 2);
- LPC_TIMER0->EMR = 0;
- LPC_TIMER0->TCR |= 1;
- while (!((LPC_TIMER0->IR) & (1 << 0))) { ; };
- LPC_TIMER0->IR |= (1 << (0));
- }
Drugi delay jest identyczny, różnica polega jedynie na ustawieniu innej częstotliwości taktowania:
- void Tim0_Blocking_us_Delay(uint32_t time)
- {
- LPC_SYSCTL->PCONP |= ((uint32_t)(1<<1)) & 0xEFEFF7DE;
- LPC_SYSCTL->PCLKSEL[0] &= (~(0x03 << 2));
- LPC_TIMER0->CCR &= ~TIM_CTCR_MODE_MASK;
- LPC_TIMER0->CCR |= 0;
- LPC_TIMER0->TC =0;
- LPC_TIMER0->PC =0;
- LPC_TIMER0->PR =0;
- uint64_t clkdlycnt;
- clkdlycnt = (uint64_t)SystemCoreClock/4;
- clkdlycnt = (clkdlycnt * 1) / 1000000;
- LPC_TIMER0->PR = clkdlycnt-1;
- LPC_TIMER0->IR = 0xFFFFFFFF;
- LPC_TIMER0->MR[0] = time;
- LPC_TIMER0->MCR &=~ ((uint32_t)(7<<0));
- LPC_TIMER0->MCR |= (1 << 0);
- LPC_TIMER0->MCR |= (1 << 2);
- LPC_TIMER0->EMR = 0;
- LPC_TIMER0->TCR |= 1;
- while (!((LPC_TIMER0->IR) & (1 << 0))) { ; };
- LPC_TIMER0->IR |= (1 << (0));
- }
Przebiegi na analizatorze stanów logicznych zbadam przez zastosowanie prostej funkcji ustawiającej i kasującej stan na linii:
- oid TEST_DELAY(void)
- {
- LPC_GPIO[0].SET |= 1UL << 20;
- Tim0_Blocking_ms_Delay(400);
- LPC_GPIO[0].CLR |= 1UL << 20;
- }
Dla czasu 400ms:
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.