[TC375] TC375 이해 및 GPIO

CS·2025년 7월 2일

Device Driver

목록 보기
2/5
  • MCU 명명 규칙

ST 마이크로닉스 MCU 시리즈처럼 Infineon역시 다 뜻을 두고 MCU를 명명한다.

  1. Tricore v.1.6.2 아키텍처 기반 MCU

  2. 멀티 코어 지원
    최대 300MHz에서 동작하는 3개의 32BIT Core 탑재
    당연히, 온도에 따라 정확도가 달라진다.
    기존에 쓰던 STM은 싱글 코어였는데 3 Core임으로 성능이 아주 좋아졌다.
    Core간 통신이 가능하겠다.

  3. 128채널 DMA 지원
    DMA를 거쳐 CPU를 방해하지 않고 주변 장치들을 쓸 수 있다.

  4. LQFP-176 Package Type
    176개의 Pin을 갖는다. 이건 STM 시리즈와 같은 용어를 쓰는듯

나는 이 MCU를 기반으로 Lite Kit 보드를 사용한다.


  • Board
  1. 동작 전원 : DC 5V
  2. USB(Micro 5Pin) Debug Interface 지원(JTAG)

파란색 원 부분을 보면 크리스털을 통해 20MHz 외부 Clock을 제공하고 있다. EEPROM도 붙어있고, CAN controller, Transceiver도 있어서 점퍼선으로 간단하게 연결이 가능하다. 이더넷도 있고 써먹기 아주 좋은 보드

실제 차량 분야에서도 Infineon 시리즈 MCU로 PCB를 만들어 쓴다고 한다.
여하튼 써 본 소감으로는 STM시리즈와는 비교도 안될만큼 좋다.
단점이라면 STM처럼 HAL 라이브러리는 없고

대신 Aurix iLLD Driver를 달고 있다. 이거라도 갖고 있는게 어딘가
실제로 F3누르면서 추적해가고 Datasheet를 읽으면 그나마 편하다.
단점은 User Manual이랑 Datasheet 합쳐서 너무나도 양이 방대하다는 점
어차피 다 외울수도 없는 노릇이긴 하니까 뭐...

현업에서는 아마 MCAL을 쓰기 때문에 직접 레지스터 단을 조작하지는 않겠지만,
Device Driver를 작성할 줄 알아야 입맛대로 바꿔쓸 수 있으니 기본소양이라고 생각하긴 한다. 단 책임은 해당 단위 모듈 개발자가 져야겠지


  • ECU 회로 관련 용어
  1. PCB(Printed Circuit Board)
    도체, 절연체가 기판 형태로 적층되어 반도체, 커패시터 등 다양한 부품을 장착할 수 있음. 전기 선로를 효율적으로 설계해 전자기기의 크기를 줄여 성능/효율성을 높인다.

Lite Kit에는 불필요한 장비들과 회로들이 장착되어 있지만,
실제 차량 내에 들어가는 ECU중 GPIO같은건 필요도 없는 곳이 많다.
요구사항에 맞게 수정하여 PCB를 설계하게 된다.

  1. PCB 회로도
    PCB의 각 부품을 어떻게 연결할 것인지 설계한 도면으로 실제 부품 크기/종류와 무관히 부품간 위치/연결관계만 정의함

  2. PCB LayOut
    PCB 회로도 기반으로 각 부품을 연결하는 작업

  3. SMT(Surface Mount Technology)
    장비를 통해 PCB기판에 부품을 조립하는 공정


  • 실습 키트 회로도

User Manual을 참고


  • Easy Module Shield
  1. 가변 저항
  2. R/G LED
  3. 부저
  4. Switch 2개
  5. 적외선 모듈
  6. 온습도 센서 모듈

TC375 Lite Kit에 결합해 사용 가능하다.

세부 모듈들은 이러하다.

결합하여 Device Driver를 작성하려면 이 회로도를 고려해야한다.
보면 LED는 D12/D13에 Pull-Down으로
Switch는 D2/D3에 Pull-Up 저항으로 연결되어 있다.
여기서 내부 회로도까지 고려하여 Device Driver를 작성해야 한다.

결합하면

이렇게 되는데 LED를 보면 P12/P13에 연결되어 있음을 확인 가능하다.


  • Motor Driver(L298P)

DC모터를 PWM 방식으로 제어하기 위한 부품
이거 없으면 한 방향으로만 회전한다.

예전에 L9110s를 썼는데 걔는 채널당 0.8A였나?
2개써서 4륜으로 구동하니까 힘이 너무 안좋았다.
뭔가 회로적으로 개선이 가능할 것 같았는데
회로쪽으론 딥한 지식이 없다보니... 좀 아쉬웠음

  • Motor Driver Shield

ARDUINO Motor Shield Rev3 :
DC 모터 최대 전류 : 각 채널당 2A
DC 모터용 채널 2개
동작 전압 : DC 5V ~ 12V

  • DC Motor(MB2832E-1268)

정격 전압 : DC 12V
회전 수 : 5450 RPM
정격 토크 : 54.4 gf.cm
정격 전류 : 0.44A

Encoder가 내장되어 회전수, 각도, 속도를 감지해 Detail한 제어가 가능하다.


  • 초음파 센서(HC-SR04/TOF)

스트레스의 근원. 고장이 아주 잘남
옛날에도 프로젝트 할 때 자꾸 고장나서 5개나 샀었다.
어느 정도냐면 방금까지 되던 형의 센서를 빼서 연결했는데
측정이 제대로 안될 정도
오전에 되던게 갑자기 오후에 안되는 정도

VCC/GND가 있고(정격 : 5V)
TRIG에서 초음파를 발사하고(SDA)
ECHO에서 반사된 Edge를 감지한다.(SDL)
그리고 그 시간 차이를 거속시 공식을 통해 거리를 측정 가능함.

측정거리는 2~400cm이고, 해상도는 0.3cm라고 되어 있긴하지만
사실 그렇게 정밀하진 못하다. 값 튀는건 다반사고
손으로 거리 테스팅하면 잘 안됨
벽같이 평평한 곳에 측정해야 읽힌다.

  • 레이저 센서(TOFSense)

940nm 파장의 근적외선을 이용하는 센서
UART : 921600 baudrate
CAN : 최대 1Mbps
통신속도를 지원한다.
처음 써봐서 뭔가 측정값이 신기했다.


  • TC375 구조

사실 제일 중요하지 않나? 생각함
주어진 아키텍처는 알아야 왜 그런 구조로 설계를 했는지 얘기할 수가 있지

좀 주목할만한 점이라면, 아무래도 Bus아닐까 싶다.
Clock Source를 알아야하니까.
SPB Bus에 주변 장치들이 연결되어 있는데 이게 100MHz인걸로 알고있다.
분주를 해야하는 Timer에서 가장 중요한 부분

또 LMU로 Core간 Shared Memory가 존재하고
Core를 보면
SRAM인 PSPR(주로 코드 저장용)
DSPR(주로 변수 버퍼 등Data저장용)이 존재한다.
얘내는 L1 Cache처럼 빠른 접근이 가능하여
엔진 동작같이 빨리 해야되는 때 사용한다.
링커 파일을 통해 설정이 가능하다.

역시 범용 컴퓨팅 환경에 비해 제약적인 메모리를 갖는다.
참고로 범용 컴퓨터 구조와는 다르게 가상 메모리같은건 없다.
그니까 논리주소 이런거 없고 Mapping Table도 없단 얘기
그냥 직접적으로 메모리에 접근할 뿐(Memory Mapped I/O)

외에도 PF같은 Program Flash Memory,
DF같은 EEPROM에서 쓰는 Data Flash, RAM
Flash에 R/W하는 DMU
CPU나 DMA가 어느 Slave에 연결될지 정하는 DMA0 등이 있다.


  • Core 레지스터 구성

범용 레지스터(GPR) 32개 :
주소 레지스터 16개 / 데이터 레지스터 16개

  • 특수 기능 레지스터

Implicit Register :

대부분 16-bit Instruction은 2개의 특수 레지스터를 사용한다.

A[15]는 Implicit 주소 레지스터
D[15]는 Implicit 데이터 레지스터

A[10]은 Stack Pointer Register로, 현재 명령어가 수행되고 있는 함수의 주소를 저장한다.

A[11]은 Return Address Register로, 함수 종료 후 이전 함수의 다음 명령어 주소를 저장한다. 즉 돌아갈 곳을 저장함.

전역 레지스터 : Global Register

A[0], A[1], A[8], A[9]는 상하부 Context에 포함되지 않는 레지스터
Call, Trap, Interrupt간 저장 및 복원되지 않는 레지스터
일반적으로 OS에서 시스템 Overhead를 줄이기 위해 사용됨

A[0] : small data section의 Base pointer
A[1] : 리터럴 data section의 Base pointer(Read-Only)
A[8], A[9] : 확장된 주소 모드를 위한 Base 레지스터

시스템 레지스터 3개를 갖고 있다
1. PCXI(Previous ConteXt Information register) :
CSA(Context Saving Area)영역의 문맥을 Linked-List로 저장되는걸 추적하도록 돕는다.
이전 상태를 추적해야 ISR처리 후 다시 돌아갈 수 있으니까.

  1. PSW(Program Status Word) :
    현재 상태(Flag) - 연산 오류 등 상태를 나타냄
  2. PC(Program Counter) :
    다음 실행할 Instruction의 주소를 담는 레지스터

  • TC375의 Stack 종류
  1. iStack : ISR이 사용하는 Stack
  2. uStack : 사용자가 사용하는 Stack

크기는 Lcf_Tasking_Tricore_Tc.lsl에서 변경 가능함
여기 Heap size도 조정가능하고 시작 주소 등 기타등등이 define되어 있음

  • 관련 Register

A[10] : Stack Pointer
Interrupt Stack Pointer(ISP) : ISR이 쓸 Stack 주소 저장

PSW :

보면, IS에 0/1에따라 uStack/iStack중 어떤 것을 제어할지 결정 할 수 있음.

만약 PSW.IS = 0 인데, CAN Controller에서 Interrupt가 발생했다면
PC에 ISR를 넣고 CAN_RX_ISR를 가르키게 한다.
이때 SP는 현재 수행하던 uStack 메모리를 가르키고있음.

이후, PSW.IS = 1이 되면서 SP는 ISP를 가져와 iStack 메모리를 가르키게 된다.


  • CSA(Context Saving Area)

Context 저장을 위한 메모리 공간

빠른 Context저장을 위해 HW에의해 high context는
자동저장 및 복구되며 low context는 필요에 따라 SW에의해 저장/복구됨

TC375 Context 는 low, upper context 로 나뉨

Registers A[2] to A[7] and D[0] to D[7] are part of the lower context.

Registers A[10] to A[15] and D[8] to D[15] are part of the upper context.


  • Memory Mapped I/O

    메모리와 I/O를 하나의 연속된 주소 영역에 할당
    물리 메모리 주소를 I/O 장치와 Mapping해 I/O장치에 직접 접근

    즉, I/O를 메모리처럼 다룰 수 있으며 별도 명령 필요 없음
    레지스터에 주소값을 define해 RAM처럼 R/W하도록 함.
    .bss / .data / heap / stack / text 그 어디도 아닌 별도 공간에 속함

    F000 0000 ~ FFFF FFFF에 256MB로 저장되어 있음.
    주변 장치들도 여기 해당됨. 그래서 직접 접근할 수가 있다.


  • Bit Masking

    VAR & ~(1<<n) : n번째 bit clear
    VAR | (1<<n) : n번째 bit set
    (VAR >> n) & 0x01 : n번째 bit 추출
    VAR ^ (1<<n) : n번째 bit Toggle

  • iLLD 구조체를 활용한 Bit 조작

    typedef struct _Ifx_P_IOCR0_Bits
    {
    Ifx_UReg_32Bit reserved_0:3;  /**< \brief [2:0] \internal Reserved */
    Ifx_UReg_32Bit PC0:5;         
    /**< \brief [7:3] Port Control for Pin 0 (rw
    Ifx_UReg_32Bit reserved_8:3;  /**< \brief [10:8] \internal Reserved */
    Ifx_UReg_32Bit PC1:5;       
    ) */
    /**< \brief [15:11] Port Control for Pin 1 (rw
    ) */
    Ifx_UReg_32Bit reserved_16:3; /**< \brief [18:16] \internal Reserved */
    Ifx_UReg_32Bit PC2:5; 
    /**< \brief [23:19] Port Control for Pin 2 (rw
    ) */
    Ifx_UReg_32Bit reserved_24:3; /**< \brief [26:24] \internal Reserved */
    Ifx_UReg_32Bit PC3:5; 
    /**< \brief [31:27] Port Control for Pin 3 (rw
    ) */
    } Ifx_P_IOCR0_Bits;
    

iLLD에서 IOCR0에 대고 F3을 누르고 Bit Field 정의에 또 F3을 누르면 볼 수 있다.

PORTx에 대해 0번 1번 2번 3번 Pin을 나타내고 있다.
IOCR은 I/O Control Register로 Pin의 I/O설정을 담당하고 있다.

  • 구조체와 공용체
struct bits
 {
 unsigned char a1 : 2;
 unsigned char a2 : 2;
 unsigned char a3 : 2;
 unsigned char a4 : 2;
 };
 union data
 {
 unsigned char ch;
 struct bits ch_bit;
 };

이렇게 함께 작성하면 하나의 변수를 bit단위로 나눠 사용 가능하다.


  • 주변장치 모듈 정의

각 모듈은 다수의 레지스터로 구성
각 모듈에 속한 레지스터는 구조체로 표현가능함.

  • TC375 Port Module 구조체 활용 예시

여하튼 구조체 비트필드를 통하여

#include "main.h"
 int main(void) {
 	
    //Mapping I/O를 Port로 Casting
    
   Ifx_P* const pPort = (Ifx_P*)0xF003A000u;
   SYSTEM_Init();
   pPort->IOCR0.B.PC0 = 0x10;
   pPort->IOCR0.B.PC1 = 0x11;
   pPort->IOCR0.B.PC2 = 0x12;
   pPort->IOCR0.B.PC3 = 0x14;
 
   my_printf("IOCR0 = %X\n", pPort->IOCR0.U);
   while(1);
   return 0;
 }

이렇게 0번, 1번, 2번, 3번 Pin에 대해 IOCR설정을 가할 수가 있다.

  • Port Register 정의 활용

ex) IfxPort_reg.h

#define P00_ACCEN0 /*lint --e(923)*/ (*(volatile Ifx_P_ACCEN0*)0xF003A0FCu)
 ... 중략 ...
 #define P00_IOCR0 /*lint --e(923)*/ (*(volatile Ifx_P_IOCR0*)0xF003A010u)
 ... 중략 ...
 #define P40_PDISC /*lint --e(923)*/ (*(volatile Ifx_P_PDISC*)0xF003E060u)

레지스터에 값을 쓰기 위해 각 레지스터의 주소를 알아야함
툴체인에서 레지스터의 시작주소를 기본 제공

#include "main.h"
 int main(void)
 {
   MODULE_P00.IOCR0.B.PC0 = 0x10; 
   // *(unsigned char*)0xF003A013u=0x10;와 동일
   // (*(Ifx_P*)0xF003A010u).IOCR0.B.PC0와 동일
   
   MODULE_P00.IOCR0.B.PC1 = 0x10;
   MODULE_P00.IOCR0.B.PC2 = 0x10;
   MODULE_P00.IOCR0.B.PC3 = 0x10;
   while(1);
   return 0;
 }

이러면 Port 0번의 0, 1, 2, 3 Pin의 IOCR을 설정가능하다.

즉 MODULE_Px 구조체를 사용하면 레지스터 접근해 R/W가 편하다.
직관적으로 보이니까.
(unsigned char)0xF003A013u
((Ifx_P)0xF003A010u).IOCR0.B.PC0 = 0x10;
이렇게 쓰면 뭔지 어떻게 아나

다른 예시로, CAN MODULE이 3개의 Register로 구성된다고 치면

#define CAN0 (*(MODULE CAN*)0xFFFFFF00)
 #define UART (*(MODULE UART*)0xFFFFE00)
 struct MODULE_CAN {
   unsigned int *reg1;
   unsigned int *reg2;
   unsigned int *reg3;
 }
 struct UART {
   …
 }
 void main() {
   CAN0.reg1 = 10;
   CAN0.reg2 = 20;
   CAN0.reg3 = 30;
   …
 }

딱 봐도 뭐하는지가 보인다.

근데

unsigned int *MODULE_CAN_reg1 = 0xFFFFFF00;
unsigned int *MODULE_CAN_reg2 = 0xFFFFFF04;
unsigned int *MODULE_CAN_reg3 = 0xFFFFFF08;
unsigned int *MODULE_UART_reg1 = 0xFFFFFE00;
 …
 void main() {
   *MODULE_CAN_reg1 = 10;
   *MODULE_CAN_reg2 = 20;
   *MODULE_CAN_reg3 = 30;
   …
 }  

이렇게 쓰면 어떻게 알고 쓰냐.
iLLD 구조체를 활용하는게 맞다


  • GPIO

LED / Switch / Serial / CAN 통신 등 외부 장치 제어에 쓰는

Digital 신호 Pin

I/O 신호는 1 or 0으로 표현하며
Pull-up/Down 구성에 따라
두 가지 전압 레벨(3.3 or 5V / 0V)로 구분하게 된다.

Pull-Down 구성 Pin에 신호 1을 보내면 LED를 켤 수 있다.
사용자는 I/O pin의 동작을 변경할 수가 있다.

쓰려는 Pin의 I/O설정하고 Data Register를 통해 R/W를 수행한다.

  • Port Module

총 25개의 Port Module을 지원한다.
각 Port당 GPIO Pin의 수는 다르고 최대 16개 Pin이 설정 가능하다.

주요 레지스터는 다음과 같다.

  1. Pn_IOCRx :
    n번 Port의 x번 pin의 I/O set

  2. Pn_OUT :
    Port 출력 Data 레지스터

  3. Pn_IN :
    Port 입력 Data 레지스터

회로를 보면 Pin과 연결되어 있음을 볼 수 있다.

조금 더 상세히 뜯어보면

  • Pn_IOCRx

Pin의 I/O를 결정하는 IOCR 레지스터로 4개 존재한다.(0, 4, 8, 12)

쓰려는 Pin NO. 에 따라 IOCR을 다르게 쓰면 된다.

[Pin기준]
0번~3번 : IOCR0 사용
4번~7번 : IOCR4 사용
8번~11번 : IOCR8 사용
12번~15번 : IOCR12 사용

이렇게 되어있고

IOCR 설정 값은 이렇다.

I2C, SPI 등의 규격화 된 입력 신호(alterante function)

  • Pn_IN

Pn_IN.Px (n번 Port의 x번 Pin)

보면 rh라서 Hardware(주변장치)에 의해서 읽을 수가 있다.
h가 붙냐 안붙냐를 잘 봐야 한다. 기본적으로 SW에 의해선 읽을 수 있는데
h가 붙으면 HW에 의해서 읽고 쓸 수가 있는거임.
어쨋든 읽어서 Trigger가 된다. Interrupt든 Input이든

  • Pn_OUT

Pn_OUT.Px (n번 Port의 x번 Pin)

rwh로 되어있다. 즉 SW로도 HW로도 R/W가 모두 가능함
0을 쓰면 LOW, 1을 쓰면 High 신호를 뱉게 된다.


  • GPIO 실습

LED를 보면 D12/D13 Pull-Down으로 되어 있다.
그리고 이 Pin을 쫓으면 TC375에선 P10.2 / P10.1과 연결되어 있음을 알 수 있음.

만약 LED1을 점등하고 싶다면

#include "main.h"
 int main(void)
 {
   SYSTEM_Init();
   
   //10번 Port Module의 2번(10.2)니까 IOCR0에서
   //Push-Pull Output
   MODULE_P10.IOCR0.B.PC2 = 0x10; 
   
   
   MODULE_P10.OUT.B.P2 = 1; // OUT
   while(1);
   return 0;
 }
  • LED Toggle
#include "main.h"
 int main(void)
 {
   SYSTEM_Init();
   MODULE_P10.IOCR0.B.PC2 = 0x10;
   
   while(1) {
      MODULE_P10.OUT.B.P2 ^= 1; //Toggle
      delay_ms(500);
   } 
   
   return 0;
 }
  • LED 번갈아 점등
#include "main.h"
 int main(void)
 {
   SYSTEM_Init();
   MODULE_P10.IOCR0.B.PC2 = 0x10; 
   MODULE_P10.IOCR0.B.PC1 = 0x10;
   MODULE_P10.OUT.B.P2 = 1; 
   MODULE_P10.OUT.B.P1 = 0;
   delay_ms(100);
   
   while (1) {
      MODULE_P10.OUT.B.P1 ^= 1;
      MODULE_P10.OUT.B.P2 ^= 1; 
      delay_ms(100);
   }
   return 0;
 }

이번엔 Switch를 보자

각각 D2/D3와 연결되어 있고 이들은 P2.0/P2.1과 연결된다.
그리고 Pull-Up으로 되어있다.

P2의 IOCR0를 쓰겠다.

  • SW1을 누르는 동안 LED1이 점등
#include "main.h"
 int main(void)
 {
   SYSTEM_Init();
   MODULE_P10.IOCR0.B.PC2 = 0x10;
   
   MODULE_P02.IOCR0.B.PC0 = 0x2; // Input Pull-UP
   while (1) {
   
      if (MODULE_P02.IN.B.P0 == 0) { // Pull-up이니 누르면 0
         MODULE_P10.OUT.B.P2 = 1;
      } else {
         MODULE_P10.OUT.B.P2 = 0;
      }
   }
   return 0;
 }

IMAGE ALT TEXT HERE

  • SW1은 LED1 ON/OFF , SW2는 LED2 ON/OFF
#include "main.h"
 int main(void) {
   SYSTEM_Init();
   
   //switch
   MODULE_P02.IOCR0.B.PC0 = 0x2; 
   MODULE_P02.IOCR0.B.PC1 = 0x2; 
   
   //LED
   MODULE_P10.IOCR0.B.PC2 = 0x10; 
   MODULE_P10.IOCR0.B.PC1 = 0x10; 
   
   //INIT
   MODULE_P10.OUT.B.P2 = 0;
   MODULE_P10.OUT.B.P1 = 0;
   
   while (1) {
      if (MODULE_P02.IN.B.P0 == 0) { //SW1 누르면 0 -> Toggle
         MODULE_P10.OUT.B.P2 ^= 1;
      }
      if (MODULE_P02.IN.B.P1 == 0) { //SW2 누르면 0 -> Toggle
         MODULE_P10.OUT.B.P1 ^= 1;
      }
   }
   return 0;
 }

물론 이렇게 하면 스위치를 잘 누르는거 아니면 왔다갔다 한다.
그래서 flag 변수를 선언하고 그에 따라 동작하도록 하는게 성능 개선엔 낫다. 물론 그래도 조금의 오차는 발생함

IMAGE ALT TEXT HERE


  • 트랜지스터를 이용한 증폭회로

Port Module은 신호 0/1을 전달하는 역할인데
출력 전력 세기가 mw수준으로 낮아서 LED 동작엔 용이하지만

부저, 모터 동작을 위한 V, A는 부족하다.

즉 CPU -> PORT -> LED는 되는데
CPU -> PORT -> BUZZER/MOTOR는 안된다.

그래서 Motor의 경우 Port->Motor Drive 회로 -> Motor
Buzzer의 경우 Port -> 트랜지스터 -> Buzzer로 설계해야됨.

모터의 경우는 일반적으로 증폭/보호 회로가 포함되지만
부저 등 단순 Actuator는 트랜지스터를 이용해 회로 구성이 가능하다
차량용 Actuator 제어시 OBD(Open, Short to Battery, Short to Ground)
진단을 위한 스마트 IC를 활용한다.

  1. 능동(Active) 부저 :
    전류를 내보낼 때만 소리를 내는 단순한 스피커로
    LED ON/OFF와 동일함

  2. 수동(Passive) 부저 :
    Hz에 따라 소리 높낮이를 다르게 출력함
    Hz 설정에 따라 7음계를 출력이 가능함.

우리는 수동 부저가 달려있다.
Buzzer를 보면 D5에 연결되어 있고
이는 보드의 P02.3과 연결된다.

P02.3 Pin으로 High 출력시, 트랜지스터에 의해 충분한 전류가 흐른다.
각각 Rising/Falling Edge에서 출력을 내게 된다.
회로도 보면, Push-Pull로 연결되어 있다.

5옥타브 도의 진동수는 약 523Hz인데 주기와 주파수 역수 관계로 계산시
한 주기가 1912us가 된다.

그러면 Toggle 주기는 그 절반이 되어야함

#include "main.h"
 523hz에 대한ns 계산
void buzzer(unsigned int hz) {
   volatile int loop = 1000000000 / hz / 2 / 5 / 2;
   MODULE_P02.OUT.B.P3 = 1; // High
   for (int i = 0; i < loop; i++);
   MODULE_P02.OUT.B.P3 = 0; // Low
   for (int i = 0; i < loop; i++);
 }
 
 void main(void) {
   SYSTEM_Init();
   MODULE_P02.IOCR0.B.PC3 = 0x10;
   volatile unsigned int j = 0;
   while (j++ < 1000) { // 약 2초간 (1.91ms * 1000)
      buzzer(523);
   }
   while(1);
 }

이러면 주기적인 진동이 만들어지며 소리를 출력하게 된다.
사실은 Timer를 쓰는게 맞다. 이건 너무 하드 코딩이고
근사를 취한거라

#include "main.h"
 void main(void)
 {
   SYSTEM_Init(); // 부저초기화 포함
   Buzzer_Beep(NOTE_E5, 500); // 미(5옥타브)
   Buzzer_Beep(NOTE_D5, 200); // 레
   Buzzer_Beep(NOTE_C5, 300); // 도
   Buzzer_Beep(NOTE_D5, 250); // 레
   Buzzer_Beep(NOTE_E5, 300); // 미
   delay_ms(20); // 무음 구간
   Buzzer_Beep(NOTE_E5, 300); // 미
   delay_ms(20);
   Buzzer_Beep(NOTE_E5, 300); // 미
   while(1);
 }

IMAGE ALT TEXT HERE

profile
학습

0개의 댓글