from palette import colorful_colors

[마이크로프로세서] C언어와 ARM 명령어 비교 예시2 (feat. GPIO) 본문

EE 학부과목/마이크로프로세서

[마이크로프로세서] C언어와 ARM 명령어 비교 예시2 (feat. GPIO)

colorful-palette 2023. 6. 12. 16:46

1. GPIO의 ODR 비트 조절하기

예시: GPIOA의 ODR의 8번 비트를 1로 켜기

 

C 언어:

GPIOA->ODR |= 1UL << 8; 		// GPIOA -> ODR의 bit8을 1로 만든다

 

ARM 어셈블리어:

GPIOA_BASE EQU 0x48000000	; GPIOA의 BASE 주소는 0x48000000이다.
GPIO_ODR EQU 0x14		; ODR은 GPIO BASE에서 0x14만큼 떨어져 있으므로

LDR r7, = GPIOA_BASE
LDR r1, [r7, #GPIO_ODR]		; r1에 GPIOA의 ODR에 있는 값을 불러온다
ORR r1, r1, #2_100000000	; 8번 비트를 1로 만든다
STR r1, [r7, #GPIO_ODR]		; GPIOA의 ODR에 다시 변경한 값을 저장한다.

 

 

2. GPIO OUTPUT

예시: GPIOB의 pin2번을 push-pull 방식으로 설정하여 LED를 켜고 싶을때:

 

C 언어:

RCC->AHB2ENR |= RCC_AHB2ENR_GPIOBEN;	// portB의 RCC clock을 enable 시켜준다
GPIOB->MODER &= ~(3UL<<4);		// bit4, 5번을 clear시킨다(pin2가 4,5번 비트고, "01"이 output mode니깐 우선 4번,5번 비트를 "00"으로 만들어준다.)
GPIOB->MODER |= 1UL<<4;			// bit4번을 1로 set시켜 "01"을 만든다.
GPIO->OTYPE &= ~(1UL<<2)		// pin2의 output type을 open-drain(1)이 아닌 push-pull(0)로 만든다
GPIO->ODR |= 1UL << 2;			// pin2의 비트(3번째 비트)를 1(HIGH)로 설정한다.

 

ARM 어셈블리어:

    AREA main, CODE, READONLY
    EXPORT _main		; main을 외부 파일에서도 쓸 수 있게 한다(linker에게 보일 수 있게 한다)

_main PROC
    ;/////GPIOB RCC 설정하기/////
    LDR r2, =RCC_BASE
    LDR r1, [r2, #RCC_AHB2ENR]
    ORR r1, r1, #2;			; RCC에서 GPIOB는 2번째 bit이므로, 해당 비트를 1 시켜준다.
    STR r1, [r2, #RCC_AHB2ENR]		; RCC의 GPIOB(2번째 비트)를 1로 켜는 작업 마무리
    
    LDR r3, = GPIOB_BASE
 
    ;/////GPIO_BASE의 pin2의 MODER 설정하기/////
    LDR r1, [r3, #GPIO_MODER]
    BIC r1, r1, #(3 << 4)
    ORR r1, r1, #(1 << 4)	; 최종적으로 pin2(4,5번 비트)를 01로 설정하게 된다.
    STR r1, [r3, #GPIO_MODER]	; GPIOB의 MODER의 pin2를 01로 설정하는 작업 마무리 
    
    ;/////Push-pull(0) or Open drain(1) 설정하기/////
    LDR r1, [r3, #GPIO_OTYPER]
    BIC r1, r1, #(1<<2)		; pin2번의(3번째 비트)를 0으로 설정해서 push-pull 방식으로 설정했다.
    STR r1, [r3, #GPIO_OTYPER]	
    
    ;/////ODR 설정해서 LED 켜기/////
    LDR r1, [r3, #GPIO_ODR]
    ORR r1, r1, #(1<<2)		; pin2번을 1로 설정한다. #(1<<2) 말고 #2_00000100으로 설정해도 된다. 
    STR r1, [r3, #GPIO_ODR]
stop
    B_stop
    ENDP
    END

 

 

3. GPIO INPUT

예시: GPIOA의 pin0번에서 pull-up, pull-down 사용하지 않고 입력값 받기

 

C 언어:

uint32_t input;
RCC->AHB2ENR |= RCC_AHB2ENR_GPIOAEN;	// RCC에서 GPIOA의 clock 을 open시킨다.
GPIOA->MODER &= ~3UL;		// GPIOA의 MODE를 input(00)으로 변경시킨다.
GPIO->PUPDR &= ~3UL;		// GPIOA의 pull-up, pull-down을 설정하지 않는다(00)

// pin0 읽기
input = (GPIOA->IDR & 1UL)	// GPIOA->IDR를 input변수에 넣는다

if (input == 0){
	// 입력이 안 들어왔을때 실행할 것들
}

else {
	// 입력이 들어왔을때 실행할 것들
}

 

ARM 어셈블리어:

    AREA main, CODE, READONLY
    EXPORT _main
    ENTRY
    
_main PROC
    ;////GPIOA RCC 설정하기////
    LDR r2, =RCC_BASE
    LDR r1, [r2, #RCC_AHB2ENER]
    ORR r1, r1, #1		; GPIOA는 첫번째 비트이므로, RCC를 1로 설정
    STR r1, [r2, #RCC_AHB2ENER]
    
    LDR r3, =GPIOA_BASE
    
    ;////GPIOA MODE - Input으로 변경시키기////
    LDR r1, [r3, GPIO_MODER]
    BIC r1, r1, #3 		; pin0을 00으로 바꾸고 싶으므로, MODER의 제일 처음 두 비트를 0으로 만든다
    STR r1, [r3, GPIO_MODER]
    
    ;////Pull up, Pull down 설정하기////
    LDR r1, [r3, #GPIO_PUPDR]
    BIC r1, r1, #3		; pin0을 00으로 만들어 pull up, pull down 둘 다 설정 안 함
    STR r1, [r3, #GPIO_PUPDR]
    
    ;////input 변수에 핀 상태 읽어오기////
    LDR r1, [r3, #GPIO_IDR]
    AND r1, r1, #1		; 나는 pin0만 관심있으므로, 첫번째 비트만 가져온다.
    CMP r1, #1			; r1과 1 비교, 
    BLEQ center_pressed		; r1 == 1일때 center_pressed 함수 호출
    BLNE center_not_pressed	; r1 != 1일때 center_not_pressed 함수 호출

stop
	B	stop
    ENDP

 

4. Interrupt Control Bits

예시: interrrupt number가 IRQn = j + 32xi 일때, 해당 interrupt의 ISER 혹은 ICER 접근하기

C 언어:

WordOffset = IRQn >> 5;		// WordOffset은 i, 몫이 된다.
BitOffset = IRQn & 0x1F;	// BitOffset은 j, 나머지가 된다. 
NVIC->ISER[WordOffset] = 1<<BitOffset;	// 해당 인터럽트 enable한다고 가정할 시, 이렇게 표현 가능하다. (dissable이라면 ICER로 대신)

 

ARM 어셈블리어:

; input arguments:
; r0: interrupt number of a peripheral interrupt
; r1: 1 = Enable, 0 = Disable

Peripheral_Interrupt_Enable		PROC
	PUSH {r4, lr}		; 이전 환경 저장
    AND r2, r0, #0x1F		; r2에는 32로 나눈 나머지인 j가 담긴다.
    MOV r3, #1		
    LSL r3, r3, r2		; r3에는 1을 j만큼 left shift시킨 값이 담긴다.
    LDR r4, =NVIC_BASE
    
    CMP r1, #0
    LDRNE r1, = NVIC_ISER0		; r1이 1이라면 enable, ISER이다.
    LDREQ r1, = NVIC_ICER0		; r1이 0라면 Disable, ICER이다.
    
    ADD r1, r4, r1		; r1에는 해당 NVIC의 ISER 혹은 ICER 주소가 담긴다.
    LSR r2, r0, #5		; r0를 32로 나눠 r2에는 몫 i가 담긴다.
    LSL r2, r2, #2		; 번지는 4씩 증가하므로 몫(i)에 4를 곱해준다.
    STR r3, [r1, r2]		; left shift한 비트를 [r1, r2]에 담는다.
    POP {r4, pc}		; 이전 환경 복귀
    ENDP