SP2LUB - LA1BUA
SP2LUB - Poland QTH Gdynia ITU Zone 28 -CQ Zone 15 - Grid Square JO94gl
LA1BUA - Norway QTH Lye ITU Zone 18 - CQ Zone 14 - Grid Square JO28TR
Budwa transceivera CW, SSB na pasma 50MHz i 70MHz w oparciu o układy Atmega i Cypress oraz programowanie w języku C
Kilka lat temu z nudów zbudowałem sobie mały prosty transceiver pracujący wyłącznie na paśmie 50MHz. Pomysł zbudowania takiego urządzenia narodził się kiedy stałem się posiadaczem kultowego transceivera FT-1000 MK-V.
Ponieważ FT-1000 nie posiadał właśnie tego pasma, a ja już zasmakowałem w pracy na tej magicznej częstotliwości.
Transceiver który sobie zbudowałem to była klasyczna superheterodyna z podwójną przemianą częstotliwości, która pracowała wyłącznie emisjami SSB i CW z mocą około 5W doprowadzoną do anteny. Do budowy urządzenia użyłem filtrów drabinkowych które też sam poskładałem. Pierwsza przemiana 40MHz na jednym filtre czterokwarcowym i druga przemiana 8MHz na dwóch filtrach ośmiokwarcowych. Radio pracowało tylko na górnej wstędze, a jako VFO użyłem układu PLL z mieszaniem częstotliwości.
Transceiver pracował bardzo fajnie jednak przepadł mi po tym jak pożyczyłem go jednemu z kolegów który chciał sobie odwzorować to radio.
Teraz kiedy podniosłem trochę swoją wiedzę na temat programowania mikrokontrolerów, przyszedł mi pomysł zbudowania sobie nowego urządzenia o nieco poszerzonym zakresie pracy. Chcę mieć transceiver który pozwoli mi na swobodną pracę na pasmach 50MHz oraz 70MHz emisjami SSB i CW (nie mam w zamiarze używać modulacji FM).
Być może jeżeli nowa konstrukcja okaże się udana, posłużę się nim jako bazą do dobudowania innych pasm.
Na początku jednak skupiam się na na podstawowych założeniach, czyli pasma 6m i 4m w wykonaniu modułowym, co pozwoli mi na ewentualne rozbudowy.
Podstawowe założenia to:
-Podwójna przemiana częstotliwości 1 P.CZ 38MHz i 2 P.CZ 8MHz.
Syngał SSB formowany na 8MHz. / filtry kwarcowe w układzie drabinkowym z ogólnie dostępnych rezonatorów kwarcowych które można kupować tanio w dowolnych ilościach.
-Urządzenie sterowane mikrokontrolerem z rodziny Cypress Psoc CY8C5888LTI-LP
-Jako wspomagający mikroprocesor Atmega 32 lub 128 , jeszcze nie zdecydowałem i w chwili obecnej pracuję nad oprogramowaniem na wszystkie te układy. Możliwe że w wersji końcowej zastosuję wyłącznie układy Cypress jeżeli uda mi się opracować software optymalny na ten mikroprocesor.
-Jako wyświetlacz użyję HX8357B sterowany magistralą równoległą 16bit.
-jako VFO oraz generatory fali nośnej użyję popularnych układów DDS AD8951 które w prosty sposób można obsłużyć sterując za pomocą interfejsu SPI zarówno przez układy Atmega jak i Psoc z rdzeniem ARM.
Ponieważ cały czas pracuję nad prototypem transceivera oraz jego oprogramowaniem, sukcesywnie będę dzielił się postępami w pracy i przedstawiał poszczególne moduły urządzenia wraz z opisem oprogramowania.
Chcę zaznaczyć że piszę oprogramowanie wyłącznie w języku C. Biblioteki użyte do konstruowania modułów, są albo napisane w całości przeze mnie, albo są przeze mnie modyfikowane z dostępnych w sieci bibliotek udostępnianych przez użytkowników głównie w języku C++ dla Arduino.
Ponieważ nie znam języka C++ ani Arduino, biblioteki te służą mi wyłącznie jako wspomaganie dla kodów które staram się sam opracować w języku C. Nie jestem programistą dla tego też materiały zaczerpnięte z innych rozwiązań są dla mnie dużą pomocą. Każde zapożyczenie będzie opatrzone stosownym komentarzem w programie źródłowym z zawartą tam informacją o jego pochodzeniu.
AD9850 oraz AD9851 DDS - Syntezery częstotliwości do układów VFO, BFO
Jako pierwsze opiszę układy bardzo popularnych, prostych w obsłudze i niezwykle użytecznych syntezerów DDS AD9850 i AD9851. Oba układy różnią się miedzy sobą zakresem generowanych częstotliwości oraz częstotliwością generatora wzorca. Wyprowadzenia i sposób sterowania obu układów są dokładnie takie same z jedną małą różnicą w słowie konfiguracyjnym. Oprogramowanie jakie później przedstawię, będzie uniwersalne dla obu układów z zaznaczoną różnicą którą należy zmienić w przypadku użycia jednego bądź drugiego układu.
AD9850 - graniczna częstotliwość jaką według noty katalogowej może wygenerować ten układ to 50MHz, natomiast częstotliwość taktowania tego układu to 125MHz. i taki też generator 125MHz jest używany przy tym module.
AD9851 - graniczna częstotliwość jaką według noty katalogowej może wygenerować ten układ to 70MHz, natomiast częstotliwość taktowania tego układu to 180MHz. Generator częstotliwości wzorca użyty do tego układy to 30MHz i ta częstotliwość jest mnożona wewnątrz układu AD9851 razy 6 co daje częstotliwość taktowania 180MHz.
Mnożnik częstotliwości razy 6 włączany jest programowo za pomocą bitu konfiguracyjnego w ramce sterującej. Wszystko opiszę dokładniej przy okazji omawiania oprogramowania dla tego modułu. Zaznaczam że domyślnie w modułach AD9851, mnożnik zegara jest wyłączony, jeżeli o tym zapomnimy, nasz układ będzie generował znacznie niższe częstotliwości od oczekiwanych.
Dla uproszczenia i oszczędności pinów mikroprocesora sterującego układami AD985X, będziemy je sterować za pomocą magistrali SPI.
Ja dla oszczędności i ułatwienia pracy, zaopatrzyłem się w gotowe moduły z układami AD9851 które są dostępne w sprzedaży internetowej już zmontowane i gotowe do użycia. W dodatku często jest tak że zmontowany moduł z AD9851 na pokładzie , jest tańszy od samego układu scalonego. Używałem kilku modułów z różnych źródeł i nigdy nie miałem z nimi problemu. Co prawda nie kupowałem ich z najtańszych źródeł, tylko od sprawdzonych dostawców.
Moduły jakich ja użyłem wyglądają tak

Moduły przygotowane są do pracy z napięciem 5V i zaopatrzone są we wszystkie niezbędne wyprowadzenia.
podczas zabaw z tymi modułami, zauważyłem że pracują one wyżej niż graniczna częstotliwość podana w nocie katalogowej, lecz im bliżej częstotliwości granicznej, tym na sygnale pojawia się więcej pasożytów. Na pewno spowodowane jest to między innymi raczej przeciętnej jakości filtrem dolnoprzepustowym który jest umieszczony na module. W naszym przypadku jednak, moduł który zastosujemy do VFO, nie będzie pracował wyżej niż 35MHz.
Przy częstotliwości pierwszej P.CZ 38MHz, dla pracy na zakresie częstotliwości 70 do 73MHz, do mieszacza musimy dostarczyć częstotliwość 32 do 35MHz (70-38 = 32, 73-38 = 35MHz ). Zatem z powodzeniem możemy zastosować moduł DDS z układem AD9850 którego graniczna częstotliwość pracy wynosi 50MHz, jednak właśnie do VFO ja preferuję użyć AD9851 aby w razie potrzeby poszerzyć docelowy zakres pracy.... a także ze względu na mniejszą ilość "śmieci" jakie generuje układ AD9851 pracujący znacznie poniżej swojej częstotliwości granicznej.
Tańsze moduły AD9850 możemy użyć sobie w generatorach fali nośnej albo w generatorze przemiany dla 2 P.CZ
Aby nasz moduł wydał z siebie ładną sinusoidę o żądanej przez nas częstotliwości, musimy wysłać do niego poprzez interfejs SPI słowo w którym zawarte będą informacje o częstotliwości jaką chcemy uzyskać, oraz dane konfiguracyjne. Dokładne informacje można znaleźć w notach katalogowych naszych układów. Ja ograniczę się do przedstawienia skondensowanych informacji dotyczących metody obliczania częstotliwości oraz konfiguracji SPI.
Słowo które będziemy wysyłać do AD985X ma długość 40 bitów czyli 5 bajtów. Pierwsze 4 bajty zawierają informację o częstotliwości i ostatni bajt to bajt konfiguracyjny naszego DDSa. Dla poprawnej transmisji słowa po SPI, musimy ustawić interfejs w konfiguracji "LSB first".
A oto wzór jaki należy zastosować do wyliczenia wartości jaką będziemy wysyłać do DDSa by poinformować go o żądanej częstotliwości:
QRG = F * 4294967296 / zegar
gdzie QRG to żądana częstotliwość w Hz (10MHz = 10000000, 21MHz = 21000000 itd), a zegar przyjmuje wartość częstotliwości zegara taktującego nasz DDS wyrażonej też w Hz czyli 125MHz = 125000000 dla układu AD9850 albo 180MHZ = 180000000 dla układu AD9851.
zatem aby uzyskać częstotliwość w zakresie pasma dwudziestu metrów 14,250.230 MHz nasz wzór będzie wyglądał tak: QRG = 14250230 * 4294967296 / 180000000
jak widać mamy możliwość ustawiania każdej częstotliwości z dokładnością do jednego HZ.
Poniżej przedstawię kod dla mikroprocesora Atmega 32 z wykorzystaniem sprzętowego SPI
Przedstawiam zatem treść biblioteki składającej się z dwóch plików.
Plik nagłówkowy SP_AD9851.h
Plik główny SP_AD8951.c
/////////////////////////////////////////////////////////////////////SP_AD9851.h/////////////////////////////////////////////////////////////////////////
#ifndef SP_AD9851_SP_AD9851_H_
#define SP_AD9851_SP_AD9851_H_
/* konfiguracja hardware SPI do AD9851*/
#define MOSI (1<<PB3) // <---- A (SER IN) = D7
#define SCK (1<<PB5) // <---- SHIFT CLOCK (SC) = WCLK
#define LT (1<<PB2) // <---- LATCH CLOCK (LT) = FQ_UP
#define RESET (1<<PD1) // <---- Reset AD9851 (RESET)
#define RESETDIR DDRD
#define RESETPORT PORTD
// definicje makr LT, SCK, RST //
#define LT_ON PORTB |= LT
#define LT_OFF PORTB &= ~LT
#define SCK_ON PORTB |=SCK
#define SCK_OFF PORTB &= ~SCK
#define RESET_ON PORTD |= RESET
#define RESET_OFF PORTD &= ~RESET
uint32_t qrg;
//Funkcje obsługi AD9851
void InitSpi(void);
void SendSpi(uint32_t qrg);
void AD985Xinit (void);
#endif /* SP_AD9851_SP_AD9851_H_ */
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Drugi plik biblioteczny SP_AD9851.c
////////////////////////////////////////////////////////////////Teraz plik SP_AD8951.c////////////////////////////////////////////////////////////////////////
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <avr/eeprom.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include "SP_AD9851.h"
//----------------------------DEFINICJE FUNKCJI-------------------------------------------------//
// funkcja inicjalizacji sprzętowego SPI dla AD985/9851 =LSB First //
void InitSpi(void) {
DDRB |= MOSI|SCK|LT; // piny SPI jako wyjścia
SPCR |= (1<<SPE)|(1<<MSTR)|(1<<DORD); // włącz SPI i ustaw Master oraz DORD na LSB first
SPSR |= (1<<SPI2X); // fosc/2
}
//.....................................................................//
// funkcja Wysyłania słowa 40 bit do AD9851 bajt po bajcie//
void SendSpi( uint32_t qrg) {
LT_ON;
LT_OFF;
SPDR = qrg;
while( !(SPSR & (1<<SPIF)) );
SPDR = qrg>>8;
while( !(SPSR & (1<<SPIF)) );
SPDR = qrg>>16;
while( !(SPSR & (1<<SPIF)) );
SPDR = qrg>>24;
while( !(SPSR & (1<<SPIF)) );
SPDR = 0x01;
//bajt konfiguracyjny - 0x01 dla AD9851 ustawia mnożnik zegara x6 w przypadku AD9850 należy wysłac 0x00 //
while ( ! (SPSR & (1<<SPIF)) );
LT_ON;
LT_OFF;
}
//.........................................................................................
//Funkcja inicjująca uklad DDS AD9850_9851 Bezpośrednio po wywolaniu inicjalizacji należy
// kolejno wywolac funkcje sendspi nawet z ustawieniem zero.
void AD985Xinit (void) {
RESET_ON; // Reset AD9851 stanem wysokim
_delay_ms (5);
RESET_OFF;
SCK_ON;
_delay_ms (5);
SCK_OFF;
LT_ON;
_delay_ms (5);
LT_OFF;
SendSpi ( qrg ); // wyslanie zerowej qrg z bajtem konfiguracyjnym (inicjacja AD985X)
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
W dwóch powyższych plikach mamy ustawioną konfigurację dla układu Atmega32. Wykorzystuję tam sprzętowy interfejs SPI.
Jak widać z opisów w pliku nagłówkowym, moduł DDS podłączamy do mikroprocesora w następujący sposób:
#define MOSI (1<<PB3) // <---- A (SER IN) = D7
#define SCK (1<<PB5) // <---- SHIFT CLOCK (SC) = WCLK
#define LT (1<<PB2) // <---- LATCH CLOCK (LT) = FQ_UP
#define RESET (1<<PD1) // <---- Reset AD9851 (RESET)
PB3 = D7
PB5 = WCLK
PB2 = FP_UP
PD1 = RESET
Korzystając z powyższej biblioteki, możemy teraz umieścić w programie taką funkcję:
qrg = data * 4294967296 / zegar;
SendSpi ( qrg );
Teraz wpisując w miejsce "data" rządną częstotliwość w Hz, wysyłam wynik do moduły i uzyskuję na jego wyjściu zadaną częstotliwość.
Dalej już możemy sobie wykorzystać ten kod w dowolny sposób do sterowania naszymi DDSami w Transceiverze.
Kolejny moduł który użyjemy to wyświetlacz graficzny. Moduł dostępny w internecie w wielu różnych odmianach.
HX8357B 3,2'' 420x380 16bit bus
Ponieważ wyświetlacze z tym interfejsem HX8357 dostępne są w wielu różnych wykonaniach, przedstawię zdjęcia tych egzemplarzy które trafiły w moje ręce zakupione u jednego ze sprzedawców internetowych.




Ten rodzaj wyświetlacza gotowy jest do pracy z napięciem 5V, jego rozdzielczość to 420x380 a przekątna ekranu to 3,2'' , ale uwaga!! wykorzystuje on równoległy interfejs 16 bitowy czyli będziemy musieli użyć dwóch ośmiobitowych portów do sterowania tym wyświetlacze ( są dostępne te wyświetlacze także z interfejsem SPI, jednak ja mam właśnie takie z interfejsem równoległym i takiego użyję). Na jego pokładzie znajdziemy też interfejs karty SD który obsługuje się przez SPI, jednak ja nie będę się nim zajmował (przynajmniej nie na tym etapie konstruowania transceivera).
Ten wyświetlacz posłuży mi do obrazowania aktywnych ustawień urządzenia oraz częstotliwości na której Transceiver pracuje. Moja wiedza nie pozwala mi jeszcze na zbudowanie popularnej obecnie ramki monitora pasma, jednak nie wykluczam takiej możliwości w przyszłości.
Opanowanie programowe takiego wyświetlacza jest bardzo cenną nauką, ponieważ większość innych wyświetlaczy graficznych działa bardzo podobnie i nie ma już większego kłopotu żeby opanować ich obsługę.
Zaczynamy
Do wysterowania tego właśnie wyświetlacza potrzebujemy aż dwudziestu pinów z naszego mikroprocesora.
16 dla magistrali danych, oraz 4 dla sterowania kontrolera wyświetlacza.
Na zdjęciu poniżej widać mapę wyprowadzeń:


Nas interesują piny DB0 do DB15 to właśnie 16 bitów magistrali danych, oraz LCD_RS, LCD_WR, LCD_CS, LCD_RST.
Oczywiście piny 5V oraz GND aby zasilić nasz wyświetlacz. Pozostałe piny SPI które są tam widoczne, to właśnie interface do obsługi slotu kart SD.
Przykład który teraz opisuję, dotyczy sterowania tego wyświetlacza za pomocą mikroprocesora Atmega 32.
W internecie znalazłem całą masę przykładów bibliotek dla sterownika HX8357, jednak albo były przygotowane w języku C++ dla Arduino którego nie znam, albo dla interfejsu SPI którego nie ma w moim wyświetlaczu.
Najważniejsze jest przygotowanie podstawowych funkcji za pomocą których będziemy sterować tym wyświetlaczem, czyli takich funkcji sprzętowych. Resztę funkcji już tych z wyższego poziomu, do sterowania samym interfejsem graficznym, będę mógł po modyfikacji wykorzystać z istniejących w internecie bibliotek. Oczywiście niezbędne będzie ich przerobienie z C++ czy Arduino do czystego języka C którym się posługuję.
Żeby jeszcze bardziej uprościć sobie obsługę interfejsu równoległego, postanowiłem użyć dwóch pełnych portów mikrokontrolera, a dokładnie wybór padł na porty PA oraz PC. Dla sygnałów sterujących użyłem pinów z portów PD oraz PB. Oto lista połączeń jakie użyłem w moim projekcie:
TFT AVR
-------------------
D15 PA7
D14 PA6
D13 PA5
D12 PA4
D11 PA3
D10 PA2
D9 PA1
D8 PA0
-------------------
D7 PC7
D6 PC6
D5 PC5
D4 PC4
D3 PC3
D2 PC2
D1 PC1
D0 PC0
RS PD7
WR PB2
CS PB1
RST PB0
Ze względu na objętość kodu potrzebnego do obsługi HX8357B, przedstawię tutaj tylko podstawowe informacje i niezbędne funkcje aby posłużyć się naszym wyświetlaczem.
Na początek potrzebujemy skonfigurować połączenia TFT z Atmega32 oraz stworzyć niezbędne makra do obsługi sygnałów sterujących i to wszystko w pliku nagłówkowym SP_HX8357B.h:
///////////////////////////////////////////////////////////////////////SP_HX8357.h///////////////////////////////////////////////////////////////////////////
//----------------------------------------------------------------------------------------
//
// Ustawienia sprzętowe połączeń sterownika z mikrokontrolerem
//
//----------------------------------------------------------------------------------------
// tu definiuje piny procesora do których podłączam sygnały RS,WR,CS, RST
//! konfiguracja pinów sterujących LCD TFT HX8357B
#define RST (1<<PB0) //ustawiam pin RST
#define RST_PORT PORTB
#define RST_DDR DDRB
#define CS (1<<PB1) //ustawiam pin CS
#define CS_PORT PORTB
#define CS_DDR DDRB
#define WR (1<<PB2) //ustawiam pin WR
#define WR_PORT PORTB
#define WR_DDR DDRB
#define RS (1<<PD7) //ustawiam pin RS
#define RS_PORT PORTD
#define RS_DDR DDRD
// tu konfiguruje porty dla linii D0..D15 LCD muszą byc dwa pelne porty od 0 do 7
#define DATA_PORT1 PORTC // ustawiam port pierwszy D0-D7
#define DATA_PORT1_DDR DDRC
#define DATA_PORT2 PORTA // ustawiam port drugi D8-D15
#define DATA_PORT2_DDR DDRA
//------------------------------------------------ koniec ustawień sprzętowych ---------------
//definicje makr pinów sterujących wyświetlacz//
#define DEB_H PORTD |= DEB
#define DEB_L PORTD &= ~DEB
#define DEB_B PORTD ^= DEB
#define RST_H PORTB |= RST
#define RST_L PORTB &= ~RST
#define CS_H PORTB |= CS
#define CS_L PORTB &= ~CS
#define WR_H PORTB |= WR
#define WR_L PORTB &= ~WR
#define WR_STB PORTB &= ~WR; PORTB |= WR;
#define RS_H PORTD |= RS
#define RS_L PORTD &= ~RS
/////////////////////////////////////////////////////cdn. //////////////////////////////////////////////////////////////////
Do naszego wyświetlacza będziemy wysyłać dwa rodzaje informacji, komendy - Command i dane - Data.
Komendy żeby było łatwiej i jaśniej, zdefiniujemy sobie również w pliku nagłówkowy. Także dane o kolorach oraz inne dane potrzebne do konfiguracji wyświetlacza . Zaznaczam że poniższe dotyczy dokładnie tego wyświetlacza jakim się teraz zajmujemy. Dla innego modelu z tym sterownikiem HX8357 poniżesz komendy mogą się nieco różnić. To też dotyczy danych konfiguracyjnych. Jednak to co tutaj przedstawiam jest sprawdzone i działa poprawnie z wyświetlaczem który tutaj omawiam.
///////////////////////////////////////////////////////////dalszy ciąg SP_HX8357B.h////////////////////////////////
// komendy dla sterownika HX8357D
#define HX8357B 0xB
#define HX8357_TFTWIDTH 480 //320
#define HX8357_TFTHEIGHT 320 //480
#define HX8357_NOP 0x00
#define HX8357_SWRESET 0x01
#define HX8357_RDDID 0x04
#define HX8357_RDDST 0x09
#define HX8357_RDPOWMODE 0x0A
#define HX8357_RDMADCTL 0x0B
#define HX8357_RDCOLMOD 0x0C
#define HX8357_RDDIM 0x0D
#define HX8357_RDDSDR 0x0F
#define HX8357_SLPIN 0x10
#define HX8357_SLPOUT 0x11
#define HX8357B_PTLON 0x12
#define HX8357B_NORON 0x13
#define HX8357_INVOFF 0x20
#define HX8357_INVON 0x21
#define HX8357_DISPOFF 0x28
#define HX8357_DISPON 0x29
#define HX8357_CASET 0x2A
#define HX8357_PASET 0x2B
#define HX8357_RAMWR 0x2C
#define HX8357_RAMRD 0x2E
#define HX8357B_PTLAR 0x30
#define HX8357_TEON 0x35
#define HX8357_TEARLINE 0x44
#define HX8357_MADCTL 0x36
#define HX8357_COLMOD 0x3A
#define HX8357_SETOSC 0xB0
#define HX8357_SETPWR1 0xB1
#define HX8357B_SETDISPLAY 0xB2
#define HX8357_SETRGB 0xB3
#define HX8357D_SETCOM 0xB6
#define HX8357B_SETDISPMODE 0xB4
#define HX8357D_SETCYC 0xB4
#define HX8357B_SETOTP 0xB7
#define HX8357D_SETC 0xB9
#define HX8357B_SET_PANEL_DRIVING 0xC0
#define HX8357D_SETSTBA 0xC0
#define HX8357B_SETDGC 0xC1
#define HX8357B_SETID 0xC3
#define HX8357B_SETDDB 0xC4
#define HX8357B_SETDISPLAYFRAME 0xC5
#define HX8357B_GAMMASET 0xC8
#define HX8357B_SETCABC 0xC9
#define HX8357_SETPANEL 0xCC
#define HX8357B_SETPOWER 0xD0
#define HX8357B_SETVCOM 0xD1
#define HX8357B_SETPWRNORMAL 0xD2
#define HX8357B_RDID1 0xDA
#define HX8357B_RDID2 0xDB
#define HX8357B_RDID3 0xDC
#define HX8357B_RDID4 0xDD
#define HX8357D_SETGAMMA 0xE0
#define HX8357B_SETGAMMA 0xC8
#define HX8357B_SETPANELRELATED 0xE9
//definicje kolorów
#define black 0x0000
#define navy 0x000F
#define dark_green 0x03E0
#define dark_cyan 0x03EF
#define maroon 0x7800
#define purple 0x780F
#define olive 0x7BE0
#define light_grey 0xC618
#define dark_grey 0x7BEF
#define blue 0x001F
#define green 0x07E0
#define cyan 0x07FF
#define red 0xF800
#define magenta 0xF81F
#define yellow 0xFFE0
#define white 0xFFFF
#define orange 0xFD20
#define green_yellow 0xAFE5
#define brown 0x79E0
//definicje kierunków
#define MADCTL_MY 0x80
#define MADCTL_MX 0x40
#define MADCTL_MV 0x20
#define MADCTL_ML 0x10
#define MADCTL_RGB 0x00
#define MADCTL_BGR 0x08
#define MADCTL_MH 0x04
#define MADCTL_SS 0x02 // SP2LUB dodane dla HX8357X 16 bit interface równoległy
#define MADCTL_GS 0x01 // SP2LUB dodane dla HX8357X 16 bit interface równoległy
//definicje pomocnicze
#define swap(a, b) { int16_t t = a; a = b; b = t; }
//deklaracje zmiennych
uint16_t _width, _height;
extern const uint8_t font[];
extern int crsor_x, cursor_y;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Teraz czas na zadeklarowanie funkcji z których później będziemy korzystać. Ciała tych funkcji przedstawię przy okazji is definiowania w pliku bibliotecznym .c Funkcje tutaj przedstawione to fukcje najbardziej podstawowe czyli te związane z hardwarem. Te właśnie funkcje bezpośrednio współpracują ze sterownikiem wyświetlacza. One będą podstawowymi składnikami funkcji wyższego poziomu z których tylko kilka tutaj przedstawię.
///////////////////////////////////////////////////////////dalszy ciąg SP_HX8357B.h////////////////////////////////
// ### deklaracje funkcji ###
//funkcje sprzętowe
void writecommand( uint8_t dat );
void writedata( uint8_t dat );
void hx8357d_init ( void );//
void hx8357d_setAddrWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1);
void hx8357d_setRotation(uint8_t rotation);
uint16_t hx8357d_color565(uint8_t r, uint8_t g, uint8_t b); //convert three 8 bit RGB levels to a 16 bit colour value
void hx8357d_pushColor(uint16_t color);
////////////////////////////////////////////////////// END ///////////////////////////////////////////////
Dwie najbardziej podstawowe funkcje które będę odpowiedzialne za transfer informacji do wyświetlacza
Pierwsza to void writecommand( uint8_t dat ); Ta funkcja będzie służyć do wysyłania Komend które zdefiniowaliśmy sobie wcześniej w pliku nagłówkowym.
Druga to writedata( uint8_t dat ); Ta funkcja będzie służyć do wysyłania danych.
Kolejna bardzo ważna funkcja to funkcja inicjująca wyświetlacz hx8357d_init ( void );
w pliku bibliotecznym SP_HX8357B.c zawarte są ciała tych funkcji które teraz przedstawię
/////////////////////////////////////////////////SP_HX8357.c/////////////////////////////////////
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <stdlib.h>
#include <util/delay.h>
#include "SP_HX8357B.h"
//deklaracje zmiennych
int cursor_x, cursor_y;
uint16_t _width, _height;
// wysłanie komendy
void writecommand( uint8_t cmd ) {
CS_L;
RS_L;
PORTA = 0;
PORTC = cmd;
WR_STB;
RS_H;
CS_H;
}
//wysładnie danych
void writedata( uint8_t dat ) {
CS_L;
PORTA =dat>>8;
PORTC =dat;
WR_STB;
CS_H;
}
// Inicjacja wyswietlacza HX8357-B 16bit
void hx8357d_init ( void ) {
DDRA = 0xff;
DDRC = 0xff;
RST_DDR |= RST;
CS_DDR |= CS;
WR_DDR |= WR;
RS_DDR |= RS;
RST_H;
_delay_ms (50);
RST_L;
_delay_ms (10);
RST_H;
_delay_ms (10);
writecommand(0x11);
_delay_ms(120);
writecommand(0xD0); // HX8357B_SETPOWER
writedata(0x07);
writedata(0x42);
writedata(0x18);
writedata(0x00);
writecommand(0xD1); //HX8357B_SETVCOM
writedata(0x00);
writedata(0x07);
writedata(0x10);
writecommand(0xD2); //HX8357B_SETPWRNORMAL
writedata(0x01);
writedata(0x02);
writecommand(0xC0);
writedata(0x10);
writedata(0x3B);
writedata(0x00);
writedata(0x02);
writedata(0x11);
writecommand(0xC5);
writedata(0x08);
writecommand(0xC8);
writedata(0x00);
writedata(0x32);
writedata(0x36);
writedata(0x45);
writedata(0x06);
writedata(0x16);
writedata(0x37);
writedata(0x75);
writedata(0x77);
writedata(0x54);
writedata(0x0C);
writedata(0x00);
writecommand(0x36); // HX8357_MADCTL
writedata(0x0a);
writecommand(0x3A); // Interface pixel format
writedata(0x55); // 16 bits per pixel
writecommand(0x2A); // HX8357_CASET
writedata(0x00);
writedata(0x00);
writedata(0x01);
writedata(0x3F);
writecommand(0x2B); // HX8357_PASET
writedata(0x00);
writedata(0x00);
writedata(0x01);
writedata(0xDF);
_delay_ms(120);
writecommand(0x29);
_delay_ms(25);
CS_L;
}
///////////////////////////////////////////////////////////////////////////////////cdn./////////////////////////////////////////////////////////////
Właściwie to co przedstawiłem powyżej już wystarczy żeby podłączyć i zainicjować wyświetlacz z Atmega32.
Potrzebne są jeszcze funkcje wyższego poziomu, czyli te które pozwolą nam na czyszczenie ekranu, ryzowanie figur geometrycznych, wysyłanie znaków i stringów... Te funkcje można w dość prosty sposób przerobić z bibliotek napisanych dla tego wyświetlacza na platformę Ardunio. Pisane są one w języku C++ ale po odrobinie wysiłku można je przerobić opierając się na bazowych funkcjach które powyżej przestawiłem. Jeżeli ktoś jest zainteresowany uzyskaniem więcej informacji o przedstawionej bibliotece, proszę o email na sp2lub@gmail.com