이 문서에서는 Texas Instruments의 TMS320F28388D 마이크로컨트롤러에서 CLA(Control Law Accelerator)를 DriverLib API를 사용하여 설정하고 사용하는 방법을 상세히 다룹니다. C2000 시리즈의 고성능 마이크로컨트롤러인 TMS320F28388D의 CLA 모듈을 활용하여 병렬 연산을 수행하는 방법을 배우고, 다양한 독립적인 예제 코드를 통해 실제 구현 방법을 익힐 수 있습니다. 각 코드에는 상세한 주석이 포함되어 있으며, Code Composer Studio(CCS) 환경에서 실행 가능합니다.
1. TMS320F28388D CLA 개요
TMS320F28388D는 Texas Instruments의 C2000 시리즈에 속하는 고성능 32비트 마이크로컨트롤러로, CLA(Control Law Accelerator)는 CPU와 독립적으로 동작하는 부동소수점 코프로세서입니다. CLA는 모터 제어, 디지털 전력 변환, 신호 처리 등 고속 연산이 필요한 실시간 제어 애플리케이션에서 CPU 부하를 줄이며 병렬 처리를 수행합니다.
CLA 모듈의 주요 특징
- 독립적인 프로세서: CLA는 CPU와 별도의 프로그램 및 데이터 메모리(LS RAM)를 가지며, 병렬로 연산을 수행합니다.
- 부동소수점 지원: 32비트 부동소수점 연산을 지원하여 복잡한 제어 알고리즘(예: PID, FFT)을 효율적으로 처리.
- 태스크 기반 실행: 최대 8개의 태스크(Task 1~8)를 지원하며, ADC, ePWM, 타이머 등 다양한 트리거 소스로 활성화.
- 메모리 구조: LS RAM(L0~L7)을 CPU와 CLA 간 공유 가능, 전용 CLA 메모리 할당.
- 인터럽트: 각 태스크는 독립적인 인터럽트를 가지며, CPU와의 통신은 공유 메모리 또는 메시지 RAM을 통해 이루어짐.
- 클럭: 시스템 클럭(최대 200MHz)과 동기화, CPU와 동일한 클럭 속도로 동작.
- 고급 기능: 빠른 컨텍스트 스위칭, 낮은 레이턴시, ADC 및 ePWM과의 긴밀한 통합.
DriverLib API는 CLA 설정을 간소화하며, 하드웨어 레지스터를 직접 조작하는 대신 추상화된 함수를 제공합니다.
2. 주요 CLA DriverLib API 함수
아래는 TMS320F28388D의 CLA 모듈을 제어하기 위해 자주 사용되는 DriverLib API 함수와 그 사용 방법입니다.
2.2. 태스크 및 인터럽트 관련 함수
- CLA_forceTasks(base, tasks)
- 설명: 소프트웨어로 CLA 태스크를 강제로 트리거합니다.
- 매개변수:
- base: CLA 모듈의 베이스 주소 (CLA1_BASE).
- tasks: 실행할 태스크 플래그 (CLA_TASKFLAG_1 등, 비트 마스크로 조합 가능).
- 사용 예:
CLA_forceTasks(CLA1_BASE, CLA_TASKFLAG_1); // 태스크 1 실행
- Interrupt_clearACKGroup(group)
- 설명: CLA 태스크 완료 인터럽트 플래그를 지웁니다.
- 매개변수:
- group: 인터럽트 그룹 (INTERRUPT_ACK_GROUP11 for CLA 태스크 1).
- 사용 예:
Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP11); // 그룹 11 플래그 지우기
2.3. 메모리 및 데이터 관련 함수
- 이 예제에서는 별도의 메모리 설정 함수를 사용하지 않습니다. 메모리 할당은 링커 파일(2838x_RAM_CLA_lnk_cpu1.cmd)에서 정의됩니다:
- RAMLS0 (Cla1Prog): CLA 프로그램 (태스크 코드).
- RAMLS1 (CpuToCla1MsgRAM, Cla1ToCpuMsgRAM): 입력 및 출력 데이터.
3. CLA 설정 및 동작 원리
이 예제는 다음과 같은 절차로 CLA를 설정하고 동작합니다:
- 시스템 초기화:
- Device_init()로 시스템 클럭과 PLL을 설정합니다.
- Device_initGPIO()와 사용자 정의 initGPIO()로 GPIO31을 LED 출력 핀으로 초기화합니다.
- Interrupt_initModule()과 Interrupt_initVectorTable()로 인터럽트 시스템을 설정합니다.
- EINT와 ERTM으로 글로벌 인터럽트와 디버깅을 활성화합니다.
- CLA 메모리 설정:
- CLA 프로그램은 RAMLS0 (Cla1Prog)에 할당됩니다.
- 데이터 교환은 RAMLS1의 메시지 RAM (CpuToCla1MsgRAM, Cla1ToCpuMsgRAM)을 통해 수행됩니다.
- 메모리 매핑은 링커 파일(2838x_RAM_CLA_lnk_cpu1.cmd)에서 정의됩니다.
- CLA 태스크 설정:
- 태스크 벡터와 트리거는 링커 파일과 기본 설정에 의존하며, 별도의 API 호출 없이 소프트웨어 트리거(CLA_forceTasks)로 태스크 1을 실행합니다.
- CLA 코드 작성:
- .cla 파일(cla_multiply.cla)에 CLA 태스크 코드를 작성합니다.
- 태스크 1은 C 언어로 작성되며, 부동소수점 곱셈(inputData[0] * inputData[1])을 수행합니다.
- 미사용 태스크(2~8)는 디버깅용 MSTOP 명령어로 종료됩니다.
- CLA 활성화:
- CLA_forceTasks(CLA1_BASE, CLA_TASKFLAG_1)로 태스크 1을 실행합니다.
- 태스크 완료 후 Interrupt_clearACKGroup로 인터럽트 플래그를 지웁니다.
- 데이터 교환:
- CPU는 inputData[0] = 2.5, inputData[1] = 3.5를 CpuToCla1MsgRAM에 저장합니다.
- CLA 태스크 1은 곱셈 결과(8.75)를 Cla1ToCpuMsgRAM의 outputData에 저장합니다.
- CPU는 결과를 읽어 오차를 검증하고, 오차 < 0.0001일 경우 pass를 증가시키며, 성공 시 GPIO31 LED를 ON으로 설정합니다.
4. CLA 예제 코드
아래는 독립적인 CLA 예제 코드로, C2000Ware의 DriverLib를 기반으로 작성되었습니다.
예제 : 기본 CLA 태스크 실행
CLA Task 1을 사용하여 간단한 부동소수점 연산을 수행합니다.
// main.c
//###########################################################################
// 파일: main.c
// 제목: CLA 곱셈 예제
//
// 설명:
// 이 예제는 F2838x 디바이스의 CLA를 사용하여 2.5 * 3.5 = 8.75 곱셈을 수행합니다.
// CLA 태스크 1이 inputData[0]과 inputData[1]을 곱하여 outputData에 저장합니다.
// cla_ex1_asin.c 예제의 구조를 따르며, 최소 초기화를 사용합니다.
//
// 메모리 할당:
// - CpuToCla1MsgRAM (RAMLS0): inputData - 곱셈 입력 값
// - Cla1ToCpuMsgRAM (RAMLS0): outputData - 곱셈 결과
//
// 관찰 변수:
// - inputData: 입력 배열 [2.5, 3.5]
// - outputData: 곱셈 결과 (예상: 8.75)
// - pass: 성공한 테스트 수 (오차 < 0.0001)
// - fail: 실패한 테스트 수
//
// 하드웨어:
// - GPIO31: LED (pass > 0 && fail == 0일 때 ON)
//
// C2000Ware v6.00.00.00
//###########################################################################
#include "driverlib.h" // 시스템 및 GPIO 초기화 함수
#include "device.h" // 디바이스 설정
#include "cla_multiply_shared.h" // 공유 변수 및 CLA 태스크
// 디버깅용 상태 변수
uint16_t pass = 0; // 성공 테스트 수: 오차 < 0.0001
uint16_t fail = 0; // 실패 테스트 수: 오차 >= 0.0001
// 공유 메모리: CPU와 CLA 간 데이터 교환 (RAMLS0)
#ifdef __cplusplus
#pragma DATA_SECTION("CpuToCla1MsgRAM")
float inputData[2]; // 입력 데이터 배열
#pragma DATA_SECTION("Cla1ToCpuMsgRAM")
float outputData; // 곱셈 결과
#else
#pragma DATA_SECTION(inputData, "CpuToCla1MsgRAM")
float inputData[2] = {2.5f, 3.5f}; // 입력: [0] = 2.5, [1] = 3.5
#pragma DATA_SECTION(outputData, "Cla1ToCpuMsgRAM")
float outputData; // 출력: CLA에서 저장
#endif // __cplusplus
// initGPIO - GPIO31을 LED로 초기화
void initGPIO(void)
{
GPIO_setPinConfig(GPIO_31_GPIO31); // GPIO31 설정
GPIO_setDirectionMode(31, GPIO_DIR_MODE_OUT); // 출력 모드
GPIO_setPadConfig(31, GPIO_PIN_TYPE_PULLUP); // 풀업 저항
GPIO_writePin(31, 0); // 초기 LED OFF
}
// CLA_runTest - CLA 태스크 실행 및 결과 검증
void CLA_runTest(void)
{
float error; // 오차 계산용
const float expectedResult = 8.75f; // 예상 결과: 2.5 * 3.5
// 입력 데이터 설정
inputData[0] = 2.5f; // 첫 번째 입력
inputData[1] = 3.5f; // 두 번째 입력
// CLA 태스크 1 실행 (소프트웨어 트리거)
CLA_forceTasks(CLA1_BASE, CLA_TASKFLAG_1);
asm(" RPT #255 || NOP"); // 태스크 완료 대기
// 결과 검증: 오차 계산
error = fabsf(expectedResult - outputData);
if (error < 0.0001f)
{
pass++; // 오차 < 0.0001: 성공
}
else
{
fail++; // 오차 >= 0.0001: 실패
}
}
// cla1Isr1 - CLA 태스크 1 인터럽트 처리
__interrupt void cla1Isr1(void)
{
Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP11); // 그룹 11 플래그 지우기
}
// main - 프로그램 진입점
void main(void)
{
Device_init(); // 시스템 초기화 (클럭, PLL 등)
Device_initGPIO(); // GPIO 초기화
initGPIO(); // GPIO31 설정
Interrupt_initModule(); // PIE 초기화
Interrupt_initVectorTable(); // 인터럽트 벡터 설정
EINT; // CPU 인터럽트 활성화
ERTM; // 실시간 디버깅 활성화
CLA_runTest(); // CLA 테스트 실행
for(;;)
{
GPIO_writePin(31, (pass > 0 && fail == 0) ? 1 : 0); // LED 제어
}
}
// cla_multiply.cla
//###########################################################################
// 파일: cla_multiply.cla
// 제목: CLA 곱셈 태스크
//
// 설명:
// CLA를 사용하여 inputData[0] * inputData[1] (2.5 * 3.5 = 8.75)을 계산합니다.
// cla_ex1_asin.c 예제의 구조를 따르며, 태스크 1만 사용합니다.
//
// 메모리 할당:
// - Cla1Prog (RAMLS5): Cla1Task1 - 태스크 코드
// - CpuToCla1MsgRAM (RAMLS0): inputData - 입력 값
// - Cla1ToCpuMsgRAM (RAMLS0): outputData - 결과
//
// 주의:
// - 태스크 2~8은 미사용이며, MSTOP으로 종료 (CLA 전용 디버깅 명령어).
// - 태스크 플래그는 자동 관리되거나 ISR에서 처리.
//
// C2000Ware v6.00.00.00
//###########################################################################
#include "cla_multiply_shared.h"
// Cla1Task1 - 곱셈 수행
__interrupt void Cla1Task1(void)
{
outputData = inputData[0] * inputData[1]; // 2.5 * 3.5 = 8.75
}
// 미사용 태스크: 디버깅용 MSTOP
__interrupt void Cla1Task2(void) { asm(" MSTOP"); }
__interrupt void Cla1Task3(void) { asm(" MSTOP"); }
__interrupt void Cla1Task4(void) { asm(" MSTOP"); }
__interrupt void Cla1Task5(void) { asm(" MSTOP"); }
__interrupt void Cla1Task6(void) { asm(" MSTOP"); }
__interrupt void Cla1Task7(void) { asm(" MSTOP"); }
__interrupt void Cla1Task8(void) { asm(" MSTOP"); }
// cla_multiply_shared.h
//###########################################################################
// 파일: cla_multiply_shared.h
// 제목: CLA 곱셈 예제 공유 헤더
//
// 설명:
// main.c와 cla_multiply.cla 간 공유 변수와 CLA 태스크 프로토타입을 정의합니다.
// cla_ex1_asin_shared.h 구조를 따릅니다.
//
// 공유 변수:
// - inputData: 곱셈 입력 배열 [2.5, 3.5]
// - outputData: 곱셈 결과 (8.75)
//
// 함수 프로토타입:
// - Cla1Task1 ~ Cla1Task8: CLA 태스크 진입점
//
// C2000Ware v6.00.00.00
//###########################################################################
#ifndef _CLA_MULTIPLY_SHARED_H_
#define _CLA_MULTIPLY_SHARED_H_
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
// 공유 변수: 메시지 RAM에 할당
extern float inputData[2]; // 입력: [0] = 2.5, [1] = 3.5
extern float outputData; // 출력: 곱셈 결과
// CLA 태스크 프로토타입
__interrupt void Cla1Task1(void); // 태스크 1: 곱셈
__interrupt void Cla1Task2(void); // 미사용
__interrupt void Cla1Task3(void); // 미사용
__interrupt void Cla1Task4(void); // 미사용
__interrupt void Cla1Task5(void); // 미사용
__interrupt void Cla1Task6(void); // 미사용
__interrupt void Cla1Task7(void); // 미사용
__interrupt void Cla1Task8(void); // 미사용
#ifdef __cplusplus
}
#endif
#endif // _CLA_MULTIPLY_SHARED_H_
CLA_SCRATCHPAD_SIZE = 0x100;
--undef_sym=__cla_scratchpad_end
--undef_sym=__cla_scratchpad_start
MEMORY
{
/* BEGIN is used for the "boot to SARAM" bootloader mode */
BEGIN : origin = 0x000000, length = 0x000002
BOOT_RSVD : origin = 0x000002, length = 0x0001AF /* Part of M0, BOOT rom will use this for stack */
RAMM0 : origin = 0x0001B1, length = 0x00024F
RAMM1 : origin = 0x000400, length = 0x0003F8 /* on-chip RAM block M1 */
// RAMM1_RSVD : origin = 0x0007F8, length = 0x000008 /* Reserve and do not use for code as per the errata advisory "Memory: Prefetching Beyond Valid Memory" */
RAMD0 : origin = 0x00C000, length = 0x000800
RAMD1 : origin = 0x00C800, length = 0x000800
RAMLS0 : origin = 0x008000, length = 0x000800
RAMLS1 : origin = 0x008800, length = 0x000800
RAMLS2 : origin = 0x009000, length = 0x000800
RAMLS3 : origin = 0x009800, length = 0x000800
RAMLS4 : origin = 0x00A000, length = 0x000800
RAMLS5 : origin = 0x00A800, length = 0x000800
RAMLS6 : origin = 0x00B000, length = 0x000800
RAMLS7 : origin = 0x00B800, length = 0x000800
RAMGS0 : origin = 0x00D000, length = 0x001000
RAMGS1 : origin = 0x00E000, length = 0x001000
RAMGS2 : origin = 0x00F000, length = 0x001000
RAMGS3 : origin = 0x010000, length = 0x001000
RAMGS4 : origin = 0x011000, length = 0x001000
RAMGS5 : origin = 0x012000, length = 0x001000
RAMGS6 : origin = 0x013000, length = 0x001000
RAMGS7 : origin = 0x014000, length = 0x001000
RAMGS8 : origin = 0x015000, length = 0x001000
RAMGS9 : origin = 0x016000, length = 0x001000
RAMGS10 : origin = 0x017000, length = 0x001000
RAMGS11 : origin = 0x018000, length = 0x001000
RAMGS12 : origin = 0x019000, length = 0x001000
RAMGS13 : origin = 0x01A000, length = 0x001000
RAMGS14 : origin = 0x01B000, length = 0x001000
RAMGS15 : origin = 0x01C000, length = 0x000FF8
// RAMGS15_RSVD : origin = 0x01CFF8, length = 0x000008 /* Reserve and do not use for code as per the errata advisory "Memory: Prefetching Beyond Valid Memory" */
/* Flash sectors */
FLASH0 : origin = 0x080000, length = 0x002000 /* on-chip Flash */
FLASH1 : origin = 0x082000, length = 0x002000 /* on-chip Flash */
FLASH2 : origin = 0x084000, length = 0x002000 /* on-chip Flash */
FLASH3 : origin = 0x086000, length = 0x002000 /* on-chip Flash */
FLASH4 : origin = 0x088000, length = 0x008000 /* on-chip Flash */
FLASH5 : origin = 0x090000, length = 0x008000 /* on-chip Flash */
FLASH6 : origin = 0x098000, length = 0x008000 /* on-chip Flash */
FLASH7 : origin = 0x0A0000, length = 0x008000 /* on-chip Flash */
FLASH8 : origin = 0x0A8000, length = 0x008000 /* on-chip Flash */
FLASH9 : origin = 0x0B0000, length = 0x008000 /* on-chip Flash */
FLASH10 : origin = 0x0B8000, length = 0x002000 /* on-chip Flash */
FLASH11 : origin = 0x0BA000, length = 0x002000 /* on-chip Flash */
FLASH12 : origin = 0x0BC000, length = 0x002000 /* on-chip Flash */
FLASH13 : origin = 0x0BE000, length = 0x002000 /* on-chip Flash */
CPU1TOCPU2RAM : origin = 0x03A000, length = 0x000800
CPU2TOCPU1RAM : origin = 0x03B000, length = 0x000800
CPUTOCMRAM : origin = 0x039000, length = 0x000800
CMTOCPURAM : origin = 0x038000, length = 0x000800
CANA_MSG_RAM : origin = 0x049000, length = 0x000800
CANB_MSG_RAM : origin = 0x04B000, length = 0x000800
RESET : origin = 0x3FFFC0, length = 0x000002
CLA1_MSGRAMLOW : origin = 0x001480, length = 0x000080
CLA1_MSGRAMHIGH : origin = 0x001500, length = 0x000080
CLA1_DMA_MSGRAM : origin = 0x001680, length = 0x000080
DMA_CLA1_MSGRAM : origin = 0x001700, length = 0x000080
}
SECTIONS
{
codestart : > BEGIN
.text : >> RAMM0 | RAMD0 | RAMLS2 | RAMLS3 | RAMLS4
.cinit : > RAMLS3
.switch : > RAMM0
.reset : > RESET, TYPE = DSECT /* not used, */
.stack : > RAMM1
#if defined(__TI_EABI__)
.bss : > RAMLS4
.bss:output : > RAMLS3
.init_array : > RAMM0
.const : > RAMLS4
.data : > RAMLS4
.sysmem : > RAMLS4
#else
.pinit : > RAMM0
.ebss : > RAMLS4
.econst : > RAMLS4
.esysmem : > RAMLS4
#endif
ramgs0 : > RAMGS0, type=NOINIT
ramgs1 : > RAMGS1, type=NOINIT
MSGRAM_CPU1_TO_CPU2 > CPU1TOCPU2RAM, type=NOINIT
MSGRAM_CPU2_TO_CPU1 > CPU2TOCPU1RAM, type=NOINIT
MSGRAM_CPU_TO_CM > CPUTOCMRAM, type=NOINIT
MSGRAM_CM_TO_CPU > CMTOCPURAM, type=NOINIT
dclfuncs : > RAMLS3
/* CLA specific sections */
Cla1Prog : > RAMLS5
CLADataLS0 : > RAMLS0
CLADataLS1 : > RAMLS1
Cla1ToCpuMsgRAM : > CLA1_MSGRAMLOW, type=NOINIT
CpuToCla1MsgRAM : > CLA1_MSGRAMHIGH, type=NOINIT
Cla1ToDmaMsgRAM : > CLA1_DMA_MSGRAM, type=NOINIT
DmaToCla1MsgRAM : > DMA_CLA1_MSGRAM, type=NOINIT
Cla1DataRam : >> RAMLS0 | RAMLS1
/* CLA C compiler sections */
//
// Must be allocated to memory the CLA has write access to
//
CLAscratch :
{ *.obj(CLAscratch)
. += CLA_SCRATCHPAD_SIZE;
*.obj(CLAscratch_end) } > RAMLS1
.scratchpad : > RAMLS1
.bss_cla : > RAMLS1
.const_cla : > RAMLS1
cla_shared : > RAMLS1
.TI.ramfunc : {} > RAMM0
}
/*
//===========================================================================
// End of file.
//===========================================================================
*/
설명:
- 기능: CLA 태스크 1은 공유 메모리(CpuToCla1MsgRAM)에 저장된 두 입력 데이터(inputData[0] = 2.5, inputData[1] = 3.5)를 곱하여 결과를 공유 메모리(Cla1ToCpuMsgRAM)의 outputData에 저장합니다.
- 설정: 소프트웨어 트리거(CLA_forceTasks)를 사용하여 CLA 태스크 1을 실행합니다. CLA 프로그램은 RAMLS0 (Cla1Prog)에, 입력 및 출력 데이터는 RAMLS1 (CpuToCla1MsgRAM, Cla1ToCpuMsgRAM)에 할당됩니다. 링커 파일(2838x_RAM_CLA_lnk_cpu1.cmd)에 따라 메모리와 태스크 벡터가 설정됩니다.
- GPIO: GPIO31을 LED로 사용하며, 테스트 성공(pass > 0 && fail == 0, 즉 결과 오차 < 0.0001) 시 LED를 ON, 실패 시 OFF로 설정합니다.
- 출력: outputData = 2.5 * 3.5 = 8.75. 결과 오차가 0.0001 미만이면 정상 동작으로 간주하여 pass를 증가시키고 GPIO31 LED를 ON으로 설정합니다.
5. 추가 고려 사항
- C2000Ware: 예제 코드는 C2000Ware의 DriverLib 기반. C:\ti\c2000에 설치.
- 디버깅: CCS의 CLA 디버깅 모드를 사용하여 태스크 실행 추적 가능. .syscfg 툴로 CLA 설정 시각적 구성 가능.
- 성능 최적화: CLA는 CPU와 병렬 실행으로 실시간 성능 향상. 복잡한 연산(PID, 필터링)을 CLA로 오프로드 권장.
키워드: TMS320F28388D, CLA, Control Law Accelerator, DriverLib, C2000, 부동소수점, 병렬 처리, Code Composer Studio, ADC 트리거, ePWM 트리거, 공유 메모리, 실시간 제어
'MCU > C2000' 카테고리의 다른 글
TMS320F28388D DSP SPI 사용법: DriverLib API로 SPI 설정 및 코드(수정) (3) | 2025.08.17 |
---|---|
TMS320F28388D DSP CAN 사용법: DriverLib API로 CAN 설정 및 코드(수정) (0) | 2025.08.17 |
TMS320F28388D DSP SDFM 사용법: DriverLib API로 SDFM 설정 및 코드(수정) (0) | 2025.08.17 |
TMS320F28388D DSP CLB 사용법: DriverLib API로 CLB 설정 및 코드(수정) (0) | 2025.08.17 |
TMS320F28388D DSP EMIF 사용법: DriverLib API로 EMIF 설정 및 코드(수정) (0) | 2025.08.17 |
TMS320F28388D DSP eQEP 사용법: DriverLib API로 eQEP 설정 및 코드(수정) (0) | 2025.08.17 |
TMS320F28388D DSP eCAP 사용법: DriverLib API로 eCAP 설정 및 코드(수정) (3) | 2025.08.17 |
TMS320F28388D DSP ePWM 사용법: DriverLib API로 ePWM 설정 및 코드(수정본) (0) | 2025.08.16 |