Popularny post lukpep Napisano Lipiec 24, 2011 Popularny post Udostępnij Napisano Lipiec 24, 2011 » Sprawdź nowy kurs STM32 » Witam,Przede wszystkim to przepraszam za opoznienia - przeprowadzka, zmiana pracy itp wplynely na terminy publikacji kolejnej czesci kursu. Postaram sie to nadrobic teraz prezentujac od razu w miare spory projekt laczacy w sobie kilka podstawowych rzeczy takich jak obsluga portow IO, ADC, kontroler przerwan oraz korzystanie z wyswietlacza. Zalozenia sa takie - prosta aplikacja w postaci wielopoziomwe menu, z ktorego wybieramy powiedzmy monitoring wejsc cyfrowych i na ekranie mamy podglad wszystkich we oraz ich logiczne stany. Inna pozycja to ustawianie wyjsc - mamy mozliwosc ustawienia 1 lub 0 na konkretnym wyjsciu 'sterownika'. W kolejnym menu mozemy podejrzec stany wejsc analogowych (ADC) itp itd.Efektem ma byc cos w postaci: Z tego co pamietam obiecalem kilka slow poswiecic bibliotece CMSIS - wiec o co z nia chodzi?O ile bezposrednie operacje na rejestrach w prockach 8bitowych sa jeszcze okej to przy procesorach 32bit staja sie odrobine problematyczne. Rzecz zdaje sie banalne takie jak powiedzmy ustawienie odpowiedniego trybu pracy przetwornika czy timerow to czesto kilkanascie linijek bezposrednich wrzutow do rejestow. Fajnie - napiszemy to raz a powiedzmy za miesiac po powrocie do programu bedzie trzeba zachodzic w glowe / sprawdzac w dokumentacji za co odpowiadaj konkretne rejestry oraz, ze np. 0x080 to bedzie pomiar pojedynczy przetwornikaW zwiazku z powyzszym wygodnie jest wykorzystywac predefiniowane funkcje operujace na zespolach wielu rejestrow za nas.przyklad ustawienia pinow 6, 7, 8 i 9 portu C jako wyjsc: RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOC, &GPIO_InitStruct); Prawda ze czytelny? Od razu widac ktory port i ktore piny na nim odpalamy, widac ze ustawiamy je jako wyjscia z podciagnieciem (GPIO_Mode_Out_PP), z jaka max predkoscia moga pracowac i inicjujemy calosc.Korzystanie z biblioteki rzecz jasna nie wyklucza bezposrednich operacji na rejestrach - mozna to mieszac a nawet nalezy - dla przykladu powiedzmy korzystamy z jednego z timerow w trybie PWM no i w czasie dzialania programu zachodzi koniecznosc zmiany wspolczynnika wypelnienia (czyli w praktyce wartosc do ktorej ma zliczac timer) - jasne ze mozna od poczatku przygotowac cala strukturke i zainicjowac timer od poczatku ale po co? Skoro mozna zmodyfikowac tylko jeden rejestr co jest szybsze? Analigicznie z przykladem powyzej - w celu zmiany np. predkosci portu mozna by przygotowac cala strukture od nowa i pchnac ja do inicjalizacji lub tez zmienic tylko rejestr odpowiadajacy za predkosci.W tym miejscu chcialbym powiedziec dwie wazne rzeczy zwiazane z biblioteka i samym prockiem - w odroznieniu od powiedzmy AVR w STM wszystkie peryferia zanim zaczniemy z nich korzystac wymagaja... wlaczenia zasilania 😃 W celu energooszczednosci standardowo po stacie procek ma wylaczone zasilania na praktycznie wszystkim co sie da - liniach GPIO, kontrolerze przerwan itp - o tym trzeba pamietac - odbywa sie (nadal korzystajac z powyzszego przykladu) w linijce - RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); RCC_APB2PeriphClockCmd jest funkcja odpowiadajaca za wlaczenie zegarow taktujacych dla peryferii siedzacych na APB2 (jedna z dwoch wewnetrznych magistral na ktorej dzialaja wszystkie wbudowane w rdzen peryferia) RCC_APB2Periph_GPIOC informuje z kolei co wlaczamy - GPIOC czyl port C. Druga sprawa - tym razem zwiazana z biblioteka - wszystkie peryfieria, interfejsy komunikacyjne itp inicjuje sie w sposob bardzo podobny - deklaruje sie pewna strukture, wypelnia ja danym oraz przekazuje do funkcji inicjujacej, ktora to pooperuje sobie za nas na wszystkich niezbednych rejestrach danego urzadzenia wewnetrznego i odpali sprzet.wyglada to nastepujaco - deklaracja tej struktury o nazwie powiedzmy GPIO_strukturka GPIO_InitTypeDef GPIO_strukturka; wypelnienie jej niezbednymi danymi: GPIO_strukturka.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9; GPIO_strukturka.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_strukturka.GPIO_Speed = GPIO_Speed_50MHz; i przekazanie do funkcji inicjujacej: GPIO_Init(GPIOC, &GPIO_strukturka); Oczywiscie nalezy pamietac tez o wlaczeniu zasilania na danym perfyferium no ale nie jest to czesc struktury wiec pominalem w tym akapicie.W celu korzystania z tych jakze wygodnych funkcji bibliotecznych niezbedne jest podpiecie (includowanie) odpowiednich plikow z biblioteki za nie odpowiedzialnych i tak dla przykladu funkcje odpowiedzialne za porty GPIO znajduja sie w pliku stm32f10x_gpio.c (includujemy rzecz jasna naglowek wiec .h zamiast .c), te od timerow w stm32f10x_tim.c a od ADC w stm32f10x_adc.c.Generalnie struktura calej biblioteki wyglada nastepujaco: czyli mamy 2 katalogi \STM32F10x_StdPeriph_Driver oraz \CMSIS. W CMSIS mamy rzeczy bardziej 'wewnetrzne' jak pliki startowe procesora, systemowe itp. Drugi katalog zawiera z zrodla do funkcji operujacych na peryferiach i z nich czesto bedziemy korzystac.Wazne sa rowniez 2 pliki tworzone z nowym projektem w poprawnie skonfigurowanym toolchainie - stm32f10x_conf.h oraz stm32f10x_it.c. W pierwszym pliku nagłówkowym włączamy lub wyłączamy (za pomocą komentarzy) dołączanie do projektu plików nagłówkowych z modułu STM32F10x_StdPeriph_Driver czyli wybieramy w sumie moduly, z ktorymi bedziemy dzialac, drugi z kolei odpowiada za obsluge przerwan - w nim zamieszczone beda wszystkie funkcje wywolywane po wystapieniu odpowiednich zdarzen - czyli np. przerwania zew. na odpowiedniej linii itp, przepelnienia timera, skonczenia przetwarzania pomiaru ADC i tak dalej.Mam nadzieje ze ten przydlugi wstep w miare rozjasnil sprawy zwiazane z biblioteka CMSIS i jej zastosowaniami. Oczywiscie rozwiazanie to nie posiada samych plusow - trzeba sobie zdawac sprawe ze funkcje biblioteczne czesto sa no delikatnie mowiac 'srednio' optymalne - zdarza sie ze pelne sa tworow w postaci if (warunek) (instrukcja) else if(warunek) (instrukcja) itp. Ale na poczatek mysle ze wystarczaja no i sam nie jestem jakims wymiataczem wiec poki co uzywam biblioteki.No to do samego projektu - czego bedziemy potrzebowali? Pewnych klockow z ktorych posklejamy calosc.mamy sprawdzac stany wejsc cyfrowych, ustawiac wyjscia cyfrowe i korzystac z ADC, potrzebny jest tez pomysl na menu oraz nawigacje po nim - z racji uzycia zestawu ZL29ARM proponowalbym joystick, warto by bylo zeby nacisniecia przyciskow byly obslugiwane jako zdarzenia zewnetrzne i wywolywalyby przerwania zamiast marnotrwaic czas procka na ciagle sprawdzanie czy cos zostalo wcisniete - podsumowujac bedzie trzeba uzyc kontrolera przerwan. Calosc bedzie prezentowana na ekranie, ktory trzeba by obsluzyc (tu akurat mam gotowa biblioteke do odpowiedniego sterownika wiec sie nie przepracujemy ). I jeszcze jedno slowo wstepu o konwencji w jakiej bede pisal - poszczegolne peryferia i ich konfiguracje (lacznie z opcjami ktorych my nie uzywamy) postaram sie opisywac na biezaco przy pisaniu poszczegolnych funkcji na nich operujacych, nie bedzie wiec przynudzania zanim w ogole napiszemy linijke koduTo od czego zaczynamy? Tworzymy nowy projekt, wybieramy odpowiedni procesor z listy dostepnych ukladow (dzieki czemu np. odpowiednie pliki startowe zostana podpiete do projektu) i jedziemy!Na poczatek proponowalbym rozkrecic zegary naszego zestawu do jego max. wartosci czyli 72Mhz - jako, ze jest to cos co bedzie sie powtarzac w roznych projektach proponowalbym napisanie tego w postaci oddzielnej funkcji / procedurki tak aby mozna to sobie wrzucic w jakas wlasna biblioteke i uzywac w innych projektach.u mnie tak funkcja wyglada nastepujaco: void konf_zegary(void) { ErrorStatus HSEStartUpStatus; // Reset ustawien RCC RCC_DeInit(); // Wlacz HSE RCC_HSEConfig(RCC_HSE_ON); // Czekaj za HSE bedzie gotowy HSEStartUpStatus = RCC_WaitForHSEStartUp(); if(HSEStartUpStatus == SUCCESS) { FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable); // zwloka dla pamieci Flash FLASH_SetLatency(FLASH_Latency_2); // HCLK = SYSCLK RCC_HCLKConfig(RCC_SYSCLK_Div1); // PCLK2 = HCLK RCC_PCLK2Config(RCC_HCLK_Div1); // PCLK1 = HCLK/2 RCC_PCLK1Config(RCC_HCLK_Div2); // PLLCLK = 8MHz * 9 = 72 MHz RCC_PLLConfig(RCC_PLLSource_PREDIV1, RCC_PLLMul_9); // Wlacz PLL RCC_PLLCmd(ENABLE); // Czekaj az PLL poprawnie sie uruchomi while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET); // PLL bedzie zrodlem sygnalu zegarowego RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); // Czekaj az PLL bedzie sygnalem zegarowym systemu while(RCC_GetSYSCLKSource() != 0x08); } } W STM32 mozemy wybierac sposrod kilku zrodel sygnalu zegarowego - HSE (high speed external) czyli szybkie zew. zrodlo - np kwarc, PLL - wewnetrzna petla powielacza czestotliwosci (wpuszczamy pewien takt do petli i 'wyjmujemy' ten sam takt o zwielokrotnionej czestotliwosci), HSI (gigh speed internal) - wewnetrzny generator niskiej czestotliwosci (8 Mhz). Powyzsze zrodla moga taktowac caly system, sa tez 2 kolejne o wezszych zastosowaniach - LSI (low speed internal) - 40 kHz - nadaje sie do taktowania watchodoga i RTC oraz LSE (low speed external) czyli kwarc zegarowy nadajacy sie tylko do taktowania ukladu RTC. Mnogosc sygnalow zegarowych pozwala dostosowac aplikacje pod katem wydajnosci oraz zuzycia energii.Jak bedzie u nas? Otoz odpalimy sobie kwarc 8Mhz znajdujacy sie na plytce testowej (HSE), wpuscimy go na petle powielacza PLL oraz wypuscimy sliczny sygnal 72 Mhz. Wew. generator RC - HSI rowniez ma 8 MHz oraz rowniez mozna go uzyc jako zrodla dla petli PLL. Czemu wiec nie korzystamy z niego? Otoz w porownaniu z zew. rezonatorami kwarcowymi ma on daremna dokladnosc i stabilnosc. Dobrze wiec - analizujmy nasza funkcje. Standardowo jak procesor dostaje zasilanie to zrodlem sygnalu zegarowego jest zawsze HSI. Skoro mamy wiec skorzystac z HSE to trzeba by je wlaczyc. RCC_DeInit(); - resetujemy ustawienia zegarow RCC_HSEConfig(RCC_HSE_ON); - wlaczamy HSE HSE wymaga chwili czasu zeby stac sie stabilnym - w zwiazku z tym czekamy chwile: HSEStartUpStatus = RCC_WaitForHSEStartUp(); if(HSEStartUpStatus == SUCCESS) W razie gdyby nie udalo sie odpalic HSE zrodlem sygnalu pozostanie HSI.W nastepnyh liniach trzeba ustawic prace pamieci Flash - wlaczamy jej bufor oraz ustalamy opoznienie w stosunku do zegara systemowego powniewaz pamiec Flash nie jest w stanie pracowac z zegarami > 24 MHz.W zwiazku z tym gdy nasz system pracowac ma z predkoscia do 24 MHz opoznienie nie jest wymagane, gdy jednak jestesmy w przedziale 24-48 MHz wprowadzamy opoznienie jednego cyklu, powyzej 48 MHz 2 cykle. Z tego co pamietam w nowej serii prockow F2xx zastosowano juz nowy kontrole pamieci Flash i nie jest wymagane stosowanie opoznien.Kolejna linijka: RCC_HCLKConfig(RCC_SYSCLK_Div1); ustala dzielnik zegaru sygnalowego w stosunku do zegaru rdzenia. Powiedzmy ze chcielibysmy taktowac rdzen mikroprocesora (HCLK) z inna predkoscia niz jego peryferia - moglibysmy wtedy zastosowac tutaj dzielnik. W naszym przypadku dzielnik wynosi 1 - stad HCLK = SYSCLK.Kolejne linie konfiguruja zegary dla dwoch magistrali systemowych: RCC_PCLK2Config(RCC_HCLK_Div1); RCC_PCLK1Config(RCC_HCLK_Div2); Otoz wszystkie peryferia podlaczone sa do 2 wewnetrznych magistral procesora - APB1 oraz APB2, ktorym odpowiadaja sygnaly zegarowe PCLK1 oraz PCLK2.Magistrala APB1 pracowac moze max. z predkoscia 36MHz stad tez dzielnik 2 (docelowo HCLK ma byc rozkrecony do 72 MHz), magistrala APB2 moze juz pracowac z max. 72MHz stad tez zostajemy z dzielnikiem 1. Oba sygnaly wytwarzane sa z taktu rdzenia HCLK a nie z SYSCLK o czym nalezy pamietac w przypadku gdyby te dwa sie od siebie roznily. U nas HCLK = SYSCLK wiec tego problemy nie ma. Warto jeszcze wspomniec co jest podlaczone do ktorych magistral:APB1 (max 36 MHz) - IWDG (watchdog), TIM2, TIM3, TIM4 (timery), USART2, USART3, SPI2, I2C1, I2C2, CAN, USB 2.0, SRAM, WWDG (watchdog)APB2 (max 72 MHz) - EXTI (przerwania zew), GPIOA-GPIOE, TIM1, SPI1, USART1, ADCCo dalej wiec? Trzeba by ustawic oraz wlaczyc petle powielacza PLL. RCC_PLLConfig(RCC_PLLSource_PREDIV1, RCC_PLLMul_9); RCC_PLLCmd(ENABLE); while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET); RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); while(RCC_GetSYSCLKSource() != 0x08); przed wlaczeniem PLL niezbedna jest konfiguracja - wybranie zrodla sygnalu oraz mnoznika, Wiec ustawiamy jako zrodlo sygnalu petli dzielnik PREDIV1... co jak? Czemu nie HSE? Otoz w lini Conectivity Line nie ma mozliwosci skorzystania bezposrednio z HSE jako zrodla dla petli PLL. Mozna uzyc tylko: RCC_PLLSource,: specifies the PLL entry clock source. For STM32_Connectivity_line_devices or STM32_Value_line_devices, this parameter can be one of the following values: RCC_PLLSource_HSI_Div2: HSI oscillator clock divided by 2 selected as PLL clock entry RCC_PLLSource_PREDIV1: PREDIV1 clock selected as PLL clock entry cytato z dokumentacji ARM based STM32f10xxx standard peripherals libraryZeby lepiej zrozumiec mechanizmy zegarow (wiem ze sa troszke zakrecone) mozna przeanalizowac: Tak wiec Prediv w standardowych ustawieniach kopiuje zegar z HSE. Nastepnie ustawiamy mnoznik na 9 i juz jest upragronie 72 MHz na wyjsciu petli PLL.Nastepnym krokiem jest ustawienie zrodla SYSCLK jako wyjscia petli PLL i poczekanie az sie calosc ustabilizuje. Tadam - i juz 😉Na aktualnym etapie oprocz wyzej omowionej funkcji powinnismy miec rowniez incudy: #include <stm32f10x_rcc.h> #include "stm32f10x_flash.h" #include "misc.h" aby korzystac z predefiniowanych funkcji RCC, flash i systemowych.oraz funkcje main: int main(void) { konf_zegary(); while (1) { } } Wyszlo dosyc obszernie jak tak patrze na tekst - w zwiazku z czym dzisiaj dopiszemy jeszcze tylko konfig diod LED oraz makra je zapalajace, gaszace jak rowniez prosta funkcje opozniajaca. Czyli pierwszy program to bedzie mryganie diodkami 😉 co rzecz jasna bedzie fragmentem naszego wiekszego programu z menu i sterownikiem.Konfig LED ( i przy okazji buzzera): void konf_led_buzz(void) { GPIO_InitTypeDef GPIO_InitStruct; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE); GPIO_StructInit(&GPIO_InitStruct); GPIO_InitStruct.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz; GPIO_Init(GPIOE, &GPIO_InitStruct); GPIO_WriteBit(GPIOE, GPIO_Pin_14 | GPIO_Pin_15, Bit_SET); } po kolei: GPIO_InitTypeDef GPIO_InitStruct; - strukturka inicjalizacji o nazwie GPIO_InitStruct RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE); - funkcja RCC_APB2PeriphClockCmd jak sama nazwa wskazuje dotyczy peryferii umieszczonych na magistrali APB2 - czyli miedzy innymi wszystkich portow GPIO procka.Diody led i buzzer podlaczone sa kolejno do wyprowadzen 14, 15 i 13 (buzz). W zwiazku z tym jako argument funkcji RCC_APB2PeriphClockCmd podajemy RCC_APB2Periph_GPIOE jako ze sprawa dotyczy GPIOE oraz enable jako ze chcemy cale to ustrojstwo wlaczyc. Wynika to jak wczesniej juz wspominalem z faktu ze wszystkie peryferia wymagaja najpierw wlaczenia zegarow taktujacych do poprawnego dzialania. GPIO_InitStruct.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15; - nie wymaga chyba wyjasnienia - wypelniamy odpowiednie pole struktury numerami portow ktorych dotyczyc bedzie konfiguracja GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP; - tutaj wybieramy tryb pracy danych portow - GPIO_Mode_Out_PP oznacza wyjscie z podciagnieciem przez rezystor. Inne tryby: GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz; - predkosc, max z jaka moga pracowac porty w tym procku to 50 MHz (w serii F2xxx podciagneli do 72 MHz). Inne dostepne opcje to 2 oraz 10. Nie mozna wiec ustalic np. 15. Tylko 2, 10 i 50. GPIO_Init(GPIOE, &GPIO_InitStruct); - inicjalizujemy calosc GPIO_WriteBit(GPIOE, GPIO_Pin_14 | GPIO_Pin_15, Bit_SET); - ustawiamy wyjscia jako 1 czyli zgaszone diody (ze schematu wynika ze LEDy podciagniete sa do zasilania wiec logiczne 0 spowoduje zapalenie.Trzeba dodac nastepnego includa: #include <stm32f10x_gpio.h> poniewaz korzystamy z funkcji dotyczacych portow GPIO.co dalej? Proste makra zapalajace, gaszace i invertujace stan diody: //////////////////////////////// makra led1 i led2 /////////////////////////////// #define LED1_ON GPIO_ResetBits(GPIOE, GPIO_Pin_15) #define LED1_OFF GPIO_SetBits(GPIOE, GPIO_Pin_15) #define LED1_INV ((GPIOE->ODR & GPIO_Pin_15)?(LED1_ON):(LED1_OFF)) #define LED2_ON GPIO_ResetBits(GPIOE, GPIO_Pin_14) #define LED2_OFF GPIO_SetBits(GPIOE, GPIO_Pin_14) #define LED2_INV ((GPIOE->ODR & GPIO_Pin_14)?(LED2_ON):(LED2_OFF)) Funkcje GPIO_ResetBits, GPIO_SetBits oraz GPIO_WriteBit uzyte do tej pory chyba nie wymagaja wyjasnien? Podajemy ktory port, jakie piny i ewentualnie co wpisac.Same makra umieszamy pod includami.I na koniec funkcja opozniajaca: void Delay(volatile unsigned count) { while(count--); } ktora po prostu czeka pewna podana ilosc taktow procesora - malo eleganckie ale jakze przydatne 😉no to teraz tylko modyfikujemy petle main programu do postaci: int main(void) { konf_zegary(); konf_led_buzz(); while (1) { LED1_ON; Delay(1000000); LED2_ON; Delay(1000000); LED1_OFF; Delay(1000000); LED2_OFF; Delay(1000000); } } kompilujemy i wrzucamy na procesor i otrzymujemy: W filmie wystepuje rowniez moja bosa noga - pozdrawia Was 😉To by bylo raczej na tyle jezeli chodzi o czesc pierwsza - w razie co zalaczam rowniez pelne zrodlo tego projektu do postu. Raz jeszcze przepraszam za opoznienia, literowki, orty, zbyt kolokwialny jezyk i generalna prostote umyslowa 😉W razie co pytac - chetnie odpowiem na wszystkie pytania.Trwaja prace nad kolejna czescia kursu gdzie omowimy przerwania zew. oraz przetwornik ADC, albo moze LCD? Sie zobaczy 😉Pozdrawiam [ Dodano: 24-07-2011, 19:19 ]aa - kompilator daje warninga o typ w funkcji Delay - wystarczy dodac w sekcji deklaracji funkcji wpis: void Delay(volatile unsigned); i juz nie ma warninga 😉 main.c 3 Cytuj Link do komentarza Share on other sites More sharing options...
kling Lipiec 24, 2011 Udostępnij Lipiec 24, 2011 Nareszcie:) Naprawdę zapowiada się ciekawie. Na początek mam kilka pytań: 1. Mógłbyś coś więcej napisać o bibliotekach CMSIS i StdPerph. Z tego co wiem jest sporo różnic i jedynie te drugie są znienawidzone;) 2. Co należy rozumieć przez 'prędkość' wyjścia? Chodzi tylko o max częstotliwość zmiany stanu wyjścia? Kolejne pytania pojawią się pewnie w czasie walki z stm'em. Myślę, że fajnie by było jak byś dodał alternatywą wersję, pisząc nie używając bibliotek;) Dzięki za dobry kurs! Oby tak dalej! Cytuj Link do komentarza Share on other sites More sharing options...
lukpep Lipiec 25, 2011 Autor tematu Udostępnij Lipiec 25, 2011 kling, 2. Owszem - zmieniajac predkosc zmieniamy czasy narastania i opadania zboczy sygnalu - ma to duzy wplyw na powstajace zaklocenia elektromagnetyczne oraz dopuszczalne pojemnosci obciazenia. 50 MHz - 30 pF 10 Mhz - 50 pF 2 MHz - 50 pF Co do praktyki to nie za bardzo da rade osiagnac takie wartosci. Korzystajac z biblioteki czy tez nawet operacji o dostepie atomowym i ASM nie osiagniemy 50 MHz na wyjsciu... (kazda instrukcja to 1 a czasem wiecej taktow rdzenia). Predkosci te to raczej dla sprzetowych peryferii. Tu odpowiedni watek - http://www.elektroda.pl/rtvforum/topic2037447.html Co do pytania 1 to odpowiem pozniej - po pracy 😉 Cytuj Link do komentarza Share on other sites More sharing options...
Arty Lipiec 30, 2011 Udostępnij Lipiec 30, 2011 lukpep, Witam,na początek chciałbym pogratulować kursu. Ale mam parę pytań jako, że pomimo tego iż przedzieram się przez literaturę podobną, to kolega jest dużo bardziej zaawansowany. Korzystam (i to zupełnym przypadkiem) z tej samej płytki, tego samego środowiska i tego samego programatora, a przedzieram się przez książkę w której autor korzysta z płytką ZL27ARM z prockiem STM32F103VBT6 zamiast tego jaki jest na ZL29ARM (STM32F107VCT6) i są pewne rozbieżności w funkcjach bibliotecznych, choćby w ustawieniu taktowania rdzenia, ale to już przebrnąłem. Problem mój polega na tym że na tym że na ZL29ARM mamy kwarca 10MHz a nie 8MHz, a maksymalna częstotliwość rdzenia w dalszym ciągu jest 72MHz, dlatego RCC_PLLConfig(RCC_PLLSource_PREDIV1, RCC_PLLMul_9) teoretycznie nie powinno zadziałać bo daje to 90MHz, ale ku mojemu zdziwieniu działa. Nie wiem tylko czy jest to poprawne i rzeczywiście daje 90MHz, próbowałem ustawiać też RCC_PLLConfig(RCC_PLLSource_PREDIV1, RCC_PLLMul_7) co teoretycznie powinno dać 70MHz ale ponieważ ugrzązłem w mnogości ustawień jakie daje system_stm32f10x.c to nie wiem czy częstotliwość żądana 70MHz jest rzeczywiście ustawiona i co gorsza nie wiem jak to sprawdzić. Problem ten dla mnie jest o tyle poważny że muszę stworzyć aplikację która równo co 500ms będzie pobierać dane z przetwornika i zapisywać pomiary z znacznikiem czasowym, a jeżeli nie wiem jaką mam częstotliwość to nie ma możliwości poprawnie ustawić timerów i wszystko się rozjeżdża. Mam nadzieję że kolega pomoże mi rozwiązać problem. Cytuj Link do komentarza Share on other sites More sharing options...
Polecacz 101 Zarejestruj się lub zaloguj, aby ukryć tę reklamę. Zarejestruj się lub zaloguj, aby ukryć tę reklamę. Produkcja i montaż PCB - wybierz sprawdzone PCBWay! • Darmowe płytki dla studentów i projektów non-profit • Tylko 5$ za 10 prototypów PCB w 24 godziny • Usługa projektowania PCB na zlecenie • Montaż PCB od 30$ + bezpłatna dostawa i szablony • Darmowe narzędzie do podglądu plików Gerber Zobacz również » Film z fabryki PCBWay
danioto Lipiec 31, 2011 Udostępnij Lipiec 31, 2011 Nie wiem dokładnie jak to jest z wyższymi częstotliwościami, lecz jeśli potrzebujesz dokładnie 500 ms, to Ci aż tak wielkich częstotliwości nie potrzeba. Może pomoże Ci mój kod: http://wklej.to/bVwTe Przerwanie od Timera co 2 us i na tej podstawie mam funkcje opóźniające. Mam również diodkę zmieniającą stan co 1s podpiętą do pinu 1 portu B. Może akurat Ci się to przyda. Pozdrawiam! Daniel 🙂 Cytuj Link do komentarza Share on other sites More sharing options...
lukpep Sierpień 2, 2011 Autor tematu Udostępnij Sierpień 2, 2011 Arty, Troszke zakrecilem z sygnalami zegarowymi - juz spiesze z wyjasnieniem: zgodnie z tym rysunkiem: zegar taktujacy rdzen to HCLK i moze to byc max 72 MHz. HLCK otrzymujemy wpuszczajac takt SYSCLK na dzielnik AHB. SYSCLK mozemy otrzymac na 3 sposoby: 1. HSI - high speed internal - wew. oscylator RC 8 MHz 2. HSE - high speed external - zew. oscylator kwarcowy 3-25 MHz 3. Sygnal PLLCLK z wyjscia petli powielacza PLL1 Wybieramy opcje numer 3 😉 Sygnal na wejsciu petli PLL1 moze pochodzic z 2 zrodel: 1. HSI podzielone przez 2 2. Wyjscie dzielnika PREDIV1 Wybieramy bramke numer 2 Sygnal na wejscie dzielnika PREDIV1 moze pochodzic z 2 zrodel: 1. HSE 2. Sygnal PLL2CLK z wyjscia petli powielacza PLL2. Ponownie bramka numer 2 nas interesuje. Na wejscie petli PLL2 idzie zawsze sygnal HSE podzielony przez dzielnik PREDIV2. Wiec jak to wszystko konfigurujemy? HSE mamy 10 MHz. PREDIV2 ustawiamy na 2 wiec na petle PLL2 wchodzi 5 MHz. Mnoznik PLL2MUL petli PLL2 ustawiamy na 8 wiec z petli PLL2 wychodzi 40 MHz. Sygnal z PLL2 (40 MHz) wchodzi na dzielnik PREDIV1 ktory ustawiamy na 5 wiec za dzielnikiem mamy 8 MHz. Sygnalem z PREDIV1 wchodzimy na petle PLL1 z ustawionym mnoznikiem PLLMUL na 9 otrzymujac sygnal SYSCLK 72 MHz. Dzielkik AHB ustawiamy na 1 wiec HLCK = SYSCLK = 72 MHz Dzielnik APB1 ustawiamy na 2 co daje nam taktowanie 36 MHz PCLK1 dla peryferii na magistrali APB1 Dzielnik APB2 ustawiamy na 1 czyli na magistrali APB2 mamy sygnal PCLK2 = 72 MHz I juz 😉 Troszke zakrecilem poniewaz rozkrecanie zegarow w moim projekcie pochodzi z bardziej rozbudowanej wersji korzystajacej z ethernetu - (stad tez 40 MHz na wyjsciu PLL2 ktore podaje sobie na MCU i taktuje tym PHY ethernetowe). Teraz mam nadzieje juz wszystko jasne - kod, ktory wkleilem w zrodle jest niepelny ale mimo to dziala. Prawde mowiac nie wiem jak w takim przypadku zachowuje sie zegar - sygnal biore z PREDIV1, ktory ma na wejsciu albo sygnal z petli PLL2 albo HSE. Sygnal z PLL2 raczej odpada bo petla nie jest w ogole w tym kodzie konfigurowana i odpalana - pozostaje wiec HSE. I teraz pytanie - czy na SYSCLK idzie 90 MHz? W co watpie... czy tez zaraz po inicjacji PREDIV ma jakas wartosc rozna od 1 - np. 2 co by dawalo 36 MHz zegar - w co wierze 😉 Doczytam w dokumentacji jak to wyglada i dam znac. W kazdym razie poprawny i kompletny kod rozkrecania zegarow wyglada tak: int konf_zegary(void) { RCC_DeInit(); RCC_HSEConfig(RCC_HSE_ON); if (RCC_WaitForHSEStartUp() == ERROR) return -1; FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable); FLASH_SetLatency(FLASH_Latency_2); /* preskaler AHB, HCLK = SYSCLK = 72 MHz */ RCC_HCLKConfig(RCC_SYSCLK_Div1); /* preskaler APB1, PCLK1 = HCLK / 2 = 36 MHz */ RCC_PCLK1Config(RCC_HCLK_Div2); /* preskaler APB2, PCLK2 = HCLK = 72 MHz */ RCC_PCLK2Config(RCC_HCLK_Div1); /* PLL2: PLL2CLK = HSE / 2 * 8 = 40 MHz */ RCC_PREDIV2Config(RCC_PREDIV2_Div2); RCC_PLL2Config(RCC_PLL2Mul_8); RCC_PLL2Cmd(ENABLE); while(!RCC_GetFlagStatus(RCC_FLAG_PLL2RDY)); /* PLL1: PLLCLK = (PLL2 / 5) * 9 = 72 MHz */ RCC_PREDIV1Config(RCC_PREDIV1_Source_PLL2, RCC_PREDIV1_Div5); RCC_PLLConfig(RCC_PLLSource_PREDIV1, RCC_PLLMul_9); RCC_PLLCmd(ENABLE); while(!RCC_GetFlagStatus(RCC_FLAG_PLLRDY)); /* Ustaw SYSCLK = PLLCLK i czekaj az PLL zostanie ustawiony jako zegar systemowy. */ RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); while(RCC_GetSYSCLKSource() != 0x08); return 0; } [ Dodano: 02-08-2011, 11:31 ] nie udalo mi sie znalezc jak to jest z PREDIV1 w stanie po resecie - bez konfiguracji. W kazdym razie jak chcesz wyciagnac predkosci poszczegolnych zegarow to: void RCC_GetClocksFreq ( RCC_ClocksTypeDef * RCC_Clocks ) Returns the frequencies of different on chip clocks. Parameters: RCC_Clocks,: pointer to a RCC_ClocksTypeDef structure which will hold the clocks frequencies. Return values: None Definition at line 901 of file stm32f10x_rcc.c. 2 Cytuj Link do komentarza Share on other sites More sharing options...
Arty Sierpień 4, 2011 Udostępnij Sierpień 4, 2011 danioto, Wielkie dzięki na pewno się przyda. lukpep, Dzięki za rozwiązanie problemu, trochę mi się wyjaśniło. Będę musiał albo ustawić wszystkie mnożniki, dzielniki i preskalery albo wybadać z jakimi mnożnikami się uruchamia. Swoją drogą byłem przekonany że z 10MHz kwarca to nie wykręcę 72MHz nawet nie wpadłem na to by dzielić i mnożyć na przemian. Muszę przyznać że trochę to zakręcone ale działa. Cytuj Link do komentarza Share on other sites More sharing options...
lukpep Sierpień 4, 2011 Autor tematu Udostępnij Sierpień 4, 2011 Arty, Ano 😉 Jakbys mial kwarc 8 MHz i STM32F103 np. to moglbys odrazu kwarc wpuscic na PLL, wymnozyc x 9 i byloby 72 - zero kombinowania 😉 Cytuj Link do komentarza Share on other sites More sharing options...
razors Sierpień 5, 2011 Udostępnij Sierpień 5, 2011 A ja trochę z innej beczki. Mianowicie mam problem z układem RTC w STM32F105, a dokładnie to z podtrzymywaniem czasu i daty. Czy jest jakieś rozwiązanie by nie trzeba było ciągle ustawiać daty i godziny w mikrokontrolerze ? Wszystko jest ok, gdy mikrokontroler jest włączony (mam zaimplementowana cały algorytm obsługi kalendarza). Ale oczywiście, gdy wyłączymy mikr. na pare dni to po ponownym włączeniu godzina będzie miała wartość kilkudziesięciu godzin, a data będzie kilka dni do tyłu (bo bateria podtrzymuje tylko licznik czasu, którego i tak nie zmieni bo jest wył. ;/ ?). Jakieś pomysły ? Cytuj Link do komentarza Share on other sites More sharing options...
lukpep Sierpień 5, 2011 Autor tematu Udostępnij Sierpień 5, 2011 Owszem sa rejestry specjalnego przeznaczenia o podtrzymywanym przez baterie stanie. Backup registers sie zwa chyba - doczytaj. Jak nie znajdziesz to daj znac - napisze jak sie tego uzywa 🙂 EDIT: Co do zegarow to uzylem fukcji RCC_GetClocksFreq przed poprawna konfiguracja i zwracalo zegary SYSCLK i HCLK po 90 MHz - ale on to liczy na podstawie konfiguracj iw rejestrach a nie faktycznych przebiegow. Po zastosowaniu konfigu poprawnego z PREDIVami co wrzucilem pare postow wyzej pokazuje juz poprawne po 72 MHz. 1 Cytuj Link do komentarza Share on other sites More sharing options...
razors Sierpień 7, 2011 Udostępnij Sierpień 7, 2011 W nawiązaniu do kawałka dokumentacji: In the case of a device in low-power mode or whose external supply is off when the counter reaches 86399, the counter cannot be reset and so the date is not updated. This is why, just after power reset, it is necessary to check the counter value and update the date as many times as the number of days during which the device remained in low-power mode or had its external main supply switched off. Stworzyłem kawałek kodu: // Aktualizacja daty i czasu //load_date(); // z rejestru backup while(RTC_GetFlagStatus(RTC_FLAG_RTOFF) == RESET); czas = RTC_GetCounter(); liczba_dni = czas/86400; for(l=liczba_dni ; l == 0 ; l--) //86400 sekund - 1 doba { update_date(); } //save_date(); // do rejestru backup czas -= 86400 * liczba_dni; //while(RTC_GetFlagStatus(RTC_FLAG_RTOFF) == RESET); //RTC_SetCounter(czas); //while(RTC_GetFlagStatus(RTC_FLAG_RTOFF) == RESET); Czyli tak: - pobieram wartość licznika RTC - sprawdzam ile dni urządzenie nie miało zasilania poprzez obliczenie liczby_dni - tyle razy aktualizuje date ile jest tych dni - uaktualniam czas Teraz 3 rzeczy, które nie działaja !? ;/ - po pierwsze nie mogę zaktualizować wartości licznika RTC (RTC_SetCounter(czas)), gdy jest dołączona bateria - sick !?? (gdy wyjmę baterię, licznik aktualizuje się bez problemu przy tym kodzie) - po drugie zastosowany algorytm nie działa (nie aktualizuje mi daty - czyli tak jakby liczba_dni była zawsze zero !?) dodam, ze gdy sprawdzam godzinę na wyświetlaczu to mam wynik ponad 300godzin:xxmin:xxsekund co by się zgadzało bo ostatni raz czas ustawiałem (oczywiście bez baterii) 25 lipca - po trzecie jak zapisywać aktualna date do tych backup_registers ? bo należałoby zapisywać rok, miesiąc i dzień po aktualizacji i wczytywać je po każdym uruchomieniu urządzenia Cytuj Link do komentarza Share on other sites More sharing options...
Harnas Sierpień 7, 2011 Udostępnij Sierpień 7, 2011 Ja do oczekiwania czy została zakończona operacja na RTC używam funkcji RTC_WaitForLastTask(); Do zapisu danych w rejestrach backup używam funkcji: BKP_WriteBackupRegister(0x04,dzien); BKP_WriteBackupRegister(0x08,miesiac); BKP_WriteBackupRegister(0x0C,rok); a do odczytu: dzien = BKP_ReadBackupRegister(0x04); miesiac = BKP_ReadBackupRegister(0x08); rok = BKP_ReadBackupRegister(0x0C); Wszystkie te funkcje są w standardowych bibliotekach, w pliku stm32f10x_rtc.c. Zmianę wartości licznika przeprowadzam tak: if(RTC_GetCounter() > 86398) { RTC_SetCounter(RTC_GetCounter() - (86400 - 1)); //reszta kodu Cytuj Link do komentarza Share on other sites More sharing options...
razors Sierpień 7, 2011 Udostępnij Sierpień 7, 2011 no wiec trochę się wyjaśniło ... ale w dalszym ciągu mam problem z wgraniem nowego czasu do licznika 🙁 ... jak wyjmę baterię to mogę ustawić godzinę bez problemu (całkowity bezsens ) ... i jeszcze mam pytanie jak inicjalizujesz rejestry chronione ? [ Dodano: 07-08-2011, 22:16 ] juz poszlo ... nie wiem dlaczego ale pomoglo dodanie linijki kodu: PWR_BackupAccesCmd(Enable); tuż przed wpisaniem nowej wartości do licznika Cytuj Link do komentarza Share on other sites More sharing options...
Harnas Sierpień 8, 2011 Udostępnij Sierpień 8, 2011 Rejestrów backup nie inicjalizuje. Po prostu zapisuje aktualną wartością dnia,miesiąca itp. Ja linijkę PWR_BackupAccesCmd(Enable); mam użytą raz, na samym początku procedurki konfiguracji RTC. 1 Cytuj Link do komentarza Share on other sites More sharing options...
razors Sierpień 8, 2011 Udostępnij Sierpień 8, 2011 tez mam tam ja użytą, ale jej powtórzenie w kodzie pozwoliło na poprawne działanie systemu Cytuj Link do komentarza Share on other sites More sharing options...
Pomocna odpowiedź
Dołącz do dyskusji, napisz odpowiedź!
Jeśli masz już konto to zaloguj się teraz, aby opublikować wiadomość jako Ty. Możesz też napisać teraz i zarejestrować się później.
Uwaga: wgrywanie zdjęć i załączników dostępne jest po zalogowaniu!