Piotr Błędowski Maciej Pawlak 1. Cel laboratorium LABORATORIUM TM 5 10.06.2010 Zadanie polegało na zaprojektowaniu oraz realizacji wyświetlania napisu przesuwanego, o zmiennym kierunku, na terminalu na komputerze PC, sterowanym przez port szeregowy z kontrolera MSP430. Przy dodaniu do ciągu znaków specjalnego znaku napis będzie przesuwany w drugą stronę. 2. Schemat połączenia 3. Kod programu: #include <msp430x14x.h> #define TABLE_SIZE 100 // wielkosc tablicy z napisem #define SPEED_COUNTER 600 // liczba tykniec przed kolejnym wyswietleniem // napisu int table_changed_size = 8; // poczatkowa dlugosc napisu
int transmition_counter_int = 2; // dlugosc opoznienia, w celu poprawnego odebrania // wyznacza kierunek w ktora przesuwane sa napisy int kierunek = 0; // tablica z naszym napisen char tablica[table_size]; // tablica tymczasowa char temp_table[table_size]; // tablica, z ktorej pobierane sa znaki do wyswietlania. // rozmiar 2 razy wiekszy niz tablicy z napisem, gdyz wstawiamy // do niej znaki kasujace poprzedni napis char temp_send_table[2*table_size]; // flaga informujaca o zmianie napisu (potrzebna do obsluzenia przerwania timera) int table_changed = 0; // flaga informujaca czy przyszlo przerwanie od ukladu UART int odbierz = 0; // pozycja litery w glownej tablicy, od ktorej bedziemy // zaczynac wyswietlanie napisu short int litera = 0; // licznik odbieranych znakow int temp_table_counter = 0; // licznik wysylanych znakow int temp_send_table_counter = 0; // opoznienie w odbiorze int transmition_counter = transmition_counter_int; // ile tykniec czekamy na wyswietlenie kolejnego napisu int counter = SPEED_COUNTER; // ilosc znakow do skasowania, po poporzednim napisie int old_number_of_backspaces = 0; void main( void ) // wylaczenie watchdoga WDTCTL = WDTPW + WDTHOLD; // Ustawienie timera w tryb zliczania w gore TACTL = TASSEL_2 + MC_1; // ustawiamy pierwsza i druga nozke jako Rx i Tx P3SEL = 0x30; // Wlaczamy modul do wysylania danych i odbierania w UART ME1 = UTXE0 + URXE0; // Ustawiamy bit parzystosci, dlugosc danych 8 bitow, i 2 bity stopu UCTL0 = PEV+PENA+CHAR+SPB; // Ustawiamy UTCTL0 = SSEL1; // Korzystamy z zewnetrznego kwarcu jako zrodla taktowania BCSCTL2 = SELS; // Ustawienia dla 115kHz UBR00 = 0x40; // pierwszy dzielnik częstotliwości UBR10 = 0x00; // drugi dzielnik UMCTL0 = 0x00; UCTL0 &= ~SWRST; // wylaczenie software reset // Na starcie tylko modul odbierajacy dane moze zglaszac przerwania IE1 = URXIE0;
// Ustawiamy ze kanal 0 timera A moze zglaszac przerwania CCTL0 = CCIE; // Okreslenie do jakiej wartosci ma zliczac timer w trybie UP CCR0 = 2400; // zerujemy tablice z napisem for(int i=0; i<table_size; i++) tablica[i] = ' '; // zerujemy tymczasowa tablice for(int i=0; i<table_size; i++) temp_table[i] = ' '; // napis domyslny tablica[0] = 'C'; tablica[1] = 'O'; tablica[2] = 'C'; tablica[3] = 'A'; tablica[4] = 'C'; tablica[5] = 'O'; tablica[6] = 'L'; tablica[7] = 'A'; table_changed_size = 8; while(1) // Przechodzimy w tryb uspienia procesora i odblokowujemy globalnie przerwania _BIS_SR(LPM0_bits + GIE); _NOP(); // ################ obsluga zdarzen #################### // // przerwania obslugiwane sa w petli glownej, dzieki temu // nie blokujemy innych przychodzacych przerwan // obsluga przerwania, w ktorym z zewnetrznego ukladu // przychodzi bajt if(odbierz == 1) // jesli nie czekamy na odbior // lub wczytalismy cala tablice znakow // to zerujemy licznik wstawiania if(transmition_counter == 0 temp_table_counter >= 99) temp_table_counter=0; // flaga informujaca o tym, ze zmienil sie napis table_changed = 1; // odczytujemy bajt z bufora wejsciowego ukladu UART temp_table[temp_table_counter] = (char)rxbuf0; // zwiekszamy licznik pobranych znakow temp_table_counter++; // ustawiamy licznik pozwalajacy poczekac na poprawne odczytanie bajtu transmition_counter = transmition_counter_int; // odebralismy bajt, koniec obslugi przerwania odbierz = 0;
// jesli mozemy wyswietlac oraz // nastapila zmiana napisu LUB wyslalismy juz wszystkie znaki poprzednieggo napisu if(counter <= 0 && (temp_send_table_counter <= 0 table_changed == 1)) // jesli czekamy jeszcze na pobranie znaku, to nic nie robimy if(transmition_counter > 0) transmition_counter--; continue; // jesli nastapila zmiana napisu (nowy jest w temp_table[]) if(table_changed == 1) // sprawdzamy czy wystapil na poczatku znak kontrolny, sluzacy // do odwrocenia kierunku if(temp_table[0] == '-') kierunek = 1; kierunek = 0; // zerujemy licznik wysylanych znakow temp_send_table_counter = 0; for(int i = temp_table_counter; i>=kierunek; i--) // wstawiamy nowy napis do glownej tablicy // omijamy znak kontrolny jesli wystapil tablica[i-kierunek] = temp_table[i]; // zapamietujemy ilosc znakow poprzedniego napisu, // tyle bowiem trzeba bedzie usunac old_number_of_backspaces = table_changed_size; // zapamietujemy ilosc znakow nowego napisu table_changed_size = temp_table_counter-kierunek; // ustawiamy licznik szybkosci przesuwania napisu counter = SPEED_COUNTER; // zmieniamy poczatkowa litere od ktorej ma sie zaczac wyswietlanie // w zaleznosci od kierunku if(kierunek == 0) litera++; if(litera >= table_changed_size) litera = 0; litera--; if(litera <= 0) litera = table_changed_size; // zerujemy licznik wysylania temp_send_table_counter = 0; // wstawiamy napis do tablicy wysylania w odwrotnej kolejnosci. // bedzie on odczytywany od konca. for(int i=table_changed_size; i>0; i--) temp_send_table[temp_send_table_counter++] = tablica[(i+litera)%table_changed_size]; // jesli nie zmienil sie napis (jego dlugosc), // to bedziemy usuwac tyle znakow ile ma aktualny napis if(table_changed == 0) for(int i = 0; i<table_changed_size; i++) temp_send_table[temp_send_table_counter++] = 0x08;
// jesli zmienila się dlugosc napisu, musimy skorzystac z zapamietanej // liczby znakow starego napisu i wstawic tyle znakow kasujacych // do wyswietlanej tablicy for(int i = 0; i<old_number_of_backspaces; i++) temp_send_table[temp_send_table_counter++] = 0x08; old_number_of_backspaces = 0; table_changed = 0; // mamy juz co wysylac do odbiornika, odblokowujemy wiec przerwania // i ustawiamy flage, aby zgloszone zostalo przerwanie IE1 =UTXIE0; U0IFG = UTXIFG0; #pragma vector=timera0_vector interrupt void Timer_A (void) // przerwanie timera // budzimy msp co jakis czas, dzieki temu mozemy kontrolowac // predkosc przesuwanego sie napisu. // nie ma też sensu resetowac trybu LPM0, jesli nie wstawiono // nowego napisu badz nie wyslalismy jeszcze poprzedniego counter--; if(counter <= 0 && (temp_send_table_counter <= 0 table_changed == 1)) _BIC_SR_IRQ(LPM0_bits + GIE); #pragma vector=usart0rx_vector interrupt void usart0_rx (void) // przerwanie od ukladu UART, odbieramy znak odbierz = 1; _BIC_SR_IRQ(LPM0_bits + GIE); #pragma vector=usart0tx_vector interrupt void usart0_tx (void) // jesli mamy jeszcze jakies znaki do wyslania... if(temp_send_table_counter > 0) //...wstawiamy znak do bufora wysylajacego... TXBUF0 = temp_send_table[temp_send_table_counter-1]; //...i zmniejszamy licznik znakow do wyslania temp_send_table_counter--; // jesli nie mamy juz znakow do wyslania, blokujemy przerwania IE1&=~UTXIE0;
4. Podsumowanie W projekcie fundamentalnym problemem okazała się odpowiednia synchronizacja działań, jakie mają miejsce podczas wykonywania się programu. Znaki mogą przychodzić oraz być wysyłane w dowolnej chwili. Posłużyliśmy się więc odpowiednimi flagami, które pomagają synchronizować zmiany danych. W przypadku tablicy potrzebne okazało się stworzenie tablicy tymczasowej, do której to w razie odebrania nowego ciągu znaków zapisywane są odczytane dane. Jest to spowodowane faktem, iż podczas wysyłania znaków nie może dojść do zmiany tablicy charów. Tak więc odbierane znaki przepisywane są do tablicy bufora, a flaga zmiany zostaje podniesiona. Zakończenie transmisji jest wykrywane poprzez odliczanie timera jeżeli przez pewien okres czasu żaden nowy znak nie zostanie odebrany, transmisję uznaje się za zakończoną. Dopiero podniesienie flag zakończenia transmisji i zmiany wzorcowej tablicy znaków powodują przepisanie nowego napisu do głównej tablicy. Ostatnim zagadnieniem na które warto zwrócić uwagę jest ograniczenie kodu wykonywanego w przerwaniach do minimum. Sprowadza się on praktycznie do wykonania niezbędnych akcji i podniesienia odpowiedniej flagi przy spełnieniu odpowiednich warunków. Po wyjściu z przerwania, jeżeli wystąpiło zdarzenie wymagające większej porcji obliczeń, procesor jest budzony, a w pętli głównej programu wykonywane są odpowiednie algorytmy. Układ udało się zrealizować z prędkością transmisji wynosząca 115 khz. Większa nie była możliwa do osiągnięcia zapewne z powodu ograniczeń prądowych nakładanych przez układ max232