DonPablo Napisano Luty 1, 2017 Udostępnij Napisano Luty 1, 2017 Cześć, potrzebuję pomysłu na synchronizację timerów w kilku mikrokontrolerach. Robię na uczelni pewien projekt, który trochę mnie przerasta. Jedną z części jest uruchamianie peryferiów podłączonych do kilku mikrokontrolerów bardzo 'precyzyjnie czasowo' (brakuje mi słowa). Docelowo ma to wyglądać jak na poniższym schemacie - fanout buffer powiela sygnał 1 PPS wysyłany przez odbiornik GPS i wprowadza go na GPIO mikrokontrolerów. A, B, C, D, E to różnego rodzaju peryferia dwustanowe - światełka, brzęczyki, przekaźniki, itp. Zbocze narastające PPS ma zresetować licznik, który to osiągając określone wcześniej wartości (tak jak w tabeli na rysunku poniżej) będzie zmieniał stan przypisanego do tej wartości pinu. Czyli po prostu wartość licznika ma być co sekundę ustawiana na 0, licznik liczyć ma w górę i po doliczeniu do wartości A ustawić na pewien czas (około 20 ms) stan pinu PC8 na wysoki i tak dalej. Myślę, że te schematy wyglądają sensownie i ma to prawo działać (chyba że jest inaczej - dajcie znać). Cały problem sprowadza się do programowania. Jak to ugryźć? Muszę dodać, że na razie projekt jest na etapie walidacji, więc mam do dyspozycji tylko jeden mikrokontroler i fanout buffer. Generuję 1 PPS na STMie, wprowadzam go do powielacza, a z niego z powrotem na GPIO tego samego STMa. I nie wiem co dalej. Jak zresetować licznik za każdym razem gdy na pinie wejściowym pojawi się zbocze narastające? Jak potem W reference manualu znalazłem tryb działania external clock source / external trigger - czy to dobry trop? Byłbym wdzięczny za pokierowanie mnie w tej podróży po mało znanej mi krainie :-> Mam nadzieję, że napisałem zrozumiale i niczego nie pominąłem. Wrzucam też to, co do tej pory napisałem, chociaż nie wiem czy jest po co. #include <stdbool.h> #include "stm32f10x.h" volatile uint32_t timer_ms = 0; void SysTick_Handler() { if(timer_ms) { timer_ms--; } } void delay_ms(int time) { timer_ms = time; while(timer_ms > 0){}; } void RCC_Config_HSI_PLL_Max(void) // 64 MHz { RCC_DeInit(); RCC_HSICmd(ENABLE); // enable HSI clock FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable); FLASH_SetLatency(FLASH_Latency_2); // set wait state RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); RCC_PLLConfig(RCC_PLLSource_HSI_Div2, RCC_PLLMul_16); // configure HSI PLL clock RCC_PLLCmd(ENABLE); while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET); // wait till HSI PLL is ready RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); // select HSI PLL as system clock source while(RCC_GetSYSCLKSource() != 0x08); // wait till HSI PLL is used as system clock source RCC_HCLKConfig(RCC_SYSCLK_Div1); // 64 MHz RCC_PCLK1Config(RCC_HCLK_Div2); // 32 MHz RCC_PCLK2Config(RCC_HCLK_Div1); // 64 MHz } void InitializeTimer2(void) // 1 PPS signal generation { RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); TIM_TimeBaseInitTypeDef tim; TIM_TimeBaseStructInit(&tim); tim.TIM_CounterMode = TIM_CounterMode_Up; tim.TIM_Prescaler = 64000 - 1; tim.TIM_Period = 1000 - 1; tim.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseInit(TIM2, &tim); // TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); TIM_Cmd(TIM2, ENABLE); } void InitializeTimer3(void) // reset timer on rising edge of 1 PPS signal { RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); TIM_ICInitTypeDef tim; TIM_TimeBaseStructInit(&tim); tim.TIM_ICPolarity = TIM_ICPolarity_Rising; tim.TIM_ICSelection = TIM_ICSelection_DirectTI; tim.TIM_ICPrescaler = TIM_ICPSC_DIV1; tim.TIM_ICFilter = 0; tim.TIM_Channel = TIM_Channel_1; TIM_ICInit(TIM3, &tim); TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1); TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset); // TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); // TIM_Cmd(TIM2, ENABLE); } void InitializeClockOutput(void) // master clock output { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitTypeDef gpio; GPIO_StructInit(&gpio); gpio.GPIO_Pin = GPIO_Pin_5; gpio.GPIO_Speed = GPIO_Speed_50MHz; gpio.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(GPIOA, &gpio); } void InitializeClockInput(void) // slave clock input { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); GPIO_InitTypeDef gpio; GPIO_StructInit(&gpio); gpio.GPIO_Pin = GPIO_Pin_6; gpio.GPIO_Speed = GPIO_Speed_50MHz; gpio.GPIO_Mode = GPIO_Mode_IPU; GPIO_Init(GPIOB, &gpio); } int main(void) { //NVIC_InitTypeDef nvic; RCC_Config_HSI_PLL_Max(); InitializeTimer2(); InitializeClockOutput(); InitializeClockInput(); /*nvic.NVIC_IRQChannel = TIM2_IRQn; nvic.NVIC_IRQChannelPreemptionPriority = 0; nvic.NVIC_IRQChannelSubPriority = 0; nvic.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&nvic); SysTick_Config(SystemCoreClock / 1000);*/ while (1) { int timerValue = TIM_GetCounter(TIM2); if(timerValue == 990) GPIO_WriteBit(GPIOA, GPIO_Pin_5, Bit_SET); else if(timerValue == 1000 - 1) GPIO_WriteBit(GPIOA, GPIO_Pin_5, Bit_RESET); } } Link do komentarza Share on other sites More sharing options...
marek1707 Luty 1, 2017 Udostępnij Luty 1, 2017 To wygląda na tryb "one-pulse mode", gdzie możesz zaprogramować opóźnienie i długość impulsu po zewnętrznym wyzwoleniu (TIM1/8 - Advanced Control Timer). Na pewno musisz pracować z zegarem wewnętrznym, bo będziesz taktował timer z głównego generatora procesora. Zewnętrzny jest tylko trigger, który powinien zerować i stratować timer i jego prescaler. Nie wiem o jakich precyzjach czasowych mylisz, ale jeśli potrzebujesz naprawdę ścisło, to pomyślcie nad taktowaniem wszystkich procesorów z jednego wspólnego zegara. Możecie też wykorzystać tryb Input Capture timera w połączeniu z jego wyjściami Output Compare. Wtedy wejście zatrzaskuje chwilę czasową gdy się pojawiło, program (obsługi tego przerwania) liczy opóźnienia od tej chwili i odpowiednio programuje rejestry TIMx_CCRx. Sygnały na wyjściach pojawią się wtedy "same", wyzwolone przez timer w odpowiednich chwilach. To kwestia organizacji programu. Ale tak jeszcze na marginesie: czy naprawdę timingi diodek i przekaźników będą tak ścisłe, że musisz używać sprzętowych timerów i synchronizacji? Nikt nie zauważy zwłoki zapalenia LEDa z błędem nawet 10ms a sam przekaźnik tyle czasu to się w ogóle załącza. A system przerwań i prędkość tych procesorów pozwalają na zrobienie tego z dokładnością nawet 1us. Wystarczy doprowadzić 1pps do wejścia przerwania o wysokim priorytecie, gdzie będziesz programowo startował timer systemowy. Czy możesz zdradzić jakie jest zastosowanie tego urządzenia? Chyba się tym przejąłeś i postawiłeś sobie zupełnie niepotrzebne wymagania. Czasem tak jest, gdy nie panujesz nad całością a chcesz coś zrobić jak najlepiej. Czy mógłbyś podać przykładową sekwencję sygnałów wyjściowych, co dokładnie będziesz nimi sterował i po co? Link do komentarza Share on other sites More sharing options...
DonPablo Luty 2, 2017 Autor tematu Udostępnij Luty 2, 2017 Co do zastosowania, to trochę uprościłem pisząc "światełka, brzęczyki [...]", dlatego że faktyczne urządzenia peryferyjne będą sterowane dwustanowo (albo będą włączone albo wyłączone - bez stanów przejściowych). W rzeczywistości jest to pewien moduł do AUV (robota podwodnego), który ma za zadanie wyzwalać różnego rodzaju źródła światła (moduły LED, diody laserowe) i systemy akwizycji obrazu (kamery CMOS). Ma to służyć do gromadzenia informacji o ukształtowaniu powierzchni zbiornika wodnego i pozyskiwaniu fotografii tych powierzchni. Na podstawie analizy fotografii będzie badany materiał powierzchni (spektrografia, transmisja-absorpcja, te sprawy) - dlatego zdjęć potrzeba dużo, w oświetleniu różnymi długościami światła. Dodając, że robot ma poruszać się dosyć szybko (1 m/s), czas rzeczywiście jest istotny. Diody laserowe to część skanera triangulacyjnego. Muszę uninąć sytuacji, w której jednocześnie dioda laserowa i LED będą uruchomione jednocześnie, bo może to spowodować jakieś opóźnienie (nie obchodzi mnie w tej chwili po jakim czasie diody faktycznie gasną - to zadanie innego zespołu). To tyle tytułem wyjaśnienia 🙂 Trafiłem do projektu bez odpowiedniego doświadczenia, ale taryfy ulgowej nie dostaję i wciąż muszę to "dowieźć". Więc tak wygląda obecnie mój kod: - sygnał 1 PPS symuluję ustawiając stan wysoki na pinie PA15 przy wykorzystaniu przerwania od TIM2 - kanał CH2 timera TIM1 próbowałem skonfigurować jako Input Capture - przemapowując częsciowo pin PB0 - poprowadziłem przewód połączeniowy z pinu PA15 do wejścia wspomnianego wcześniej układu powielacza (fanout buffer) - wyjście tego układu połączyłem z pinem PB0 skonfigurowanym jako Alternative Function Push-Pull, więc stan tego wejścia będzie się zmieniał co pół sekundy - dodałem fragment: TIM_SelectInputTrigger(TIM1, TIM_TS_TI2FP2); TIM_SelectSlaveMode(TIM1, TIM_SlaveMode_Reset); TIM_SelectMasterSlaveMode(TIM1, TIM_MasterSlaveMode_Enable); który, jak zrozumiałem, spowoduje że z każdym zboczem narastającym licznik TIM1 będzie zaczynał od zera, o co właśnie mi chodziło w pierwszym poście - żeby sprawdzić czy rzeczywiście tak jest, w IRS sprawdzam czy flaga przerwania CH2 TIM1 jest ustawiona, i jeśli jest, to chciałem zaświecić diodą (PA5 - tak tylko, dla sprawdzenia), no ale niestety dioda się nie zaświeca Co tu jest nie tak? W dokumentacjach STM32 panuje taki chaos, że nie potrafię się połapać. To poniżej jest najlepszą wersją na jaką wpadłem, ale nadal złą. #include <stdbool.h> #include "stm32f10x.h" void RCC_Config_HSI_PLL_Max(void) // 64 MHz { RCC_DeInit(); RCC_HSICmd(ENABLE); // enable HSI clock FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable); FLASH_SetLatency(FLASH_Latency_2); // set wait state RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); RCC_PLLConfig(RCC_PLLSource_HSI_Div2, RCC_PLLMul_16); // configure HSI PLL clock RCC_PLLCmd(ENABLE); while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET); // wait till HSI PLL is ready RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); // select HSI PLL as system clock source while(RCC_GetSYSCLKSource() != 0x08); // wait till HSI PLL is used as system clock source RCC_HCLKConfig(RCC_SYSCLK_Div1); // 64 MHz RCC_PCLK1Config(RCC_HCLK_Div2); // 32 MHz RCC_PCLK2Config(RCC_HCLK_Div1); // 64 MHz } void InitializeTimer2(void) // 1 PPS signal generation { RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); TIM_TimeBaseInitTypeDef tim; TIM_TimeBaseStructInit(&tim); tim.TIM_CounterMode = TIM_CounterMode_Up; tim.TIM_Prescaler = 64000 - 1; tim.TIM_Period = 500 - 1; tim.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseInit(TIM2, &tim); TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); TIM_Cmd(TIM2, ENABLE); } void InitializeClockOutput(void) // master clock output { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitTypeDef gpio; GPIO_StructInit(&gpio); gpio.GPIO_Pin = GPIO_Pin_5; gpio.GPIO_Speed = GPIO_Speed_50MHz; gpio.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(GPIOA, &gpio); gpio.GPIO_Pin = GPIO_Pin_15; gpio.GPIO_Speed = GPIO_Speed_50MHz; gpio.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(GPIOA, &gpio); } int main(void) { TIM_ICInitTypeDef tim; GPIO_InitTypeDef gpio; NVIC_InitTypeDef nvic; // TIM1 clock enable RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE); // GPIOB clock enable RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); // AFIO clock enable RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); GPIO_PinRemapConfig(GPIO_PartialRemap_TIM1, ENABLE); // TIM1 channel 2 (PB0) configuration (alternate function) gpio.GPIO_Pin = GPIO_Pin_0; gpio.GPIO_Mode = GPIO_Mode_AF_PP; gpio.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &gpio); // Enable the TIM1 global interrupt nvic.NVIC_IRQChannel = TIM1_CC_IRQn; nvic.NVIC_IRQChannelPreemptionPriority = 0x00; nvic.NVIC_IRQChannelSubPriority = 0x01; nvic.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&nvic); /* TIM2 interrupt configuration */ nvic.NVIC_IRQChannel = TIM2_IRQn; nvic.NVIC_IRQChannelPreemptionPriority = 0x00; nvic.NVIC_IRQChannelSubPriority = 0x02; nvic.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&nvic); // TIM1 configuration: Input Capture mode // External signal is connected to TIM1 CH2 pin (PB0) // Rising edge is used as an active edge, tim.TIM_Channel = TIM_Channel_2; tim.TIM_ICPolarity = TIM_ICPolarity_Rising; tim.TIM_ICSelection = TIM_ICSelection_DirectTI; tim.TIM_ICPrescaler = TIM_ICPSC_DIV1; tim.TIM_ICFilter = 0x5; TIM_ICInit(TIM1, &tim); TIM_SelectInputTrigger(TIM1, TIM_TS_TI2FP2); TIM_SelectSlaveMode(TIM1, TIM_SlaveMode_Reset); TIM_SelectMasterSlaveMode(TIM1, TIM_MasterSlaveMode_Enable); // assuming PCLK1 ticks at 64[MHz] -> slow down timer to 640 KHz TIM_PrescalerConfig(TIM1, 100, TIM_PSCReloadMode_Immediate); // TIM enable counter TIM_Cmd(TIM1, ENABLE); // Enable the CC2 Interrupt Request TIM_ITConfig(TIM1, TIM_IT_CC2, ENABLE); RCC_Config_HSI_PLL_Max(); InitializeTimer2(); InitializeClockOutput(); while (1) { } } void TIM1_CC_IRQHandler(void) { // capture/compare interrupt source ? if(TIM_GetITStatus(TIM1, TIM_IT_CC2) == SET) { // clear TIM1 capture/compare interrupt pending bit TIM_ClearITPendingBit(TIM1, TIM_IT_CC2); // switch on the LED GPIO_SetBits(GPIOA, GPIO_Pin_5); } else if(TIM_GetITStatus(TIM1, TIM_IT_CC2) == RESET) { // clear TIM1 capture/compare interrupt pending bit TIM_ClearITPendingBit(TIM1, TIM_IT_CC2); // switch off the LED GPIO_ResetBits(GPIOA, GPIO_Pin_5); } TIM_ClearFlag(TIM1, TIM_FLAG_CC2); } void TIM2_IRQHandler() { if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET) { TIM_ClearITPendingBit(TIM2, TIM_IT_Update); if (GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_15)) GPIO_ResetBits(GPIOA, GPIO_Pin_15); else GPIO_SetBits(GPIOA, GPIO_Pin_15); } } Link do komentarza Share on other sites More sharing options...
Pomocna odpowiedź
Bądź aktywny - zaloguj się lub utwórz konto!
Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony
Utwórz konto w ~20 sekund!
Zarejestruj nowe konto, to proste!
Zarejestruj się »Zaloguj się
Posiadasz własne konto? Użyj go!
Zaloguj się »