솔루션빌드 : 컴파일러가 코드의 무결함을 판단, 무결성이 검증되면 컴파일러가 해석 후 실행(기계어)파일 생성
.exe .elf .hex 등
디버그 : 기계어 파일을 실행해 메모리에 직접 접근하여 확인
실수 계산에는 오차가 발생하므로 여러번 실수 계산이 누적되면 다른 값으로 변경될 수 있다. 따라서 casting을 통해 정수로 표현해주는것이 좋다.
주로 풀업 스위치를 사용하는 이유는 풀다운에서 vdd의 전원이 저항없이 그대로 mcu로 흘러들어가 위험부담이 생긴다.
PLL : 주파수 뻥튀기
DMA : CPU대신 메모리와 I/O장치 사이의 데이터교환을 제어
Firmware Program : 기계장치를 동작시키기 위한 프로그램
Stack Overflow : stack영역에 메모리가 계속 추가되다가 메모리영역이 over되어 heap영역까지 도달했을 때
ST의 32bit MCU로 cortex-M4 탑재
임베디드 실습에는 NUCLEO보드를 사용한다.
STM32CubeIDE로 개발한다.
사용할 MCU를 선택하고 프로젝트를 만들어준다.
초기 상태는 핀 연결이 설정되지 않은 상태다. 처음에 SWD를 설정해준다.
RCC(Reset & Clock Control) : STM32에서 사용하는 Clock을 설정
HSI(내부)와 HSE(외부) 두개의 Clock이 있다.
실습에서는 16MHz의 HSI를 PLL을 통해 HCLK : 100MHz로 만들어 사용했다.
보드의 내부 LED와 내부 button을 사용하기위해 핀설정을 해준다.
pin설정 이후 code generation을 해준다.
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
위와 같이 주석 사이에 코드를 작성해야 UI에서 설정 후 code generation을 해도 작성했던 코드가 사라지지 않는다.
main.c에서 코드를 작성하지 않고 새로운 source파일 user.c를 만들어 작성할 것이다.
#include "user.h"
void User_Task(void){
while(1){
if (!HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_13)) { // 버튼을 눌렀을 때
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, 0); // LED_OFF
}
else { // 안눌렀을 때
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, 1); // LED_ON
}
}
}
HAL 라이브러리 함수를 사용해 보드 내부의 버튼 상태에 따라 내부 LED를 ON/OFF동작시킨다. 내부 스위치는 Pull-up으로 동작한다. 따라서 버튼이 눌리면 0이 되고 평시에는 1을 유지한다.
#include "user.h"
unsigned int sec1ms=0;
void User_Task(void){
while(1){
if(sec1ms>=500){ // 0.5초마다 LED Toggle
sec1ms = 0;
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
}
}
}
extern unsigned int sec1ms; // 외부에서 사용하기 위해 extern으로 선언, 초기화 불가
user.h에 extern 변수 추가
void SysTick_Handler(void)
{
/* USER CODE BEGIN SysTick_IRQn 0 */
++sec1ms;
/* USER CODE END SysTick_IRQn 0 */
HAL_IncTick();
/* USER CODE BEGIN SysTick_IRQn 1 */
/* USER CODE END SysTick_IRQn 1 */
}
stm32f4xx_it.c의 SysTick_Handler에 ++sec1ms;를 추가해준다.
#include "user.h"
unsigned char sw_flag = 0;
void User_Task(void){
while(1){
if (!HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_13) && (sw_flag == 0)) { // Pushed
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
sw_flag = 1;
}
else if (HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_13) && (sw_flag == 1)) { // Released
sw_flag = 0;
}
}
}
stm32에 내장된 PC13 Push Button을 사용하면 눌렀을 때와 뗐을 때의 동작을 구분해줄 수 있다. flag변수를 하나 선언하고, 버튼의 상태와 flag의 상태를 함께 비교하며 Push일때 동작과 Release일때 동작을 구분지을 수 있다.
이전까지 사용한 버튼 동작은 프로그램이 동작할 때 계속해서 물어보는(요청) 방식 즉 Polling방식을 사용했다. 이번에는 인터럽트가 발생했을 때 특정동작을 하는 인터럽트 방식을 사용해보겠다.
인터럽트가 발생하면 진행중인 레지스터를 stack에 저장하고 ISR을 진행한다. ISR 실행이 끝나면 다시 진행중이던 레지스터를 복원하여 이전 실행 위치부터 계속 작업을 진행한다.
PC13을 EXTI로 핀설정한다.
mode는 Falling-edge, Rising-edge, Falling/Rising-edge방식이 있다. 실습에서는 Falling선택한다.
NVIC(Nested Vectored Interrupt Controller)는 STM32에서 인터럽트 제어를 담당한다. Nested는 인터럽트의 우선순위에 따라 순위가 높은 순서대로 처리하고 다시 순위가 낮은 인터럽트를 처리한다. Vectored는 미리 정해진 vector table에 따라 인터럽트 핸들러 주소가 정해져있음을 말한다.
NVIC에서 enable을 체크하고 우선순위를 정할 수 있다.
code generation을 진행하면 stm32f4xx_it.c에 IRQHandler가 생성된다.
void EXTI15_10_IRQHandler(void)
{
/* USER CODE BEGIN EXTI15_10_IRQn 0 */
/* USER CODE END EXTI15_10_IRQn 0 */
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_13);
/* USER CODE BEGIN EXTI15_10_IRQn 1 */
/* USER CODE END EXTI15_10_IRQn 1 */
}
인터럽트가 호출될 때마다 Callback함수가 실행된다. 이 Callback함수를 이용해 인터럽트 실행코드를 작성할것이다.
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin){ // 인터럽트 발생 시 수행
if(GPIO_Pin == GPIO_PIN_13){
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
}
}
PC13 버튼이 누르면 High->Low로 되는 Falling-edge가 발생하고, falling-edge를 detect한 CPU는 인터럽트를 실행한다.
외부인터럽트는 Level이 아닌 Edge를 탐지한다. 또한 Callback함수 안에서는 연산 코드와 같은 리소스를 많이 먹는 코드를 작성하면 안된다. 만약 리소스를 많이 먹게되면 main함수의 scan rate이 줄어들어 프로그램이 불안정해진다.