Do pisania używać będziemy środowiska Atollic 7.0.1 Lite. Kwestie ściągnięcia, instalacji i uruchomienia nie będą tu poruszane.
Obsługa wyświetlacza zostanie uruchomiona z wykorzystaniem bibliotek SPL. Dlaczego nie HAL? SPL wciąż cieszy się sporą popularnością, poza tym kod jest, wbrew pozorom, łatwiejszy do zrozumienia.
Poniżej linki do ebay.com do użytych w tym materiale narzędzi: (proszę zgłaszać nie działające linki)
http://www.ebay.ca/itm/STM32F103C8T6-ARM-STM32-Minimum-System-Development-Board-Module-For-Arduino-D-/112035273840?hash=item1a15d29c70:g:CioAAOSwMNxXaqSp
http://www.ebay.com/itm/IIC-I2C-0-91-128x32-white-OLED-LCD-Display-Module-3-3v-5v-For-Arduino-PIC-/152106706368?hash=item236a4425c0:g:BHQAAOSw6btXSWm3
Podstawowa konfiguracja i rozpoczęcie projektu.
Na poczatku wybieramy opcję FILE->NEW->C PROJECT
klikamy NEXT
Następnie nadajemy nazwę dla naszego projektu i wybieramy EMBEDDED C PROJECT z ramki po lewej i ATOLLIC ARM TOOLS z prawej strony
klikamy NEXT
Teraz wybieramy procek z jakim mamy do czynienia. Żeby było szybciej, górze mamy okno filtra i wpiszemy tam nazwę naszego kontrolera, czyli STM32F103c8, w tym czasie w oknie DEVICE powinien pojawić się właściwy i na niego klikamy. Automatycznie wypełnią się podstawowe dane łącznie z informacją o dostępnej ilości pamięci FLASH i RAM.
klikamy NEXT
Następna opcja to wybór biblioteki i opcje optymalizacji, niczego tu nie zmieniamy
klikamy NEXT
Wybieramy programator. Z racji, że używam J-LINK'a (w sumie jego klona) na obrazku własnie ten jest wybrany, ale są tam inne opcje i należy wybrać własciwą i odpowiednią dla nas i naszego sprzętu.
klikamy NEXT
I na koniec możemy wybrać konfiguracje z jakimi będziemy pracować, ja zawszę zostawiam jak jest, czyli opcje Debug oraz Release. Z reguły używam tylko opcji Debug, dlatego że tylko pracuję nad projektem, a nie wcielam go w życie w jakimś urządzeniu.
klikamy FINISH
Na naszym ekranie powinno pojawić się coś w ten desń
klikamy RESTORE, tam gdzie wskazuje strzałka i powinno pojawić się nam to:
Strzałkami zaznaczone jest:
1 - Outline, to możemy zminimalizować, raczej nam to nie będzie potrzebne
2 - Information Center, to polecam zamknąć, bo raczej przeglądarka ze stroną informacyjną Atollica nam się nie przyda
3 - Tu przełączamy na CONSOLE, jest to najważniejsze miejsce zawierające wszystko o wynikach naszej kompilacji, czyli błędy, ostrzeżenia
4 - Build Analyzer, .. to niestety nie będzie nam potrzebne, dlatego, że jest to opcja działająca w wersji PRO Atollica.
W edytorze pliku main.c ja z reguły pozbywam się całej wygenerowanej automatycznie zawrtości, dlatego że w większości zawiera dane debbugera i opcje dla zestawów, których nie posiadamy.
CTRL-A i DEL
plik main.c
I tu się wszystko zaczyna. Na początek zaczynamy od “inkludowania” najpotrzebniejszych plików systemowych.
Kod: Zaznacz cały
#include <string.h> //zawiera memset
#include <stm32f10x.h> // to wszystkie podstawowe (i nie tylko) "cechy" naszego mikrokontrolera
#include <ssd1306.h> // a to pliczek zawierający komendy dzięki którym możemy sterować ekranem
Teraz kolej na definicje funkcji które będziemy konstruować
Kod: Zaznacz cały
void oled_init(void); // funkcja inicjalizująca nasz ekran
void oled_cls(void); // funkcja czyszcząca
void update_screen(void); // funkcja wysyłająca zawartość bufora do sterownika ekranu
Teraz zajmiemy się utworzeniem tabeli zawierającej zestaw komend odpowiedzialnych za inicjalizację wyświetlacza. ( i tu należą się wielkie podziękowania koledze Antystatycznemu za znalezienie i optymalizację sekwencji inicjalizującej ten wyświetlacz ). Tablica jest static, by była widoczna tylko w tej jednostce kompilacji:
Kod: Zaznacz cały
static uint8_t Init_Table[]=
{
SSD1306_DISPLAYOFF,
SSD1306_SETLOWCOLUMN,
SSD1306_SETHIGHCOLUMN,
SSD1306_SETPAGESTARTADDRESS,
SSD1306_SETSTARTLINE,
SSD1306_SEGREMAP | 0x01,
SSD1306_SETCOMPINS,
0x02, /* Set com pins data. */
SSD1306_SETDISPLAYOFFSET,
0x00, /* Set display offset data. No offset. */
SSD1306_COMSCANDEC,
SSD1306_NORMALDISPLAY,
SSD1306_DISPLAYALLON_RESUME,
SSD1306_SETCONTRAST,
0x01, /* Set contrast data. */
SSD1306_MEMORYMODE,
0x00, /* Memory addressing mode data. Horizontal addressing. */
SSD1306_SETMULTIPLEX,
0x1F, /* Set MUX ratio data. 1/32 duty cycle. */
SSD1306_SETPRECHARGE,
0xF1, /* Set pre-charge period data. */
SSD1306_SETVCOMDESELECT,
0x40, /* Set V com deselect data. */
SSD1306_CHARGEPUMP,
0x14, /* Charge pump setting data. */
SSD1306_DISPLAYON,
}
Zadeklarujmy jeszcze bufor, który odzwierciedla całą pamięć graficzną naszego wyświetlaczyka. SSD1306_Width, SSD_Height to makra zdefiniowane w pliku SSD1306.h i definiują szerokość i wysokość ekranu.
Kod: Zaznacz cały
static uint8_t buffer [ SSD1306_Width * SSD_Height / 8 ];
Ekran składa się z czterech stron ułożonych poziomo. Każda strona składa się ze 128-miu bajtów. Każdy bajt strony to osiem pikseli (bitów) na ekranie ułożonych w pionie.
Jak widzimy na obrazku wpisanie do pierwszego bajtu bufora liczby 1 zaświeci nam punkt w lewym górnym rogu
a jeżeli wpiszemy 0xFF (255, czyli wypełnimy bajt jedynkami) zaświeci się nam pionowa linia po lewej stronie ekranu. Linia ta symbolizuje też wysokość pierwszej strony pamięci wyświetlacza.
----------------------------------------------------------------------------------------------
Zaczynamy z właściwym programem.
Na początek trzeba zainicjalizować piny odpowiedzialne za I2C. Dla F103 są to PB6 i PB7 - SCL,SDA. Zaczynamy więc od przypisana Definicji Typu Inicjalizacyjnego dla portów wejścia-wyjścia ogólnego przeznaczenia:
Kod: Zaznacz cały
GPIO_InitTypeDef gpio;
Aby móc korzystać z portów GPIO [ i nie tylko ] na mikrokontrolerach z rdzeniem Cortex-M3 [ i nie tylko ] koniecznie trzeba (należy?) zacząć konfigurację portu od uruchomienia sygnału zegarowego dla danego peryferium. W tym przypadku będzie to:
Kod: Zaznacz cały
RCC_APB2PeriphClockCmd (RCC_APB2Periph_GPIOB);
Kolejną rzeczą jaką musimy zrobić to “odblokować” alternatywne funkcje pinów. Jest to nam niezbędne do uruchomienia wbudowanego, sprzętowego I2C:
Kod: Zaznacz cały
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
Następnym krokiem jest uruchomienie magistrali I2C:
Kod: Zaznacz cały
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
I teraz, kiedy mamy już włączone podstawowe elementy uC, możemy skonfigurować właściwe piny. Najpierw struktura inicjalizacji GPIO (ta, którą powołaliśmy do życia chwilę wcześniej):
Kod: Zaznacz cały
GPIO_StructInit(&gpio);
Ustalmy, z którymi pinami chcemy pracować:
Kod: Zaznacz cały
gpio.GPIO_pin = GPIO_Pin_6 | GPIO_Pin_7; // SCL, SDA odpowiednio
Następnie podajemy informację o tym, że w/w piny będą pracować ze swoją alternatywną funkcją:
Kod: Zaznacz cały
gpio.GPIO_Mode = GPIO_Mode_AF_OD; // alternatywna funkcja, Open Drain
(*OD oznacza Open Drain, I2C wymaga aby jego piny pracowały w ten sposób)...to chyba kazdy wie, co? A jeśli nie wie, to RTFM
Kolejnym krokiem konfiguracji pinów jest określenie maksymalnej prędkości, z jaką mogą pracować:
Kod: Zaznacz cały
gpio.GPIO_Speed = GPIO_Speed_50MHz;
(umawiamy się na 50 MHz, czyli maksimum dla tego procesora. Nie zasilamy układu bateryjnie, więc nie musimy się zbytnio przejmować oszczędzaniem energii).
I na koniec wstępu do początku wcielimy w życie strukturę, którą właśnie skonfigurowaliśmy:
Kod: Zaznacz cały
GPIO_Init(GPIOB, &gpio);
Kolejny etap to konfiguracja I2C.
Jak przy konfiguracji pinów powołujemy do życia strukturę inicjalizacyjną magistralę.
Kod: Zaznacz cały
I2C_InitTypeDef i2c;
I2C_StructInit(&i2c);
Tak samo jak w przypadku GPIO będziemy ustawiać teraz parametry pracy I2C.
Ustalimy tutaj coś, co możemy nazwać wypełnieniem zegara. Mamy do wyboru dwie opcje 2 i 16/9. Chodzi o stosunek długości stanu niskiego do stanu wysokiego na wyjściu zegarowym szyny. Nas interesuje opcja 2, czyli stosunek stanów zegara wynosi 1:1..
Kod: Zaznacz cały
i2c.I2C_DutyCycle = I2C_DutyCycle_2;
Przypisanie adresu układu MASTER (W naszym przypadku nieistotne):
Kod: Zaznacz cały
i2c.I2C_OwnAddress1 = 0x00;
Włączenie żądania (oczekiwania) na odpowiedź od urządzenia
Kod: Zaznacz cały
i2c.I2C_Ack = I2C_Ack_Enable;
Teraz decydujemy o długości adresów układów , z którymi będziemy się komunikować. Możliwe opcje to adres 7 bitowy lub 10 bitowy. Większość układów SLAVE posiada 7 bitowy adres. Aby uniknąć pomyłki, zawsze należy zajrzeć do dokumentacji wykorzystywanego układu SLAVE.
Kod: Zaznacz cały
i2c.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
W tym miejscu decydujemy w jakim trybie będzie pracowała nasza magistrala ( do wyboru jest jeszcze SMBus_Host i SMBus_Device, ale to nas nie interesuje)
Kod: Zaznacz cały
i2c.I2C_Mode = I2C_Mode_I2C;
… oraz, co bardzo ważne, ustalamy prędkość z jaką nasze urządzenia będą się komunikować (tu też bardzo ważne jest dokładne przeczytanie Datasheeta naszego wyświetlacza, czy czegokolwiek innego, bo właśnie tam producent podaje wartości z jakimi układ może pracowac. W naszym przypadku jest również ważna errata do DS, gdzie dowiadujemy się, iż nasz oledzik pracuje poprawnie tylko z prędkością 400KHz)
Kod: Zaznacz cały
i2c.I2C_ClockSpeed = 400000;
Inicjalizujemy strukturę
Kod: Zaznacz cały
I2C_Init(I2C1, &i2c);
i włączamy “peryferium” (cudne słowo)
Kod: Zaznacz cały
I2C_Cmd(I2C1, ENABLE);
KONIEC CZĘŚCI PIERWSZEJ, NIE OSTATNIEJ.