본문 바로가기
DSP, MCU/TMS320F2838x (C28x)

TMS320F28388D ] CPU2에서 SCI, SPI 사용하기 위한 설정방법

by eteo 2022. 12. 6.

cpu1은 cpu2 또는 cm을 부트 시켜줄 수 있다. 또한 부트 전 pin mux 세팅을 하고 peripheral을 할당해 줄 수 있는 것도 cpu1 뿐이다.

 

CPU1 코드

#include "driverlib.h"
#include "device.h"

void Board_init();
void SCI_config();
void SPI_config();

void main(void)
{
    Device_init();
    Device_initGPIO();

    Interrupt_initModule();
    Interrupt_initVectorTable();
    
    // syscfg툴로 생성된 Board.c의 함수가 아니라 생성되는 코드를 참고해 직접 작성한 함수이다. 이 편이 직관성이 높아서 좋은 것 같다.
    Board_init();

    //
    // Boot CPU2 core
    // 부트모드에 따라 CPU2를 플래시 부트 또는 램 부트한다.
    #ifdef _FLASH
        Device_bootCPU2(BOOTMODE_BOOT_TO_FLASH_SECTOR0);
    #else
        Device_bootCPU2(BOOTMODE_BOOT_TO_M0RAM);
    #endif

    EINT;
    ERTM;

    for(;;)
    {
        GPIO_togglePin(DEVICE_GPIO_PIN_LED1);
        DEVICE_DELAY_US(1000000);
    }
}

void Board_init()
{
    EALLOW;
    LED_init();
    SCI_config();
    SPI_config();
    EDIS;
}

void LED_init()
{
    //
    // Initialize GPIO and configure the GPIO pin as a push-pull output
    // LED1, LED2 핀 config
    GPIO_setPadConfig(DEVICE_GPIO_PIN_LED1, GPIO_PIN_TYPE_STD);
    GPIO_setDirectionMode(DEVICE_GPIO_PIN_LED1, GPIO_DIR_MODE_OUT);
    GPIO_setPadConfig(DEVICE_GPIO_PIN_LED2, GPIO_PIN_TYPE_STD);
    GPIO_setDirectionMode(DEVICE_GPIO_PIN_LED2, GPIO_DIR_MODE_OUT);
    //
    // Configure CPU2 to control the LED GPIO
    // LED2 핀의 MasterCore를 CPU2로 설정
    GPIO_setMasterCore(DEVICE_GPIO_PIN_LED2, GPIO_CORE_CPU2);
    
    // default MasterCore가 CPU1이니까 LED1핀을 위한 MasterCore는 설정해줄 필요가 없다.
}

void SCI_config()
{
    //
    // Configuration for the SCI Rx/Tx pin.
    // SCI RX/TX 핀 config
    GPIO_setPinConfig(DEVICE_GPIO_CFG_SCIRXDA);
    GPIO_setPinConfig(DEVICE_GPIO_CFG_SCITXDA);

    //
    // Hand-over the SCIA module access to CPU2
    // SCIA 모듈을 CPU2에 연결
    SysCtl_selectCPUForPeripheral(SYSCTL_CPUSEL5_SCI, 1, SYSCTL_CPUSEL_CPU2);
}

void SPI_config()
{    
    // CS핀 설정. 해당 GPIO핀의 MasterCore를 CPU2로 설정
    // GPIO103 -> mySDCardCS Pinmux
    GPIO_setPinConfig(GPIO_103_GPIO103);
    GPIO_writePin(103, 1);
    GPIO_setPadConfig(103, GPIO_PIN_TYPE_STD);
    GPIO_setQualificationMode(103, GPIO_QUAL_SYNC);
    GPIO_setDirectionMode(103, GPIO_DIR_MODE_OUT);
    GPIO_setMasterCore(103, GPIO_CORE_CPU2);
    
    //
    // SPIC -> mySDCardSPI Pinmux
    // SPIC 모듈을 설정하고 CPU2에 연결
    GPIO_setPinConfig(GPIO_100_SPIC_SIMO);
    GPIO_setPadConfig(100, GPIO_PIN_TYPE_STD);
    GPIO_setQualificationMode(100, GPIO_QUAL_ASYNC);

    GPIO_setPinConfig(GPIO_101_SPIC_SOMI);
    GPIO_setPadConfig(101, GPIO_PIN_TYPE_STD);
    GPIO_setQualificationMode(101, GPIO_QUAL_ASYNC);

    GPIO_setPinConfig(GPIO_102_SPIC_CLK);
    GPIO_setPadConfig(102, GPIO_PIN_TYPE_STD);
    GPIO_setQualificationMode(102, GPIO_QUAL_ASYNC);

    SysCtl_selectCPUForPeripheral(SYSCTL_CPUSEL6_SPI, 3, SYSCTL_CPUSEL_CPU2);

}

 

 

 

 

 

 

각 페리페럴에 연결될 CPU를 선택할 수 있는 CPUSELx 레지스터의 구성은 다음과 같다.

매뉴얼을 읽어보니 CPU2 부트 전에 페리페럴의 권한을 넘겨줘야 하는것 같다.(CPU2가 부트되자마자 device init()함수 안에서 enable peripheral clocks 하므로) 이걸 모르고 부트 후에 CPU2에 권한을 주었는데 작동이 되긴 했었다. CPUSEL 레지스터는 EALLOW 보호를 받는다.

 

 

 

 

 

 

 

 

그리고 이것도 나중에 안건데 SysCtl_selectCPUForPeripheral() 함수의 설명을 읽어보니 SysCtl_selectCPUForPeripheralInstance() 함수를 쓰는 것을 추천한다고 한다. 매개변수가 2개로 줄었긴한데 사용법은 크게 다르지 않다.

 

 

 

 

CPU2 코드

#include "driverlib.h"
#include "device.h"

#include <stdio.h>
#include <stdarg.h>
uint32_t uartPrintf(uint8_t ch, char *fmt, ...);

void board_init();
void SCI_init();
void CS_init();
void SPI_init();

void main(void)
{
	uint16_t loopCount = 0;
    
    Device_init();
    Device_initGPIO();

    Interrupt_initModule();

    Interrupt_initVectorTable();

    // 이것도 syscfg 툴로 생성된 코드를 참조해 직접 작성한 함수이다.
    board_init();

    EINT;
    ERTM;

    for(;;)
    {
    	GPIO_togglePin(DEVICE_GPIO_PIN_LED2);
        
        uartPrintf(SCIA_BASE, "Hello World %d\n", loopCount++);
        
        DEVICE_DELAY_US(1000000);    
    }
}

void board_init()
{
    SCI_init();
    SPI_init();
}

void SCI_init()
{
	// SCI port software reset 후 설정, 다시 software reset
    SCI_performSoftwareReset(SCIA_BASE);

    // SCI 설정
    SCI_setConfig(SCIA_BASE, DEVICE_LSPCLK_FREQ, 115200, (SCI_CONFIG_WLEN_8 |
                                                        SCI_CONFIG_STOP_ONE |
                                                        SCI_CONFIG_PAR_NONE));
    SCI_resetChannels(SCIA_BASE);
    SCI_resetRxFIFO(SCIA_BASE);
    SCI_resetTxFIFO(SCIA_BASE);
    SCI_clearInterruptStatus(SCIA_BASE, SCI_INT_TXFF | SCI_INT_RXFF);
    SCI_enableFIFO(SCIA_BASE);
    SCI_enableModule(SCIA_BASE);
    SCI_performSoftwareReset(SCIA_BASE);
}

// 칩셀렉트 핀 init
void CS_init()
{
    GPIO_writePin(103, 1);
    GPIO_setPadConfig(103, GPIO_PIN_TYPE_STD);
    GPIO_setQualificationMode(103, GPIO_QUAL_SYNC);
    GPIO_setDirectionMode(103, GPIO_DIR_MODE_OUT);
}

void SPI_init()
{
	// SPI 모듈 disable 후 설정, 다시 enable
    SPI_disableModule(SPIC_BASE);
    SPI_setConfig(SPIC_BASE, DEVICE_LSPCLK_FREQ, SPI_PROT_POL0PHA1,
                  SPI_MODE_MASTER, 12500000, 8);
    SPI_enableFIFO(SPIC_BASE);
    SPI_disableLoopback(SPIC_BASE);
    SPI_setEmulationMode(SPIC_BASE, SPI_EMULATION_STOP_AFTER_TRANSMIT);
    SPI_enableModule(SPIC_BASE);
}

// printf 함수로 시리얼 터미널에 출력하기 위한 함수
uint32_t uartPrintf(uint8_t ch, char *fmt, ...)
{
  char buf[256];
  va_list args;
  int len;
  uint32_t ret = 0;

  va_start(args, fmt);
  len = vsnprintf(buf, 256, fmt, args);

  SCI_writeCharArray(ch, (uint16_t*)buf, len);
  //ret = uartWrite(ch, (uint8_t *)buf, len);

  va_end(args);
  
  return ret;
}

 

 

 

 

참고로 CPU2 프로젝트 설정에서 Predefined Symbol에 CPU2가 선언되어있으면 Device_init() 함수에서 수행되는 코드는 

SysCtl_disableWatchdog(); // 워치독 disable

Device_enableAllPeripherals(); // 모든 peripheral clock enable

Device_initGPIO(); // GPIO pin locks 해제 및 enable Pull ups

그리고 플래시 부트인 경우 램 실행 함수들을 복사하고 flash initialization 함수를 호출하는 것 뿐이다.