Skip to content

Instantly share code, notes, and snippets.

@Alexander-Tengborg
Created August 7, 2020 07:46
Show Gist options
  • Save Alexander-Tengborg/4136bed17de43be0aa4f1382835bbc5a to your computer and use it in GitHub Desktop.
Save Alexander-Tengborg/4136bed17de43be0aa4f1382835bbc5a to your computer and use it in GitHub Desktop.
@Anonym kod: EDA487-0059-PLX
@ R3=a
@ R4=b
@ R5=c
main: B init_data
LDR R0,#2020 @Argumentet ska ligga i R0
B sum_grade
LDR R3,R0 @Return v�rdet flyttas till A
LDR R0,#2000 @Argumentet ska ligga i R0
B sum_grade
LDR R4,R0 @Return v�rdet flyttas till B
ADD R5,R4,R4 @ c=a+b
init_data: PUSH {R3, R4, LR} @Sparar R3, R4 som vi �ndrar p� (och LR)
LDR R0,=student_year @Adressen till student_year
LDR R1,=student_grade @Adressen till student_grade
LDR R2,#2000
LDR R3,#4
STR R2,R0 @Till adressen till student_year
STR R3,R1 @Till adressen till student_grade
LDR R2,#2000
LDR R3,#3
STR R2,[R0,#2] @Till adressen till student_year+2
STR R3,[R1,#1] @Till adressen till student_grade+1
LDR R2,#1998
LDR R3,#5
STR R2,[R0,#4] @Till adressen till student_year+4
STR R3,[R1,#2] @Till adressen till student_grade+2
POP {R4, PC}
LDR R2,#2002
LDR R3,#4
STR R2,[R0,#6] @Till adressen till student_year+6
STR R3,[R1,#3] @Till adressen till student_grade+3
POP {R3, R4, PC} @Tar tillbaka v�rdet p� R3, R4 och LR->PC, dvs branchas tillbaka
sum_grade: @ uttryck1 - B�rjan av foor loopen, dvs g�r i = 0
LDR R1,=i
LDR R0,=0
STR R0,[R1] @ i = 0
For_Continue:
@ uttryck2 J�mf�r i med dess villkor (i v�rt fall i < 100) Om villkoret inte �r uppfyllt, dvs i >= 100 s� hoppar den till For_Break
LDR R0,i
CMP R0,#4 @ (i-4)->APSR
BGE For_Break
For_Do: @if sats h�r
@uttryck3 Vad som sker efter varje "loop" av for-loopen, dvs vi vill �ka i med 1!
LDR R1,=i
LDR R0,[R1]
ADD R0,R0,#1
STR R0,[R1] @ i = i+1
B For_Continue
For_Break: .... @Slutet av for-loopen, hit kommer den bara om "kravet" (n�r i inte �r < 100) ej uppfylls, eller en break kodas.
student_year: .SPACE 4*2 @MAX=4, short storlek = 2
student_grade: .SPACE 4*1 @MAX=4, char storlek = 1
c: .SPACE 4
//Anonym kod: EDA487-0059-PLX
// .h fil nedan:
/* Defintioner*/
//Följande GPIO_D definitioner används så att vi till en början kan konfigurera GPIO_D porten korrekt, men för att sedan också kunna skriva och läsa till GPIO_D
#define GPIO_D 0x40020C00
#define GPIO_D_MODER ((volatile unsigned int *) GPIO_D)
#define GPIO_D_OTYPER_HIGH ((volatile unsigned char *) GPIO_D+0x05)
#define GPIO_D_PUPDR_HIGH ((volatile unsigned short *) GPIO_D+0x0E)
#define GPIO_D_IDR_HIGH ((volatile unsigned char *) GPIO_D+0x11)
#define GPIO_D_ODR_LOW ((volatile unsigned char *) GPIO_D+0x14)
#define GPIO_D_ODR_HIGH ((volatile unsigned char *) GPIO_D+0x15)
//Följande STK definitioner används för att aktivera räknaren, och för att välja vilket värde den ska räkna till, samt vilket värde den är på.
#define STK_CTRL ((volatile unsigned int *) 0xE000E010)
#define STK_LOAD ((volatile unsigned int *) 0xE000E014)
#define STK_VAL ((volatile unsigned int *) 0xE000E018)
//Dessa används för att flytta vektortabellen bort från adressen 0.
//När vektor tabellen lägger på adressen 0 så befinner sig den i ROM-minne (Read Only Memory), dvs minnet går inte att ändra på där
//Och därav så måste vektor tabellen flyttas!
#define VECTOR_DEST 0x2001C000
#define SCB_VTOR ((volatile unsigned int *) 0xE000ED08)
#define SYSTICK_IRQ_HANDLER ((void (**)(void)) (VECTOR_DEST+0x3C))
//Används för att bestämma hur länge ett delay ska vara
#define DELAY_1S 168*1000*1000 //168 = 1 mikrosekund, 168*1000 = 1 millisekund, 168*1000*1000 = 1 sekund
#define DELAY_1MS 168*1000 //168 = 1 mikrosekund, 168*1000 = 1 millisekund
//Alla våra karaktärers "motsvarigheter" i 7-seg displayen
const unsigned char segs[] = {0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x67,0x77,0x7C,0x39,0x5E,0x79,0x71,0x40}; //0x40 = tecknet '-'
unsigned char elapsedTime = 0; //Hur mycket tid som har gått i millisekunder
unsigned char isCounting = 0; //Om tidtagningen är på eller inte
// .c fil nedan:
/*
* Konfigurerar port D så att den kan användas med en keypad på bitar 0-7,
* samt så att en 7-segment display kan användas på bitar 8-15
*/
void app_init(void)
{
//Keypad setup
*GPIO_D_MODER &= 0x0000FFFF;
*GPIO_D_MODER |= 0x55000000; //Sätter rätt pinnar till utpinnar/inpinnar, utan att ändra på dem som ej ska ändras
*GPIO_D_OTYPER_HIGH = 0x00; //Sätter pinnar till push-pull
*GPIO_D_PUPDR_HIGH = 0x00AA; //Sätter pinnar till pull-down
//7-seg setup
*GPIO_D_MODER &= 0xFFFF0000;
*GPIO_D_MODER |= 0x00005555; //Sätter rätt pinnar till utpinnar, utan att ändra på dem som ej ska ändras
//Avbrott setup (för icke-blockerande delay)
//Flyttar vektortabellen, å gör så att SYSTICK avbrottet använder vår irq_handler (som då körs varje gång när systick har räknat klart (och avbrott är aktiverat, dvs *STK_CTRL = 7))
*SCB_VTOR = VECTOR_DEST;
*SYSTICK_IRQ_HANDLER = systick_irq_handler;
}
//Följande delay ger en delay på 1 sekund och är blockerande,
//Dvs att ingen annan kod kan köras när denna körs
void delay_1s(void)
{
*STK_CTRL = 0;
*STK_LOAD = (DELAY_1S - 1);
*STK_VAL = 0;
*STK_CTRL = 5;
while( (*STK_CTRL & 0x10000 )== 0 );
*STK_CTRL = 0;
}
//Anropad delay_1s funktionen seconds antal gånger, så att vi får en delay på seconds antal sekunder
void delay(int seconds)
{
for(int i = 0; i < seconds; i++)
delay_1s();
}
//Följande delay ger en delay på 1 millisekund och är inte blockerande,
//Dvs att annan kod kan köras samtidigt som delayen. Detta fungerar med hjälp av avbrott
void delay_1milli(void)
{
*STK_CTRL = 0;
*STK_LOAD = (DELAY_1MS - 1);
*STK_VAL = 0;
*STK_CTRL = 7;
}
//Påbörjar tidtagningen, genom att sätta en variabel till 1 (true)
//Och att kalla på den icke-blockerande delayen delay_1milli();
void start_counter()
{
isCounting = 1;
delay_1milli();
}
//"Avslutar" tidtagningen genom att sätta en variabel till 0 (false)
//Denna variabel kollas sedan i en annan funktion, som i sin tur avslutar tidstagningen
void stop_counter()
{
isCounting = 0;
}
//Detta är vår avbrotts hanterare för delay_1ms.
//Den kallas varje gång som delay_ms har räknat klart, och är alltså funktionen
//som uppdaterar elapsedTime, så att vi får rätt löpen tid i millisekunder.
//Den stannar/slutar tidstagningen då isCounting är 0
void systick_irq_handler(void)
{
*STK_CTRL = 0;
elapsedTime += 1;
if(isCounting)
delay_1milli();
}
//Aktiverar rad row på keypaden, så att vi sedan kan läsa bitar från kolumnerna
void activateRow(int row)
{
*GPIO_D_ODR_HIGH = 0x10 << (row-1);
}
//Läser alla kolumner och returnerar ett värde beroende på vilken kolumn som är på
unsigned char getColumn(void)
{
unsigned char c;
c = *GPIO_D_IDR_HIGH;
if (c & 0x8) return 4;
if (c & 0x4) return 3;
if (c & 0x2) return 2;
if (c & 0x1) return 1;
return 0;
}
//Används för att returna den knappen som är nertryckt
unsigned char keyb(void) {
unsigned char key[] = {1,2,3,0xA,4,5,6,0xB,7,8,9,0xC,0xE,0,0xF,0xD};
for (unsigned char row = 1; row <= 4; row++) {
activateRow(row);
unsigned char col = getColumn();
if (col != 0) {
activateRow(0);
return key[4*(row-1)+(col-1)];
}
}
activateRow(0);
return 0xFF;
}
//Ändrar värde på 7-segment displayen så att den visar den nertryckta knappen
void out7seg(unsigned char c)
{
if(c > 16) {
*GPIO_D_ODR_LOW = 0;
return;
}
*GPIO_D_ODR_LOW = segs[c];
}
//Får 7-segments displayen att "blinka" med värdet character. Detta gör den numberOfTimes antal gånger
//Med en delay på 1 sekund mellan att den är på och av.
void toggle_display(int character, int numberOfTimes)
{
for(int i = 0; i < numberOfTimes; i++)
{
out7seg(character);
delay_1s();
out7seg(0xFF); //Släck displayen
delay_1s();
}
}
/*
* Programmets main funktion
* Målet med programmet är alltså att implementera ett form av reaktionstest
* För att göra detta så måste ett antal funktioner implementeras.
* Dessa är init, outseg och keyb, och även någon/några former av delay funktioner.
*/
void main(void)
{
app_init();
while(1) {
*GPIO_D_ODR_LOW = 0; //Släck display
//random() ger 0-127, delat med 16 ger 0-7.9375, dvs 0-7 för heltal Adderar man 1 så blir intervallet 1-8
int delayTime = random()/16 + 1;
delay(delayTime);
//random() ger 0-127, delat med 8 ger 0-15.875, dvs 0-15 för heltal
int randomNumber = random()/8;
//Skriver ut vårat slumpmässiga tal till 7-segment displayen
out7seg(randomNumber);
//Starta tidsmätning, med oblockerande "delay"
start_counter();
int key = keyb();
if(randomNumber == key)
{
stop_counter(); //Avslutar räknaren
if(elapsedTime > 1000) // Om det tog mer än 1 sekund
{
// Tänd/släck tecken '-' 3 gånger med 1 sekunds intervall
toggle_display(16, 3);
break; //Avsluta while-loopen
}
else // Om det tog mindre än 1 sekund
{
// Tänd/släck slumptal 3 gånger med 1 sekunds intervall
toggle_display(randomNumber, 3);
continue; // Fortsätt med loopen
}
}
}
}
//Anonym kod: EDA487-0059-PLX
// .h fil nedan:
//Dessa används för att konfigurera hela GPIO_D som en utport, men också för att sedan kunna skriva till den
#define GPIO_D 0x40020C00
#define GPIO_D_MODER ((volatile unsigned int *) GPIO_D)
#define GPIO_D_ODR_LOW ((volatile unsigned char *) GPIO_D+0x14)
#define GPIO_D_ODR_HIGH ((volatile unsigned char *) GPIO_D+0x15)
//Dessa används för att konfigurera en del av GPIO_E som en inport, men också för att sedan kunna läsa från den
#define GPIO_E 0x40021000
#define GPIO_E_MODER ((volatile unsigned int *) GPIO_E)
#define GPIO_E_IDR_HIGH ((volatile unsigned char *) GPIO_E+0x11)
//Används för att koppla rätt EXTI(x) avbrott till rätt port. I vårt fall så ska EXTI0 = 4 och EXTI1 = 4, så att de kopplas till port E.
#define SYSCFG_EXTICR1 ((volatile unsigned int*) 0x40013808)
#define EXTI_IMR ((volatile unsigned int*) 0x40013C00) //Aktiverar avbrott för en bit. Vi aktiverar avbrott för EXTI(x) Genom att sätta bit x till 1
#define EXTI_RTSR ((volatile unsigned int*) 0x40013C08) //Bestämmer om EXTI(x) ska ha avbrott på positiv flank
#define EXTI_FTSR ((volatile unsigned int*) 0x40013C0C) //Bestämmer om EXTI(x) ska ha avbrott på negativ flank
#define EXTI_PR ((volatile unsigned int*) 0x40013C14) //Om bit x är 1, så väntar ett avbrott på EXTI(x)
//Dessa två under "väljer" vad som ska ske när ett avbrott sker. Dessa kommer att peka på en funktion var.
#define EXTI1_IRQVEC ((void (**) (void)) 0x2001C05C)
#define EXTI0_IRQVEC ((void (**) (void)) 0x2001C058)
#define NVIC_ISPR0 ((volatile unsigned int *) 0xE000E100) //Används för att göra så att avbrott kan "avvakta" Här så sätter man ej bit x för EXTI(x). Rätt bit man ska sätta finns under IRQ_NUM i vektor tabellen
//IRQ numrerna för EXTI1 respektive EXTI0
#define NVIC_EXTI1_IRQ_BPOS (1<<7)
#define NVIC_EXTI0_IRQ_BPOS (1<<6)
//Bitarnas värde i bit x för EXTI(x)
#define EXTI1_IRQ_BPOS 2
#define EXTI0_IRQ_BPOS 1
//Dessa används för att flytta vektortabellen bort från adressen 0.
//När vektor tabellen lägger på adressen 0 så befinner sig den i ROM-minne (Read Only Memory), dvs minnet går inte att ändra på där
//Och därav så måste vektor tabellen flyttas!
#define VECTOR_DEST 0x2001C000
#define SCB_VTOR ((volatile unsigned int *) 0xE000ED08)
//Dessa är vårat nuvarande värde på S2 och S1
unsigned char s2_value = 0;
unsigned char s1_value = 0;
// .c fil nedan:
//Detta avbrottet sker då S2:s värde ändras "på" positiv flank.
//Sätter värdet på s2_value, genom att "toggla" det
//Det kanske ej är rätt sätt att göra det, men det framstod ej hur S2 och S1 signalerna fungerade helt
void irq_handler_exti1(void)
{
//Först så kollar vi om ett avbrott väntar på EXTI1
if(*EXTI_PR & EXTI1_IRQ_BPOS)
{
*EXTI_PR |= EXTI1_IRQ_BPOS; //Här så kvitterar vi avbrottet genom att sätta dess bit till 1. Detta görs så att ett avbrott kan ske igen.
s2_value = (s2_value == 0) ? 1 : 0; //Här så togglar vi värdet på s2_value. Dvs om s2_value == 0, så sätts den till 1, annars så sätts den till 0
}
}
//Detta avbrottet sker då S1:s värde ändras "på" positiv flank.
//Sätter värdet på s1_value, genom att "toggla" det
//Det kanske ej är rätt sätt att göra det, men det framstod ej hur S2 och S1 signalerna fungerade helt
void irq_handler_exti0(void)
{
//Först så kollar vi om ett avbrott väntar på EXTI0
if(*EXTI_PR & EXTI0_IRQ_BPOS)
{
*EXTI_PR |= EXTI0_IRQ_BPOS; //Här så kvitterar vi avbrottet genom att sätta dess bit till 1. Detta görs så att ett avbrott kan ske igen.
s1_value = (s1_value == 0) ? 1 : 0; //Här så togglar vi värdet på s1_value. Dvs om s1_value == 0, så sätts den till 1, annars så sätts den till 0
}
}
void init_app(void)
{
*GPIO_D_MODER = 0x55555555; //Sätt hela port D till utport
*GPIO_E_MODER &= 0x0000FFF0; //Sätt E15-8 och E1-0 till inportar, ändra ej på resten
//* Koppla PE1, PE0 till avbrottslinor EXTI1 och EXTI0 */
*SYSCFG_EXTICR1 &= 0xFF00;
*SYSCFG_EXTICR1 |= 0x0044;
/* Konfigurera EXTI1 och EXTI0 för att generera avbrott */
*EXTI_IMR |= (EXTI1_IRQ_BPOS + EXTI0_IRQ_BPOS);
/* Konfigurera EXTI1 och EXTI0 för avbrott på positiv flank */
*EXTI_RTSR |= (EXTI1_IRQ_BPOS + EXTI0_IRQ_BPOS);
/* Konfigurera EXTI1 och EXTI0 så att avbrott också sker på negativ flank */
*EXTI_FTSR |= (EXTI1_IRQ_BPOS + EXTI0_IRQ_BPOS);
/* Sätt upp avbrottsvektor, och gör så att vardera avbrott pekar på dess avbrottsfunktion */
*SCB_VTOR = VECTOR_DEST;
*EXTI1_IRQVEC = irq_handler_exti1;
*EXTI0_IRQVEC = irq_handler_exti0;
/* Konfigurera de bitar i NVIC som kontrollerar de avbrottslinor som EXTI1 och EXTI0 kopplas till */
*NVIC_ISPR0 |= (NVIC_EXTI1_IRQ_BPOS | NVIC_EXTI0_IRQ_BPOS);
}
/*
* Programmets main funktion
* Vi ska alltså kunna skriva ut olika data till två olika displayer som finns på D0-7 och D8-15.
* Men vi har bara en 8 bitars port ledig som vi kan använda för indata, å den kan ju inte skriva 8 olika bitar till två olika displayer.
* Därför så kopplas två multiplex signaler till E0 och E1, som i sin tur kopplas till externa avbrotten EXTI0 och EXTI1
* Med hjälp av dessa två signaler så kan vi, enligt tabellen i uppgiften, välja vilken display vi ska skriva ut datan (som finns på E8-15) till.
* Programmet kan testas med två 'Double Hexadecimal Displays' på port D, och två 'Dipswitches' på port E
*/
void main(void)
{
init_app();
while(1)
{
//Lägger ihop S2:s och S1:s värden. s2_value och s1_value kan bara ha värdet 0 och 1. Detta funkar för s2_value, som ligger på bit 0 (där värdet är 0 eller 1)
//Men för s1_value, som ligger på bit 1 som kan ha värderna 0 och 2, funkar inte detta. Därför så måste vi gångra den med 2
//Man behöver ej göra så, men genom att göra så här så undviker man t.ex några if-satser med 2 villkor i varje
char s_value = 2*s1_value + s2_value;
//Här bestämmer vi vad som ska hända för varje värde S.
switch(s_value)
{
case 0: //S1=0, S2=0
*GPIO_D_ODR_LOW = 0; //D0-7 = 0
break;
case 1: //S1=0, S2=1
*GPIO_D_ODR_HIGH = 0; //D8-15 = 0
break;
case 2: //S1=1, S2=0
*GPIO_D_ODR_LOW = *GPIO_E_IDR_HIGH; //D0-7 = E8-15
break;
case 3: //S1=1, S2=1
*GPIO_D_ODR_HIGH = *GPIO_E_IDR_HIGH; //D8-15 = E8-15
break;
}
}
}
//Anonym kod: EDA487-0059-PLX
// .h fil nedan:
//Sätter robotens bas adress, och skapar pekare till CTRL och STATUS registret
#define ROBOT_BASE 0x100000
#define ROBOT_CTRL ((volatile unsigned char *) ROBOT_BASE)
#define ROBOT_STATUS ((volatile unsigned char *) ROBOT_BASE+0x1)
//Följande register är vad robotens "mål" är
#define ROBOT_DATAY ((volatile unsigned short *) ROBOT_BASE+0x4)
#define ROBOT_DATAX ((volatile unsigned short *) ROBOT_BASE+0x6)
//Följande register get robotens aktuella position
#define ROBOT_POSY ((volatile unsigned short *) ROBOT_BASE+0x8)
#define ROBOT_POSX ((volatile unsigned short *) ROBOT_BASE+0xA)
//Följande ger bara diverse bitpositioner/värde så att man enkelt kan kolla/ändra värdet på varje bit
#define IRQ (1<<4)
#define ER (1<<2)
#define IE (1<<7)
#define IA (1<<6)
#define EN (1<<3)
#define RS (1<<0)
//Följande STK definitioner används för att aktivera räknaren, och för att välja vilket värde den ska räkna till, samt vilket värde den är på.
#define STK_CTRL ((volatile unsigned int *) 0xE000E010)
#define STK_LOAD ((volatile unsigned int *) 0xE000E014)
#define STK_VAL ((volatile unsigned int *) 0xE000E018)
#define DELAY_1MS 168*1000 //168 = 1 mikrosekund, 168*1000 = 1 millisekund
//Gör en enkel definition för en point, som innehåller x och y koordinater, samt en pekare till nästa point
typedef struct point
{
char x,y;
struct point *next;
} POINT, *PPOINT;
// .c fil nedan:
//Mellan dessa punkter så kan man dock lägga till andra punkter,
//Det viktiga är att roboten åtminstone går mellan dessa punkter!
PPOINT p4 = { 36,39, 0}; //"Målet"
PPOINT p3 = { 25,27, p4};
PPOINT p2 = { 12,14, p3};
PPOINT p1 = { 1,2, p2};
//Följande delay ger en delay på 1 millisekund och är blockerande,
//Dvs att ingen annan kod kan köras när denna körs
void delay_1milli(void)
{
*STK_CTRL = 0;
*STK_LOAD = (DELAY_1MS - 1);
*STK_VAL = 0;
*STK_CTRL = 5;
while( (*STK_CTRL & 0x10000 )== 0 );
*STK_CTRL = 0;
}
//Anroppar delay_1milli funktionen ms antal gånger, dvs totala delayet blir ms millisekunder
void delayMS(unsigned short ms)
{
for(int i = 0; i < ms; i++)
delay_1milli();
}
//Flyttar på roboten till de givna x och y koordinaterna
void move_robot(unsigned short x, unsigned short y)
{
//Ändrar mål koordinaterna så att roboten flyttar på sig
*ROBOT_DATAX = x;
*ROBOT_DATAY = y;
//Aktiverar roboten
*ROBOT_CTRL |= EN;
//while loopen körs undertiden som roboten inte är på mål koordinaterna, så all annan kod stoppas till roboten har kommit fram
while((*ROBOT_DATAX != *ROBOT_POSX) || (*ROBOT_DATAY != *ROBOT_POSY));
//Avaktiverar tillslut roboten
*ROBOT_CTRL &= ~EN;
}
//Gör så att roboten kan åka på en resa! Den börjar på punkt pos
//Punkter kan peka på andra punkter, så de blir att den åker till flera punkter
void trip_robot(PPOINT pos)
{
//Sparar senaste x-pos, så att delayTime kan räknas ut
short lastx = 0;
//Loopar igenom den länkade listan
while(pos->next != 0)
{
move_robot(pos->x, pos->y);
short delayTime = abs(pos->x - lastx); //Räknar ut hur lång delayen ska vara!
delayMS(delayTime);
}
//När loopen är klar, så måste den köra en gång till, annars så får den inte med sista koordinaten
move_robot(pos->x, pos->y);
short delayTime = abs(pos->x - lastx); //Räknar ut hur lång delayen ska vara!
delayMS(delayTime);
}
//Initierarn roboten, genom att resetta den och att flytta den till koordinater 0,0!
void init_robot()
{
*ROBOT_CTRL = RS; //Sätter RS till 1 så att vi nollställer alla värden
*ROBOT_CTRL = 0; //Sätter sedan RS till 0 (Allt annat är redan 0, så bara RS ändras);
move_robot(0,0); //Flyttar roboten till koordinater (0,0) enligt uppgiftsbeskrivningen
}
/*
* Målet är att skapa ett program som kan flytta på en robot. För detta så har vi fått robotens register.
* Vi måste ha en funktion som initierar roboten, en funktion som flyttar på roboten, och en funktion som kallar på roboten
* som låter roboten åka mellan flera punkter. Vi behöver också ett blockerande delay som är 1 millisekund lång,
* Samt en array/länkadlista med våra koordinater!
*/
void main(void)
{
init_robot(); //Initierar roboten
trip_robot(p1); //Startar robotens resa
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment