printf("ho_tari\n");
7일차 본문
2025.04.02
Piezo Buzzer


<buzzer.c>
#include "buzzer.h"
#include "button.h"
/*************************************************************************************************************
옥타브 및 음계별 주파수표(단위:Hz)
음계 0Oct 1Oct 2Oct 3Oct 4Oct 5Oct 6Oct 7Oct 8Oct 9Oct
C 16.3516 32.7032 65.4064 130.8128 261.6256 523.2511 1046.502 2093.005 4186.009 8372.019
C# 17.3239 34.6478 69.2957 138.5913 277.1826 554.3653 1108.731 2217.461 4434.922
D 18.3541 36.7081 73.4162 146.8324 293.6648 587.3295 1174.659 2349.318 4698.636
D# 19.4454 38.8909 77.7817 155.5635 311.127 622.254 1244.508 2489.016 4978.032
E 20.6017 41.2034 82.4069 164.8138 329.6276 659.2551 1318.51 2637.02 5274.041
F 21.8268 43.6535 87.3071 174.6141 349.2282 698.4565 1396.913 2793.826 5587.652
F# 23.1247 46.2493 92.4986 184.9972 369.9944 739.9888 1479.978 2959.955 5919.911
G 24.4997 48.9994 97.9989 195.9977 391.9954 783.9909 1567.982 3135.963 6271.927
G# 25.9565 51.913 103.8262 207.6523 415.3047 830.6094 1661.219 3322.438 6644.875
A 27.5 55 110 220 440 880 1760 3520 7040
A# 29.1352 58.2705 116.5409 233.0819 466.1638 932.3275 1864.655 3729.31 7458.62
B 30.8677 61.7354 123.4708 246.9417 493.8833 987.7666 1975.533 3951.066 7902.113
※참고: C=도, D=레, E=미, F=파, G=솔, A=라, B=시
옥타브 및 음계별 펄스폭표(단위:us)
Oct 0 1 2 3 4 5 6 7 8 9
C 61156 30578 15289 7645 3822 1911 956 478 239 119
C# 57724 28862 14431 7215 3608 1804 902 451 225
D 54484 27242 13621 6810 3405 1703 851 426 213
D# 51426 25713 12856 6428 3214 1607 804 402 201
E 48540 24270 12135 6067 3034 1517 758 379 190
F 45815 22908 11454 5727 2863 1432 716 358 179
F# 43244 21622 10811 5405 2703 1351 676 338 169
G 40817 20408 10204 5102 2551 1276 638 319 159
G# 38526 19263 9631 4816 2408 1204 602 301 150
A 36364 18182 9091 4545 2273 1136 568 284 142
A# 34323 17161 8581 4290 2145 1073 536 268 134
B 32396 16198 8099 4050 2025 1012 506 253 127
*****************************************************************************************************/
/*
=== 피에조 부저 제어 방법 ====
피에조 부저 Resonant Frequency(공진 주파수): 4kHz
STM32에서 주파수를 만들 때 3개의 레지스터를 설정한다.
PSC(Prescaler), ARR(Peroid), CCRx(Duty)
- Prescaler register(TIMx_PSC) : APB를 통해 공급되느 클럭이 빠를 경우 이 레지스터의 값만큼 분주하여 사용할 수 있다.
- Auto reload register(TIMx_ARR) : TIMx_CNT 레지스터를 초기화 시켜주기 위한 레지스터이다.
Up Counting에서 TIMx_CNT가 이 값에 도달하면 0으로 초기화되어 다시 카운팅한다.
- Capture/Compare register(TIMx_CCR) : Capture/Compare 두 개의 모드로 외부 입력 신호에 따라
TIMx_CNT의 값을 기록(Capture)하거나 TIMx_CNT와 TIMx_CCR를 비교하여(Compare) 같아지면 인터럽트나 출력 등의 이벤트를 발생시킨다.
다음과 같이 적용 하면 된다.
- PSC : Timer Clock / 기준으로 사용할 주파수 - 1
- ARR : 기준 주파수 / 실제 주파수 - 1
- CCRx : ARR값 * (적용할 백분율 값 0 ~ 100) / 100 CCR(Counter Capture Register)
예를 들어 동작 클럭이 84Mhz이고 4Khz에 50%비율로 동작하는 PWM을 만들고 싶다면 식은 다음과 같다.
Prescaler(기준 클럭) : 1.6Mhz을 만든다면 - 84,000,000(타이머 클럭) / 1,600,000(만들 클럭) = 52.5
실제 레지스터 PSC에 적용시 1을 뺀 51.5값을 적용한다.
PSC = 52.5-1
Period : 4khz (실제 주파수) - 1,600,000(기준 클럭) / 4000(실제 주파수) = 400 실제 레지스터 ARR에 적용할땐 1을 뺀 399값을 적용한다.
ARR = 399; Duty : 50% - 399(ARR) * 50(퍼센트) / 100 = 199
CCRx = 199
*/
extern TIM_HandleTypeDef htim3;
enum notes
{
C4 = 262, // 도 261.63Hz
D4 = 294, // 래 293.66Hz
E4 = 330, // 미 329.63Hz
F4 = 349, // 파 349.23Hz
G4 = 392, // 솔 392.00Hz
A4 = 440, // 라 440.00Hz
B4 = 494, // 시 493.88Hz
C5 = 523 // 도 523.25Hz
};
// 학교종이 떙떙땡
unsigned int school_bell[] =
{
G4,G4,A4,A4,G4,G4,E4,G4,G4,E4,E4,D4,
G4,G4,A4,A4,G4,G4,E4,G4,E4,D4,E4,C4
};
// happybirthday to you
unsigned int happy_birthday[] =
{
C4,C4,D4,C4,F4,E4,C4,C4,D4,C4,G4,
F4,C4,C4,C5,A4,F4,E4,D4,B4,B4,A4,
A4,G4,F4
};
unsigned int duration[] = {1,1,2,2,2,2,1,1,2,2,2,2,1,1,2,2,2,2,2,1,1,2,2,2,2};
void noTone()
{
htim3.Instance->CCR1=0;
HAL_Delay(50);
}
void set_buzzer(int frequency)
{
int divide_freq = 1600000; // 4KHZ 부저 주파수를 내기 위해 기본 클럭을 분주해서 얻은 주파수
__HAL_TIM_SET_AUTORELOAD(&htim3, divide_freq / frequency); // PWM
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, divide_freq / frequency / 2); // Duty를 50%로 설정 한다.
}
void siren(int repeat)
{
int frequency=1111; // 1.1kHz로 세팅
set_buzzer(frequency);
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);
for (int i=0; i < repeat; i++)
{
for (int j=0; j < 100; j++)
{
frequency += 10; // 100kHz로 add
set_buzzer(frequency);
HAL_Delay(20);
}
for (int j=0; j < 100; j++)
{
frequency -= 10; // sub 100kHz
set_buzzer(frequency);
HAL_Delay(20);
}
noTone();
}
HAL_TIM_PWM_Stop(&htim3, TIM_CHANNEL_1);
}
void rrr(void)
{
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);
for (int i=0; i < 20; i++)
{
set_buzzer(880); // 880Hz로 세팅
HAL_Delay(100);
noTone();
HAL_Delay(20);
}
HAL_TIM_PWM_Stop(&htim3, TIM_CHANNEL_1);
}
void beep(int repeat)
{
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);
for (int i=0; i < repeat; i++)
{
set_buzzer(2000); // 2KHZ
HAL_Delay(500);
// beep stop
noTone();
HAL_Delay(200);
}
HAL_TIM_PWM_Stop(&htim3, TIM_CHANNEL_1);
}
void firetruck(int repeat)
{
int frequency=700; // 700Hz로 세팅
set_buzzer(frequency);
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);
for (int i=0; i < repeat; i++)
{
while(frequency < 1500)
{
frequency += 35;
if(frequency > 1500)
{
frequency = 1500;
}
set_buzzer(frequency);
HAL_Delay(20);
}
while(frequency > 700)
{
frequency -= 15;
if(frequency < 700)
{
frequency = 700;
}
set_buzzer(frequency);
HAL_Delay(20);
}
noTone();
}
HAL_TIM_PWM_Stop(&htim3, TIM_CHANNEL_1);
}
void close_buzzer()
{
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);
set_buzzer(1000); // 1kHz로 세팅
HAL_Delay(70);
set_buzzer(2000); // 2kHz로 세팅
HAL_Delay(70);
set_buzzer(3000); // 3kHz로 세팅
HAL_Delay(70);
set_buzzer(4000); // 4kHz로 세팅
HAL_Delay(70);
HAL_TIM_PWM_Stop(&htim3, TIM_CHANNEL_1);
}
void open_Buzzer()
{
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);
set_buzzer(261); // 261Hz로 세팅
HAL_Delay(70);
set_buzzer(329); // 329Hz로 세팅
HAL_Delay(70);
set_buzzer(392); // 392Hz로 세팅
HAL_Delay(70);
set_buzzer(554); // 554Hz로 세팅
HAL_Delay(70);
HAL_TIM_PWM_Stop(&htim3, TIM_CHANNEL_1);
}
void buzzer_main()
{
int divide_freq = 1600000; // 4KHZ 부저 주파수를 내기 위해 기본 클럭을 분주해서 얻은 주파수
while (1)
{
firetruck(1);
#if 0
beep(5);
HAL_Delay(1000);
rrr();
HAL_Delay(1000);
#endif
#if 0
// 학교 종이 땡땡땡
for (int i=0; i < 24; i++)
{
__HAL_TIM_SET_AUTORELOAD(&htim3, divide_freq / school_bell[i]);
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, divide_freq / school_bell[i] / 2); // Duty를 50%로 설정 한다.
HAL_Delay(500);
noTone(); /* note 소리 내고 50ms 끊어주기 */
}
/* 음악 끝나고 3초 후 시작*/
HAL_TIM_PWM_Stop(&htim3, TIM_CHANNEL_1) ;
HAL_Delay(3000);
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1) ;
// happy birthday to you
for (int i=0; i < 25; i++)
{
__HAL_TIM_SET_AUTORELOAD(&htim3, divide_freq / happy_birthday[i]);
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, divide_freq / happy_birthday[i] / 2);
HAL_Delay(300*duration[i]);
noTone();
}
/* 음악 끝나고 3초 후 시작 */
HAL_TIM_PWM_Stop(&htim3, TIM_CHANNEL_1) ;
HAL_Delay(3000);
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1) ;
#endif
}
}
#if 0
//------------------- happy birthday START ---------------
uint32_t Frequency = 0;
uint32_t CLOCK = 16000000; // 4KHZ 부저 주파수를 내기 위해 기본 클럭을 분주해서 얻은 주파수
int i;
/* Hap py Birth Day to you, Hap py birth day to
C4 C4 D4 C4 F4 E4 C4 C4 D4 C4 G4 */
unsigned int notes[] = { 262, 262, 294, 262, 349, 330, 262, 262, 294, 262, 392,
/* you, Hap py Birth Day dear xxxx Hap py birth
F4 C4 C4 C5 A4 F4 E4 D4 B4b B4b A4 */
349, 262, 262, 523, 440, 349, 330, 294, 466, 466, 440,
/* day to you
F4 G4 F4 */
349, 392, 349
};
enum note1
{
C4 = 262, // 도(261.63Hz)
D4 = 294, // 래(293.66Hz)
E4 = 330, // 미(329.63Hz)
F4 = 349, // 파(349.23Hz)
G4 = 392, // 솔(392.00Hz)
A4 = 440, // 라(440.00Hz)
B4 = 494, // 시(493.88Hz)
C5 = 523 // 도(523.25Hz)
};
/* 학교종
* 솔 솔 라 라 솔 솔 미 솔 솔 미 미 래
* 솔 솔 라 라 솔 솔 미 솔 미 래 미 도*/
enum note1 A[] = {G4,G4,A4,A4,G4,G4,E4,G4,G4,E4,E4,D4,
G4,G4,A4,A4,G4,G4,E4,G4,E4,D4,E4,C4};
unsigned int duration[] = {1,1,2,2,2,2,1,1,2,2,2,2,1,1,2,2,2,2,2,1,1,2,2,2,2};
void noTone()
{
htim3.Instance->CCR1=0;
HAL_Delay(50);
}
void playSong()
{
for (int i = 0 ; i < 24; i++)
{
Frequency = CLOCK/A[i];
htim3.Instance->ARR=Frequency;
htim3.Instance->CCR1=Frequency/2; // OK
HAL_Delay(400*duration[i]);
noTone();
}
HAL_Delay(5000);
for (i = 0; i <25; i++)
{
Frequency = CLOCK/notes[i];
htim3.Instance->ARR=Frequency;
htim3.Instance->CCR1=Frequency/2; // OK
HAL_Delay(400*duration[i]);
noTone();
}
}
//--------- happy birthday END ---------------
#endif
<buzzer.h>
#include "main.h"
void beep(int repeat);
void set_buzzer(int frequency);
void buzzer_main();
void PowerOnBuzzer();
void rrr(void);
서보 모터

서보(servo)는 “추종한다”, “따른다”는 의미이다. 해당 기기를 시스템이 요구하는 특정 위치로 이동하거나, 특정한 속도나 토크로 가동시킬 때, 피드백이나 에러 정정을 통해 정확하게 제어할 수 있는 구조를 갖추고 있다는 의미이다. 예를 들어, 명령에 의해 제어되는 모터를 서보모터라고 한다. 즉, 서보모터의 경우 움직임을 지정하면 원형으로 빙빙 돌기만 하는 일반적인 모터와는 달리 제어계측 회로에 의해 정확하게 움직일 수 있는 모터란 뜻이다.
로봇팔, 산업용공작기계 프린터, DVD, CCTV 카메라, 캠코더 등에 사용되는 모터처럼 명령에 따라 정확한 위치와 속도를 맞출 수 있는 모터를 서보모터라고 한다.
서보 모터 제어
- 서보 모터는 큰 토크를 가지고 부하를 이동할 수 있다.
- 서보 모터는 pwm(펄스 폭 변조) 신호에서 작동한다. 충분한 전압, 전류 및 pwm 신호가 모터에 적용될 때 회전하는 arm이 있어서 물체를 움직 일 수 있다.


서보 모터는 pwm 신호에서 작동한다. 대부분의 DC 서보 모터는 가변 듀티 사이클로 작동하려면 50Hz 주파수가 필요하다. 다음은 표준 요구 사항 웨이브 from 이다.

50kHz TIM5 → CH1
- system clock : 84MHz
- TIM5 50kHz 입력
84000000 / 1680(분주비) => 50000Hz(50kHz)
- T = 1 / f = 1 / 50000 => 0.00002 sec(20us)
20ms = 0.0002 x 1000 EA
20ms x 500 = 1000ms
2ms (180도 회전) : 0.000002 x 1000 EA
1.5ms (90도 회전) : 0.000002 x 75 EA
1ms (0도 회전) : 0.000002 x 50 EA


<servo_motor.c>
#include "servo_motor.h"
extern volatile int TIM10_servo_motor_counter;
extern TIM_HandleTypeDef htim5;
void servo_motor_main(void);
/*
System clock: 84MHZ (84,000,000HZ)
TIM3 50KHZ 입력 : 84000000/1680 ==> 50,000HZ(50KHZ)
T=1/f = 1/50000 = 0.00002sec (20us)
20ms : 0.00002 x 1000개
2ms(180도) : 0.00002 x 100개
1.5ms(90도) : 0.00002 x 75개
1ms(0도): 0.00002 x 50개
*/
void servo_motor_main(void)
{
#if 1
HAL_TIM_PWM_Start(&htim5, TIM_CHANNEL_2); // Servo Motor start
while(1)
{
__HAL_TIM_SET_COMPARE(&htim5, TIM_CHANNEL_2,120); // 180도
HAL_Delay(1000);
__HAL_TIM_SET_COMPARE(&htim5, TIM_CHANNEL_2,76); // 90도
HAL_Delay(1000);
__HAL_TIM_SET_COMPARE(&htim5, TIM_CHANNEL_2,38); // 0도
HAL_Delay(1000);
}
#else
static uint8_t servo_state=0;
if (TIM10_servo_motor_counter >= 1000) // 1sec
{
TIM10_servo_motor_counter=0;
// 180 --> 90 --> 0
switch(servo_state)
{
case 0: // 180
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_2,100);
break;
case 1: // 90
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_2,75);
break;
case 2: // 0
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_2,50);
break;
}
servo_state = (servo_state + 1) % 3;
// servo_state++;
// servo_state %= 3;
}
#endif
}
// GPIO_PIN_1 : 발생시 서보모터 0도 --> 180도 이동 (3초유지) --> 서보 모터0도
// 3초유지는 HAL Delay를 쓰지 말고 TIM10_servo_motor_counter변수 활용
void servo_motor_control(void)
{
static uint8_t servo_state=0;
}
- 비동기식 : UART(유선), CDMA, etc : clock 신호에 의존하지 않고 별도의 부호비트를 가지고 데이터를 구분해서 송수신
- 동기식 : I2C (Halfduplex, 반이중 전송방식), SPI (Fullduplex, 전이중 전송방식) : clock 신호에 의존해서 데이터를 송수신
SPI 통신
1. 비동기 시리얼 통신

2. 동기식 시리얼 통신
- 동기식 시리얼 통신은 데이터를 보낼 때 데이터가 전달된다고 알려주는 별도의 클럭(clock)라인을 사용하는 방법이다.
- 클럭 라인을 통신 참여자가 공유함으로써 데이터 라인으로 언제 데이터가 들어오는지 알 수 있도록 해주는 방법
- I2C, SPI(Serial Peripheral Interface) 통신이 대표적인 동기식 통신 방법
- 즉, 클럭 라인으로 들어오는 rising (low to high) 또는 falling (high to low) 신호를 인식해서 즉시 데이터 라인에서 값을 읽는다.

3. SPI 개요
- SPI ( Serial Peripheral Interface )는 주로 임베디드 시스템 에서 단거리 통신(주로 Chip과 Chip간의 통신)에 사용되는 동기식 직렬 통신 인터페이스 사양
- 인터페이스는 1980년대 중반 Motorola 에서 개발했으며 사실상의 표준 일반적인 애플리케이션에는 보안 디지털 카드 및 액정 디스플레이가 포함

4. SPI Protocol

SPI 타이밍 다이어그램

CPOL : SPI버스가 IDLE일 때의 클럭 상태를 가리킨다.
CPOL(ClockPOLarity) = 0이면 평소 IDLE일 때 clock이 LOW임을 가리킴
CPOL(ClockPOLarity) = 1이면 평소 IDLE일 때 clock이 HIGH임을 가리킴
CPHA(Clock PHAse) : 데이터를 sampling하는 시점을 결정한다.
CPHA = 0이면 클럭이 IDLE → ACTIVE로 바뀌는 시점에 data를 sampling을 하고
CPHA = 1이면 클럭이 ACTIVE → IDLE로 바뀌는 시점에 data를 sampling을 한다.
CPHA(Clock PHAse) = 0인 경우
CPOL = 0이면 클럭이 상승 엣지에서 sampling하고
CPOL = 1이면 클럭이 하강 엣지에서 sampling한다.
CPHA(Clock PHAse) = 1인 경우
CPOL = 0이면 클럭이 하강 엣지에서 sampling하고
CPOL = 1이면 클럭이 상승 엣지에서 sampling한다.

디지털 출력 확장

74595 칩

74595 칩 동작 순서

74595 칩에 데이터가 저장되는 순서





<led.c>
#include "led.h"
#include "extern.h"
void led_all_on(void)
{
#if 1
//printf("int %d\n", sizeof(int)); // 4로 찍히는지 확인
GPIOB->ODR = 0xff;
#else
// HAL_GPIO_WritePin(GPIOB, 0Xff, 1);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3|GPIO_PIN_4|
GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7, 1);
#endif
}
void led_all_off(void)
{
#if 1
//printf("int %d\n", sizeof(int)); // 4로 찍히는지 확인
GPIOB->ODR = 0x00;
#else
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3|GPIO_PIN_4|
GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7, 0);
#endif
}
void shift_left_ledon(void)
{
for(int count = 0; count < 8; count++)
{
GPIOB->ODR = 0x01 << count;
HAL_Delay(100);
}
led_all_off();
HAL_Delay(100);
#if 0
char GPIO_number[8] = {GPIO_PIN_0, GPIO_PIN_1, GPIO_PIN_2, GPIO_PIN_3,
GPIO_PIN_4, GPIO_PIN_5, GPIO_PIN_6, GPIO_PIN_7};
for(int count = 0; count < 8; count++)
{
HAL_GPIO_WritePin(GPIOB, GPIO_number[count], 1);
HAL_Delay(100);
HAL_GPIO_WritePin(GPIOB, GPIO_number[count], 0);
}
led_all_off();
HAL_Delay(100);
#endif
}
void shift_right_ledon(void)
{
for(int count = 0; count < 8; count++)
{
GPIOB->ODR = 0x80 >> count;
HAL_Delay(100);
}
led_all_off();
HAL_Delay(100);
#if 0
char GPIO_number[8] = {GPIO_PIN_0, GPIO_PIN_1, GPIO_PIN_2, GPIO_PIN_3,
GPIO_PIN_4, GPIO_PIN_5, GPIO_PIN_6, GPIO_PIN_7};
for(int count = 7; count >= 0; count--)
{
HAL_GPIO_WritePin(GPIOB, GPIO_number[count], 1);
HAL_Delay(100);
HAL_GPIO_WritePin(GPIOB, GPIO_number[count], 0);
}
led_all_off();
HAL_Delay(100);
#endif
}
void shift_left_keep_ledon(void)
{
for(int count = 0; count < 8; count++)
{
GPIOB->ODR |= 0x01 << count;
HAL_Delay(100);
}
led_all_off();
HAL_Delay(100);
}
void shift_right_keep_ledon(void)
{
for(int count = 0; count < 8; count++)
{
*( unsigned int *)GPIOB_ODR |= 0x80 >> count;
HAL_Delay(100);
}
led_all_off();
HAL_Delay(100);
#if 0
char GPIO_number[8] = {GPIO_PIN_0, GPIO_PIN_1, GPIO_PIN_2, GPIO_PIN_3,
GPIO_PIN_4, GPIO_PIN_5, GPIO_PIN_6, GPIO_PIN_7};
for(int count = 7; count >= 0; count--)
{
HAL_GPIO_WritePin(GPIOB, GPIO_number[count], 1);
HAL_Delay(100);
}
led_all_off();
HAL_Delay(100);
#endif
}
void flower_on(void)
{
#if 1 // 구조체 pointer member 변수
for(int i = 0; i < 4; i++)
{
GPIOB->ODR |= 0x10 << i | 0x08 >> i;
HAL_Delay(200);
}
led_all_off();
HAL_Delay(200);
#endif
#if 0
char GPIO_number[8] = {GPIO_PIN_0, GPIO_PIN_1, GPIO_PIN_2, GPIO_PIN_3,
GPIO_PIN_4, GPIO_PIN_5, GPIO_PIN_6, GPIO_PIN_7};
for(int count = 3; count >= 0; count--)
{
HAL_GPIO_WritePin(GPIOB, GPIO_number[count]|GPIO_number[7-count], 1);
HAL_Delay(100);
}
led_all_off();
HAL_Delay(100);
#endif
}
void flower_off(void)
{
led_all_on();
HAL_Delay(100);
for(int i = 0; i < 4; i++)
{
GPIOB->ODR &= ~(0x80 >> i | 0x01 << i);
HAL_Delay(100);
}
led_all_off();
HAL_Delay(100);
#if 0
led_all_on();
HAL_Delay(100);
for(int i = 0; i < 4; i++)
{
* (unsigned int *)GPIOB_ODR &= ~(0x80 >> i | 0x01 << i);
HAL_Delay(100);
}
led_all_off();
HAL_Delay(100);
#endif
}
extern SPI_HandleTypeDef hspi2;
void led_main(void)
{
uint8_t led_buff[8] = {0xFF, 0x0F, 0xF0, 0x00,0xFF, 0x0F, 0xF0, 0x00};
while (1)
{
#if 1
HAL_SPI_Transmit(&hspi2,led_buff, 1, 1);
GPIOB->ODR &= ~GPIO_PIN_13; // latch핀을 pull-down ODR(Output Data Register)
GPIOB->ODR |= GPIO_PIN_13; // latch핀을 pull-up ODR(Output Data Register)
HAL_Delay(500);
HAL_SPI_Transmit(&hspi2, &led_buff[3], 1, 1);
GPIOB->ODR &= ~ GPIO_PIN_13;
GPIOB->ODR |= GPIO_PIN_13;
HAL_Delay(500);
#else
for (int i=0; i < 4; i++)
{
HAL_SPI_Transmit(&hspi2, &led_buff[i], 1, 1);
GPIOB->ODR &= ~ GPIO_PIN_13; // latch핀을 pull-down
GPIOB->ODR |= GPIO_PIN_13; // // latch핀을 pull-up
HAL_Delay(1000);
}
#endif
// (*GPIOB).ODR |= GPIO_PIN_0; // LED ON
// HAL_Delay(500);
// GPIOB->ODR &= ~GPIO_PIN_0;// LED OFF GPIOB 구조체 특성으로 GPIO_Typedeff에 정의된 ODR에 접근
// // 즉 GPIOB_ODR 주소값인 0x40020414에 접근하여
// GPIOB->ODR ^= GPIO_PIN_1; // LED1 toggle
// led_all_on();
// led_all_off();
// shift_left_ledon();
// shift_right_ledon();
// shift_left_keep_ledon();
// shift_right_keep_ledon();
// flower_on();
// flower_off();
// HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
// for (int i = 0; i < 50; i++)
// delay_us(1000);
}
}
<실행 결과>
'(Telechips) AI 시스템 반도체 SW 개발자 교육 > STM32CubeIDE' 카테고리의 다른 글
9일차 (0) | 2025.04.04 |
---|---|
8일차 (0) | 2025.04.03 |
6일차 (0) | 2025.04.01 |
5일차 (0) | 2025.03.31 |
4일차 (0) | 2025.03.28 |