이제 드디어 LED를 켜볼 것이다. 간단한 동작을 위해 참 돌아돌아 온 것 같다. 이번 글에서는 보드에 장착되어 있는 LED를 주기적으로 키고 끌 예정이다.
GPIO(General Purpose Input Output)는 입력이나 출력을 포함한 동작이 런타임 시에 사용자에 의해 제어될 수 있는, 집적 회로나 전기회로 기판의 디지털 신호 핀이다. GPIO를 조작하기 위해서는 다음과 같으 과정을 거쳐야 한다.
- 조작할 pin을 포함한 포트에 클럭소스를 배분한다.
- 해당 pin 입출력을 설정한다.
- 레지스터를 조작하여 디지털 출력을 조정한다.
사용하는 레지스터는 다음과 같다.
각각의 레지스터에 대해서 간단하게 설명해 보겠다. APB2ENR 레지스터는 입출력 장치에 클럭 소스를 배분할 지 안할 지를 결정한다. CPIO_CRL 레지스터는 0 ~ 7 번 pin에 대해서 각각 어떤 입출력 동작을 어떻게 수행할 지를 결정한다. BSRR 레지스터는 출력 모드에서 ouput data register가 0일지 1일지를 결정하는 역할을 한다. 더 자세하게 알 고싶다면 reference manual을 참고해라.(메뉴얼을 보면서 실습을 하는 것이 훨씬 더 공부가 많이 될것이다!!!) BSx 부분이 1인 경우 1로 출력되고, BRx 부분이 1인 경우 0으로 출력된다. 이 점을 이용하여 led를 키고 끄는 동작을 할 수 있도록 함수를 작성해보자.
우리가 동작할 LED는 Port A, 5 번 pin에 위치해 있다.
/*register.h*/
#define PERIPHERAL_BASE ((uint32_t)0x40000000)
#define APB2PERIPH_BASE (PERIPHERAL_BASE + 0x10000)
#define AHBPERIPH_BASE (PERIPHERAL_BASE + 0x20000)
#define RCC_BASE (AHBPERIPH_BASE + 0x1000)
#define GPIOA_BASE (APB2PERIPH_BASE + 0x0800)
typedef struct
{
uint32_t CR;
uint32_t CFGR;
uint32_t CIR;
uint32_t APB2RSTR;
uint32_t APB1RSTR;
uint32_t AHBENR;
uint32_t APB2ENR;
uint32_t APB1ENR;
uint32_t BDCR;
uint32_t CSR;
} RCC_type;
typedef struct
{
uint32_t CRL;
uint32_t CRH;
uint32_t IDR;
uint32_t ODR;
uint32_t BSRR;
uint32_t BRR;
uint32_t LCKR;
} GPIO_type;
#define RCC ((volatile RCC_type *) RCC_BASE)
#define GPIOA ((volatile GPIO_type *) GPIOA_BASE)
/*led.c*/
#include "led.h"
void ledInit(void)
{
//clock source
uint32_t reg32;
RCC->APB2ENR |= (1 << 2);
reg32 = GPIOA->CRL;
reg32 &= ~((uint32_t)0b1111 << 20);
GPIOA->CRL = reg32 | (0b10 << 20) | (0b00 << 22); // 5번 pin : ouptut(2MHz), General push-pull
}
//port 파라미터는 필요없지만 어떤 포트와 핀을 동작시키는 지 명확하게 하기 위해 넣었다.
void ledOn(uint8_t port, uint8_t pin)
{
//GPIOA->ODR |= (1 << pin);
GPIOA->BSRR |= (1 << pin);
}
void ledOff(uint8_t port, uint8_t pin)
{
//GPIOA->ODR &= ~(1 << pin);
GPIOA->BSRR |= (1 << (pin + 16));
}
void ledToggle(uint8_t port, uint8_t pin)
{
if((GPIOA->ODR & (1 << pin)) == (1 << pin))
ledOff(port, pin);
else
ledOn(port, pin);
}
이제는 SysTick을 설정해 줄 것이다. SysTick은 Cortex-M에서 지원하는 24 bit 타이머이다. 모든 MCU가 SysTick을 지원하지는 않지만, 우리가 사용하는 MCU에서는 지원을 한다. SysTick이 동작하는 원리는 간단하다. Reload register를 이용하여 한 Tick마다 줄어드는 SysTick Current Value Register의 값을 0이 될때마다 Reload Register를 이용하여 갱신하고, 동시에 SysTick interrupt를 발생키는 것을 반복하는 것이다. 예를 들어 72MHz 클럭을 사용하여 1ms system timer를 만든다고 가정하자. 그렇다면 Tick이 72000 번 발생하여야 1ms가 지난 것이 될 것이다. 이제 72MHz, 1ms SysTick Timer를 작동시켜 보자. 우리는 다음과 같은 것들을 해주어야 한다.
- Systick 타이머를 설정해준다.
- Reload Register에 갱신되는 값을 넣어준다.
- Current Value Register를 0으로 초기화 시켜준다.
- Systick Timer를 작동시킨다.
- Systick Interrupt가 발생할 때마다 어떤 동작을 해줄지 함수를 작성한다. 우리의 목표는 해당 인터럽트를 이용하여 msDelay(uint32_t ms)를 만드는 것이다.
다음은 코드이다.
/*register.h*/
#define SYSTICK_BASE ((uint32_t)0xE000E010)
typedef struct
{
uint32_t CTRL;
uint32_t LOAD;
uint32_t VAL;
uint32_t CALIB;
} STK_type;
#define STK ((volatile STK_type *) SYSTICK_BASE)
/*clock.c*/
void systick_init(void)
{
STK->CTRL |= 1 << 1; //counting down mode
STK->CTRL |= 1 << 2; // CLKSRC selection
STK->LOAD = 71999; // 72000 tick = 1ms (72MHz)
STK->VAL = 0; //current value init
STK->CTRL |= 1 << 0; //enable systick
}
uint32_t SysTick(void)
{
extern uint32_t SYS_TIM;
return SYS_TIM;
}
void msDelay(uint32_t ms)
{
extern uint32_t TIMER;
TIMER = ms;
while (TIMER > 0);
}
/*startup.c*/
uint32_t SYS_TIM; //just simple timer(i didn't consider overflow)
uint32_t BIGGER_SYS_TIM;
uint32_t TIMER;
void isr_systick()
{
if (TIMER > 0)
TIMER--;
if(SYS_TIM < MAX_TIM)
SYS_TIM++;
else
{
SYS_TIM = 0;
BIGGER_SYS_TIM++;
}
}
이제는 main 함수를 작성해보자.
#include "clock.h"
#include "led.h"
int main()
{
rcc_init();
systick_init();
while(1)
{
ledToggle('A', 5);
msDelay(100);
}
return 0;
}
100ms 간격으로 led가 꺼졌다가 켜졌다가 하는 것을 볼 수 있을 것이다. 다음 글에서는 Button을 이용하여 두 가지 방식(Polling, Interrupt)으로 led를 동작시켜볼 것이다.
추가로 오늘 했던 내용을 바탕으로 gpio 함수들을 작성해보는 것도 좋을 것 같다. 다음 글에서 부터는 led를 동작시킬 때 보다 일반화된 gpio 함수를 만들어서 사용할 것이다. 별도로 다루지는 않고 Git을 참고하면 좋을 것 같다.
https://github.com/youngSeok0413/embedded-system-architecture.git
GitHub - youngSeok0413/embedded-system-architecture: repository for studying embedded system architecture
repository for studying embedded system architecture - GitHub - youngSeok0413/embedded-system-architecture: repository for studying embedded system architecture
github.com
'뭐라도 만들어보자!!( 프로젝트 ) > 임베디드' 카테고리의 다른 글
JTAG/SWD 동작 안하는 경우 봐야할 것!! (0) | 2025.02.22 |
---|---|
0. PPG에 대하여 (0) | 2024.05.07 |
4. 임베디드 실습 : Main System Clock Source (1) | 2024.01.23 |
3. 임베디드 실습 : NVIC ~ 빈 main() (1) | 2024.01.23 |
2. 임베디드 실습 : ldscript 작성 (0) | 2024.01.21 |