---
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
  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 />


> 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.
* 
> Singal før vi har stengt avbruddene på fellesressurser.
* 
> 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.