zegar sprawozdanie

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:

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

  2. 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 / ftimerA = 50 000 / 500 000 Hz = 0,1 s = 100 ms – przerwanie będzie wykonywane co 100ms

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:

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 TWDT >

}

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;

}

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;

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)

#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);

}


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;


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);

}



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) ");


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;

}

}

/* 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) { }


}



/* 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

}

}

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');

}


// 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.


Wyszukiwarka

Podobne podstrony:
zegar sprawozdanie
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