zegar sprawozdanie

background image

Wydział Informatyki Politechniki Białostockiej
Laboratorium Informatyki Technicznej,
Przedmiot: Architektura Komputerów

Data:

20.12.2012

Ćwiczenie nr 6
Temat:

Zegar czasu rzeczywistego w trybie niskiego poboru mocy

Grupa:

6

Zespół:
Dariusz Mikołajczuk
Sylwester Leszczyński

Prowadzący:
dr inż. Krzysztof Bielawski

Wstęp

Tematem kolejnych ćwiczeń dotyczących układu MSP430 było zaprojektowanie i wykonanie

zegara czasu rzeczywistego. Zegar miał wykorzystywać układ Timera A, generującego przerwania
co ściśle określony czas. Pomiędzy kolejnymi przerwaniami urządzenie miało pozostawać w trybie
niskiego poboru mocy, co w praktyce oznaczało wyłączenie procesora.

Po pomyślnym zaprogramowaniu zegara i przetestowaniu go, dodaliśmy dodatkowo funkcję

budzika. Godzinę alarmu można zmieniać podczas pracy zegara (bez jego zatrzymywania). Alarm
nie wyłączony przez użytkownika, wyłącza się automatycznie po 4 minutach sygnału.

Aby przestawić czas zegara należy zrestartować urządzenie.

Realizacja

Konfiguracja oscylatora i zegarów

Pierwszym krokiem przy projektowaniu zegara było skonfigurowanie oscylatora i

podstawowych zegarów układu. Użyty został oscylator LFXT1 w trybie wysokiej częstotliwości.
Zasila on MCLK (8 MHz) oraz ACLK (4 MHz). Na koniec został wyłączony oscylator DCO i (dla
pewności) zegar SMCLK. Wszystko to zostało wykonane w funkcji InitOsc2(), której kod
zamieszczam dalej.

Konfiguracja Timera A

Następnie należało tak skonfigurować Timer A, aby wywoływał on przerwanie co pewien

ułamek sekundy. W obecnej wersji zegara przerwanie to wywołuje się co 100 ms (wersja co 250 ms
powodowała nadmierne grzanie się urządzenia).

1.

W rejestrze TACTL ustawiane są następujące bity:

TASSEL0 – timer taktowany z ACLK

MC_1 – timer cyklicznie zlicza od zera do wartości zapisanej w CCR0, w momencie
wyzerowania licznika wywoływane jest przerwanie Timer A.

ID_3 – podzielenie przez

8

częstotliwości zegara źródłowego

Ponieważ Timer A taktowany jest z ACLK (4 MHz), to częstotliwość z jaką taktowany
jest Timer A wynosi f

timerA

= 4 MHz / 8 = 500 kHz

2.

CCTL0 = CCIE; – włączenie przerwań od CCR0

3.

CCR0 = 50000; – wartość zapisana w CCR0 (rejestr 16-bitowy)

Wiedząc teraz z jaką częstotliwością taktowany jest licznik Timera A, oraz znając maksymalną

wartość do jakiej wykonywane jest zliczanie, łatwo można obliczyć jak często licznik będzie się
zerował ( a tym samym jak często będzie wywoływane przerwanie Timer A ):

CCR0 / f

timerA

= 50 000 / 500 000 Hz = 0,1 s = 100 ms – przerwanie będzie wykonywane co 100ms

background image

Po skonfigurowaniu zegarów i timera musieliśmy pamiętać jeszcze o odblokowaniu przerwań:
IE1 |= WDTIE;
_EINT();

Procedura obsługi przerwania Timer A

Procedura ta stanowi główną część programu. Wykonuje ona w skrócie następujące czynności, w

kolejności:

1.

Włącza procesor.

2.

Zwiększa wartość zmiennej licznik o 1, zmienna ta przechowuje liczbę zliczonych
dotychczas dziesiętnych części sekundy.

3.

Jeśli wartość licznika jest podzielna przez 10 oznacza to, że została odmierzona pełna
sekunda, wówczas następuje aktualizacja czasu na wyświetlaczu (o ile nie jesteśmy akurat w
trybie konfiguracji budzika).

4.

Odczytuje aktualny stan klawiszy, klawisze sterują tu ustawianiem budzika oraz jego
włączaniem/wyłączaniem.

5.

Jeśli nie jesteśmy akurat w trybie konfiguracji budzika, oraz nie dzwoni on aktualnie,
następuje wyłączenie procesora do czasu następnego przerwania.

Budzik

Budzik zostaje włączony automatycznie, gdy zmienna budzon jest równa 1 (na wyświetlaczu

pojawia się odpowiednia informacja) oraz gdy nadejdzie odpowiednia godzina. Zostaje wówczas
uruchomiony watchdog w trybie timer. Obsługa buzera następuje w procedurze obsługi przerwania
watchdoga.

Budzik można wyłączyć w dowolnym momencie klawiszami B2 i B3, w razie braku reakcji

użytkownika na sygnał alarmu, budzik zostanie wyłączony po 4 minutach.

Aby wejść w tryb konfiguracji godziny budzika należy wcisnąć B1 podczas pracy zegara.

Zaawansowana konfiguracja budzika

Sygnał budzika został w zasadzie ustalony z góry, jest to typowy sygnał: trzy piknięcia po czym

chwila przerwa i znów piknięcia. Jednakże ze względu na problemy ze skonfigurowaniem sygnału
utworzyliśmy dodatkowy konfigurator budzika.

Aby go uruchomić, należy bezpośrednio po restarcie urządzenia, podczas menu zmiany godziny,

wcisnąć klawisz B4.

Przechodzimy wówczas do kolejnego menu, w którym klawisz B3 wybiera zmienną, B1 i B2

zmienia jej wartość, natomiast B4 powraca do poprzedniego menu.

Można tu dokonać następujących zmian:

"Test mode":

wartość 1 pozwala na szybkie przetestowanie sygnału alarmu. Po dowolnej zmianie
ustawień budzika należy ustawić godzinę zegara na 00:00. Po czym szybko wcisnąć B1,
co pozwoli na zmianę czasu budzika – nie należy jej zmieniać, lecz należy szybko
wcisnąć B4. Budzik zostanie włączony. Uruchomi się dokładnie o godzinie 00:00:03

wartość 0 pozostawia domyślny sposób uruchamiania się budzika (o pełnej minucie)

background image

"WDT source" – źródło taktujące timer watchdoga odpowiedzialny za obsługę sygnału
buzera. Dostępne źródła to MCLK (8MHz) i ACLK (4MHz)

[częstotliwość taktowania układu WDT to (oznaczenie)

f

WDT

source

]

"WDT divider" – mnożnik okresu czasu odmierzanego przez timer WDT. Dostępne
wartości to 32768, 8192, 512, 64. Czas co jaki nastąpi przerwanie WDT można obliczyć ze
wzoru:

T

WDT

=

WDT_dividier

f

WDT

source

"MAXL (short)" – czas (mierzony liczbą przerwań WDT) pomiędzy sygnałami w grupie
trzech sygnałów

"MAXH (long)" – czas (liczba przerwań) pomiędzy grupami trzech sygnałów,

MAXH MAXL

"MAXP (pulse)" – długość pojedynczego sygnału mierzona liczbą przerwań

"FREQ (okres)" – wartość mająca wpływ na częstotliwość pojedynczego sygnału. Sygnał
generowany jest poprzez naprzemienne włączanie i wyłączanie buzera z odstępem
związanym z wartością FREQ.

Generowanie pojedynczego sygnału można opisać następująco:

while (1) {

for(k=0; k!=FREQ; ++k) {_NOP(); _NOP(); _NOP(); _NOP();}
P4OUT ^= BIT2; // włącz/wyłącz buzer

< odczekaj czas T

WDT

>

}

background image

Implementacja

Program napisany został z wykorzystaniem kompilatora MSPGCC. Wykorzystana została w nim

standardowa biblioteka display.h ze strony Olimexu (należy tu dodać trzy funkcje, które załączam
w dalszej części).

Folder projektu powinien zawierać następujące pliki: display.c, display.h, main.c, Makefile.bat.
Do kompilacji i flashowania potrzebny jest zainstalowany pakiet MSPGCC (w folderze

C:\mspgcc

) oraz MSP430_flasher (

C:\MSP430_FLasher

)

Aby skompilować program należy zaktualizować ścieżkę do folderu z projektem, czyli zmienną

pd w pliku Makefile.bat, po czym uruchomić plik Makefile.bat.

Zawartość pliku Makefile.bat

@echo off
:: Folder z binarkami mspgcc (bez końcowego '\')
set md=C:\mspgcc\bin

:: Folder projektu (bez końcowego '\')
set pd="C:\Documents and Settings\student\Pulpit\MSP_zegar_mspgcc_10_prz_BUDZIK"

if not exist %pd%\output (mkdir %pd%\output)
del %pd%\output\*.* /Q
::@echo on

:: Create object files
%md%\msp430-gcc.exe -Os -g -c -mmcu=msp430x149 %pd%\display.c -o %pd%\output\display.o
%md%\msp430-gcc.exe -Os -g -c -mmcu=msp430x149 %pd%\main.c -o %pd%\output\main.o

:: Link all files together
cd %md%

msp430-gcc.exe -Os -g -mmcu=msp430x149 %pd%\output\display.o %pd%\output\main.o -o %pd%\zegar.elf

cd %pd%

:: Create hex file ready to download to the device
%md%\msp430-objcopy.exe -O ihex %pd%\zegar.elf %pd%\zegar.hex

@echo on
:: Load a hex file into device
C:\MSP430_FLasher\MSP430Flasher.exe -n MSP430F149 -w %pd%\zegar.hex -i LPT1 -v -g -z [VCC]
pause

Dodatkowe funkcje umieszczone w display.c

/* Wypisuje liczbę typu short int na wyświetlaczu */

void printDec(short int dec) {

char num[7];
char p = 0;
if (dec == 0x8000) {

printString("-32768");
return;

background image

}
if (dec < 0) {

dec *= -1;

num[0] = '-';

p++;

}
else

if (dec == 0) {SEND_CHAR('0'); return;}

char c, zero = 1;
short int md = 10000;

/* first digit (most significant) */
c = dec/md;
if ((zero) && (c)) zero=0;
if (!zero) {num[p] = c+'0'; p++; dec -= md*c;}
md /= 10;
/* 2nd digit */
c = dec/md;
if ((zero) && (c)) zero=0;
if (!zero) {num[p] = c+'0'; p++; dec -= md*c;}
md /= 10;
/* 3rd digit */
c = dec/md;
if ((zero) && (c)) zero=0;
if (!zero) {num[p] = c+'0'; p++; dec -= md*c;}
md /= 10;
/* 4th digit */
c = dec/md;
if ((zero) && (c)) zero=0;
if (!zero) {num[p] = c+'0'; p++; dec -= md*c;}
md /= 10;
/* 5th digit */
c = dec/md;
if ((zero) && (c)) zero=0;
if (!zero) {num[p] = c+'0'; p++;}
/* print */
char i;
for (i = 0; i != p; i++) SEND_CHAR(num[i]);

}

/* Wypisuje znak pod określonym adresem w DDRAM. Adres przyjmuje wartość
0x00-0x0F dla pierwszej linii oraz 0x40-0x4F dla drugiej linii wyświetlacza */

void CHAR_DD(unsigned char znak, unsigned char adres) {

adres |= DD_RAM_ADDR;
int temp;
Delayx100us(10);
temp = adres & 0xf0;
LCD_Data &= 0x0f;
LCD_Data |= temp;
bitclr(P2OUT,RS);
_E();
temp = adres & 0x0f;
temp = temp << 4;

background image

LCD_Data &= 0x0f;
LCD_Data |= temp;
bitclr(P2OUT,RS);
_E();

Delayx100us(5);
temp = znak & 0xf0;
LCD_Data &= 0x0f;
LCD_Data |= temp;
bitset(P2OUT,RS);
_E();
temp = znak & 0x0f;
temp = temp << 4;
LCD_Data &= 0x0f;
LCD_Data |= temp;
bitset(P2OUT,RS);
_E();

}

/* Odczekanie (aktywne) 1ms */

void Delayx1ms(unsigned int d) {
unsigned int j,k;
for(j=0; j != d; ++j) {
for(k=0; k != 4000; ++k) _NOP();
}
}

Dodatkowe nagłówki umieszczone w display.h

void printDec(short int dec);
void CHAR_DD(unsigned char znak, unsigned char adres);
void Delayx1ms(unsigned int d);

Zawartość pliku main.c

#include "stdio.h"
#include "stdlib.h"
#include "display.h"
#include <signal.h>
#include <io.h>
#include <msp430x14x.h>

#define WDTCL *((short*) 0x120)
#ifndef WDTHOLD

#define WDTHOLD 0x0080

#endif
#ifndef WDTPW

#define WDTPW 0x5A00

#endif
#define P4IN

*((unsigned char*) 0x1C)

#define P4DIR

*((unsigned char*) 0x1E)

#define P2OUT

*((unsigned char*) 0x29)

#define P2DIR

*((unsigned char*) 0x2A)

background image

#define P1OUT

*((unsigned char*) 0x21)

#define P1DIR

*((unsigned char*) 0x22)

#define

B1 (P4IN & 0x10)

#define

B2 (P4IN & 0x20)

#define

B3 (P4IN & 0x40)

#define

B4

(P4IN & 0x80)

#define

STATUS_LED_ON (P2OUT &= ~0x02)

#define

STATUS_LED_OFF (P2OUT |= 0x02)

#define

DD_RAM_ADDR

0x80

#define

DD_RAM_ADDR2

0xC0

#define

CLR_DISP

0x01

#define

CUR_HOME

0x02

#define BUZ_ON (P4OUT |= BIT2) //P4.2
#define BUZ_OFF (P4OUT &= ~BIT2) //P4.2

/* Pozycje w DDRAM */
#define HH 0x04 // godziny
#define MIN 0x07 // minuty
#define SS 0x0A // sekundy
#define F1 0x06 // pierwszy ':'
#define F2 0x09 // drugi ':'

unsigned short int licznik=0;
unsigned char budzon=0;
unsigned char godzB=0;
unsigned char minB=0;
unsigned char minuty=0;
unsigned char godziny=0;

unsigned char disp=1;
unsigned char talarm=0;
unsigned char buz=0;
unsigned char num=0;
unsigned char pulse;

void defineChars() { // definicje polskich znaków używanych w komunikatach
char a[8]={0x00, 0x0E, 0x01, 0x0F, 0x11, 0x0F, 0x01, 0x02}; // ą
char c[8]={0x02, 0x04, 0x0E, 0x11, 0x10, 0x11, 0x0E, 0x00}; // ć
char l[8]={0x0C, 0x04, 0x04, 0x06, 0x0C, 0x04, 0x0E, 0x00}; // ł
char addr=0x40, i;
for (i = 0; i != 8; i++) { // 0x00 ą
SEND_CMD(addr++);
SEND_CHAR(a[i]);
}
for (i = 0; i != 8; i++) { // 0x01 ć
SEND_CMD(addr++);
SEND_CHAR(c[i]);
}
for (i = 0; i != 8; i++) { // 0x02 ł
SEND_CMD(addr++);
SEND_CHAR(l[i]);
}
SEND_CMD(DD_RAM_ADDR);
}

background image

void InitPorts2() {

// Inicjuje porty klawiszy, diod, styczników i buzera

P4DIR &= ~0x10;

// Ustawienie bitu P4.4 na 0 (tryb wejsciowy) B1 key

P4DIR &= ~0x20;

// Ustawienie bitu P4.5 na 0 (tryb wejsciowy) B2 key

P4DIR &= ~0x40;

// Ustawienie bitu P4.6 na 0 (tryb wejsciowy) B3 key

P4DIR &= ~0x80;

// Ustawienie bitu P4.7 na 0 (tryb wejsciowy) B4 key

P4DIR |= BIT2;

// Inicjalizacja buzera

BUZ_OFF;

// wyłącz buzer

P2DIR |= 0x02;

// Ustawienie bitu P2.1 na 1 (tryb wyjściowy) STATUS_LED

P2OUT &= ~0x02;

// Zgaś diodę STATUS_LED jeśli zapalona

P1DIR |= 0x20;

// Ustawienie bitu P1.5 na 1 (tryb wyjściowy) REL_1

P1OUT &= ~0x20;

// Wyłącz stycznik REL_1 i powiązaną z nim diodę

P1DIR |= 0x40;

// Ustawienie bitu P1.6 na 1 (tryb wyjściowy) REL_2

P1OUT &= ~0x40;

// Wyłącz stycznik REL_2 i powiązaną z nim diodę

}

/* Inicjalizacja oscylatorów i zegarów

* Ostatecznie pracuje tylko oscylator LFXT1, który zasila ACLK (4MHz) i MCLK (8MHz) */

void InitOsc2() {
BCSCTL1 |= XTS; // oscylator LFXT1 w trybie wysokiej częstotliwości (8MHz)
_BIC_SR(OSCOFF); // włączenie oscylatora LFXT1

char i;
do {
IFG1 &= ~OFIFG;

// wyczyszczenie flagi OFIFG rejestru IFG1

for (i = 0xFF; i > 0; i--); // odczekanie
}
while (IFG1 & OFIFG);

// czekaj dopóki flaga błędu oscylatora jest ustawiona

BCSCTL1 |= DIVA0;

// częstotliwość ACLK = LFXT1/2 = 4MHz

BCSCTL1 &= ~DIVA1;

//

BCSCTL2 |= SELM0 | SELM1;

// częstotliwość MCLK = LFTX1 = 8MHz

_BIS_SR(SCG0);

// wyłączenie oscylatora DCO

_BIS_SR(SCG1);

// wyłączenie zegara SMCLK

}

/* Kontrola budzika (domyślne wartości zmiennych) */

#define WDTCONFIG (WDTPW+WDTTMSEL+WDTCNTCL+WDTSSEL+WDTIS0)

// domyślne ustawienia

unsigned short maxl=30;
unsigned short maxh=150;
unsigned short maxp=30;
unsigned short freq=50;
unsigned short wdtconfig=0;
unsigned char ws=0;
unsigned char ds=1;
unsigned char dbg=0;

void alarmConf() {

// konfigurator budzika

wdtconfig=WDTTMSEL+WDTCNTCL;
while ((B4) == 0) {_NOP(); _NOP(); _NOP(); _NOP();}
Delayx1ms(300);
unsigned char md=6;

unsigned char pr=1;

background image

SEND_CMD(CLR_DISP);
SEND_CMD(CUR_HOME);
SEND_CMD(DD_RAM_ADDR2);
printString("Test mode");
while (1) {

if (pr) {

pr = 0;
SEND_CMD(DD_RAM_ADDR);
SEND_CMD(CUR_HOME);
if (md == 0) {

if (ws) printString("MCLK 8MHz");
else printString("ACLK 4MHz");

}
else {

if (md==1) {

switch (ds) {

case 0: printString("32768"); break;
case 1: printString("8192 "); break;
case 2: printString("512 "); break;
case 3: printString("64 "); break;

};

}
else {

if (md == 6) {

if (dbg) CHAR_DD('1', 0x00);
else CHAR_DD('0', 0x00);

}
else {

printString(" ");
SEND_CMD(DD_RAM_ADDR);
switch (md) {

case 2: printDec(maxl); break;
case 3: printDec(maxh); break;
case 4: printDec(maxp); break;
case 5: printDec(freq); break;

};

}

}

}

}
if ((B2) == 0) {

switch(md) {

case 0: ws=!ws; break;
case 1: ds++; ds%=4; break;
case 2: maxl++; break;
case 3: maxh++; break;
case 4: maxp++; break;
case 5: freq++; break;
case 6: dbg = !dbg; break;

};
pr=1;
Delayx1ms(150);

}

background image

if ((B1) == 0) {

switch(md) {

case 0: ws=!ws; pr=1; Delayx1ms(150); break;
case 1: if (ds) ds--; else ds=3; pr=1; Delayx1ms(150); break;
case 2: if (maxl) {maxl--; pr=1; Delayx1ms(150);} break;
case 3: if (maxh) {maxh--; pr=1; Delayx1ms(150);} break;
case 4: if (maxp) {maxp--; pr=1; Delayx1ms(150);} break;
case 5: if (freq) {freq--; pr=1; Delayx1ms(150);} break;
case 6: dbg=!dbg; pr=1; Delayx1ms(150); break;

};

}
if ((B3) == 0) {

SEND_CMD(CLR_DISP);
SEND_CMD(CUR_HOME);
SEND_CMD(DD_RAM_ADDR2);
md++;
md %= 7;
pr=1;
switch (md){

case 0: printString("WDT source"); break;
case 1: printString("WDT divider"); break;
case 2: printString("MAXL (short)"); break;
case 3: printString("MAXH (long)"); break;
case 4: printString("MAXP (pulse)"); break;
case 5: printString("FREQ (okres)"); break;
case 6: printString("Test mode"); break;

};
Delayx1ms(250);

}
if ((B4) == 0) {

if (! ws) wdtconfig += WDTSSEL;
switch (ds) {

case 1: wdtconfig += WDTIS0; break;
case 2: wdtconfig += WDTIS1; break;
case 3: wdtconfig += (WDTIS0+WDTIS1); break;

}
Delayx1ms(200);
return;

}

}

}

/* Ustawienie zegara przy starcie urządzenia */

void setClock(unsigned char *godziny, unsigned char *minuty, char budzik) {

SEND_CMD(CLR_DISP);
SEND_CMD(DD_RAM_ADDR);
if (budzik) {

printString(" GodzBudz ");
SEND_CHAR((*godziny)/10+'0'); // wypisanie pierwszej cyfry godzin pod adresem DDRAM 0x0A
SEND_CHAR((*godziny)%10+'0'); // wypisanie drugiej cyfry godzin pod kolejnym adresem

}
else

printString(" Godzina: 00");

SEND_CMD(DD_RAM_ADDR2); printString(" B1- B2+ B3(OK) ");

background image

while (1) {

if ((B2) == 0) {

(*godziny)++;
*godziny %=24;
CHAR_DD((*godziny)/10+'0', 0x0A);

// wypisanie pierwszej cyfry godzin pod adresem DDRAM 0x0A

SEND_CHAR((*godziny)%10+'0');

// wypisanie drugiej cyfry godzin pod kolejnym adresem

Delayx1ms(200);

}
if ((B1) == 0) {

if (*godziny == 0) (*godziny) = 23;
else (*godziny)--;
CHAR_DD((*godziny)/10+'0', 0x0A);

// wypisanie pierwszej cyfry godzin pod adresem DDRAM 0x0A

SEND_CHAR((*godziny)%10+'0');

// wypisanie drugiej cyfry godzin pod kolejnym adresem

Delayx1ms(200);

}
if ((B3) == 0) { Delayx1ms(400); break; }
if (((B4) == 0) && (budzik)) {SEND_CMD(CLR_DISP); return;}
if (((B4) == 0) && (! budzik)) {

alarmConf();
SEND_CMD(CLR_DISP);
SEND_CMD(CUR_HOME);
printString(" Godzina: ");
SEND_CHAR((*godziny)/10+'0');

// wypisanie pierwszej cyfry godzin pod adresem DDRAM 0x0A

SEND_CHAR((*godziny)%10+'0');

// wypisanie drugiej cyfry godzin pod kolejnym adresem

SEND_CMD(DD_RAM_ADDR2); printString(" B1- B2+ B3(OK) ");

}

}
SEND_CMD(CLR_DISP); SEND_CMD(DD_RAM_ADDR);
if (budzik) {

printString(" MinBudz ");
SEND_CHAR((*minuty)/10+'0');

// wypisanie pierwszej cyfry minut pod adresem 0x09

SEND_CHAR((*minuty)%10+'0');

// wypisanie drugiej cyfry minut pod kolejnym adresem

}
else

printString(" Minuta: 00");

SEND_CMD(DD_RAM_ADDR2); printString(" B1- B2+ B3(OK) ");
while (1) {

if ((B2) == 0) {

(*minuty)++;
*minuty %=60;
SEND_CMD(DD_RAM_ADDR+9);
CHAR_DD((*minuty)/10+'0', 0x09); // wypisanie pierwszej cyfry minut pod adresem 0x09
SEND_CHAR((*minuty)%10+'0'); // wypisanie drugiej cyfry minut pod kolejnym adresem
Delayx1ms(200);

}
if ((B1) == 0) {

if (*minuty == 0) *minuty = 59;
else (*minuty)--;
CHAR_DD((*minuty)/10+'0', 0x09); // wypisanie pierwszej cyfry minut pod adresem 0x09
SEND_CHAR((*minuty)%10+'0'); // wypisanie drugiej cyfry minut pod kolejnym adresem
Delayx1ms(200);

}
if ((B3) == 0) break;

}

}

background image

/* Funkcja główna */

int main(void) {

WDTCTL = WDTPW + WDTHOLD;
InitPorts();
InitOsc2();
InitLCD();
InitPorts2();
defineChars();
STATUS_LED_OFF;
SEND_CMD(CLR_DISP);
setClock(&godziny, &minuty, 0);
// ustawienie domyślnych parametrów budzika jeśli nie były modyfikowane
if (! wdtconfig) wdtconfig=WDTCONFIG;

SEND_CMD(CLR_DISP); SEND_CMD(CUR_HOME);
CHAR_DD(godziny/10+'0', HH); // wypisanie pierwszej cyfry godzin pod adresem HH
SEND_CHAR(godziny%10+'0'); // wypisanie drugiej cyfry godzin pod kolejnym adresem
SEND_CHAR(':');
SEND_CHAR(minuty/10+'0'); // wypisanie pierwszej cyfry minut pod adresem MM
SEND_CHAR(minuty%10+'0'); // wypisanie drugiej cyfry minut pod kolejnym adresem
SEND_CHAR(':');
SEND_CHAR('0');
SEND_CHAR('0');

/* Konfiguracja Timera A

TASSEL0 - źródło zegarowe ACLK
MC_1 - cyklicznie zlicza od zera do wartości zapisanej w TACCR0

podczas zerowania licznika następuje przerwanie Timer_A

ID_3 - podzielenie przez 8 częstotliwości zegara źródłowego (4MHz/8 = 500kHz)

(oba bity ustawione)

Przerwanie następuje co (CCR0) / (f_timerA) = 50000 / (4MHz/8) = 100 ms */

TACTL = TASSEL0 + MC_1 + ID_3;

// Włączenie przerwań od CCR0
CCTL0 = CCIE;

// Przerwanie Timer_A uruchamiane co 100 ms
CCR0 = 50000;

//Włączenie przerwanń
IE1 |= WDTIE;

// aktywuje przerwania NMI

_EINT();

licznik = 0;

_BIS_SR(CPUOFF); // wyłącz procesor

/* Pętla nieskończona */
while (1) { }

}

background image

/* Procedura obsługi przerwania watchdoga odpowiedzialnego za sygnał budzika */

int k;
char m;
interrupt(WDT_VECTOR) watchdog_timer(void) {

if (pulse) { // pojedyncze piknięcie

for(k=0; k!=freq; ++k) {_NOP(); _NOP(); _NOP(); _NOP();}
P4OUT ^= BIT2;
if (++num == maxp) {num=pulse=0; BUZ_OFF;}

}
else { // odstęp pomiędzy sygnałami alarmu

num++;
if (m == 3) { // dłuższy odstęp między grupą trzech piknięć alarmu

if (num == maxh) {m=num=0; pulse=1;}

}
else { // krótszy odstęp między poszczególnymi piknięciami w danej trójce

if (num == maxl) {num=0; pulse=1; m++;}

}

}

}

/* Procedura obsługi przerwania Timer A

* Uruchamia się ono 10 razy w ciągu sekundy (w pozostałym czasie urządzenie pozostaje w trybie
niskiego poboru mocy) procesor jest wyłączony, a przetwarzanie wstrzymane */

interrupt(TIMERA0_VECTOR) Timer_A(void) {

_BIC_SR_IRQ(CPUOFF); // włącz procesor
++licznik;

if ((licznik % 10) == 0) { // odmierzono pełną sekundę

if ((buz) && (++talarm == 240)) { // budzik dzwonił 4 minuty, wyłącz go

talarm = buz = budzon = 0;
BUZ_OFF;
SEND_CMD(DD_RAM_ADDR2);
printString(" ");
WDTCTL = WDTPW|WDTHOLD;

}
if ((dbg) && (budzon) && (godziny==godzB) && (minuty==minB) && licznik==30 && (disp){

// włączenie budzika w trybie testowym o godzinie 00:00:03
m=talarm=num=0;
budzon=pulse=buz=1;
WDTCTL = WDTPW|wdtconfig;

}
/* */
if (licznik == 600) { // odmierzono pełną minutę 60*100ms

licznik = 0; // zerowanie licznika dziesiętnych części sekundy
minuty++;
if (minuty == 60) { // odmierzono pełną godzinę

minuty = 0;
godziny++;
if (godziny == 24) godziny = 0; // odmierzono pełną dobę
if (disp) {

CHAR_DD(godziny/10+'0', HH); // wypisanie pierwszej cyfry godzin pod adresem HH
SEND_CHAR(godziny%10+'0'); // wypisanie drugiej cyfry godzin pod kolejnym adresem

}

}

background image

if (disp) {

CHAR_DD(minuty/10+'0', MIN); // wypisanie pierwszej cyfry minut pod adresem MM
SEND_CHAR(minuty%10+'0'); // wypisanie drugiej cyfry minut pod kolejnym adresem
CHAR_DD('0', SS);
SEND_CHAR('0');

}
if ((!dbg) && (budzon) && (godziny==godzB) && (minuty==minB) && (disp)) {

// włączenie budzika w zwykły sposób (o pełnej minucie)
m=talarm=num=0;
budzon=pulse=buz=1;
WDTCTL = WDTPW|wdtconfig;

}
/* */

}
else {

// wypisz sekundy
if (disp) {

CHAR_DD(licznik/100+'0', SS); // wypisanie pierwszej cyfry sekund pod adresem SS
SEND_CHAR((licznik%100)/10 + '0');

// wypisanie drugiej cyfry sekund pod kolejnym adresem

}

}

}

// opuszczenie trybu zegara i wejście w tryb konfiguracji budzika
if (((B1) == 0) && (disp==1)) {

if (buz) { // wyłącz alarm jeśli akurat dzwoni

talarm = buz = budzon = 0;
BUZ_OFF;
WDTCTL = WDTPW|WDTHOLD;

}
disp=0;
SEND_CMD(DD_RAM_ADDR);
SEND_CHAR('B'); SEND_CHAR('u'); SEND_CHAR('d'); SEND_CHAR('z'); SEND_CHAR('i');
SEND_CHAR('k'); SEND_CHAR(':'); SEND_CHAR(' ');
SEND_CHAR(godzB/10+'0'); // wypisanie pierwszej cyfry godzin pod adresem HH
SEND_CHAR(godzB%10+'0'); // wypisanie drugiej cyfry godzin pod kolejnym adresem
SEND_CHAR(':');
SEND_CHAR(minB/10+'0'); // wypisanie pierwszej cyfry minut pod adresem MM
SEND_CHAR(minB%10+'0'); // wypisanie drugiej cyfry minut pod kolejnym adresem

SEND_CMD(DD_RAM_ADDR2);
printString("B2-HH B3-MM B4OK");

}

if (((B2) == 0) && (disp==0)) { // zmiana godziny budzika w trybie konfiguracji budzika

godzB++; godzB %= 24;
CHAR_DD(godzB/10+'0', 0x08); SEND_CHAR(godzB%10+'0');

}

if (((B3) == 0) && (disp==0)) { // zmiana minuty budzika w trybie konfiguracji budzika

minB++; minB %= 60;
CHAR_DD(minB/10+'0', 0x0B); SEND_CHAR(minB%10+'0');

}

background image

// opuszczenie trybu konfiguracji budzika i powrót do trybu zegara
if (((B4) == 0) && (disp==0)) {

budzon=1;
disp=1;
SEND_CMD(CLR_DISP); SEND_CMD(CUR_HOME);
CHAR_DD(godziny/10+'0', HH); // wypisanie pierwszej cyfry godzin pod adresem HH
SEND_CHAR(godziny%10+'0'); // wypisanie drugiej cyfry godzin pod kolejnym adresem
SEND_CHAR(':');
SEND_CHAR(minuty/10+'0'); // wypisanie pierwszej cyfry minut pod adresem MM
SEND_CHAR(minuty%10+'0'); // wypisanie drugiej cyfry minut pod kolejnym adresem
SEND_CHAR(':');
SEND_CHAR(licznik/100+'0'); // wypisanie pierwszej cyfry sekund pod adresem SS
SEND_CHAR((licznik%100)/10 + '0'); // wypisanie drugiej cyfry sekund pod kolejnym adresem
SEND_CMD(DD_RAM_ADDR2);
printString("Budzik W"); SEND_CHAR(2); SEND_CHAR(0); printString("czony");

}

// wyłączenie budzika w trybie zegara
if ((budzon==1) && (((B2) == 0) || ((B3) == 0)) && (disp==1)) {

if (buz) { // budzik akurat dzwonił

talarm = buz = budzon = 0;
BUZ_OFF;
WDTCTL = WDTPW|WDTHOLD;

}
else // budzik nie dzwonił

budzon = 0;

SEND_CMD(DD_RAM_ADDR2);
printString(" ");

}

// wyłączenie procesora o ile jesteśmy w trybie zegara, ani nie dzwoni budzik
if ((disp==1) && (! buz)) _BIS_SR_IRQ(CPUOFF);

}

Wnioski

Zegar działa poprawnie, a czas odmierzany jest dokładnie. Obsługa samego zegara oraz Timera

A była dość prosta do zaprogramowania. Sporo czasu zajęła obsługa budzika i próby
zaimplementowania jednoczesnej możliwości zmiany godziny budzika bez zatrzymywania zegara.

Ostatecznie zostało to wykonane w najprostszy możliwy sposób, który działa. Czyli,

uwzględniono konfiguracje budzika w ramach tej samej funkcji obsługi przerwania Timera A.

Co ważne, samo przestawianie godziny budzika nie powinno powodować opóźnień czasu.
W trybie zegara i konfiguracji godziny budzika, funkcje zostały przydzielone klawiszom w taki

sposób aby nie kolidowały one ze sobą. Ponadto, użycie aktywnego czekania po naciśnięciu
klawisza zmiany godziny lub minuty budzika, zostało wyeliminowane – przeskok wartości
następuje co przerwanie – 100 ms. Wszystko to odbywa się minimalnym kosztem zmniejszenia
wygody użytkowania.

Pewnym problemem mogą być procedury obsługi przerwania watchdoga. W standardowej

konfiguracji, uruchamia się ona co 2ms. Jej uruchamianie się może potencjalnie powodować
opóźnienia w uruchamianiu przerwania Timer A, a w konsekwencji na opóźnianiu się zegara
(zwłaszcza, że w przerwaniu watchdoga nie udało się uniknąć aktywnego czekania, które przy
domyślnych ustawieniach zajmuje około 4*FREQ = 200 cykli procesora)

Jednak nie sądzę, aby mogło to znacząco wpłynąć na dokładność odmierzanego czasu.


Document Outline


Wyszukiwarka

Podobne podstrony:
zegar sprawozdanie
2 definicje i sprawozdawczośćid 19489 ppt
PROCES PLANOWANIA BADANIA SPRAWOZDAN FINANSOWYC H
W 11 Sprawozdania
Wymogi, cechy i zadania sprawozdawczośći finansowej
Analiza sprawozdan finansowych w BGZ SA
W3 Sprawozdawczosc
C++ zegar
1 Sprawozdanie techniczne
Karta sprawozdania cw 10
eksploracja lab03, Lista sprawozdaniowych bazy danych
2 sprawozdanie szczawianyid 208 Nieznany (2)
Fragmenty przykładowych sprawozdań
Lab 6 PMI Hartownosc Sprawozdan Nieznany

więcej podobnych podstron