;This program shows that the system uses PC to return from interrupt not LR
;since LR's value is 0xFFFFFFF9
AREA Block1, DATA, READWRITE, ALIGN=2
cnt DCD 0x0102
AREA Ex1, CODE, READONLY, ALIGN=2
NVIC_PRI17_R EQU 0xE000E444
NVIC_PRI7_R EQU 0xE000E41C
NVIC_EN0_R EQU 0xE000E100
NVIC_EN1_R EQU 0xE000E104
GPIO_PORTF_LOCK_R EQU 0x40025520
GPIO_PORTF_CR_R EQU 0x40025524
GPIO_PORTF_AMSEL_R EQU 0x40025528
GPIO_PORTF_PCTL_R EQU 0x4002552C
GPIO_LOCK_KEY EQU 0x4C4F434B
GPIO_PORTF_DATA_R EQU 0x400253FC
GPIO_PORTF_DIR_R EQU 0x40025400
GPIO_PORTF_AFSEL_R EQU 0x40025420
GPIO_PORTF_PUR_R EQU 0x40025510
GPIO_PORTF_DEN_R EQU 0x4002551C
GPIO_PORTF_IBE_R EQU 0x40025408
GPIO_PORTF_IEV_R EQU 0x4002540C
GPIO_PORTF_IM_R EQU 0x40025410
GPIO_PORTF_RIS_R EQU 0x40025414
GPIO_PORTF_ICR_R EQU 0x4002541C
GPIO_PORTF_IS_R EQU 0x40025404
DELAY EQU 8000000 ; delay 0.5sec (=8000,000*1/16MHz)
SYSCTL_RCGCGPIO_R EQU 0x400FE608
EXPORT Start
EXPORT GPIOPortF_Handler
ENTRY
Start
BL Port_Init
BL PF_Int_Init
MOV R4, #1
;MSR PRIMASK, R4
MOV R0, #0xA1
LoopS
ADD R1, R0, #1
ADD R2, R1, #1
ADD R3, R2, #1
ADD R4, R3, #1
ADD R5, R4, #1
ADD R6, R5, #1
ADD R0, R6, #1
B LoopS
GPIOPortF_Handler
PUSH {LR}
BL Check_PF
POP {LR}
LDR R0, =GPIO_PORTF_ICR_R
MOV R1, #0x11
STR R1, [R0]
LDR R0, =cnt
LDR R1, [R0]
ADD R1, R1, #1
STR R1, [R0]
BX LR
B_PF0
LDR R4, = GPIO_PORTF_DATA_R
LDR R5, [R4]
AND R5, R5, #0x2
EOR R5, R5, #0x2
STR R5, [R4]
BX LR
B_PF4
LDR R4, = GPIO_PORTF_DATA_R
LDR R5, [R4]
AND R5, R5, #0x4
EOR R5, R5, #0x4
STR R5, [R4]
BX LR
Check_PF
LDR R0, =GPIO_PORTF_RIS_R
LDR R1, [R0]
ANDS R2, R1, #0x01
BNE B_PF0
ANDS R2, R1, #0x10
BNE B_PF4
BX LR
PF_Int_Init
MOV R1, #1
MSR PRIMASK, R1
LDR R0, =GPIO_PORTF_IM_R
MOV R1, #0x00
STR R1, [R0]
LDR R0, =GPIO_PORTF_IS_R
MOV R1, #0x00 ; Edge detect
STR R1, [R0]
LDR R0, =GPIO_PORTF_IBE_R
MOV R1, #0x00 ; 1 Edge
STR R1, [R0]
LDR R0, =GPIO_PORTF_IEV_R
MOV R1, #0x00 ; Falling Edge detect
STR R1, [R0]
LDR R0, = NVIC_PRI7_R
LDR R1, =0xFF0FFFFF
LDR R2, [R0]
AND R2, R2, R1
LDR R1, =0x00A00000 ; priority 5
ORR R2, R2, R1
STR R1, [R0]
LDR R0, = NVIC_EN0_R
LDR R1, =0x40000000
LDR R2, [R0]
ORR R2, R2, R1
STR R2, [R0]
MOV R2, #0x0
LDR R0, =GPIO_PORTF_IM_R
MOV R1, #0x11
STR R1, [R0]
MOV R1, #0
MSR PRIMASK, R1 ; store 0 to PRIMASK register to enable the global
intrrrupt
BX LR
Port_Init
; Enable Port F
LDR R1, =SYSCTL_RCGCGPIO_R
LDR R0, [R1]
ORR R0, R0, #0x20
STR R0, [R1]
NOP
NOP ; Wait for portF clock to stablize
; Port F Init, port F pin 4,0 for SW1, Sw2 respectively
LDR R0,=GPIO_PORTF_LOCK_R ; unlock portF
LDR R1,=0x4C4F434B
STR R1, [R0]
LDR R0,=GPIO_PORTF_CR_R
MOV R1, #0xFF
STR R1, [R0]
LDR R0, =GPIO_PORTF_DIR_R ;input portF pin 4 and 0
LDR R1, [R0]
BIC R1, R1, #0x11
;LDR R1, [R0]
ORR R1, R1, #0xE; output portF pin 7,3-1
STR R1, [R0]
LDR R0, =GPIO_PORTF_PUR_R
LDR R1, [R0]
ORR R1, R1, #0x11; pullup
STR R1, [R0]
LDR R0, =GPIO_PORTF_AMSEL_R
LDR R1, [R0]
BIC R1, R1, #0x1F
STR R1, [R0]
LDR R0, =GPIO_PORTF_DEN_R
LDR R1, [R0]
ORR R1, R1, #0x1F
STR R1, [R0]
LDR R0, =GPIO_PORTF_AFSEL_R
LDR R1, [R0]
BIC R1, R1, #0x1F
STR R1, [R0]
LDR R0, =GPIO_PORTF_PCTL_R
LDR R1, [R0]
LDR R2, =0xFFF00000
AND R1, R1, R2 ;
STR R1, [R0]
;Initialize cnt
LDR R0, =cnt
LDR R1, =0x0
STR R1, [R0]
BX LR
ALIGN
END
#include <stdint.h>
// Register Definitions
#define NVIC_PRI7_R (*((volatile uint32_t *)0xE000E41C))
#define NVIC_EN0_R (*((volatile uint32_t *)0xE000E100))
#define GPIO_PORTF_DATA_R (*((volatile uint32_t *)0x400253FC))
#define GPIO_PORTF_DIR_R (*((volatile uint32_t *)0x40025400))
#define GPIO_PORTF_IS_R (*((volatile uint32_t *)0x40025404))
#define GPIO_PORTF_IBE_R (*((volatile uint32_t *)0x40025408))
#define GPIO_PORTF_IEV_R (*((volatile uint32_t *)0x4002540C))
#define GPIO_PORTF_IM_R (*((volatile uint32_t *)0x40025410))
#define GPIO_PORTF_RIS_R (*((volatile uint32_t *)0x40025414))
#define GPIO_PORTF_ICR_R (*((volatile uint32_t *)0x4002541C))
#define GPIO_PORTF_AFSEL_R (*((volatile uint32_t *)0x40025420))
#define GPIO_PORTF_PUR_R (*((volatile uint32_t *)0x40025510))
#define GPIO_PORTF_DEN_R (*((volatile uint32_t *)0x4002551C))
#define GPIO_PORTF_LOCK_R (*((volatile uint32_t *)0x40025520))
#define GPIO_PORTF_CR_R (*((volatile uint32_t *)0x40025524))
#define GPIO_PORTF_AMSEL_R (*((volatile uint32_t *)0x40025528))
#define GPIO_PORTF_PCTL_R (*((volatile uint32_t *)0x4002552C))
#define SYSCTL_RCGCGPIO_R (*((volatile uint32_t *)0x400FE608))
#define GPIO_LOCK_KEY 0x4C4F434B
// Function prototypes for global interrupt control
void DisableInterrupts(void); // Maps to CPSID I
void EnableInterrupts(void); // Maps to CPSIE I
// Global variable
volatile uint32_t cnt = 0x0102;
// ***************** Port_Init *****************
// Initialize Port F pins for input and output
void Port_Init(void)
{
volatile uint32_t delay;
// Enable Port F clock
SYSCTL_RCGCGPIO_R |= 0x20;
delay = SYSCTL_RCGCGPIO_R; // Delay for clock to stabilize
// Unlock Port F
GPIO_PORTF_LOCK_R = GPIO_LOCK_KEY;
GPIO_PORTF_CR_R = 0xFF;
// Configure PF4, PF0 as inputs and PF3-PF1 as outputs
GPIO_PORTF_DIR_R &= ~0x11; // Clear PF4, PF0
GPIO_PORTF_DIR_R |= 0x0E; // Set PF3, PF2, PF1
// Enable pull-up resistors for PF4, PF0
GPIO_PORTF_PUR_R |= 0x11;
// Disable analog function
GPIO_PORTF_AMSEL_R &= ~0x1F;
// Enable digital I/O for PF4-PF0
GPIO_PORTF_DEN_R |= 0x1F;
// Disable alternate function
GPIO_PORTF_AFSEL_R &= ~0x1F;
// Clear Port control register for PF4-PF0
GPIO_PORTF_PCTL_R &= 0xFFF00000;
// Initialize cnt to 0
cnt = 0;
}
// ***************** PF_Int_Init *****************
// Configure Port F interrupts (PF0, PF4)
void PF_Int_Init(void)
{
DisableInterrupts(); // Disable global interrupts during setup
// Disable Port F interrupts during configuration
GPIO_PORTF_IM_R = 0x00;
// Configure PF4 and PF0 for edge-sensitive interrupts
GPIO_PORTF_IS_R = 0x00; // Edge sensitive
GPIO_PORTF_IBE_R = 0x00; // Not both edges
GPIO_PORTF_IEV_R = 0x00; // Falling edge detect
// Set priority 5 for Port F (Interrupt 30, bits 23-21 in PRI7)
NVIC_PRI7_R = (NVIC_PRI7_R & 0xFF0FFFFF) | 0x00A00000;
// Enable Interrupt 30 in NVIC (Bit 30 in EN0)
NVIC_EN0_R |= 0x40000000;
// Unmask interrupts for PF4 and PF0
GPIO_PORTF_IM_R = 0x11;
EnableInterrupts(); // Enable global interrupts
}
// ***************** GPIOPortF_Handler *****************
// ISR for Port F
void GPIOPortF_Handler(void)
{
// Check if PF0 caused the interrupt
if (GPIO_PORTF_RIS_R & 0x01)
{
// Toggle PF1 (equivalent to the AND/EOR logic in assembly)
GPIO_PORTF_DATA_R = (GPIO_PORTF_DATA_R & 0x02) ^ 0x02;
}
// Check if PF4 caused the interrupt
if (GPIO_PORTF_RIS_R & 0x10)
{
// Toggle PF2
GPIO_PORTF_DATA_R = (GPIO_PORTF_DATA_R & 0x04) ^ 0x04;
}
// Acknowledge the interrupt by clearing the flags for PF4 and PF0
GPIO_PORTF_ICR_R = 0x11;
// Increment global counter
cnt = cnt + 1;
}
// ***************** main *****************
int main(void)
{
volatile uint32_t dummy_r0, dummy_r1, dummy_r2, dummy_r3, dummy_r4, dummy_r5, dummy_r6;
Port_Init();
PF_Int_Init();
// Simulate the dummy addition loop in the assembly 'Start' routine
dummy_r0 = 0xA1;
while (1)
{
dummy_r1 = dummy_r0 + 1;
dummy_r2 = dummy_r1 + 1;
dummy_r3 = dummy_r2 + 1;
dummy_r4 = dummy_r3 + 1;
dummy_r5 = dummy_r4 + 1;
dummy_r6 = dummy_r5 + 1;
dummy_r0 = dummy_r6 + 1;
}
return 0;
}
가장 기본입니다. 어셈블리의 단순한 상수 정의(EQU)를 C 언어에서는 "이 주소는 메모리가 아니라 하드웨어 핀이니까 맘대로 최적화하지 말고 직접 건드려!"라는 뜻의 volatile 포인터로 캐스팅해야 합니다.
Assembly:
GPIO_PORTF_DIR_R EQU 0x40025400
C Language:
// Map hardware address using volatile pointer
#define GPIO_PORTF_DIR_R (*((volatile uint32_t *)0x40025400))
임베디드에서 가장 많이 쓰는 Read-Modify-Write 패턴입니다. 기존 값을 읽어와서(LDR), 특정 비트만 1로 합치고(ORR), 다시 저장(STR)하는 3줄이 C 언어에서는 |= 하나로 끝납니다.
Assembly:
LDR R0, =GPIO_PORTF_DEN_R ; 1. Load address into R0
LDR R1, [R0] ; 2. Read current value into R1
ORR R1, R1, #0x1F ; 3. Bitwise OR to set bits 0-4
STR R1, [R0] ; 4. Store modified value back
코드를 입력하세요
C Language:
```c
// Read, set bits 0-4 to 1, and write back
GPIO_PORTF_DEN_R |= 0x1F;
이 부분이 시험에서 가장 많이 헷갈리는 구간입니다. 어셈블리의 BIC(Bit Clear) 명령어는 명시한 비트를 0으로 밀어버립니다. C 언어에서는 이를 "반전시킨 값과 AND 연산(&= ~)" 하는 방식으로 똑같이 구현합니다.
Assembly:
LDR R0, =GPIO_PORTF_DIR_R
LDR R1, [R0]
BIC R1, R1, #0x11 ; Clear bit 4 and bit 0
STR R1, [R0]
C Language:
// Read, clear bits 4 and 0, and write back
GPIO_PORTF_DIR_R &= ~0x11;
회원님의 어셈블리 코드에서 스위치가 눌렸을 때 LED를 껐다 켰다(토글)하는 핵심 로직입니다. 어셈블리의 EOR(Exclusive OR)는 C 언어의 ^= (XOR) 연산자와 완벽하게 대응합니다.
Assembly (B_PF0 라벨 부분):
LDR R4, =GPIO_PORTF_DATA_R
LDR R5, [R4]
AND R5, R5, #0x2 ; Isolate bit 1 (PF1)
EOR R5, R5, #0x2 ; Toggle bit 1 using XOR
STR R5, [R4]
C Language:
// Isolate bit 1 and toggle it using XOR
GPIO_PORTF_DATA_R = (GPIO_PORTF_DATA_R & 0x02) ^ 0x02;
인터럽트가 발생했을 때 PF0 스위치가 눌렸는지, PF4 스위치가 눌렸는지 검사하는 로직입니다. 어셈블리는 ANDS(AND 연산 후 상태 플래그 업데이트)와 BNE(0이 아니면 점프)를 조합해서 씁니다.
Assembly (Check_PF 라벨 부분):
LDR R0, =GPIO_PORTF_RIS_R
LDR R1, [R0]
ANDS R2, R1, #0x01 ; Check if bit 0 is 1
BNE B_PF0 ; If not zero (True), branch to B_PF0
C Language:
// Check if PF0 caused the interrupt (bit 0 is 1)
if(GPIO_PORTF_RIS_R & 0x01) {
// Execute B_PF0 logic
}
단순히 레지스터가 아니라 RAM에 있는 전역 변수 cnt의 값을 1 올리는 작업도 구조는 똑같습니다.
Assembly:
LDR R0, =cnt ; Load memory address of 'cnt'
LDR R1, [R0] ; Read value from memory
ADD R1, R1, #1 ; Add 1
STR R1, [R0] ; Store value back to memory
C Language:
// Read memory, add 1, write back to memory
cnt = cnt + 1;