[STM32#32] 블루필보드로 전자산업기사, 임베디드기능사 국가자격증 실기시험 공개문제에 있는 일부 문제를 해결해보기!(녹칸다 내맘대로 STM32)
프로그래밍/STM32 2026. 2. 4. 18:40
https://youtube.com/live/Pl1SAaUqfII
[STM32#32] 블루필보드로 전자산업기사, 임베디드기능사 국가자격증 실기시험 공개문제에 있는 일부 문제를 해결해보기!(녹칸다 내맘대로 STM32)
심심한녹칸다의 내맘대로 STM32시리즈이다!
STM32시리즈의 모든 자료는 구글 슬라이드에 작성하고 모두에게 공유되어있음!
https://docs.google.com/presentation/d/1myA5iYbjuKsLWLqtRLKAiRfwUwvqB1d1RGjiMIIgp3I/edit?slide=id.g3bcab2a4904_2_225#slide=id.g3bcab2a4904_2_225
그냥 막 하면 재미 없으니 국가 자격증 시험에 있는 문제를 STM32로 접근해봅시당!
1.STM32의 PB9에 주파수발생기를 연결하고 PB8에 LED를 연결해서 주파수 발생기에서 엣지를 검출할때 마다 PB8의 LED가 토글되도록 하시오!

/* USER CODE BEGIN 0 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if(GPIO_Pin == GPIO_PIN_9)
{
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_8);
}
}
/* USER CODE END 0 */
2.주파수 발생기에서 나오는 신호를 검출해서 1씩 증가하는 카운터를 구성한다음 단위시간으로 나눠서 USB CDC로 출력하시오!

/* USER CODE BEGIN 0 */
volatile uint16_t cnt = 0;
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if(GPIO_Pin == GPIO_PIN_9)
{
//10ms초마다 1씩 증가
cnt++;
}
}
/* USER CODE END 0 */
/* USER CODE BEGIN 2 */
uint8_t buff[100];
/* USER CODE END 2 */
while (1)
{
//1초 간격으로 cnt값을 USB CDC로 출력하고 cnt를 0으로 초기화한다!
sprintf(buff,"now_cnt = %d\n",cnt);
CDC_Transmit_FS(buff, strlen(buff));
cnt = 0;
HAL_Delay(1000);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
3.예제2에서 측정한 주파수값을 1602LCD(PB6,PB7)에 형식에 맞게 출력하시오!

#define delay_ms HAL_Delay
#define SYS_CLOCK 72
#define SYSTICK_LOAD 72000-1
uint32_t millis_cnt = 0;
uint32_t millis() {
return millis_cnt;
}
uint32_t micros() {
return (millis_cnt & 0x3FFFFF) * 1000 + (SYSTICK_LOAD - SysTick->VAL) / SYS_CLOCK;
}
void delay_us(uint32_t us) { // 64MHz 보정
if (us > 1) {
uint32_t count = us * 7 - 6;
while (count--);
} else {
uint32_t count = 2;
while (count--);
}
}
#define ADDRESS 0x27<<1
#define RS1_EN1 0x05
#define RS1_EN0 0x01
#define RS0_EN1 0x04
#define RS0_EN0 0x00
#define BackLight 0x08
// RS-Q0 / RW-Q1 / EN-Q2 / BackLight-Q3 / D4-Q4 / D5-Q5 / D6-Q6 / D7-Q7
void LCD_DATA(uint8_t data) {
uint8_t temp=(data & 0xF0)|RS1_EN1|BackLight;
while(HAL_I2C_Master_Transmit(&hi2c1, ADDRESS, &temp, 1, 1000)!=HAL_OK);
temp=(data & 0xF0)|RS1_EN0|BackLight;
while(HAL_I2C_Master_Transmit(&hi2c1, ADDRESS, &temp, 1, 1000)!=HAL_OK);
delay_us(4);
temp=((data << 4) & 0xF0)|RS1_EN1|BackLight;
while(HAL_I2C_Master_Transmit(&hi2c1, ADDRESS, &temp, 1, 1000)!=HAL_OK);
temp = ((data << 4) & 0xF0)|RS1_EN0|BackLight;
while(HAL_I2C_Master_Transmit(&hi2c1, ADDRESS, &temp, 1, 1000)!=HAL_OK);
delay_us(50);
}
void LCD_CMD(uint8_t cmd) {
uint8_t temp=(cmd & 0xF0)|RS0_EN1|BackLight;
while(HAL_I2C_Master_Transmit(&hi2c1, ADDRESS, &temp, 1, 1000)!=HAL_OK);
temp=(cmd & 0xF0)|RS0_EN0|BackLight;
while(HAL_I2C_Master_Transmit(&hi2c1, ADDRESS, &temp, 1, 1000)!=HAL_OK);
delay_us(4);
temp=((cmd << 4) & 0xF0)|RS0_EN1|BackLight;
while(HAL_I2C_Master_Transmit(&hi2c1, ADDRESS, &temp, 1, 1000)!=HAL_OK);
temp=((cmd << 4) & 0xF0)|RS0_EN0|BackLight;
while(HAL_I2C_Master_Transmit(&hi2c1, ADDRESS, &temp, 1, 1000)!=HAL_OK);
delay_us(50);
}
void LCD_CMD_4bit(uint8_t cmd) {
uint8_t temp=((cmd << 4) & 0xF0)|RS0_EN1|BackLight;
while(HAL_I2C_Master_Transmit(&hi2c1, ADDRESS, &temp, 1, 1000)!=HAL_OK);
temp=((cmd << 4) & 0xF0)|RS0_EN0|BackLight;
while(HAL_I2C_Master_Transmit(&hi2c1, ADDRESS, &temp, 1, 1000)!=HAL_OK);
delay_us(50);
}
void LCD_INIT(void) {
delay_ms(100);
LCD_CMD_4bit(0x03); delay_ms(5);
LCD_CMD_4bit(0x03); delay_us(100);
LCD_CMD_4bit(0x03); delay_us(100);
LCD_CMD_4bit(0x02); delay_us(100);
LCD_CMD(0x28); // 4 bits, 2 line, 5x8 font
LCD_CMD(0x08); // display off, cursor off, blink off
LCD_CMD(0x01); // clear display
delay_ms(3);
LCD_CMD(0x06); // cursor movint direction
LCD_CMD(0x0C); // display on, cursor off, blink off
}
void LCD_XY(char x, char y) {
if (y == 0) LCD_CMD(0x80 + x);
else if (y == 1) LCD_CMD(0xC0 + x);
else if (y == 2) LCD_CMD(0x94 + x);
else if (y == 3) LCD_CMD(0xD4 + x);
}
void LCD_CLEAR(void) {
LCD_CMD(0x01);
delay_ms(2);
}
void LCD_PUTS(char *str) {
while (*str) LCD_DATA(*str++);
}
/* USER CODE BEGIN 0 */
volatile uint16_t cnt = 0;
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if(GPIO_Pin == GPIO_PIN_9)
{
//10ms초마다 1씩 증가
cnt++;
}
}
/* USER CODE END 0 */
/* USER CODE BEGIN 2 */
uint8_t line1[20];
uint8_t line2[20];
LCD_INIT(); //1회호출(메인루프 시작전)
LCD_XY(0, 0) ; LCD_PUTS("[Pulse Mode] "); //첫번째줄
/* USER CODE END 2 */
while (1)
{
//1초간격으로 1602LCD에 출력한다!
sprintf(line2,"Freq: %3dHz ",cnt);
LCD_XY(0, 1) ; LCD_PUTS(line2); //두번째줄
cnt = 0;
HAL_Delay(1000);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
4.전자산업기사 공개문제 2번에서 주파수발생기로 시계만드는 부분을 블루필보드로 해결하시오!

//(LCD코드는 생략)
/* USER CODE BEGIN 0 */
volatile uint16_t h = 11; //시
volatile uint16_t m = 59; //분
volatile uint16_t s = 50; //초
volatile uint16_t ms = 0; //밀리초
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if(GPIO_Pin == GPIO_PIN_9)
{
//10ms초마다 1씩 증가
ms++;
if(ms >= 100){
ms = 0;
s++;
}
if(s >= 60){
s = 0;
m++;
}
if(m >= 60){
m = 0;
h++;
}
if(h >= 24){
h = 0;
}
}
}
/* USER CODE END 0 */
/* USER CODE BEGIN 2 */
uint8_t line1[20];
uint8_t line2[20];
LCD_INIT(); //1회호출(메인루프 시작전)
LCD_XY(0, 0) ; LCD_PUTS("[ DigitalClock ]"); //첫번째줄
/* USER CODE END 2 */
while (1)
{
//1초간격으로 1602LCD에 출력한다!
sprintf(line2,"Freq: %3dHz ",cnt);
LCD_XY(0, 1) ; LCD_PUTS(line2); //두번째줄
cnt = 0;
HAL_Delay(1000);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
while (1)
{
//1초간격으로 1602LCD에 출력한다!
sprintf(line2,"[%s]%02d:%02d:%02d:%02d ",(h>=12)?"PM":"AM",h,m,s,ms);
LCD_XY(0, 1) ; LCD_PUTS(line2); //두번째줄
HAL_Delay(10);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
5.임베디드기능사에서 나오는 문제를 P6~P9까지를 PB6~PB9까지 연결 P10을 PB4, P11을 PB5에 연결해서 FND에 숫자 두자리(15)를 출력하시오!

/* USER CODE BEGIN 0 */
void set_fnd(uint8_t num){
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, num&0b0001); //하위
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, (num>>1)&0b0001);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, (num>>2)&0b0001);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, (num>>3)&0b0001); //상위
}
uint8_t fnd_num = 15;
uint8_t fnd_sw = 0;
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(fnd_sw == 0){
//십의자리
set_fnd(fnd_num/10);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_4, 1);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, 0);
fnd_sw = 1;
}else if(fnd_sw == 1){
//일의자리
set_fnd(fnd_num%10);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_4, 0);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, 1);
fnd_sw = 0;
}
}
/* USER CODE END 0 */
/* USER CODE BEGIN 2 */
HAL_TIM_Base_Start_IT(&htim3);
/* USER CODE END 2 */
6.예제5에서 PB12,PB13에 버튼을 1개씩 추가하고 PB12버튼을 누르면 FND에 1씩 업카운트되고 PB13을 누르면 다운카운트되는데 표시되는 숫자의 범위가 00~99까지 되도록 하시오!

/* USER CODE BEGIN 0 */
void set_fnd(uint8_t num){
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, num&0b0001); //하위
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, (num>>1)&0b0001);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, (num>>2)&0b0001);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, (num>>3)&0b0001); //상위
}
uint8_t fnd_num = 15;
uint8_t fnd_sw = 0;
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(fnd_sw == 0){
//십의자리
set_fnd(fnd_num/10);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_4, 1);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, 0);
fnd_sw = 1;
}else if(fnd_sw == 1){
//일의자리
set_fnd(fnd_num%10);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_4, 0);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, 1);
fnd_sw = 0;
}
}
/* USER CODE END 0 */
/* USER CODE BEGIN 2 */
HAL_TIM_Base_Start_IT(&htim3);
for(int i = 0;i<2;i++){
fnd_control = 1;
fnd_num = 88;
HAL_Delay(1000);
fnd_control = 0;
HAL_Delay(1000);
}
fnd_control = 1;
fnd_num = 0;
/* USER CODE END 2 */
while (1)
{
if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_12) == 0){
if(fnd_num != 99){
fnd_num++;
}
HAL_Delay(200);
}
if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_13) == 0){
if(fnd_num != 0){
fnd_num--;
}
HAL_Delay(200);
}
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
7.예제6에서 PB12를 누르면 0에서 99까지 0.5초간격으로 업카운트되고 PB13을 누르면 99에서 0까지 1씩 다운카운트되는 원래 임베디드기능사에 있는 문제를 구현하시오!

/* USER CODE BEGIN 0 */
void set_fnd(uint8_t num){
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, num&0b0001); //하위
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, (num>>1)&0b0001);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, (num>>2)&0b0001);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, (num>>3)&0b0001); //상위
}
uint8_t fnd_num = 15;
uint8_t fnd_sw = 0;
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(fnd_sw == 0){
//십의자리
set_fnd(fnd_num/10);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_4, 1);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, 0);
fnd_sw = 1;
}else if(fnd_sw == 1){
//일의자리
set_fnd(fnd_num%10);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_4, 0);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, 1);
fnd_sw = 0;
}
}
/* USER CODE END 0 */
/* USER CODE BEGIN 2 */
HAL_TIM_Base_Start_IT(&htim3);
for(int i = 0;i<2;i++){
fnd_control = 1;
fnd_num = 88;
HAL_Delay(1000);
fnd_control = 0;
HAL_Delay(1000);
}
fnd_control = 1;
fnd_num = 0;
uint32_t t = 0;
//task_num이 0이면 아무것도 안함, 1이면 task1, 2면 task2
uint8_t task_num = 0;
/* USER CODE END 2 */
while (1)
{
if(task_num == 1){
if(HAL_GetTick() - t >= 500){
t = HAL_GetTick();
//0부터 상승
if(fnd_num == 99){
fnd_num = 0;
}else{
fnd_num++;
}
}
}else if(task_num == 2){
if(HAL_GetTick() - t >= 500){
t = HAL_GetTick();
//99부터 하강
if(fnd_num == 0){
fnd_num = 99;
}else{
fnd_num--;
}
}
}
if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_12) == 0){
//task1 작동
task_num = 1;
fnd_num = 0;
HAL_Delay(200);
}
if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_13) == 0){
//task2 작동
task_num = 2;
fnd_num = 99;
HAL_Delay(200);
}
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}

