Generator sin

Projekty użytkowników forum zarówno sprzętowe, jak i związane z programowaniem w dowolnym języku.
Awatar użytkownika
gaweł
Geek
Geek
Posty: 1321
Rejestracja: wtorek 24 sty 2017, 22:05
Lokalizacja: Białystok

Generator sin

Postautor: gaweł » środa 21 cze 2017, 17:36

Generator sygnału sin i trójkąta
fgen1_ilu00.jpg

w pracowni elektronicznej przydatnym elementem jest generator dający na wyjściu sygnał sinusoidalny. Do zastosowań amatorskich dobrą alternatywą jest zastosowanie układów DDS (ang. Direct Digital Synthesis). Spośród szerokiej gamy tych układów został wybrany generator AD9833 będący w ofercie firmy Analog Devices. Układ ten ma możliwość generowania przebiegów o kilku kształtach. Pomimo, że głównie oczekiwane jest generowanie sygnału o przebiegu sinusoidalnym, a AD9833 pozwala na uzyskanie sygnału trójkątnego, więc finalny generator niejako w promocji generuje sygnał o przebiegu trójkątnym. Ta ekstra funkcjonalność nie wiąże się z koniecznością stosowania dodatkowych elementów, więc dodatkowe możliwości wyszły niejako gratis.
Koncepcja rozwiązania układu sprowadza się to tego, że generator ma w swoim menu możliwość wybrania określonej częstotliwości i określonego kształtu. Pozwala to na szybkie wybranie funkcji, jaką ma realizować generator. Do operowania opcjami menu służy czteroprzyciskowa klawiatura. Jednak takie rozwiązanie może być postrzegane jako „swoiste marnotrawstwo” możliwości. W rzeczywistości generator jest „full wypas” a wszystko, co nie jest dostępne z frontpanelu jest rozwiązane poprzez „zdalne sterowanie”. Sprowadza się to do tego, że generator ma zimplementowany interfejs asynchronicznej transmisji szeregowej w oparciu którego istnieje możliwość wpływania na wszelkie funkcje realizowane przez urządzenie. Sprowadza się to do tego, że do kompletu wchodzi dodatkowy program uruchamiany w komputerze PC, który poprzez komunikację via serial pozwoli na „przekroczenie horyzontu zawartego w małym pudełku”.
Rozwiązanie elektroniczne przedstawiają następujące rysunki:
Fgen1_ilu01.png
Do sterowania zastosowany został mikrokontroler ATMEGA644 (U101, ilustracja 2), do którego przyłączone są:
  • interfejs szeregowy w standardzie RS232,
  • moduł wyświetlacza LCD (2 * 16 znaków),
  • niewielka klawiatura,
  • generator DDS wraz z „obudową” do kształtowania sygnału.
Fgen1_ilu02.png
Oprócz klasycznych detali wymaganych przez środowisko mikrokontrolera, zawarty jest maleńki głośniczek (BZ101) do generowania odpowiednich beepów.
Fgen1_ilu03.png
Ilustracja 3 zawiera schemat modułu wyświetlacza z pełnym sterowaniem. Jest to typowe rozwiązanie i nie wymaga dodatkowego komentarza.
Fgen1_ilu04.png
Do sygnalizacji wybranych zdarzeń oraz stanów generatora wykorzystane jest 8 LED'ów. Zastosowanie układu CD4094 (U301, rejestr o wejściu szeregowym i wyjściu równoległym z zatrzaskiem) pozwala na redukcję liczby wyprowadzeń koniecznych do obsługi LED'ów (na 3 bitach jest zrealizowane sterowanie 8 diód, chociaż w rzeczywistości łącząc układ CD4094 kaskadowo jest możliwość sterowania większą ich liczbą).
Fgen1_ilu05.png
Ilustracja 5 pokazuje schemat interfejsu RS232, który nie wymaga komentarza.
Fgen1_ilu06.png
Zespół frontpanelu zawiera cztery przyciski oraz kilka LED'ów. Dioda D605 świeci na stałe (sygnalizuje zasilanie generator). Diody D601 .. D604 są sterowane za pośrednictwem układu CD4094 (ilustracja 4) oraz cztery diody podświetlenia przycisków klawiatury (ładne przyciski typu push button z podświetleniem).
Fgen1_ilu07.png
Tor analogowy zawiera generator DDS AD9833 (U703), który napędzany jest sygnałem zegarowym wytworzonym przez generator (U701) o częstotliwości 25MHz. Sam generator wymaga napięcia zasilającego o wartości 3.3V (co implikuje konieczność zastosowania układu U702 [jednobramkowy układ logiczny] do konwersji poziomów sygnału). Początkowy zamiar dotyczył generatora o rozdzielczości 0.1 Hz, jednak w trakcie zaistniało widzimisię do uzyskania rozdzielczości 0.01 Hz, co z kolei skutkuje użyciem licznika U708 (układ początkowo dzieli częstotliwość z generator przez 5 oraz w następnym etapie przez 2, co daje 50% wypełnienie impulsów zegarowych). Początkowo schemat nie zawierał układu U708, więc jest on „dostawką” do pierwotnej wersji PCB.
Sygnał z generatora DDS jest podany na analogową „mnożarką” U704 z możliwością dodania wartości stałej (odnosząc się do schematu z ilustracji 7, napięcie W=X1 * Y1 + Z). „Mnożnik” do sygnału z generatora oraz wartość stałego offsetu pochodzą z przetworników cyfrowo-analogowych (U705 przetwornik 12 bitowy i U706 przetwornik 10 bitowy). Oba przetworniki DAC są z interfejsem I2C.
Tu spotkała mnie niesympatyczna niespodzianka, gdyż napięcie stałego offsetu powinno być ujemne a przetwornik DAC daje napięcie dodatnie. Zaistniała konieczność „doklejenia” układu U709, który zmienia znak napięcia offsetu (w początkowej wersji nie było układu U709 i został on „domontowany” do PCB; taka pomyłka nie jest wystarczającym powodem by „wyprodukować” nową wersję PCB). Wynik mnożenia analogowego jest wzmocniony w układzie U707.
Fgen1_ilu08.png
Całość generatora zasila zespół stabilizatorów napięcia. Zastosowane są impulsowe stabilizatory napięcia:
  • U501 produkujący +5V,
  • U502 pracujący w układzie inwertera napięcia produkujący -5V,
  • U503 układ LDO dający na wyjścia 3.3V (do zasilania generatora 25MHz).
Fgen1_ilu09.jpg
Nie masz wymaganych uprawnień, aby zobaczyć pliki załączone do tego posta.

Prawdziwe słowa nie są przyjemne. Przyjemne słowa nie są prawdziwe.
Lao Tse

Awatar użytkownika
dambo
Expert
Expert
Posty: 645
Rejestracja: czwartek 17 mar 2016, 17:12

Re: Generator sin

Postautor: dambo » środa 21 cze 2017, 19:14

uwielbiam te twoje schematy blokowe. Też muszę zacząć je generować do projekcików, zawsze jakoś mi szkoda było czasu na robienie tego :/ obiecuję poprawę :)

mam pytanko odnośnie rezystorów podciągających przy danych dla LCD - sam nigdy tego nie stosowałem i nie było problemów z LCDkami - jakieś wyjaśnienie dlaczego to dodałeś?
Nowy blog o tematyce embedded -> https://www.embedownik.pl/

Awatar użytkownika
gaweł
Geek
Geek
Posty: 1321
Rejestracja: wtorek 24 sty 2017, 22:05
Lokalizacja: Białystok

Re: Generator sin

Postautor: gaweł » środa 21 cze 2017, 21:44

dambo pisze:mam pytanko odnośnie rezystorów podciągających przy danych dla LCD - sam nigdy tego nie stosowałem i nie było problemów z LCDkami - jakieś wyjaśnienie dlaczego to dodałeś?

Teraz jest to raczej kwestia przyzwyczajenia. W tym przypadku może ich nie być. Często zdarza się tak, że przykładowo szyna danych do modułu LCD jest przypięta do portu B. Nieokreślone stany na liniach sterujących potrafią aktywować moduł LCD, który jest w stanie zaburzyć przebiegi generowane przez programator i powstaje problem z programowaniem. W ten sposób linie do modułu LCD są wysterowane do logicznych jedynek. Przy obecnych kosztach rezystorów podraża to konstrukcję o kilkanaście groszy.
Zdarzało mi się w niektórych konstrukcjach "walczyć z wiatrakami" przy programowaniu (w sensie ładowania kodu do FLASH), gdzie dopiero odłączenie modułu było skuteczne, więc jest to takie "elektroniczne odpięcie modułu".
dambo pisze:uwielbiam te twoje schematy blokowe. Też muszę zacząć je generować do projekcików, zawsze jakoś mi szkoda było czasu na robienie tego :/ obiecuję poprawę :)

Zgadza się, czas jest istotnym elementem, jednak w sumie to inwestycja, która może procentować. Zauważ, że przykładowo numeracja elementów jest zawsze trzycyfrowa, gdzie pierwsza cyfra identyfikuje scheet w strukturze blokowej, więc lokalizacja jakiegoś elementu z PCB jest natychmiastowa. Poza tym, często zdarza się tak, że pewne fragmenty są w 100% identyczne, więc wklejam do projektu cały scheet z innego projektu. No i wnosi to jakiś ład i klarowność w całej dokumentacji.
Ostatnio zmieniony środa 21 cze 2017, 22:30 przez gaweł, łącznie zmieniany 1 raz.

Prawdziwe słowa nie są przyjemne. Przyjemne słowa nie są prawdziwe.
Lao Tse

Awatar użytkownika
gaweł
Geek
Geek
Posty: 1321
Rejestracja: wtorek 24 sty 2017, 22:05
Lokalizacja: Białystok

Re: Generator sin

Postautor: gaweł » środa 21 cze 2017, 22:13

Pudełko
Fotograficzna relacja z budowy.
IMG_5911.JPG
IMG_5912.JPG
IMG_5913.JPG
IMG_5914.JPG
IMG_5915.JPG
IMG_5916.JPG
IMG_5917.JPG
IMG_5918.JPG
IMG_5919.JPG
IMG_5920.JPG
IMG_5921.JPG
IMG_5922.JPG
IMG_5923.JPG
IMG_5924.JPG
IMG_5925.JPG
IMG_5926.JPG
IMG_5927.JPG
IMG_5928.JPG
IMG_5929.JPG
IMG_5930.JPG
IMG_5931.JPG
IMG_5932.JPG
IMG_5933.JPG
IMG_5934.JPG
IMG_5935.JPG
IMG_5936.JPG
IMG_5937.JPG
IMG_5938.JPG
IMG_5940.JPG
IMG_5941.JPG
IMG_5942.JPG
IMG_5943.JPG
IMG_5945.JPG
Na ściance tylnej wyprowadzone jest złącze do zasilania (zwykły zasilacz 12VDC z wtyczką power jack, złącze do serial w standardzie RS232 (DB9) oraz wyprowadzenie złącza do programowania (DB15, potrzebna będzie odpowiednia przejściówka). Różne złączki raczej nie pozwalają na pomyłki.
IMG_5944.JPG
Nie masz wymaganych uprawnień, aby zobaczyć pliki załączone do tego posta.

Prawdziwe słowa nie są przyjemne. Przyjemne słowa nie są prawdziwe.
Lao Tse

Awatar użytkownika
gaweł
Geek
Geek
Posty: 1321
Rejestracja: wtorek 24 sty 2017, 22:05
Lokalizacja: Białystok

Re: Generator sin

Postautor: gaweł » niedziela 02 lip 2017, 20:47

Generator sygnału sin i trójkąta
oprogramowanie generatora

Część związana z frontpanelem.
fgen2_ilu00.jpg


W oprogramowaniu generatora zostały wykorzystane następujące technologie:

Po zaprogramowaniu pamięci FLASH mikrokontrolera ATMEGA można cieszyć się nową zabawką (dla ułatwienia programowania, na panel tylny zostało wyprowadzone jako DB15 złącze programatora, dysponując odpowiednią przejściówką można modyfikować oprogramowanie generatora bez konieczności zdejmowania pokrywy górnej obudowy).
fgen2_ilu01.jpg
Na frontpanelu generatora znajdują się następujące elementy (istotne z punktu widzenia funkcjonalności urządzenia):
  • cztery przycisku do operowania funkcjonalnością generatora,
  • cztery diody LED (żółte) do sygnalizacji określonych stanów generatora,
  • dioda LED (zielona) jako wskaźnik zasilania,
  • moduł wyświetlacza LCD (2 wiersze po 16 znaków),
  • złącze z sygnałem wyjściowym.
Przyciski spełniają następującą funkcję:
fgen2_ilu02.jpg
  • przycisk „←” służy do wybierania poprzedniej pozycji w strukturze menu (jest przyciskiem kontekstowym), używa się go w standardowym trybie (zwyczajowe przyciśnięcie),
  • przycisk „→” służy do wybierania następnej pozycji w strukturze menu (jest przyciskiem kontekstowym), używa się go w standardowym trybie (zwyczajowe przyciśnięcie),
  • przycisk „Wyb.” służy do wybierania pozycji w strukturze menu i pełni rolę odpowiadającą klawiszowi Enter na klawiaturze (jako zwyczajowe naciśnięcie akceptuje odpowiednią opcję, jako naciśnięcie z przytrzymaniem uruchamia generowanie sygnału o parametrach wynikających z wybranej opcji w strukturze menu),
  • przycisk „Zan.” służy do zaniechania, wycofania do poprzedniej pozycji w strukturze menu i pełni rolę odpowiadającą klawiszowi ESC na klawiaturze (jako zwyczajowe naciśnięcie wraca do poprzedniego poziomu w menu, jako naciśnięcie z przytrzymaniem wyłącza generowania sygnału).
Diody LED mają następujące znaczenie:
  • dioda „Status” sygnalizuje „rytm serca”,
  • dioda „Zdalny” sygnalizuje techniczne połączenie portu szeregowego z zdalnym programem sterującym lub terminalem/emulatorem terminala,
  • dioda „Praca” sygnalizuje stan połączenia z zdalnym programem sterującym (lub terminalem/emulatorem terminala)
  • dioda „Gener.” sygnalizuje stan generowania sygnału wyjściowego.
Menu do wyboru parametrów sygnału jest zorganizowane jako menu dwupoziomowe. Na pierwszym poziome wybierana jest grupa sygnałów, na drugim poziomie wybierane są określone sygnały w obrębie danej grupy. Taki mechanizm pozwala na optymalizację czynności związanej z wyborem poszukiwanej opcji określającej żądanie generowania sygnału o odpowiednich parametrach. W obecnej wersji programu struktura menu jest następująca:
SIN pojed. Hz
   SIN 1 Hz
   SIN 2 Hz
   SIN 5 Hz
SIN dzies. Hz
   SIN 10 Hz
   SIN 20 Hz
   SIN 30 Hz
   SIN 40 Hz
   SIN 50 Hz
   SIN 60 Hz
   SIN 70 Hz
   SIN 80 Hz
   SIN 90 Hz
SIN setki Hz
   SIN 100 Hz
   SIN 200 Hz
   SIN 300 Hz
   SIN 400 Hz
   SIN 500 Hz
   SIN 600 Hz
   SIN 700 Hz
   SIN 800 Hz
   SIN 900 Hz
SIN pojed. kHz
   SIN 1 kHz
   SIN 2 kHz
   SIN 3 kHz
   SIN 4 kHz
   SIN 5 kHz
   SIN 6 kHz
   SIN 7 kHz
   SIN 8 kHz
   SIN 9 kHz
SIN dzies. kHz
   SIN 10 kHz
   SIN 20 kHz
   SIN 30 kHz
   SIN 40 kHz
   SIN 50 kHz
   SIN 60 kHz
   SIN 70 kHz
   SIN 80 kHz
   SIN 90 kHz
TROJ pojed. Hz
   TROJK. 1 Hz
   TROJK. 2 Hz
   TROJK. 5 Hz
TROJ dzies. Hz
   TROJK. 10 Hz
   TROJK. 20 Hz
   TROJK. 30 Hz
   TROJK. 40 Hz
   TROJK. 50 Hz
   TROJK. 60 Hz
   TROJK. 70 Hz
   TROJK. 80 Hz
   TROJK. 90 Hz
TROJ setki Hz
   TROJK. 100 Hz
   TROJK. 200 Hz
   TROJK. 300 Hz
   TROJK. 400 Hz
   TROJK. 500 Hz
   TROJK. 600 Hz
   TROJK. 700 Hz
   TROJK. 800 Hz
   TROJK. 900 Hz
TROJ pojed. kHz
   TROJK. 1 kHz
   TROJK. 2 kHz
   TROJK. 3 kHz
   TROJK. 4 kHz
   TROJK. 5 kHz
   TROJK. 6 kHz
   TROJK. 7 kHz
   TROJK. 8 kHz
   TROJK. 9 kHz
TROJ dzies. Khz
   TROJK. 10 kHz
   TROJK. 20 kHz
   TROJK. 30 kHz
   TROJK. 40 kHz
   TROJK. 50 kHz
   TROJK. 60 kHz
   TROJK. 70 kHz
   TROJK. 80 kHz
   TROJK. 90 kHz
Sygnaly specj.
   Nuta C1
   Nuta D1
   Nuta E1
   Nuta F1
   Nuta G1
   Nuta A1
   Nuta H1
   Nuta C2
   Harmon 7.83
   Harmon 432
   Harmon 528
   Harmon 639
   Harmon 963
Zestaw opcji menu można w prosty sposób dowolnie kształtować. Cała struktura menu znajduje się w pliku o nazwie „fgentable.c”. Bieżąca jego postać jest następująca (we fragmentach):

Kod: Zaznacz cały

static MenuElementRecType Traing_10kHzInFlash [ ] PROGMEM =
 {
   { "TROJK. 10 kHz " , ( uint32_t ) 1000000 , TriangularWaveFormat , DefaultAmplitude , DefaultDCLevel } , // 10000.00 Hz
   { "TROJK. 20 kHz " , ( uint32_t ) 2000000 , TriangularWaveFormat , DefaultAmplitude , DefaultDCLevel } , // 20000.00 Hz
   { "TROJK. 30 kHz " , ( uint32_t ) 3000000 , TriangularWaveFormat , DefaultAmplitude , DefaultDCLevel } , // 30000.00 Hz
   { "TROJK. 40 kHz " , ( uint32_t ) 4000000 , TriangularWaveFormat , DefaultAmplitude , DefaultDCLevel } , // 40000.00 Hz
   { "TROJK. 50 kHz " , ( uint32_t ) 5000000 , TriangularWaveFormat , DefaultAmplitude , DefaultDCLevel } , // 50000.00 Hz
   { "TROJK. 60 kHz " , ( uint32_t ) 6000000 , TriangularWaveFormat , DefaultAmplitude , DefaultDCLevel } , // 60000.00 Hz
   { "TROJK. 70 kHz " , ( uint32_t ) 7000000 , TriangularWaveFormat , DefaultAmplitude , DefaultDCLevel } , // 70000.00 Hz
   { "TROJK. 80 kHz " , ( uint32_t ) 8000000 , TriangularWaveFormat , DefaultAmplitude , DefaultDCLevel } , // 80000.00 Hz
   { "TROJK. 90 kHz " , ( uint32_t ) 9000000 , TriangularWaveFormat , DefaultAmplitude , DefaultDCLevel } , // 90000.00 Hz
   { EndOptions }
} ;

static MenuElementRecType SpecialOptionInFlash [ ] PROGMEM =
 {
   { "Nuta C1        " , ( uint32_t ) 26160 , SineWaveFormat , DefaultAmplitude , DefaultDCLevel } , // 261.60 Hz
   { "Nuta D1        " , ( uint32_t ) 29370 , SineWaveFormat , DefaultAmplitude , DefaultDCLevel } , // 293.70 Hz
   { "Nuta E1        " , ( uint32_t ) 32960 , SineWaveFormat , DefaultAmplitude , DefaultDCLevel } , // 329.60 Hz
   { "Nuta F1        " , ( uint32_t ) 34920 , SineWaveFormat , DefaultAmplitude , DefaultDCLevel } , // 349.20 Hz
   { "Nuta G1        " , ( uint32_t ) 39190 , SineWaveFormat , DefaultAmplitude , DefaultDCLevel } , // 391.90 Hz
   { "Nuta A1        " , ( uint32_t ) 44000 , SineWaveFormat , DefaultAmplitude , DefaultDCLevel } , // 440.00 Hz
   { "Nuta H1        " , ( uint32_t ) 49390 , SineWaveFormat , DefaultAmplitude , DefaultDCLevel } , // 493.90 Hz
   { "Nuta C2        " , ( uint32_t ) 52320 , SineWaveFormat , DefaultAmplitude , DefaultDCLevel } , // 523.20 Hz
   { "Harmon 7.83    " , ( uint32_t )   783 , SineWaveFormat , DefaultAmplitude , DefaultDCLevel } , // 7.83 Hz
   { "Harmon 432     " , ( uint32_t ) 43200 , SineWaveFormat , DefaultAmplitude , DefaultDCLevel } , // 432.00 Hz
   { "Harmon 528     " , ( uint32_t ) 52800 , SineWaveFormat , DefaultAmplitude , DefaultDCLevel } , // 528.00 Hz
   { "Harmon 639     " , ( uint32_t ) 63900 , SineWaveFormat , DefaultAmplitude , DefaultDCLevel } , // 639.00 Hz
   { "Harmon 963     " , ( uint32_t ) 96300 , SineWaveFormat , DefaultAmplitude , DefaultDCLevel } , // 963.00 Hz
   { EndOptions }
} ;


GroupOptionsRecType GroupFlashArray [ ] PROGMEM =
 {
   { "SIN pojed. Hz  " , Sin_1HzInFlash } ,
   { "SIN dzies. Hz  " , Sin_10HzInFlash } ,
   { "SIN setki Hz   " , Sin_100HzInFlash } ,
   { "SIN pojed. kHz " , Sin_1kHzInFlash } ,
   { "SIN dzies. kHz " , Sin_10kHzInFlash } ,
   { "TROJ pojed. Hz " , Traing_1HzInFlash } ,
   { "TROJ dzies. Hz " , Traing_10HzInFlash } ,
   { "TROJ setki Hz  " , Traing_100HzInFlash } ,
   { "TROJ pojed. kHz" , Traing_1kHzInFlash } ,
   { "TROJ dzies. kHz" , Traing_10kHzInFlash } ,
   { "Sygnaly specj. " , SpecialOptionInFlash } ,
   { "***************" , NIL }
 } ;
Każdy element tablicy (opcji menu pierwszego poziomu) GroupFlashArray opisuje pojedynczą opcję na poziomie wyboru grupy. Zawiera on nazwę grupy i dowiązanie do tablicy zawierającej opis poszczególnych opcji określających określone sygnały (opcje menu drugiego poziomu). Każda opcja zawiera nazwę wyświetlaną na frontpanelu, częstotliwość sygnału wyrażoną w setnych częściach Hz, kształt generowanego sygnału, jego amplitudę oraz wartość składowej stałej (przesunięcie „zera” sygnału).

Kod: Zaznacz cały

static MenuElementRecType Sin_1HzInFlash [ ] PROGMEM =
 {
   { "SIN 1 Hz      " , ( uint32_t ) 100 , SineWaveFormat , DefaultAmplitude , DefaultDCLevel } , // 1.00 Hz
   { "SIN 2 Hz      " , ( uint32_t ) 200 , SineWaveFormat , DefaultAmplitude , DefaultDCLevel } , // 2.00 Hz
   { "SIN 5 Hz      " , ( uint32_t ) 500 , SineWaveFormat , DefaultAmplitude , DefaultDCLevel } , // 5.00 Hz
   { EndOptions }
} ;
Powyższa struktura jest łatwa do modyfikacji. Dodanie pozycji w obrębie menu grupy implikuje powołanie całej tablicy opisującej sygnały należące do danej grupy.
Obsługa klawiatury posiłkuje się modułem do obsługi dowolnej małej klawiatury. Sama funkcja do sprzętowego wczytania klawiatury nie zawiera nic nadzwyczajnego.

Kod: Zaznacz cały

static uint8_t HardwareKeybRead ( void )
{
  uint8_t KeybData ;
  /*----------------------------------------------------------------*/
  KeybData = KeybPortInput ;
  return ( KeybData ) ;
} /* HardwareKeybRead */
Natomiast ze standardowej implementacji przejęta jest obsługa zapisu kodu znaku do bufora cyklicznego. Zawiera ona dodatkowe czynności występujące przy obsłudze klawiatury, do których należą: wygenerowanie sygnału dźwiękowego (beep) przy każdym naciśnięciu przycisku oraz wygenerowaniu „błysku” diody LED podświetlającej dany przycisk. Zrealizowane jest to poprzez włączenie odpowiedniej diody oraz zakolejkowania funkcji odłożonej w czasie przeznaczonej do zgaszenia diody LED. Na podobnej zasadzie zrealizowane jest generowanie dźwięku.

Kod: Zaznacz cały

static void LapmOffSerivice ( void )
{
  /*-------------------------------------------------------------------------*/
  LEDOff ( L0BacklightBitPosition ) ;
  LEDOff ( L1BacklightBitPosition ) ;
  LEDOff ( L2BacklightBitPosition ) ;
  LEDOff ( L3BacklightBitPosition ) ;
  LapmOffSeriviceHandle = NIL ;
} /* LapmOffSerivice */


static void LocalStoreKeyService ( uint8_t NewCh )
{
  /*-------------------------------------------------------------------------*/
  StoreKey ( NewCh ) ;
  KillBeep ( ) ;
  SingleToneStartBeep ( PushKeyBeepTime , PushKeyBeepTone ) ;
  switch ( NewCh )
  {
    case StandKey0Code       :
    case LongKey0Code        :
      LEDOn ( L0BacklightBitPosition ) ;
      break ;
    case StandKey1Code       :
    case LongKey1Code        :
      LEDOn ( L1BacklightBitPosition ) ;
      break ;
    case StandKey2Code       :
    case LongKey2Code        :
      LEDOn ( L2BacklightBitPosition ) ;
      break ;
    case StandKey3Code       :
    case LongKey3Code        :
      LEDOn ( L3BacklightBitPosition ) ;
      break ;
    case StandKey01Code      :
    case LongKey01Code       :
      LEDOn ( L0BacklightBitPosition ) ;
      LEDOn ( L1BacklightBitPosition ) ;
      break ;
    case StandKey02Code      :
    case LongKey02Code       :
      LEDOn ( L0BacklightBitPosition ) ;
      LEDOn ( L2BacklightBitPosition ) ;
      break ;
    case StandKey03Code      :
    case LongKey03Code       :
      LEDOn ( L0BacklightBitPosition ) ;
      LEDOn ( L3BacklightBitPosition ) ;
      break ;
    case StandKey12Code      :
    case LongKey12Code       :
      LEDOn ( L1BacklightBitPosition ) ;
      LEDOn ( L2BacklightBitPosition ) ;
      break ;
    case StandKey13Code      :
    case LongKey13Code       :
      LEDOn ( L1BacklightBitPosition ) ;
      LEDOn ( L3BacklightBitPosition ) ;
      break ;
    case StandKey23Code      :
    case LongKey23Code       :
      LEDOn ( L2BacklightBitPosition ) ;
      LEDOn ( L3BacklightBitPosition ) ;
      break ;
  } /* switch */ ;
  LapmOffSeriviceHandle = AttachTimer ( LapmOffSerivice , PushKeyBeepTime ) ;
} /* LocalStoreKeyService */
Zainicjowanie działania modułu obsługi klawiatury do wymaganej funkcjonalności zawarte jest w funkcji SoftwareInit :

Kod: Zaznacz cały

static void SoftwareInit ( void )
{
  /*-------------------------------------------------------------------------*/
(…)
  SoftKeybInit ( KeybMask , HardwareKeybRead ) ;
  SetEncodeTable ( (uint16_t ) StandEncodeTabe , EncodeTabeSize ,
                   (uint16_t ) LongEncodeTabe , EncodeTabeSize ) ;
  MountStoreKeyService ( LocalStoreKeyService ) ;
(…)
} /* SoftwareInit */
Z obsługą klawiatury związana jest zmienna będąca wskaźnikiem do odpowiedniej funkcji (metoda), której implementacja znajduje się w module generatormodule.c, generatormodule.h. Do obsługi menu na poziomie wybory grup wykorzystywana jest funkcja:

Kod: Zaznacz cały

typedef void ( * UserKeybServiceProcT ) ( uint8_t /* KeyCode */ ) ;
UserKeybServiceProcT UserKeybService ;

void GroupSelectionService ( uint8_t KeyCode )
{
  /*-------------------------------------------------------------------------*/
  switch ( KeyCode )
  {
    case StandKey0Code                  :
      if ( ! SelectPrevGroup ( ) )
      {
        SelectLastGroup ( ) ;
      } /* if */ ;
      DisplayGroup ( ) ;
      break ;
    case StandKey1Code                  :
      if ( ! SelectNextGroup ( ) )
      {
        SelectFirstGroup ( ) ;
      } /* if */ ;
      DisplayGroup ( ) ;
      break ;
    case StandKey2Code                  :
      SelectFirstOption ( ) ;
      DisplayOption ( ) ;
      SelectionContext = OptionSelectionMode ;
      UserKeybService = OptionSelectionService ;
      break ;
  } /* switch */ ;
} /* GroupSelectionService */
Klawisz „Wyb.” (Enter; wariant StandKey2Code) przenosi funkcję „konsumpcji” znaków klawiatury do funkcji wyboru opcji. Do obsługi menu na poziomie opcji, wykorzystywana jest funkcja:

Kod: Zaznacz cały

void OptionSelectionService ( uint8_t KeyCode )
{
  /*-------------------------------------------------------------------------*/
  switch ( KeyCode )
  {
    case StandKey0Code                  :
      if ( ! SelectPrevOption ( ) )
      {
        SelectLastOption ( ) ;
      } /* if */ ;
      DisplayOption ( ) ;
      break ;
    case StandKey1Code                  :
      if ( ! SelectNextOption ( ) )
      {
        SelectFirstOption ( ) ;
      } /* if */ ;
      DisplayOption ( ) ;
      break ;
    case StandKey2Code                  :
      DisplayDetails ( ) ;
      UserKeybService = DetailsSelectionService ;
      break ;
    case StandKey3Code                  :
      DisplayGroup ( ) ;
      SelectionContext = GroupSelectionMode ;
      UserKeybService = GroupSelectionService ;
      break ;
    case LongKey2Code                   :
      DisplayDetails ( ) ;
      StartWaveGeneration ( MenuElement . Frequency , MenuElement . WaveFormat ,
                            MenuElement . Amplitude , MenuElement . DCLevel ) ;
      UserKeybService = NoOptionSelectionService ;
      SelectionContext = NoSelectionMode ;
      break ;
  } /* switch */ ;
} /* OptionSelectionService */
Klawisz „Wyb.” (Enter) przenosi funkcję „konsumpcji” znaków klawiatury do funkcji przeglądania szczegółów (wariant StandKey2Code) lub do uruchomienia generowania sygnału (wariant LongKey2Code). Do obsługi menu na poziomie szczegółów wykorzystywana jest funkcja:

Kod: Zaznacz cały

void DetailsSelectionService ( uint8_t KeyCode )
{
  /*-------------------------------------------------------------------------*/
  switch ( KeyCode )
  {
    case StandKey0Code                  :
      if ( ! SelectPrevOption ( ) )
      {
        SelectLastOption ( ) ;
      } /* if */ ;
      DisplayDetails ( ) ;
      break ;
    case StandKey1Code                  :
      if ( ! SelectNextOption ( ) )
      {
        SelectFirstOption ( ) ;
      } /* if */ ;
      DisplayDetails ( ) ;
      break ;
    case StandKey2Code                  :
      break ;
    case StandKey3Code                  :
      UserKeybService = OptionSelectionService ;
      DisplayOption ( ) ;
      break ;
    case LongKey2Code                   :
      DisplayDetails ( ) ;
      StartWaveGeneration ( MenuElement . Frequency , MenuElement . WaveFormat ,
                            MenuElement . Amplitude , MenuElement . DCLevel ) ;
      UserKeybService = NoOptionSelectionService ;
      SelectionContext = NoSelectionMode ;
      break ;
  } /* switch */ ;
} /* DetailsSelectionService */
Uruchomienie generowania sygnału wyjściowego implikuje zmianę metody konsumpcji znaków klawiatury do następującej funkcji:

Kod: Zaznacz cały

void NoOptionSelectionService ( uint8_t KeyCode )
{
  /*-------------------------------------------------------------------------*/
  if ( KeyCode == LongKey3Code )
  {
    StopWaveGeneration ( ) ;
    UserKeybService = OptionSelectionService ;
    SelectionContext = OptionSelectionMode ;
    DisplayOption ( ) ;
  } /* if */ ;
} /* NoOptionSelectionService */
która reaguje jedynie na przycisk żądający zakończenia generowania sygnału wyjściowego.

Załącznik: projekt w AVRSTUDIO:
fgen-avr.zip
Nie masz wymaganych uprawnień, aby zobaczyć pliki załączone do tego posta.

Prawdziwe słowa nie są przyjemne. Przyjemne słowa nie są prawdziwe.
Lao Tse

Awatar użytkownika
gaweł
Geek
Geek
Posty: 1321
Rejestracja: wtorek 24 sty 2017, 22:05
Lokalizacja: Białystok

Re: Generator sin

Postautor: gaweł » środa 05 lip 2017, 01:31

Generator sygnału sin i trójkąta
oprogramowanie generatora


Część związana ze zdalnym sterowaniem.

Inną możliwością sterowania generatora jest przekazywanie odpowiednich poleceń wykorzystując komunikację szeregową. Oprócz typowego przyłączenia do zespołu obsługi transmisji szeregowej, w generatorze wykorzystano dwie linie modemowe, jedna wejściowa, druga wyjściowa.
Fgen3_ilu01.png

Pozwoli to na sygnalizację pewnych stanów pracy (wymaga to rzecz jasna użycia odpowiedniego kabelka, najlepiej pełnomodemowego). Uruchomienie przykładowo programu HyperTerminal jest sygnalizowane przez wysterowanie do stanu aktywnego linii modemoweych. Ten stan jest widziany przez mikrokontroler AVR. Tu należy pamiętać, że stan aktywny na liniach modemowych jest widziany jako logiczne zero. Wykrycie stanu aktywnego jest sygnalizowane włączeniem odpowiedniej diody LED. Również rozłączenie jest rozpoznawane przez mikrokontroler AVR (jako rozłączenie rozumiane jest wyjście z programu czy wręcz fizyczne rozłączenie → wyjęcie kabelka). Utrata łączności jest równoznaczna z wydaniem polecenia "DISCONNECT" (patrz: polecenia). Fragment programu (plik fgen.c) zawiera obsługę wymienionych zdarzeń.

Kod: Zaznacz cały

static uint8_t RTSLineStateChange ( void )
{
  uint8_t CurrentModelLineState ;
  /*-------------------------------------------------------------------------*/
  CurrentModelLineState = SerialPortInput & ( 1 << RTSSignal ) ;
             // linia modemowa = 1 -> stan pasywny
  if ( CurrentModelLineState )
  {
    if ( RTSActiveStateFlag )
    {
      RTSActiveStateFlag = FALSE ;
      return TRUE ;
    } /* if ... */
    else
    {
      return FALSE ;
    } /* if ... else */ ;
  } /* if ... */
  else
  {
    if ( RTSActiveStateFlag )
    {
      return FALSE ;
    } /* if ... */
    else
    {
      RTSActiveStateFlag = TRUE ;
      return TRUE ;
    } /* if ... else */ ;
  } /* if ... else */ ;
} /* RTSLineStateChange */


static void RTSControl ( void )
{
  /*-------------------------------------------------------------------------*/
  if ( RTSActiveStateFlag )
  {
    SerialPort &= ~ ( 1 << CTSSignal ) ;
    LEDOn ( OnlineSignalBitPosition ) ;
    SendSerialFlashTxt ( ( uint16_t ) HelloText ) ;
    SendSerialTxt ( GetSoftwareWersion ( ) ) ;
    SendSerial ( Cr ) ;
    SendSerial ( Lf ) ;
  } /* if ... */
  else
  {
    SerialPort |= ( 1 << CTSSignal ) ;
    LEDOff ( OnlineSignalBitPosition ) ;
    DisconnectService ( ) ;
  } /* if ... else */ ;
} /* RTSControl */

(...)

int main ( void )
{
  /*-------------------------------------------------------------------------*/

(...)

  for ( ; ; )
  {

(...)

    if ( RTSLineStateChange ( ) )
    {
      RTSControl ( ) ;
    } /* if */ ;
  } /* for */ ;
  return ( 0 ) ;
} /* main */


Istotne są zmiany stanu na linii RTS (RTS z punktu widzenia komputera PC). Wykrycie zmiany stanu pasywny→aktywny oznacza "pojawienie się" drugiej strony skłonnej do dialogu, co implikuje wysłanie odpowiedniego komunikatu powitalnego. Jednocześnie wysterowana zostaje linia CTS (CTS z punktu widzenia komputera PC). Wykrycie zmiany stanu aktywny→pasywny jest interpretowane jako rozłączenie (również ma to swoje odbicie w stanie linii CTS). Tu warto zauważyć, że brak handlingu linią RTS nie oznacza braku możliwości porozumienia a jedynie ogranicza funkcjonalność dotyczącą automatycznego rozłączenia.
Wszystkie odebrane znaki są gromadzone w buforze (jeżeli jest aktywowana funkcjonalność echa, są retransmitowane zwrotnie do nadawcy, ta możliwość jest przydatna przy "ręcznej" komunikacji z wykorzystaniem przykładowo emulatora terminala, bo widać co jest pisane; w przypadku sterowania generatora z odpowiedniego programu [w planie do napisania], to zwrotne echo jest niewskazane, gdyż zmusza program sterujący do interpretacji przychodzących znaków). Napotkanie znaku Cr jest interpretowane za zakończenie danego wiersza i przekazywane do odpowiedniej analizy (leksykalnej, syntaktycznej, semantycznej).
Fgen3_ilu02.png
Program rozpoznaje następujące polecenia (pisownia małymi lub wielkimi literami nie jest istotna):
  • echo on - oznacza aktywację funkcjonalności echa, od następnego polecenia, każdy odebrany znak jest retransmitowany do nadawcy (wartość default: nie jest aktywowana),
  • echo off - oznacza wyłączenie funkcjonalności echa,
  • list on - oznacza aktywacją raportowania napotkanych błędów analizy poleceń (wyświetlone będą uwagi do polecenia, wartość default: nie jest aktywowana),
  • list off - oznacza deaktywacją raportowania napotkanych błędów analizy poleceń,
  • hello - take "przywitanie", w odpowiedzi program wysyła do nadawcy "swoje Imię",
  • connect - polecenie "przejęcia kontroli nad generatorem", od tej chwili generator jest sterowalny jedynie poprzez polecenia z kanału szeregowego, lokalna klawiatura na frontpanelu jest nieaktywna,
  • disconnet - polecenie "oddania kontroli nad generatorem", wraca możliwość sterowania generatora poprzez klawiaturę na frontpanelu,
  • wave start shape = sin freq = <liczba> amplitude = <liczba> dclevel = <liczba> - polecenie nakazujące wygenerować przebieg sinusoidalny o częstotliwości podanej w specyfikacji freq= (<liczba> jest liczbą całkowitą wyrażoną w setnych częściach Hz →podanie freq=100 oznacza 1Hz), amplitudzie podanej w specyfikacji amplitude=, z podaniem wartości stałej przez specyfikację dclevel=, kolejność specyfikacji jest dowolna, obowiązkowe są shape= oraz freq=; wymagany kontekst connect (wdane wcześniej polecenie connect), brak specyfikacji amplitude= oraz dclevel= implikuje zastosowanie ostatnio określonych, przy poleceniu connect są określone na wartości default,
  • wave start shape = traing freq = <liczba> amplitude = <liczba> dclevel = <liczba> - jak wyżej z tym, że dotyczy przebiegu trójkątnego.
  • wave stop - oznacza wyłączenie generowania sygnału wyjściowego (wymagany kontekst connect),
  • change amplitude = <liczba> dclevel = <liczba> - oznacza zmianę wartości amplitudy i wartości stałej, kolejność specyfikacji jest nieistotna, musi wystąpić minimum jedna specyfikacja, wpływa na parametry aktualnie generowanego sygnału (jeżeli generator produkuje sygnał) oraz jest zapamiętana jako nowa wartość default,
  • get amplitude - jest to prośba o podanie aktualnej wartości parametru amplitude=,
  • get dclevel - jest to prośba o podanie aktualnej wartości parametru dclevel=,
  • get wave - jest to prośba o podanie czy aktualnie wytwarzany jest sygnał na wyjściu generatora.
Analiza poleceń jest zawarta w module commandmodule.c (który posiłkuje się analizą leksykalną).Przykładowa analiza polecenia change jest następująca:

Kod: Zaznacz cały

static void ChangeCommandAnalysis ( PtrLexicalAnalysisInstanceType LexicalAnalysisInstance )
{
  LexicSymbolType LexicSymbol ;
  SyntaxSymbolT SyntaxSymbol ;
  uint8_t AmplitudeFlag ;
  uint8_t DCLevelFlag ;
  uint16_t NewDCLevel ;
  uint16_t NewAmplitude ;
  /*-------------------------------------------------------------------------*/
  AmplitudeFlag = FALSE ;
  DCLevelFlag = FALSE ;
  NewDCLevel = CurrentDCLevel ;
  NewAmplitude = CurrentAmplitude ;
  if ( ! ConnectFlag )
    AnalError ( ConnectReqErrorCode , LexicalAnalysisInstance ) ;
  for ( ; ; )
  {
    LexicSymbol = ReadSymbol ( LexicalAnalysisInstance ) ;
    if ( ( LexicSymbol == LexicEndFileSy ) || ( LexicSymbol == LexicEndLineSy ) )
      break ;
    if ( LexicSymbol == LexicIdentSy )
    {
      SyntaxSymbol = SyntaxDecodeWord ( LexicalAnalysisInstance ) ;
      switch ( SyntaxSymbol )
      {
        case AmplitudeSy :
          if ( AmplitudeFlag )
            AnalError ( DuplicatedParamErrorCode , LexicalAnalysisInstance ) ;
          AmplitudeFlag = TRUE ;
          AcceptSymbol ( LexicalAnalysisInstance , LexicEqSy , ExpectedEQErrorCode ) ;
          AcceptSymbol ( LexicalAnalysisInstance , LexicCConstSy , ExpectedNumberErrorCode ) ;
          NewAmplitude = LexicalAnalysisInstance -> CInpValue ;
          if ( ( NewAmplitude == 0 ) || ( NewAmplitude > AmplitudeUpperBound ) )
            AnalError ( NumerParamOutOfRangeErrorCode , LexicalAnalysisInstance ) ;
          break ;
        case DCLevelSy :
          if ( DCLevelFlag )
            AnalError ( DuplicatedParamErrorCode , LexicalAnalysisInstance ) ;
          DCLevelFlag = TRUE ;
          AcceptSymbol ( LexicalAnalysisInstance , LexicEqSy , ExpectedEQErrorCode ) ;
          AcceptSymbol ( LexicalAnalysisInstance , LexicCConstSy , ExpectedNumberErrorCode ) ;
          NewDCLevel = LexicalAnalysisInstance -> CInpValue ;
          if ( ( NewDCLevel == 0 ) || ( NewDCLevel > DCLevelUpperBound ) )
            AnalError ( NumerParamOutOfRangeErrorCode , LexicalAnalysisInstance ) ;
          break ;
        default :
          AnalError ( ExpectedFreqErrorCode , LexicalAnalysisInstance ) ;
      } /* switch */ ;
    } /* if ... */
    else
    {
      AnalError ( ExpectedFreqErrorCode , LexicalAnalysisInstance ) ;
    } /* if ... else */ ;
  } /* for */ ;
  if ( ! ( AmplitudeFlag || DCLevelFlag ) )
    AnalError ( IncompleteDataErrorCode , LexicalAnalysisInstance ) ;
  if ( ! ErrorsPresent ( LexicalAnalysisInstance ) )
  {
    if ( DCLevelFlag )
    {
      CurrentDCLevel = NewDCLevel ;
      SetDCLevel ( NewDCLevel ) ;
    } /* if */ ;
    if ( AmplitudeFlag )
    {
      CurrentAmplitude = NewAmplitude ;
      SetAmplitude ( NewAmplitude ) ;
    } /* if */ ;
  } /* if */ ;
} /* ChangeCommandAnalysis */
Nie masz wymaganych uprawnień, aby zobaczyć pliki załączone do tego posta.

Prawdziwe słowa nie są przyjemne. Przyjemne słowa nie są prawdziwe.
Lao Tse


Wróć do „DIY”

Kto jest online

Użytkownicy przeglądający to forum: Obecnie na forum nie ma żadnego zarejestrowanego użytkownika i 2 gości