background image

 

1

 

 

 

Architektura Komputerów i Systemy 

Operacyjne 

 
 
 
 
 

Laboratorium 1 

 
 
 

Ćwiczenie wstępne 

 
 
 
 
 
 
 
 
 
 
 
 
 

Autor mgr inŜ. Paweł Wujek 

Data 2.10.2011 

background image

 

2

1. Wstęp 

Celem  niniejszego  laboratorium  jest  zapoznanie  studenta  z  realizacją  projektu 

wykorzystującego mikrokontroler rodziny STM32 z rdzeniem Cortex – M3.  

Niniejsza  instrukcja  przeprowadzi  czytelnika  przez  proces  tworzenia  nowego  projektu, 

zapozna z najczęściej uŜywanymi funkcjami środowiska uruchomieniowego oraz przedstawi 

sposób  realizacji  prostego  programu  polegającego  na  zapaleniu  diody  LED  na  płycie 

uruchomieniowej.  

Czytelnik  zapozna  się  z  pisaniem  prostego  programu  w  języku  C,  asembler,  oraz  programu 

napisanego w C ze wstawkami asemblerowymi.  

 

 

2. Tworzenie nowego projektu 

 

Projekty tworzone będą z wykorzystaniem środowiska programistycznego 

Keil  µVision. 

Aby  uruchomić  środowisko  naleŜy  dwukrotnie  kliknąć  na  ikonę 

  znajdującą  się  na 

pulpicie. 

Uruchomiony program powinien wyglądać następująco 

 

 

background image

 

3

Jeśli program otworzy się i będzie widoczny poprzednio wykorzystywany projekt naleŜy  go 

zamknąć wybierając PROJECT > Close Project

 

1. Następnie naleŜy utworzyć nowy projekt wybierając PROJECT > New µVision Project

Pojawi się okno, w którym naleŜy wpisać ścieŜkę i nazwę projektu. 

Jako ścieŜkę proszę wybrać C:\AKSIO_LAB\grupa\LAB1 

Nazwa projektu lab1

 

2. Po wybraniu zapisz pojawi się okno wyboru procesora. 

 

NaleŜy wybrać firmę STMicroelectronics a model procesora STM32F103VB

background image

 

4

 

3.  Po  zatwierdzeniu  pojawi  się  pytanie,  czy  skopiować  STM32  Startup  Code  do  folderu 

projektu. NaleŜy wybrać NIE

Plik zwierający Startup Code (startup_stm32f10x_md.s) naleŜy dołączyć do projektu. 

4. Z prawej strony ekranu w oknie Project naleŜy  rozwinąć pole Target 1, następnie kliknąć 

prawym przyciskiem myszy na Source Group 1 i wybrać Add Files to Gruop….. 

 

 

Po  wybraniu  pliku  startup_stm32f10x_md.s  (plik  ten  znajduje  się  w  katalogu  z  instrukcją) 

naleŜy nacisnąć przycisk Add i Close

 

5.  Następnie  wybrać  File>New  otworzy  się  pole  tekstowe,  gdzie  naleŜy  wprowadzać  kod 

programu.  Następnie  naleŜy  zapisać  utworzony  plik  wybierając  File>Save  i  nadając  mu 

nazwę main.c  

Plik 

main.c 

naleŜy 

dołączyć 

do 

projektu 

sposób 

analogiczny 

do 

pliku 

startup_stm32f10x_md.s. 

background image

 

5

 

6.  Kolejnym  krokiem  jest  wybór  programatora.  W  tym  celu  naleŜy  wybrać 

Flash>Configure Flash Tools… 

 

Wybrać zakładkę Utilities. Zaznaczyć na niej Use Target Driver….. 

Oraz wybrać ST-Link Debugger

 

7. Następnie wybrać zakładkę Debug 

 

 

Zaznaczyć na niej USE: i wybrać w menu rozwijanym ST-Link Debuger. 

Na koniec zatwierdzić przyciskiem OK

background image

 

6

MoŜna równieŜ sprawdzić działanie programu bez wykorzystania płyty uruchomieniowej. Do 

tego  celu  moŜna  wykorzystać  symulator.  UmoŜliwia  on  sprawdzanie  zawartości  wszystkich 

rejestrów dostępnych w procesorze.  

Aby  uruchomić  symulator  naleŜy  w  poprzednim  oknie  zaznaczyć  po  prawej  stronie  USE 

Simulator

 

 

3. Kompilacja i uruchamianie pierwszego programu 

W  niniejszym  rozdziale  student  zostanie  przeprowadzony  przez  proces  pisania  pierwszego 

programu, kompilację, debugging oraz jego uruchomienie na płycie uruchomieniowej.  

 

An początek naleŜy wpisać poniŜszy kod programu w oknie edytora. 

 

#include "stm32f10x.h" 

 

int main(void) 

    

while(1) 

 

 

 

Linia pierwsza pozwala  na dołączenie pliku nagłówkowego dostarczanego przez producenta 

mikroprocesora.  Plik  ten  zawiera  zdefiniowane  nazwy  wszystkich  rejestrów,  które  będą 

uŜywane w kolejnych przykładach. 

background image

 

7

Pliki nagłówkowe udostępnione przez środowisko Keil uVision wymagają wskazania rodziny 

procesorów, której uŜywamy. Procesor dostępny w laboratorium naleŜy do rodziny „Medium 

density devices”. Wyboru rodziny moŜna dokonać poprzez wybranie  

Flash>Configure Flash Tools… 

A następnie w zakładce C/C++ w polu Define wpisać STM32F10X_MD. 

Co przedstawiono na rysunku 

 

 

Na koniec naleŜy zatwierdzić przyciskiem OK.  

 

Następnie  naleŜy  skompilować  program  naciskając  przycisk  Rebuild  all  target  files.  W  ten 

sposób tworzony jest plik wynikowy typu hex. Nie jest konieczne zapisywanie projektu, jest 

on zapisywany automatycznie przed kompilacją: 

 

Ewentualne błędny kompilacji moŜna obserwować w oknie Build Output znajdującym się na 

dole ekranu. 

 

Po pierwszej kompilacji okno powinno wyglądać podobnie jak na rysunku poniŜej 

background image

 

8

 

 

W  przypadku  braku  błędów  moŜna  wgrać  oprogramowanie  do  pamięci  mikrokontrolera,  w 

tym celu naciśnij przycisk Start/Stop Debug Session 

 

 

Po  naciśnięciu  przycisku  w  oknie  edycji  programu  pokazywany  jest  plik  po  deasemblacji, 

zauwaŜ w jaki sposób są kodowane rozkazy i ich argumenty 

 

 

 

Przydatne funkcje debuggera 

 

 

Pierwsza ikona z lewej na powyŜszym rysunku słuŜy do resetowania programu w procesorze. 

Kolejna ikona  umoŜliwia uruchomienie programu. 

Trzecia  słuŜy  do  zatrzymania  działającego  programu.    (staje  się  aktywna  w  momencie 

uruchomienia programu) 

Kolejna grupa ikon przeznaczone jest do obsługi pracy krokowej programu. UmoŜliwiają one 

wykonywanie instrukcji krok po kroku, lub przejście do kolejnej pułapki.  

 

background image

 

9

W  momencie  pisania  programu  lub  po  wejściu  w  tryb  debuggera  moŜliwe  jest  dodawanie 

„pułapek”,  w  których  program  w  czasie  pracy  zatrzyma  się.  Dodawanie  pułapek  polega  na 

dwukrotnym kliknięciu w szare pole po lewej stronie interesującej nas linii kodu. Poprawnie 

ustawiona pułapka wygląda następująco: 

 

Kolejną 

przydatną 

funkcja 

jest 

moŜliwość 

podglądania 

zawartości 

rejestrów 

odpowiedzialnych za pracę poszczególnych peryferiów.  

Przykładowo  aby  zobaczyć  rejestry  odpowiedzialne  za  pracę  portu  A  procesora  naleŜy 

wybrać  Peripherals>General Purpose I/O>GPIOA.  Aktywne  stanie  się  wtedy  następujące 

okno 

 

4. Przykładowy program 

4.1 Pierwszy program w C 

W  niniejszym  rozdziale  zostanie  zaprezentowany  prosty  program  prezentujący  sposób 

zapalenia diody LED.  

 

Na wstępie proszę wpisać następujący kod i uruchomić program. 

 

#include "stm32f10x.h" 

int main(void) 

background image

 

10

 

unsigned int licznik=0; 

 

RCC->APB2ENR=0x00000008; 

 

GPIOB->CRH=0x33333333; 

 

     while(1) 

 

 

   

GPIOB->ODR=0x00000000; 

 

 

for(licznik=1000000;licznik>0;licznik--);   

 

 

 

GPIOB->ODR=0x00000100; 

 

 

for(licznik=1000000;licznik>0;licznik--); 

 

Efektem  poprawnie  uruchomionego  programu  będzie  cykliczne  zapalenie  i  zgaszenie 

diody LED1.  

 

Kolejnym  zadaniem  jakie  naleŜy  wykonać  to  zapalanie  co  drugiej  dioda  LED  w  pętli 

(maja być zapalone diody 1,3,5,7 a potem 2,4,6,8). 

 

Pierwszym 

krokiem 

jest 

skonfigurowanie 

wszystkich 

rejestrów 

zegarowych 

mikroprocesora. 

Dokumentacja 

mikroprocesora 

znajduje 

się 

katalogu 

C:\AKSIO_LAB\PDF w pliku o nazwie stm32f10xxx.pdf. 

PoniewaŜ zadaniem jest tylko zapalanie i gaszenie diody LED nie musimy konfigurować 

źródeł  sygnału  zegarowego,  ani  jego  częstotliwości  poniewaŜ  ustawienia  domyślne  są 

wystarczające  dla  tego  zadania.  (Domyślnie  wybrany  jest  wewnętrzny  zegar  o 

częstotliwości 8MHz). 

Przed  konfiguracją  pryferiów  konieczne  jest  włączenie  sygnału  zegarowego  taktującego 

dany  układ  peryferyjny.  W  naszym  przypadku  konieczna  będzie  konfiguracja  rejestru 

APB2ENR. Aby tego dokonać naleŜy wpisać w kodzie następująca linię 

 

RCC->APB2ENR = 0x00000000; 

 

W  miejsce  zer  podstawiając  odpowiednią  wartość.  NaleŜy  zapoznać  się  ze  słowem 

konfiguracyjnym tego rejestru. W naszym przypadku naleŜy wpisać 0x00000008. 

Dlaczego? Odpowiedz powinna być jasna po dalszej konfiguracji.  

background image

 

11

 

Następnym  krokiem  jest  konfiguracji  portów  IO.  NaleŜy  zacząć  od  znalezienia  na 

schemacie płyty uruchomieniowej (schematy dostępne są na końcu instrukcji) diody LED.  

 

NaleŜy następnie śledząc drogę połączeń odnaleźć układ do którego połączone są diody.  

Z  powyŜszego  schematu  moŜna  zauwaŜyć,  Ŝe  sygnał  sterujący  diodą  D1  „przechodząc” 

przez  układ  74LVD541  (układ  buforujący)  otrzymuje  nazwę  PB8.  Nazwy  tej  naleŜy 

szukać na liniach podłączonych do innych układów. 

 

Sygnał PB8 został podłączony do mikroprocesora STM32F103VBT6 do portu PB8. 

 

Czytając  w  dokumentacji  procesora  rozdział  dotyczący  obsługi  portów  I/O  (rozdział  8) 

znajdziemy  w  nim  informację  dotyczące  funkcjonowania  GPIO(General  Purpose  IO). 

Znajdują się wśród nich schematy elektryczne portów, informacje na temat trybów pracy 

oraz konfiguracji. 

Na początek naleŜy wypisać nazwy wszystkich rejestrów oraz zastanowić się jakie jest ich 

przeznaczenie i sposób wykorzystania.  

W naszym przykładzie została wpisana linia  

background image

 

12

GPIOB->CRH = 0x33333333; 

Dlaczego akurat taka konfiguracja? Proszę się zastanowić. 

 

Po poprawnym skonfigurowaniu portu GBPIOB naleŜy ustawić określoną wartość na jego 

wyjściu. Wiemy, Ŝe chcąc zapalić diodę D1 musimy wystawić jedynkę na bicie 8 portu. 

MoŜna to zrobić na jeden z poniŜszych sposobów.  

GPIOB->ODR = 0x00000100; 

GPIOB->BSRR=0x00000100; 

 

Natomiast zero moŜna ustawić na jeden z następujących sposobów: 

GPIOB->ODR = 0x00000000; 

GPIOB->BSRR=0x01000000; 

GPIOB->BRR = 0x00000100; 

 

Proszę zastanowić się nad róŜnicami oraz zaletami i wadami powyŜszych sposobów.  

 

4.2 Pierwszy program napisany w języku asembler 

 

Na  początku  naleŜy  utworzyć  nowy  projekt  tak  samo  jak  poprzednio  jednak  w  punkcie  4 

naleŜy wpisać nazwę main.asm 

Po punkcie 7 naleŜy wybrać zakładkę Target i zaznaczyć opcję Use MicroLIB. 

 

WaŜne: 

W języku asembler kaŜde polecenie naleŜy pisać w nowej linii poprzedzając je spacją. 

Etykiety naleŜy pisać od nowej linii. 

Komentarze rozpoczynają się po średniku. 

 

 

 

background image

 

13

Na początek w oknie kodu programu naleŜy wpisać: 

 AREA    MAINE, CODE, READONLY 

 EXPORT  __main 

 

Pierwsza i  druga linia rozpoczyna się  ze spacją.  

Linie  te  odpowiadają  za  poinformowanie  kompilatora  o  miejscu  rozpoczynania  się  głównej 

funkcji programu (main) 

Następnie naleŜy wpisać etykietę funkcji głównej: 

__main 

 

MoŜna przejść teraz do pisania właściwego kodu programu. 

Podobnie jak w języku C na początek naleŜy włączyć zegar na port B. 

Poleceniu RCC->APB2ENR=0x00000008; w asemblerze odpowiada zestaw poleceń: 

 

 LDR R0, =0x40021018 

 LDR R1,=0x00000008 

 STR R1,[R0] 

 

Pierwsza  linia  powoduje  wpis  do  rejestru  R0  wartości  0x40021018.  Jest  to  adres  rejestru 

APB2ENR w przestrzeni adresowej.  

Druga linia powoduje wpis do rejestru R1 wartości 0x00000008. Jest to wartość jaka naleŜy 

wpisać do rejestru APB2ENR. 

Trzecia linia powoduje wpis do pamięci pod adres z rejestru R0 wartości z rejestru R1. 

 

Analogicznie poleceniu z języka C: 

GPIOB->CRH=0x33333333; 

W asemblerze odpowiada: 

 LDR R0, =0x40010C04 

 LDR R1, =0x33333333 ; 

 STR R1,[R0] 

 

Wywołanie  funkcji  o  nazwie  DEL  z  parametrem  w  języku  asembler  wygląda  następująco. 

Parametr zapisany jest w rejestrze R2  

 LDR R2, =0x1E8480 

background image

 

14

 BL DEL  

 

Bezwzględny skok do etykiety (nazwa etykiety loop) realizowany jest za pomocą polecenia: 

 B loop   

 

Funkcja odmierzająca czas zrealizowana jest poprzez zmniejszanie o jeden wartości zapisanej 

w  rejestrze  R2  a  następnie  sprawdzanie,  czy  jest  to  juŜ  wartość  zero.  Jeśli  nie  to  następuje 

skok do etykiety DEL. Realizuje to następujący fragment kodu: 

 

DEL 

 

 NOP 

 SUBS R2, #1 

 BNE DEL 

 BX lr 

Polecenie  NOP  dla  procesora  oznacza  pustą  instrukcję.  Zabiera  ona  czas  oraz  powoduje 

zwiększenie licznika rozkazów. Nie powoduje Ŝadnych inny efektów. 

Polecenie SUBS R2, #1 powoduje odjęcie od wartości w rejestrze R2 liczby 1 oraz ustawienie 

flagi informującej czy wynik odejmowania jest równy zero. 

Polecenie  BNE  DEL  sprawdza,  czy  flaga  ustawiana  przez  polecenie  SUBS  jest  ustawiona. 

Jeśli  nie  jest,  następuje  skok  do  etykiety  DEL.  Jeśli  jest  ustawiona  następuje  przejście  do 

kolejnej linii kodu.  

Polecenie BX lr odpowiada za skok do miejsca z którego została wywołana funkcja.  

 

W pełni funkcjonalny kod wygląda następująco: 

AREA    MAINE, CODE, READONLY 

 EXPORT  __main 

 ;wywolanie glownej funkcji main 

__main 

 

;  RCC->APB2ENR=0x00000008; 

 LDR R0, =0x40021018 

 LDR R1, =0x00000008 

 STR R1,[R0] 

 

background image

 

15

;   GPIOB->CRH=0x33333333; 

 LDR R0, =0x40010C04 

 LDR R1, =0x33333333  

 STR R1,[R0] 

loop ;petla zapalaja i gaszaca diode 

  

 ;GPIOB->ODR=0x00000000; 

 LDR R0, =0x40010C0C 

 LDR R1, =0x00000000 

 STR R1,[R0] 

 

 ;wywolanie funkcji odmierzajacej czas z parametrem zapisanym w R2 

 LDR R2, =0x1E8480 

 BL DEL  

  

 ;GPIOB->ODR=0x00000100; 

 LDR R0, =0x40010C0C 

 LDR R1, =0x00000100 

 STR R1,[R0] 

 

 ;wywolanie funkcji odmierzajacej czas z parametrem zapisanym w R2 

 LDR R2, =0x001E8480 

 BL DEL  

 

 ;skok do poczatku petli 

 B loop 

 

 

;funkcja odmierzajaca czas z parametrem R2  

DEL 

 

 NOP 

 SUBS R2, #1 

 BNE DEL 

 BX lr 

 ;koniec funkcji odmierzającej czas 

background image

 

16

 NOP 

 END 

 

 

 

4.3 Pierwszy program napisany w języku C z wstawką 

asemblerową 

 

Po napisaniu pierwszego programu w C i w asemblerze widzimy, Ŝe duŜo łatwiej i przyjaźniej 

pisze się programy z wykorzystaniem języka C.  Czasem jednak zdarza się, Ŝe chcemy mieć 

pełna kontrolę nad sprzętem lub maksymalnie przyspieszyć wykonywanie instrukcji. W takim 

przypadku moŜemy skorzystać z wstawek asemblerowych w języku C. 

 

Przykładowo w naszym kodzie napisanym w C linię  

RCC->APB2ENR=0x00000008; 

MoŜemy zastąpić wstawką asemblerową. 

W miejsce tej linii naleŜy wpisać wywołanie funkcji  

funkcja_asm(); 

 

 

Teraz naleŜy napisać funkcję która będzie wywoływana. Wygląda ona następująco: 

__asm funkcja_asm(void) 

 

LDR R0, =0x40021018 

 

LDR R1, =0x00000008 

  

STR R1,[R0] 

 

BX lr 

 

NOP 

 

 

Działanie  poszczególnych  poleceń  asemblerowych  wykorzystanych  w  funkcji  jest  zapewne 

dobrze znana.  

background image

 

17

Polecenie BX lr dodane jest po to aby po zakończeniu operacji asemblerowych powrócić do 

miejsca skąd funkcja została wywołana.  

Polecenie NOP dodane jest w celu wyeliminowania ostrzeŜenia kompilatora. 

 

Ostatecznie cały program napisany w C z wstawką asemblerową wygląda następująco: 

 

#include "stm32f10x.h" 

 

__asm funkcja_asm(void) 

 

LDR R0, =0x40021018 

 

LDR R1, =0x00000008 

  

STR R1,[R0] 

 

BX lr 

 

NOP 

 

 

 

 

 

 

 

 

 

 

  

 

 

 

 

    

 

int main(void) 

 

unsigned int licznik=0; 

 

funkcja_asm(); 

 

GPIOB->CRH=0x33333333; 

 

    

while(1) 

 

 

   

GPIOB->ODR=0x00000000; 

 

 

for(licznik=1000000;licznik>0;licznik--);   

 

 

 

GPIOB->ODR=0x00000100; 

 

 

for(licznik=1000000;licznik>0;licznik--); 

 

 

Teraz naleŜy samodzielnie wykonać zadanie postawione na początku ćwiczenia. 

background image

 

18

NaleŜy zapalić tylko diody D1, D3, D5, D7, następnie tylko diody D2, D4, D6, D8. 

Do tego celu moŜna wykorzystać jeden z powyŜej przedstawionych programów.  

 

background image

 

19

5. Schematy elektryczne płyty bazowej 

 

background image

 

20

 

background image

 

21

6. Polecenia języka Asembler 

 

background image

 

22

 

 

 

 

background image

 

23