--- title: Lab3 --- <div style="height: 1000px; padding-top: 200px"> <center><p style="font-size: 40px; font-weight: 600">Laboratorieøving 3</p></center> <center><p style="font-size: 30px">Realisering av sykkellyktstyring i programvare.</p></center> <center><p style="font-size: 20px">Gruppe: SA</p></center> <center><p style="font-size: 20px">Erik Martin (250660)</p></center> <center><p style="font-size: 20px">Tormod Kvam (250612)</p></center> </div> # Laboratorieøving 3 ## Innholdsliste [TOC] ## Realisering av en enkel blinkeløkke **Deloppgåvenr:** 6.1.1 **Dato:** 30.09.2022 **Navn:** Oppstart **Funksjon:** **Framgangsmåte for utføring:** 1. Få prosjektet inn i STM32CubeIDE 2. Sjekk optimalisering, bygg og kjør programmet. 3. Les og forstå koden. **Resultat:** * Etter å ha bygget og kjørt programmet, begynner LD2 å blinke. * Det er ingenting skrevet i while-løkka, som vil si det ikke er noe blinkekode som blir utført her. * Alternative metoder for å kjøre koden uten å bruke *while* kan være å bruke forskjellige avbruddsmetoder og *SysTick* timermodulen. **Analyse og konklusjon** Får prosjektet inn i STM32CubeIDE, bygger og kjører programmet. Vi kan bruke avbruddsmetoder og SysTick-timer modulen til å kjøre koden uten while-løkka. ## Fase 1: Systemtaimeren og metoden for denne **Deloppgåvenr:** 6.2.1 **Dato:** 30.09.2022 **Navn:** **Funksjon:** **Framgangsmåte for utføring:** 1. Studer koden og finn delen i koden som har med blinkinga å gjøre. 2. Observer og svar på relaterte spørsmål. 3. Regn ut avbrytsfrekvens. **Resultat:** * Kode som snur nivået på LD2. * ```c tikkteljar_blink++; if(tikkteljar_blink >= 500 ) { tikkteljar_blink = 0; GPIOA->ODR = GPIOA->ODR ^ GPIO_PIN_5; //Snu nivaaet på LD2 (PA5). } ``` * Vi kan se det vil ta 500 gjennomløp før nivået på LD2 endres, ettersom `tikketeljar_blink` økes med en verdi på 1 hvert millisekund pga. `SysTick` intervallet er på 1ms. Hver gang denne variabelen når 500 vil nivået på LD2 snus. * En blinke periode kan regnes ut slik: * $n = 32$ * $t_{clk} = \frac{1}{72MHz} = 13.9*10^{-9}$ * $t_{BI} = 2^{32-1}*13.9*10^{-9} = 29.8s$ * $t_{BP} = 2*t_{BI} = 2*29.8s = 59.6s \approx 1min$ * En blinkeperiode vil ta ca. ett sekund for at LD2 skal slås av og på. * 50 blinkeperioder tok ca. 50 sekund. * **T~BI~** er intervallet som lyset skifter fra av til på. Fordi et avbrudssignal blir sendt hver gang det skal skifte kan vi si at at estimert avbrudsfrekvens er lik **t~BI~**. * Maskinvarekomponenten som påvirker den estimert avbrudsfrekvensen kan være prosessoren. Prosessorene kan ha små variasjoner under produskjon som kan føre til små feil. * En annen feilkilde som kan påvirke den estimerte avbrudsfrekvensen kan være om noe med en høyere prioritet enn `SysTick_Handler()` kjører først. **Analyse og konklusjon** Finner kode som snur nivået på LD2. Regner ut tiden det tar før nivået på LD2 snus og hvor mange blink som gjøres i løpet av 50 sekunder. Finner deretter noen feilkilder som kan påvirke avbruddsfrekvensen. ## Fase 2: Realisering av enkel programstruktur basert på et hovedprogram **Deloppgåvenr:** 6.3.1 **Dato:** 01.10.2022 **Navn:** Realisering av enkel programstruktur basert på et hovedprogram **Funksjon:** **Framgangsmåte for utføring:** 1. Bygg og last ned programet, se etter endringer. 2. Finn ut hvorfor det er ingen tilstandsendring. 3. Bygg kode som gjør det mulig å endre tilstand. 4. Prøv forskjellige hastigheter trykk på bryteren og observer forskjellen. **Resultat:** * Ingen endring observert. * Det skjer ingen endringer fordi det ikke er noe som oppdaterer `gyldig_trykk_av_B1`. Variablen forblir 0 uansett om man trykker på **B1** eller ikke * Akkurat nå har den 1 tilstand, men den skal ha 3 forskjellige. *LD2_av*, *LD2_på* og en **LD2_blink* tilstand. * ```c void tilstandsmaskin(void) { // I fase 2: GPIO_sjekk_B1(); // Er det nye brytartrykk? if (gyldig_trykk_av_B1) { // I så fall, skift tilstand. // I fase 4 av oppgåva: Test om gyldig_kommando er sett. gyldig_trykk_av_B1 = 0; // Og tilsvarande i fase 4 tilstand++; // Skal over i neste tilstand if (tilstand == 3) { // Skal ha 3 tilstandar, tilstand 0-2 tilstand = 0; } switch(tilstand) { case 0: GPIO_LD2_av(); break; case 1: GPIO_snu_LD2_nivaa(); break; case 2: GPIO_LD2_paa(); break; } } ``` > Det som ble lagt inn i metoden tilstandsmaskin er en if-setning som setter tilstanden tilbake til 0 når den går over 2. * ```c void GPIO_LD2_paa() { volatile uint32_t i; GPIOA->ODR = GPIOA->ODR | GPIO_PIN_5; //Gi ut hoegt nivaa paa LD2 (pinne PA5). La dei andre vera. //Pause for(i=0;i<0x300000;i++); } void GPIO_LD2_av() { volatile uint32_t i; //Gi ut laagt nivaa paa LD2 (pinne PA5). La dei andre vera. GPIOA->ODR = GPIOA->ODR & ~GPIO_PIN_5; //Pause for(i=0;i<0x300000;i++); } ``` > I `GPIO_LD2_paa()` bruktes | (eller) operatoren for å holde LD2 på, og på `GPIO_LD2_av()` bruktes & operatoren sammen med en ~-operator (ikke-operator) nullstille og invertere biten for å slå LD2 av. * Ja, programmet skifter fra $på \rightarrow av \rightarrow blink$. * Med hurtige trykk skjer det bytt, men skiftingen er mer upålitelig. Den bytter altså bare av-og-til. Problemet kan være at fordi det brukes en form for avprelling får å unngå prell eller at avlesningen av bryternivået skjer med for store mellomrom. **Analyse og konklusjon:** Ingen endring blir observert etter å ha bygd og kjørt koden. Lager så egen kode for å kan bytte mellom de tre tilstandene, av, blinkende og på. Tester programmet og finner ut det ikke er veldig pålitelig å trykke fort for å endre tilstanden. ## Fase 3: Realisering av programstruktur basert på hovedprogram og en avbrytsmetode **Deloppgåvenr:** 6.4.1 **Dato:** 01.10.2022 **Navn:** Fase 3: Realisering av programstruktur basert på hovedprogram og en avbrudsmetode **Funksjon:** Skal prøve å gjøre bryterskjekken mer robust ved å lese bryternivået oftere. **Framgangsmåte for utføring:** 1. Svar på spørsmål relatert til fase 2. 2. Endre kode i `SysTick_Handler()` og `tilstandsmaskin()`. 3. Svar på relaterte spørsmål. 4. Regn ut startsverdi ved hjelp av formel. **Resultat:** * Fordi vi bare sjekker bryteren for hver loop av whileløkka. Så hvis whileløkka tar mer en 10ms så vil vi ikke få sjekket bryteren så ofte. * Systemet i fase 3: * LD2 har en tilstand * *While-loopen* kjører funksjonen `tilstandsmaskin()` * `tilstandsmaskin()` sjekker brytertrykk for hver loop av *while* * `gyldig_trykk_av_B1` sjekkes for hver iterasjon av `tilstandsmaskin()` og blir nullstilt hvis den er lik *1*. * I bakgrunnen kjører `Systick_Handler()` *uavhengig* og kaller opp `GPIO_sjekk_B1()` som setter `gyldig_trykk_av_B1` variablen hvis et gyldig brukertrykk er registrert (bryteren har blitt trykket inn og sluppet før det registreres på nytt). <br /> * ```c void tilstandsmaskin(void) { // I fase 2: //GPIO_sjekk_B1(); // Er det nye brytartrykk? if (gyldig_trykk_av_B1) { // I så fall, skift tilstand. // I fase 4 av oppgåva: Test om gyldig_kommando er sett. gyldig_trykk_av_B1 = 0; // Og tilsvarande i fase 4 tilstand++; // Skal over i neste tilstand if (tilstand == 3) { // Skal ha 3 tilstandar, tilstand 0-2 tilstand = 0; } ``` > `tilstandsmaskin()` etter å ha kommentert vekk `GPIO_sjekk_B1()`. ```c // 3. I Fase 3 av oppgåva: // Sjekking kvart 10. msek av om det er gyldig brytartrykk. tikkteljar_brytar++; if(tikkteljar_brytar > 10 ) { tikkteljar_brytar = 0; GPIO_sjekk_B1(); //Fjern kommentaren i fase 3 og sett kommenter på same kallet i main(). } ``` > Fjernet kommentering på `GPIO_sjekk_B1()` innenfor `SysTick_Handler()`-metoden. * Ved å trykke raskt bytter tilstanden hver gang, utenom hvis det trykkes to ganger veldig raskt (da endres tilstanden bare en gang). Dette er fordi der er en venteløkke i de forskjellige tilstands-metodene (på, av og blink) som kjøres etter hvert brytertrykk. * $T_{INT} = Y_{START}*T_{CLK} \Rightarrow \\Y_{START} = \frac{T_{INT}}{T_{CLK}} = T_{INT}*F_{CLK} = \\1ms * 72MHz = 1*10^{-3}*72*10^6 =72*10^3$ > Startverdien for SysTick-timeren er 72000. **Analyse og konklusjon** Programmet tar ikke opp knappetrykk hvert 10 millisekund. Endrer på koden for å fikse dette problemet. Etter endringen blir bryteren mye mer responsiv. Regner så ut startverdien for SysTick-timeren. <br /> ## Fase 4: Realisering av seriell tovegskommunikasjon mot eit terminalprogram på PC. **Deloppgåvenr:** 6.5.1 **Dato:** 01.10.2022 **Navn:** Fase 4: Realisering av seriell tovegskommunikasjon mot eit termi- nalprogram på PC **Funksjon:** Skal prøve å gjøre bryterskjekken mer robust ved å lese bryternivået oftere. **Framgangsmåte for utføring:** Finne koden for `USART2` som bruker til å sende og motta &emsp;&emsp;signaler mellom mikro-kontrolleren og laptopen via en *serial* port **Resultat:** * ```c void HAL_UART_MspInit(UART_HandleTypeDef* huart) { GPIO_InitTypeDef GPIO_InitStruct = {0}; if(huart->Instance==USART2) { /* USER CODE BEGIN USART2_MspInit 0 */ /* USER CODE END USART2_MspInit 0 */ /* Peripheral clock enable */ __HAL_RCC_USART2_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); /**USART2 GPIO Configuration PA2 ------> USART2_TX PA3 ------> USART2_RX */ GPIO_InitStruct.Pin = USART_TX_Pin|USART_RX_Pin; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); /* USER CODE BEGIN USART2_MspInit 1 */ /* USER CODE END USART2_MspInit 1 */ } } /** * @brief UART MSP De-Initialization * This function freeze the hardware resources used in this example * @param huart: UART handle pointer * @retval None */ void HAL_UART_MspDeInit(UART_HandleTypeDef* huart) { if(huart->Instance==USART2) { /* USER CODE BEGIN USART2_MspDeInit 0 */ /* USER CODE END USART2_MspDeInit 0 */ /* Peripheral clock disable */ __HAL_RCC_USART2_CLK_DISABLE(); /**USART2 GPIO Configuration PA2 ------> USART2_TX PA3 ------> USART2_RX */ HAL_GPIO_DeInit(GPIOA, USART_TX_Pin|USART_RX_Pin); /* USER CODE BEGIN USART2_MspDeInit 1 */ /* USER CODE END USART2_MspDeInit 1 */ } } ``` <br /> ![](https://i.imgur.com/yUZoZpP.png) ![](https://i.imgur.com/qJZ9RYq.png) > Vi kan se at *PA2* har `USART2_TX` som AF og *PA3* har `USART2_RX` som AF. <br /> **Deloppgåvenr:** 6.5.2 **Dato:** 01.10.2022 **Navn:** Realisering **Funksjon:** Realisere fase 4 **Framgangsmåte for utføring:** 1. Laste ned et terminalprogram som kan kommunisere med mikrokontrolleren og sette det opp som spesifisert. 2. Endre koden til å ta **input** fra terminalen og endre tilstanden til lysdioden. **Resultat:** * Når RESET-bryteren trykkes på så kommer oppstartsmeldingen på nytt. Dette er fordi hele programmet starter på nytt igjen. * Terminalen må sende `s` for at tilstanden skal byttes. * Når vi sender inn `s` i terminalen bytter tilstanden som om vi hadde trykket på bryteren fysisk, og vi får opp den nye tilstandsverdien og de tre LSb. * ```c if(les_ny_kommando) { if(USART2_les_teikn() == 's'){ gyldig_kommando = 1; USART2_skriv_verdi(tilstand); //Denne linja skriver ut //tilstandsverdien og de tre LSb. tilstand++; } les_ny_kommando = 0; if (tilstand == 3) { tilstand = 0; } } ``` > Denne if-setningen bruker bytter mellom de forskjellige lysmetodene ved å skrive `s` i terminalen istedenfor å bryke B1_bryteren. ## Fase 5: Måling på testsignal frå systemet i ulike situasjonar **Deloppgåvenr:** 6.6.1 **Dato:** 01.10.2022 **Navn:** Fase 5: Måling på testsignal frå systemet i ulike situasjonar **Funksjon:** Måle signalet fra sykkellyktsystemet **Framgangsmåte for utføring:** 1. Sette opp utstyret som i lab2 (men bruke porten for avbrudsmetoden istedenfor porten for lysdioden) 2. Gjøre en måling med signalet for avbrudsmetoden 3. Endre fra å bruke GPIOA til GPIOB og kommentere ut *for-løkkja* i metodane `GPIO_LD2_av()` og `GPIO_LD2_paa()` og teste på nytt. 4. Ta et *skjermbilde* direkte fra skopet. 5. Bruke `__disable_irq();` og `__enable_irq();` i tilfellene der både instruksjonene for å endre tilstanden på lysdiodene og `SysTick_Handler()` kan kolidere. **Resultat:** * Pinne PB9 styrer avbruddsmetoden. Vi målte en periode til å være 2ms så den kalles hvert 1ms som er forventet. Signalet ble oppfattet som litt ustabilt. * ![](https://i.imgur.com/eP4GxQb.png) > Singal før vi har stengt avbruddene på fellesressurser. * ![](https://i.imgur.com/nuqpBvq.png) > Signal etter vi har stengt avbruddne epå fellesressurser. * ```c __disable_irq(); GPIOA->ODR = GPIOA->ODR & ~GPIO_PIN_5; __enable_irq(); ``` * Før vi hadde stengt avbruddene der det var fellesressurser, var signalet når lysdioden på, veldig ustabilt som i den første figuren. Etter å ha stengt disse avbruddene, ble signalet mye mer stabilt. **Analyse og konklusjon:** Sjekker forskjellen mellom signalet til `SysTick_Handler()` før og etter at GPIOA (som er delt mellom `SysTick_Handler()` og funksjonene som snur på PA5 biten) er *låst* når den brukes av de andre funksjonene.