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ń