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 )는 주로 임베디드 시스템 에서 단거리 통신(주로 ChipChip간의 통신)사용되는 동기식 직렬 통신 인터페이스 사양

- 인터페이스는 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);


	}
}

 

<실행 결과>

https://youtu.be/VOZ9cGYJNYE

 

'(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