Autor: Deucalion
Redakcja: Dondu
Artykuł jest częścią cyklu: Kurs ARM: Spis treści
To już ostatnia część serii dotyczącej portów I/O w mikrokontrolerze LPC1114, ale tym razem od strony praktycznej. Poniżej znajduje się listing prostego programu demonstrujący jak można korzystać z portów GPIO.
W ramach ćwiczeń proponuję prześledzić dokładnie cały program i spróbować znaleźć inne rozwiązanie problemu.
Plik main.c
#include "LPC11xx.h" #include "syscon.h" #include "flashcon.h" #include "display.h" #include "keys.h" /* Program demonstrujący używanie portów GPIO w uC LPC1114. Funkcją programu jest wyświetlanie stanu licznika na poczwórnym multipleksowanym wyświetlaczu siedmiosegmentowym, którego zawartość zmienia się z częstotliwością 10Hz po naciśnięciu jednego z dwóch klawiszy. Cały port P2 steruje wyświetlaczem siedmiosegmentowym ze wspólną anodą. Piny P2.0 - P2.7 sterują segmentami bezpośrednio poprzez rezystory 82R, segmenty załączane stanem niskim. Piny P2.8 - P2.11 sterują anodami wyświetlaczy poprzez tranzystor PNP. Stan niski aktywuje dany wyświetlacz. Piny P3.0 i P3.1 służą jako wejścia odczytujące stan klawiszy KEY1 i KEY2. Przytrzymanie klawisza KEY1 (stan niski) powoduje zwiększanie licznika, przytrzymanie klawisz KEY2 (stan niski) powoduje zmniejszanie licznika. */ // Zmienna wykorzystywana jako licznik o zakresie 0 - 9999 static int16_t DispCnt; // Zmienna reprezentująca bufory wyświetlaczy static TDisplay Display = { // Inicjalizacja buforów do wyświetlania 0000 po uruchomieniu .buf1x4 = DisplayFillPatern(_0) }; // tablica odwzorowująca segmenty dla danej cyfry const static uint8_t digsmap[] = { _0, _1, _2, _3, _4, _5, _6, _7, _8, _9 }; //****************************************************************************** // Funkcja inicjalizujaca procesor //****************************************************************************** void LPC_Init( void ) { // Włączenie oscylatora systemowego LPC_SYSCON->PDRUNCFG &= ~PDRUNCFG_SYSOSC_PD; // Zwłoka na ustabilizowanie oscylatora poprzez wieleokrotny odczyt rejestru for( int i = 0; i < 1000; i++) { LPC_SYSCON->PDRUNCFG; } // Przełaczenie źródła sygnału taktującego PLL na zew. oscylator LPC_SYSCON->SYSPLLCLKSEL = SYSPLLCLKSEL_SYSOSC; // Potwierdzenie przełączenia źródła taktowania // poprzez wygenerowania zmiany bitu z 0 na 1 LPC_SYSCON->SYSPLLCLKUEN = 0; LPC_SYSCON->SYSPLLCLKUEN = SYSPLLCLKUEN_ENA; // Konfiguracja dzielników PLL 10MHz*5*4 = 200MHz LPC_SYSCON->SYSPLLCTRL = PLL_M_DIV_5 | PLL_P_DIV_4; // Uruchomienie pętli PLL LPC_SYSCON->PDRUNCFG &= ~PDRUNCFG_SYSPLL_PD; // Oczekiwanie na synchronizacje pętli PLL while(!(LPC_SYSCON->SYSPLLSTAT & SYSPLLSTAT_LOCK)); // Konfiguracja dostępu do pamięci FLASH (3 cykle) LPC_FLASHCFG = (LPC_FLASHCFG & ~FLASHTIM_MASK) | FLASHTIM_50MHz; // Przełączenie źródła zegara systemowego na PLL LPC_SYSCON->MAINCLKSEL = MAINCLKSEL_PLLOUT; // Potwierdzenie przełączenia źródła taktowania // poprzez wygenerowania zmiany bitu z 0 na 1 LPC_SYSCON->MAINCLKUEN = 0; LPC_SYSCON->MAINCLKUEN = MAINCLKUEN_ENA; // Wyłączenie wewnętrznego oscylatora LPC_SYSCON->PDRUNCFG |= PDRUNCFG_IRC_PD; // Włączenie taktowania portów i bloku konfiguracji portów LPC_SYSCON->SYSAHBCLKCTRL |= AHBCLKCTRL_IOCON | AHBCLKCTRL_GPIO; // Cały port P2 jako wyjście DISP_PORT->DIR = 0xFFF; // Konfiguracja timera systemowego SysTick // Przerwanie co 2,5ms (400Hz ; dla PLL = 50MHz) SysTick->LOAD = 50000000UL / 400; // Włączenie timera SysTick->CTRL = 7; // Odblokowanie przerwań od timera SysTick NVIC_EnableIRQ(SysTick_IRQn); } //****************************************************************************** // Funkcja obsługi przerwania od timera SysTick //****************************************************************************** void SysTick_Handler( void ) // 400Hz { static uint8_t // 8 bitowe zmienne statyczne dispnum, // numer następnego wyświetlacza div; // dzielnik 400Hz/40 = 10Hz // Zwiększenie dzielnika ++div; // ograniczenie zakresu dzielnika do 39 (0 - 39) div %= 40; // Wykonanie tylko dla dzielnika równego 0 if( !div ) { // Zmienna z tymczasową kopią licznika int16_t DispCntCopy = DispCnt; // Sprawdzenie czy naciśnięty klawisz KEY1 if( !KEY_PORT->MASKED_ACCESS[ KEY1 ]) { // Klawisz KEY1 naciśnięty // Zwiększenie licznika DispCnt++; // Ograniczenie zakresu licznika do 9999 DispCnt %= 10000; } // Sprawdzenie czy naciśnięty klawisz KEY2 if( !KEY_PORT->MASKED_ACCESS[ KEY2 ]) { // Klawisz KEY2 naciśnięty // zmniejszenie licznika DispCnt--; // Sprawdzenie zakresu licznika if( DispCnt < 0 ) { //Licznik mniejszy od 0, przewiniecie licznika do 9999 DispCnt = 9999; } } // Aktualizacja buforów wyświetlaczy tylko jesli zmiana licznika if( DispCntCopy != DispCnt ) { // Tymczasowa kopia licznika wykorzystywana do odwzorowania stanu // licznika na wyświetlaczach DispCntCopy = DispCnt; // Wyczyszczenie zawartości buforów wszystkich wyświetlaczy ClearDisplay(); // Odwzorowanie zawartości licznika na 4 wyświetlaczach for( int i = 3; i >= 0 ; i-- ) { Display.buf4x1[ i ] = digsmap[ DispCntCopy % 10 ]; DispCntCopy /= 10; } } } // Wyłączenie wszystkich wyświetlaczy DISP_PORT->MASKED_ACCESS[ DISP_MASK ] = DISP_MASK; // Przełączenie stanu segmentów dla następnego wyświetlacza DISP_PORT->MASKED_ACCESS[ SEG_MASK ] = Display.buf4x1[ dispnum ]; // Włączenie następnego wyświetlacza DISP_PORT->MASKED_ACCESS[ DISP_MASK ] = ~(( 1 << dispnum++ ) << 8 ); // Ograniczenie zakresu licznika 0 - 3 dispnum %= 4; } //****************************************************************************** // Funkcja pętli głównej //****************************************************************************** int main( void ) { LPC_Init(); while( 1 ) { // Kod pętli głównej __WFE(); // Uśpienie procesora } }
Plik display.h
#ifndef _DISPLAY_H_ #define _DISPLAY_H_ // Deklaracje dotyczące sterowania multipleksowanym wyświetlaczem LED // Cały port GPIO2 wykorzystany do sterowania wyświetlaczami #define DISP_PORT LPC_GPIO2 // Wyświetlacze wspólna anoda sterowane przez tranzystory PNP // Stan niski aktywuje wyświetlacze #define DISP1 (1 << 8) // P2.8 #define DISP2 (1 << 9) // P2.9 #define DISP3 (1 << 10) // P2.10 #define DISP4 (1 << 11) // P2.11 #define DISP_MASK 0xf00 #define SEGA (1 << 0) // P2.0 #define SEGB (1 << 1) // P2.1 #define SEGC (1 << 2) // P2.2 #define SEGD (1 << 3) // P2.3 #define SEGE (1 << 4) // P2.4 #define SEGF (1 << 5) // P2.5 #define SEGG (1 << 6) // P2.6 #define SEGH (1 << 7) // P2.7 #define SEG_MASK 0x0ff // Segmenty sterowane bezpośrednio z procesora przez rezystory 82R // Stan niski aktywuje segmenty #define _0 (uint8_t)( ~(SEGA | SEGB | SEGC | SEGD | SEGE | SEGF)) #define _1 (uint8_t)( ~(SEGB | SEGC)) #define _2 (uint8_t)( ~(SEGA | SEGB | SEGD | SEGE | SEGG)) #define _3 (uint8_t)( ~(SEGA | SEGB | SEGC | SEGD | SEGG)) #define _4 (uint8_t)( ~(SEGB | SEGC | SEGF | SEGG)) #define _5 (uint8_t)( ~(SEGA | SEGC | SEGD | SEGF | SEGG)) #define _6 (uint8_t)( ~(SEGA | SEGC | SEGD | SEGE | SEGF | SEGG)) #define _7 (uint8_t)( ~(SEGA | SEGB | SEGC)) #define _8 (uint8_t)( ~(SEGA | SEGB | SEGC | SEGD | SEGE | SEGF | SEGG)) #define _9 (uint8_t)( ~(SEGA | SEGB | SEGC | SEGD | SEGF | SEGG)) #define _DOT (uint8_t)( ~(SEGH)) // TDisplay - Typ definiujący bufory dla zawartości 4 wyświetlaczy LED // buf4x1[ 4 ] - zawartość poszczególnych wyświetlaczy // buf1x4 - za pomocą jednego wywołania można modyfikować na raz zawartość // wszystkich wyświetlaczy typedef union { uint8_t buf4x1[ 4 ]; uint32_t buf1x4; }TDisplay; // Kilka makr do operacji na wyświetlaczach #define ClearDisplay() ( Display.buf1x4 = ~0 ) #define DisplayPatern(a,b,c,d) ((a) << 24 | (b) << 16 | (c) << 8 | (d)) #define DisplayFillPatern(a) DisplayPatern(a, a, a, a) #define SetDisplay(a,b,c,d) ( Display.buf1x4 = DisplayPatern( a, b, c, d )) #define FillDisplay(a) SetDisplay(a, a, a, a) #endif
Plik keys.h
#ifndef _KEYS_H_ #define _KEYS_H_ //****************************************************************************** // Definicje dotyczące klawiszy KEY1 i KEY2. Stan niski po naciśnięciu // KEY1 - P3.0 - Zwiększanie licznika (10Hz) // KEY2 - P3.1 - Zmniejszanie licznika (10Hz) //****************************************************************************** #define KEY_PORT LPC_GPIO3 #define KEY1 (1<<0) #define KEY2 (1<<1) #define ANYKEY (KEY1 | KEY2) #define KEY_MASK (KEY1 | KEY2) #endif
Pobierz
Cały projekt z powyższym kodem oraz zaktualizowanymi plikami nagłówkowymi.LPC1114_GPIO.zip lub mirror: LPC1114_GPIO.zip
Przydałby się schemacik jak podłączyć. Wiem, wiem, że jest opisane w komentarzu...
OdpowiedzUsuń