사용 MCU: STM32F407ZGT

사용 IDE: STM32CubeIDE

CubeMX버전: 6.0.1

사용 HAL 버전: STM32Cube FW_F4 V1.25.2

 

 

개요[1]

- Bosch 사에서 차량 내에서 호스트 컴퓨터 없이 마이크로 콘트롤러나 장치들이 서로 통신하기 위해 설계된 통신 규격

- 1993년에 처음 ISO 11898 국제 표준으로 제정

 

 

특징[2]

- Multi Master 구조

- 최대 1Mbps 통신 속도

- 최대 8byte 통신 데이터 수

- 통신 프로토콜 / 에러 처리를 하드웨어적으로 처리

- 다수의 장치(Standard: 11bit, Extended 29bit ID구분)간 통신 가능

- Twist pair wire와 차동신호를 사용하여 노이즈 환경에 강함

- 비용이 경제적

 

 

프로토콜[3]

- ISO 11898:2003 표준에서 Standard 11bit 식별자를 사용하여 125kbps ~ 1Mbps의 통신 속도를 제공합니다.

  이 표준은 나중에 Extended 29bit 식별자로 수정되었습니다.

- Standard 11bit: 2^11, 2048개의 메세지 식별자

- Extended 29bit: 2^29, 536,870,912개의 메세지 식별자

 

Standard CAN: 11-bit 식별자 Bit Fields

SOF 11-bit
Identifier
RTR IDE r0 DLC 0~8 bytes Data CRC ACK EOF IFS

SOF(Start of frame): 프레임의 시작 비트

Identifier: 메세지의 우선순위를 설정하는 11bit 식별자, 값이 낮을수록 우선 순위가 높다

RTR(single remote transmission request): 다른 노드로부터 정보가 필요할 때 정의. (Data Frame: 0, Remote Frame:1)

IDE(dominant single identifier extension): CAN 식별자 타입 정의 (Standard: 0, Extended: 1)

r0: Reserved bit

DLC(data length code): 송신하는 데이터의 byte 수로 4bit

Data: 송신 데이터로 최대 8byte

CRC(Cyclic redundancy check): 16bit checksum (15bit + 1bit 구분기호), 오류 감지를 위한 비트

EOF(End of Frame): 프레임 끝

IFS(interframe space): 7bit

 

Extended CAN: 29-bit 식별자 Bit Fields

SOF 11-bit
Identifier
SRR IDE 18-bit
Identifier
RTR r1 r0 DLC 0~8 bytes Data CRC ACK EOF IFS

SRR(substitute remote request): Extension format의 자리를 표시

IDE(identifier extension): 더 많은 식별자 비트가 뒤따른다는 것을 나타냄

r1: Reserved bit

 



회로

우선 제가 가지고 있는 보드의 회로도는 아래와 같습니다.

- R26, R27은 사용하고자 하는 CAN Transceiver 모듈에 따라 공급전압을 선택하기 위한 저항으로 사용되었습니다.

- R40은 임피던스 매칭을 위한 저항으로 사용되었습니다.

 

Transceiver

CAN 통신을 하기 위해서는 MCU에 CAN 핀이 있더라도 별도의 Transceiver를 달아주어야 합니다.

그 이유는 CAN 통신은 CANH와 CANL 라인으로 차동 신호로 통신을 하는데

MCU의 신호를 차동신호로, 차동 신호를 MCU가 받아들일 수 있는 디지털 신호로 변환시켜주는 역할을 하기 때문입니다. 또 정전기(ESD)같은 왜란에 의해 Transceiver가 데미지를 입어 MCU가 손상되는 것을 막아주는 역할이 될 수 있습니다.

 

종단저항

또 CANH와 CANL 양 종단에 120옴 정도의 저항을 달아주어야 합니다(거리가 가까우면 달아주지 않아도 괜찮다고 하기도 하는것 같습니다.) 120옴을 달아주어야 하는 이유는 신호 왜곡을 보호하기 위한 임피던스 매칭을 위해 ISO 11898 표준으로 정의했기 때문입니다.

 

퀀텀(Quantum, tq)[4][5]: bit time의 기본 시간 단위

* Synchronization Segment(Sync_Seg): 1 Time Quantum 길이, 다양한 버스 노드를 동기화에 사용

* Propagation Time Segment(Prop_Seg): CAN 네트워크 내에서 물리적 지연 시간을 보상에 사용

* Phase Buffer Segment 1(Phase_Seg1): 에지 위상 오류를 보상하는 데 사용 

* Phase Buffer Segment 2(Phase_Seg2): 에지 위상 오류를 보상하는 데 사용

 

 

CubeMX 설정

우선 STM32F407 데이터시트를 보면, CAN은 APB1을 사용하고 최대 42MHz의 클럭을 가지는 것을 확인할 수 있습니다.

 

 

HCLK 클럭 설정을 아래와 같이 최대로 설정하고 APB1의 클럭이 42MHz라는 것을 확인하였습니다.

 

 

 

Can을 활성화 하고 각 파라미터에 대해 알아보겠습니다.

Prescaler: 통신의 속도를 설정, (1/APB1 * Prescaler = Time Quantum, 1/42M * 16 = 380ns) 

 

 

Quanta 설정

CAN Bit Time Calculation (can-wiki.info) 사이트에 들어가서 파라미터를 얻습니다.

1. 사용 MCU 선택: ST Microelectronics bxCAN

2. Sample-Point 입력: 50~90%, (CANopen와 DeviceNet에서는 일반적으로 87.5%, ARINC 825에서는 75% 사용)

3. SJW: 1 고정

4. Baud rate 입력 (125 ~ 1000)

 

 

다음과 같은 값을 얻어 아래와 같이 입력하였습니다.

 

 

NVIC에서 CAN1에 대한 설정도 Enable 시켜줍니다.

 

 

 

프로젝트를 생성하고 다음과 같이 예제 프로그램을 작성하였습니다.

// 데이터를 송신예제

/* USER CODE BEGIN PV */
CAN_TxHeaderTypeDef   TxHeader;
uint8_t               TxData[8];
uint32_t              TxMailbox;
/* USER CODE END PV */


/* USER CODE BEGIN 0 */

// printf 를 사용하기 위한 함수
int _write(int file, char *ptr, int len)
{
	HAL_UART_Transmit(&huart1, (uint8_t *)ptr, (uint16_t)len, 100);

	return (len);
}

/* USER CODE END 0 */


int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_CAN1_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
  printf("Start\r\n");

  /* Can Start */
  if (HAL_CAN_Start(&hcan1) != HAL_OK)
  {
    /* Start Error */
    Error_Handler();
  }

  /* Configure Transmission process */
  TxHeader.StdId = 0x321;                 // Standard Identifier, 0 ~ 0x7FF
  TxHeader.ExtId = 0x01;                  // Extended Identifier, 0 ~ 0x1FFFFFFF
  TxHeader.RTR = CAN_RTR_DATA;            // 전송하는 메세지의 프레임 타입, DATA or REMOTE
  TxHeader.IDE = CAN_ID_STD;              // 전송하는 메세지의 식별자 타입, STD or EXT
  TxHeader.DLC = 8;                       // 송신 프레임 길이, 0 ~ 8 byte
  TxHeader.TransmitGlobalTime = DISABLE;  // 프레임 전송 시작될 때 timestamp counter 값을 capture.

  /* Set the data to be transmitted */
  TxData[0] = 1;
  TxData[1] = 2;
  TxData[2] = 3;
  TxData[3] = 4;
  TxData[4] = 5;
  TxData[5] = 6;
  TxData[6] = 7;
  TxData[7] = 8;

  /* Start the Transmission process */

  if (HAL_CAN_AddTxMessage(&hcan1, &TxHeader, TxData, &TxMailbox) != HAL_OK)
  {
	  printf("Can Send Fail\r\n");
      Error_Handler();
  }
  printf("Can Send Success\r\n");

/* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

 

// CAN 수신 예제

/* USER CODE BEGIN PV */
CAN_FilterTypeDef  sFilterConfig;   // 필터 설정 구조체 변수
CAN_RxHeaderTypeDef   RxHeader;
uint8_t               RxData[8];
/* USER CODE END PV */


/* USER CODE BEGIN 0 */

// printf 를 사용하기 위한 함수
int _write(int file, char *ptr, int len)
{
	HAL_UART_Transmit(&huart1, (uint8_t *)ptr, (uint16_t)len, 100);

	return (len);
}

// CAN 수신 인터럽트 콜백
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *CanHandle)
{
	printf("%s\r\n", __FUNCTION__);
  /* Get RX message */
  if (HAL_CAN_GetRxMessage(CanHandle, CAN_RX_FIFO0, &RxHeader, RxData) != HAL_OK)
  {
    /* Reception Error */
    Error_Handler();
  }

  printf("StdID: %04lx, IDE: %ld, DLC: %ld\r\n", RxHeader.StdId, RxHeader.IDE, RxHeader.DLC);
  printf("Data: %d %d %d %d %d %d %d %d\r\n", RxData[0], RxData[1], RxData[2], RxData[3], RxData[4], RxData[5], RxData[6], RxData[7]);

}

// CAN Error 콜백
void HAL_CAN_ErrorCallback(CAN_HandleTypeDef *hcan)
{
	printf("%s\r\n", __FUNCTION__);
}
/* USER CODE END 0 */


int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_CAN1_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
  printf("Start\r\n");

  /* CAN Filter 설정 */
  sFilterConfig.FilterBank = 0;
  sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;
  sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;
  sFilterConfig.FilterIdHigh = 0x0000;
  sFilterConfig.FilterIdLow = 0x0000;
  sFilterConfig.FilterMaskIdHigh = 0x0000;              // 0x00000000 = 모든 ID를 받아들이겠다
  sFilterConfig.FilterMaskIdLow = 0x0000;               
  sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0;
  sFilterConfig.FilterActivation = ENABLE;
  sFilterConfig.SlaveStartFilterBank = 14;              // CAN2의 FilterBank시작 위치, CAN2를 사용한다면 FilterBank를 SlaveStartFilterBank보다 크게 설정해야 함.

  if (HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig) != HAL_OK)
  {
    /* Filter configuration Error */
    Error_Handler();
  }

  /* Can Start */
  if (HAL_CAN_Start(&hcan1) != HAL_OK)
  {
    /* Start Error */
    Error_Handler();
  }

  /* Activate CAN RX notification */
  if (HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING) != HAL_OK)
  {
    /* Notification Error */
    Error_Handler();
  }

  printf("Can Ready!!\r\n");

/* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

 

송신 쪽 디버그 창

 

수신부 쪽 디버그 창

 

 

Reference

[1] CAN 버스 - 위키백과, 우리 모두의 백과사전

[2] CAN통신사용.pdf-Realsys

[3] Introduction to the Controller Area Network - TI Application Report

[4] The Configuration of the CAN Bit Timing - BOSCH

[5] Can Bit Time Calculation

반응형

안녕하세요.

 

MCU에서 인터럽트 핸들러 안에서는 동작을 최대한 짧게 가져가야 합니다.

 

그래서 USART와 같은 통신 인터페이스를 사용할때에는 Queue Buffer 구조를 사용합니다.

 

큐(Queue)는 FIFO(First-In, First-Out)로 가장 먼저 들어온 데이터가 가장 먼저 나가는 자료 구조입니다.

Queue 구조, 먼저 들어간 데이터가 가장 먼저 나온다.

 

그 중에서도 원형큐(Circular Queue) 또는 링 버퍼(Ring Buffer)는 다음과 같은 구조를 가지고 있습니다.

원형 큐 구조

 

만약 원이 다 차서 새로운 데이터가 들어오게 된다면 어떻게 될까요?

-> Data1 자리에 덮어쓰게 됩니다. 그 다음 데이터는 Data2 자리에..

 

원으로 계속 반복하게 된다고 해서 원형 큐라고 합니다.

 

원형큐를 다음과 같이 구현할 수 있습니다.

/*
*  Queue 구조 예시
*  만약 head가 tail을 역전할 경우 처리는..?
*/

#define MAX_QUEUE_SIZE		(255)

uint8_t queue_buffer[MAX_QUEUE_SIZE] = {0,};

uint8_t head;
uint8_t tail;

// push: 새로운 데이터를 Queue buffer에 담는다.
void push(uint8_t new_data)
{
  // 새로운 데이터를 queue에 넣는다.
  queue_buffer[head] = new_data;
  
  head++;
  
  // head가 queue 마지막 위치에 도달했다면, 0으로 초기화
  if (head >= MAX_QUEUE_SIZE) {
    head = 0;
  }
}

// pop: 가장 오래된 데이터를 가져온다.
uint8_t pop(void)
{
  // tail의 위치에 데이터를 가져온다
  uint8_t pop_data = queue_buffer[tail];
  
  tail++;
  
  // tail이 queue 마지막 위치에 도달했다면, 0으로 초기화
  if (tail >= MAX_QUEUE_SIZE) {
    tail = 0;
  }
  
  return pop_data;
}

uint8_t isEmpty(void)
{
  // head와 tail의 위치가 같으면 queue가 비어있음
  return head == tail;
}

 

 

위의 코드를 이용하여 USART 데이터를 처리하는 방식을 알아봅시다.

 


/* USER CODE BEGIN PFP */

uint8_t rx_data;

/*Interrupts RX Callback************************************************************/
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
  if (huart->Instance == USART1) {
    push(rx_data);
    HAL_UART_Receive_IT(&huart1, &rx_data, 1);
  }
}

int main(void)
{
  /* USER CODE BEGIN 1 */
  uint8_t data;
  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  
  MX_GPIO_Init();
  MX_USART1_UART_Init();

  /* USER CODE BEGIN 2 */
  HAL_UART_Receive_IT(&huart1, &rx_data, 1);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    if (isEmpty() == 0) {
      data = pop();
      
      // 데이터 처리
      // ...
    }
    
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

 

상세히 살펴볼게요

HAL_UART_RxCpltCallback 함수는 Uart Rx 인터럽트가 발생하면 호출되는 함수입니다.

 

1. huart 인스턴스가 USART1로부터 Rx 인터럽트가 발생되었다면

2. rx_data를 queue buffer에 넣고

3. 1개 USART 데이터를 수신했다면 인터럽트를 발생시켜라

 

가 되겠습니다.

 

main을 분석해보면

 

1. 1개 데이터를 수신했다면 인터럽트를 발생시켜라

2. 만약 Queue Buffer에 데이터가 있다면

3. 데이터를 꺼내와라

4. - 데이터 처리 -

5. 2 반복

 

이 되겠습니다.

 

데이터 처리는 제품에 맞는 프로토콜에 따라 프로그램하면 되겠습니다.

 

 

 

이를 모듈화 하기 위해서 구조체를 사용하여 구조를 잡아주면 좋을거 같습니다.

// module_uart.h

#define MAX_BUFFER_SIZE    (255)

typedef struct{
  uint8_t head;
  uint8_t tail;
  uint8_t buffer[MAX_BUFFER_SIZE];
}uart_t;

void push(uart_t*, uint8_t);
uint8_t pop(uart_t*);
uint8_t isEmpty(uart_t*);



// module_uart.c

void init_uart(uart_t* u)
{
  u->head = 0;
  u->tail = 0;
  memset(u->buffer, 0, sizeof(u->buffer));
}

void push(uart_t* u, uint8_t data)
{
  u->buffer[u->head] = data;
  
  u->head++;
  
  if (u->head >= MAX_BUFFER_SIZE) {
    u->head = 0;
  }
}

uint8_t pop(uart_t* u)
{
  uint8_t data = u->buffer[u->tail];
  
  u->tail++;
  
  if (u->tail >= MAX_BUFFER_SIZE) {
    u->tail = 0;
  }
  
  return data;
}

uint8_t isEmpty(uart_t* u)
{
  return u->head == u->tail;
}




// main.c

#include "module_uart.h"

uint8_t rx_data;
uart_t uart;

/*Interrupts RX Callback************************************************************/
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
  if (huart->Instance == USART1) {
    push(&uart, rx_data);
    HAL_UART_Receive_IT(&huart1, &rx_data, 1);
  }
}

void main() {
  uint8_t data;


  // ..
  
  
  init_uart(&uart);
  
  HAL_UART_Receive_IT(&huart1, &rx_data, 1);
    
  while(1) {
    if (isEmpty(&uart) == 0) {
      data = pop(&uart);
    
    }
  }
}

 

반응형

VS 버전: 2013

 

        private void NumberTextBox_KeyPress(object sender, KeyPressEventArgs e)
        {
            if(!(char.IsDigit(e.KeyChar) || e.KeyChar == Convert.ToChar(Keys.Back)))    
            {
                e.Handled = true;
            }
        }

 

목표

숫자만 입력받는 TextBox 유저 컨드롤 만들기

 

 

프로젝트->추가->사용자 정의 컨트롤을 클릭하여 생성합니다. 저는 NumberTextBox.cs라고 적겠습니다.

 


추가된 NumberTextBox.cs 에 코드를 보기 위해서 NumberTextBox를 더블 클릭합니다.

 

그러면 코드는 다음과 같이 작성되어 있습니다.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Signal_processing
{
    public partial class NumberTextBox : UserControl
    {
        public NumberTextBox()
        {
            InitializeComponent();
        }
    }
}

 

저는 TextBox를 그대로 가져와서 숫자만 입력되도록 이벤트를 추가할 것이기 때문에

 

아래와 같이 UserControl을 TextBox로 수정합니다.

 

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Signal_processing
{
    public partial class NumberTextBox : TextBox
    {
        public NumberTextBox()
        {
            InitializeComponent();
        }
    }
}

 

이제 NumberTextBox.cs를 더블클릭하면 다음과 같은 에러를 보게 됩니다.

 

 

 

.Designer.cs 줄: 32 열: 1이라는 메세지를 클릭하여 오류가 난 위치로 이동합니다.

다음과 같이 this.AutoScaleMode에 빨간줄이 그어져 있습니다. 

이것은 UserControl 클래스에서는 있었던 속성이 TextBox로 바꾸면서 이 속성이 없어서 나타난 오류입니다.

 

이 줄을 지워줍니다.

 

 

 

[이벤트 추가]

NumberTextBox.cs 열면 속성창에 NumberTextBox의 속성이 나타납니다.

번개 아이콘을 클릭하여 이벤트 속성으로 변경하고

KeyPress 영역을 더블클릭 하여 해당 이벤트가 자동생성되도록 합니다.

 

 

그러면 아래와 같이 NumberTextBox_KeyPress 함수가 생성됩니다.

 

 

다음과 같이 함수 안에 로직을 작성하면 완료됩니다.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Signal_processing
{
    public partial class NumberTextBox : TextBox
    {
        public NumberTextBox()
        {
            InitializeComponent();
        }

        private void NumberTextBox_KeyPress(object sender, KeyPressEventArgs e)
        {
            //숫자와 백스페이스를 제외한 나머지를 바로 처리
            if(!(char.IsDigit(e.KeyChar) || e.KeyChar == Convert.ToChar(Keys.Back)))    
            {
                e.Handled = true;
            }
        }
    }
}

 

 

이제 컴파일 한번 하면 구성 요소에 NumberTextBox가 나타나는것을 확인 할 수 있습니다.

 

 

 

 

Reference

[C#]TextBox에 숫자만 입력받기 (tistory.com)

반응형

MCU: STM32F103VCT

 

STM에는 Flexible static memory controller(이하 FSMC)라고 하는 동기 / 비동기 외부 메모리를 컨트롤 하기 위한 인터페이스가 있습니다. 여기에서 외부 메모리라함은 SRAM, NOR Flash, NAND Flash 그리고 LCD 모듈들이 있습니다.

 

LCD를 제어하기 위해서는 SPI, I2C로 제어하는 직렬(serial) 방식이나 8bit 또는 16bit로 제어하는 병렬(parallel) 방식이 있습니다.

 

오늘은 병렬로 제어하는 방식을 알아봅시다.

 

병렬 인터페이스에는 Intel 사의 8080 타입, 모토로라 사의 6800 타입이 있습니다.

두 타입의 차이점은 아래와 같습니다.

 

6800타입과 8080타입의 차이점

 

FSMC를 사용해서 LCD를 제어하기 위해서는 아래와 같이 핀을 연결해야 합니다.

 

FSMC [D0:D15] = FSMC 16bit 데이터 버스

FSMC NEx       = FSMC Chip Select

FSMC NOE      = FSMC Output Enable

FSMC NWE      = FSMC Write Enable

FSMC Ax         = LCD Register와 LCD Display RAM을 선택하기 위한 Address line (0~25)

                       (LCD의 RS핀과 연결합니다)

 

다음은 8080 타입 인터페이스의 LCD를 STM의 FSMC 인터페이스와  연결하는 방법을 나타냅니다.

 

 

다음은 6800 타입 인터페이스의 LCD를 STM의 FSMC 인터페이스와 연결하는 방법을 나타냅니다.

또, LCD를 제어하기 위한 타이밍 에 대한 설명도 나타냅니다. (이 부분은 수식을 봐도 잘 이해가 안됩니다..)

  • Address setup time
  • Address hold time
  • Data setup time

 

STM32F1 시리즈에서 FSMC관련된 메모리는 아래와 같이 구성됩니다.

 

 

STM Application Note에 보면 Nor Flash/SRAM bank는 병렬 LCD 인터페이스를 제어하는데 알맞다고 설명합니다.

따라서 Bank1을 사용하게 되고 Bank1은 sub bank를 4개를 가지고 있습니다. 이 sub bank에 따라 NEx의 숫자가 정해집니다.

 

 

그리고 RS핀에 연결된 Ax핀에 따라 LCD Display RAM를 제어하기 위한 주소가 달라집니다.

위 Table 101에 작은 글씨를 보면, 16bit 메모리의 경우에 내부적으로 HADDR의 25:1 의 범위를 사용한다고 되어있습니다. 즉 Ax에 왼쪽으로 1칸 쉬프트 시킨 값을 사용합니다. 여기서 x는 0부터 시작합니다.

 

예를 들면,

NE1에 A16을 사용한다. (= FSMC bank1 NOR/PSRAM 1을 사용)

= Base Address: 0x60000000

   RAM Address: (0b0000 0000 0000 0001 0000 0000 0000 0000 << 1)

                    = 0b0000 0000 0000 0010 0000 0000 0000 0000 = 0x20000

 

 

그렇다면 코드로는 간단하게 아래와 같이 작성하게 됩니다.

// Bank1 NE1
#define TFT_LCD_REG         (*((volatile unsigned short *) 0x60000000))
// A16
#define TFT_LCD_DATA        (*((volatile unsigned short *) 0x60020000))

void Write_REG(uint16_t Reg)
{
	TFT_LCD_REG = Reg;
}

void Write_Data(uint16_t Data)
{
	TFT_LCD_D = Data;
}

 

 

다음에는 FSMC를 사용하여 SSD1963 드라이버가 내장되어있는 LCD를 제어해보도록 하겠습니다.

 

감사합니다.

 

 

 

Reference

 

TFT LCD interfacing with the high-density STM32F10xxx FSMC

STM32F101xx, STM32F102xx, STM32F103xx, STM32F105xx and STM32F107xx advanced Arm®-based 32-bit MCUs - Reference manual

 

반응형

개발환경: TrueSTUDIO 9.3.0

결론: 프로젝트 속성 - Tool Settings에 있는 Assembler, C Compiler, C Linker에 Target FPU 속성을 FPv4-SP-D16으로 설정

 

안녕하세요.

 

CubeMX를 통해 TrueSTUDIO 프로젝트를 만들고 컴파일을 했는데

 

아래와 같은 메세지가 나타나면서 실패되는 현상이 있었습니다.

 

저는 아무것도 안했는데 말이죠...

error: Core\Src\adc.o uses VFP register arguments, Test.elf does not
d:/program files (x86)/atollic/truestudio for stm32 9.3.0/armtools/bin/../lib/gcc/arm-atollic-eabi/6.3.1/../../../../arm-atollic-eabi/bin/ld.exe: failed to merge target specific data of file Core\Src\adc.o
d:/program files (x86)/atollic/truestudio for stm32 9.3.0/armtools/bin/../lib/gcc/arm-atollic-eabi/6.3.1/../../../../arm-atollic-eabi/bin/ld.exe: error: Core\Src\gpio.o uses VFP register arguments, Test.elf does not
d:/program files (x86)/atollic/truestudio for stm32 9.3.0/armtools/bin/../lib/gcc/arm-atollic-eabi/6.3.1/../../../../arm-atollic-eabi/bin/ld.exe: failed to merge target specific data of file Core\Src\gpio.o
d:/program files (x86)/atollic/truestudio for stm32 9.3.0/armtools/bin/../lib/gcc/arm-atollic-eabi/6.3.1/../../../../arm-atollic-eabi/bin/ld.exe: error: Core\Src\main.o uses VFP register arguments, Test.elf does not
d:/program files (x86)/atollic/truestudio for stm32 9.3.0/armtools/bin/../lib/gcc/arm-atollic-eabi/6.3.1/../../../../arm-atollic-eabi/bin/ld.exe: failed to merge target specific data of file Core\Src\main.o
d:/program files (x86)/atollic/truestudio for stm32 9.3.0/armtools/bin/../lib/gcc/arm-atollic-eabi/6.3.1/../../../../arm-atollic-eabi/bin/ld.exe: error: Core\Src\stm32f4xx_hal_msp.o uses VFP register arguments, Test.elf does not
d:/program files (x86)/atollic/truestudio for stm32 9.3.0/armtools/bin/../lib/gcc/arm-atollic-eabi/6.3.1/../../../../arm-atollic-eabi/bin/ld.exe: failed to merge target specific data of file Core\Src\stm32f4xx_hal_msp.o
d:/program files (x86)/atollic/truestudio for stm32 9.3.0/armtools/bin/../lib/gcc/arm-atollic-eabi/6.3.1/../../../../arm-atollic-eabi/bin/ld.exe: error: Core\Src\stm32f4xx_it.o uses VFP register arguments, Test.elf does not
d:/program files (x86)/atollic/truestudio for stm32 9.3.0/armtools/bin/../lib/gcc/arm-atollic-eabi/6.3.1/../../../../arm-atollic-eabi/bin/ld.exe: failed to merge target specific data of file Core\Src\stm32f4xx_it.o
d:/program files (x86)/atollic/truestudio for stm32 9.3.0/armtools/bin/../lib/gcc/arm-atollic-eabi/6.3.1/../../../../arm-atollic-eabi/bin/ld.exe: error: Core\Src\syscalls.o uses VFP register arguments, Test.elf does not
d:/program files (x86)/atollic/truestudio for stm32 9.3.0/armtools/bin/../lib/gcc/arm-atollic-eabi/6.3.1/../../../../arm-atollic-eabi/bin/ld.exe: failed to merge target specific data of file Core\Src\syscalls.o
d:/program files (x86)/atollic/truestudio for stm32 9.3.0/armtools/bin/../lib/gcc/arm-atollic-eabi/6.3.1/../../../../arm-atollic-eabi/bin/ld.exe: error: Core\Src\system_stm32f4xx.o uses VFP register arguments, Test.elf does not
d:/program files (x86)/atollic/truestudio for stm32 9.3.0/armtools/bin/../lib/gcc/arm-atollic-eabi/6.3.1/../../../../arm-atollic-eabi/bin/ld.exe: failed to merge target specific data of file Core\Src\system_stm32f4xx.o
d:/program files (x86)/atollic/truestudio for stm32 9.3.0/armtools/bin/../lib/gcc/arm-atollic-eabi/6.3.1/../../../../arm-atollic-eabi/bin/ld.exe: error: Core\Src\tim.o uses VFP register arguments, Test.elf does not
d:/program files (x86)/atollic/truestudio for stm32 9.3.0/armtools/bin/../lib/gcc/arm-atollic-eabi/6.3.1/../../../../arm-atollic-eabi/bin/ld.exe: failed to merge target specific data of file Core\Src\tim.o
d:/program files (x86)/atollic/truestudio for stm32 9.3.0/armtools/bin/../lib/gcc/arm-atollic-eabi/6.3.1/../../../../arm-atollic-eabi/bin/ld.exe: error: Core\Src\usart.o uses VFP register arguments, Test.elf does not
d:/program files (x86)/atollic/truestudio for stm32 9.3.0/armtools/bin/../lib/gcc/arm-atollic-eabi/6.3.1/../../../../arm-atollic-eabi/bin/ld.exe: failed to merge target specific data of file Core\Src\usart.o

 

 

그래서 구글링 해 본 결과

 

프로젝트 Tool 설정에서 Miscellaneous에  -mfloat-abi=softfp 옵션을 주라고 해결책이 있었습니다.

 

그래서 아래와 같이 -mfloat-abi=softfp 옵션을 줘서 컴파일을 했더니 에러가 싹 사라졌습니다.

 

 

하지만 디버그 버튼을 통해 프로그램 다운을 했는데 동작이 되지 않았습니다.

 

그래서 설정했던 옵션을 지우고, 다른 프로젝트와 설정 비교를 해보니 Target에 FPU가 None으로 되어있었습니다.

 

아래 빨간 박스에 있는 

Assembler, C Compiler, C Linker에 Target FPU 속성을 FPv4-SP-D16으로 설정해주었더니 컴파일도 정상적으로 되었고 디버그 버튼으로 다운로딩도 잘 되고 프로그램도 정상 동작하는 것을 확인 하였습니다.

 

 

 

 

출처: gcc - ARM compilation error, VFP registers used by executable, not object file - Stack Overflow

반응형

MCU: STM32F103VCT6

IDE: STM32CubeIDE

LCD Driver: SSD1963

 

목표: LCD화면을 1초마다 흰색 > 검정 > 파랑 > 초록 > 빨강으로  변경 시키기.

 

 

STM32F103에 있는 FSMC(Flexible static memory controller)를 사용하여 LCD를 제어해보도록 하겠습니다.

 

제가 가지고 있는 보드의 LCD 회로도는 아래와 같습니다.

 

D[0:15] 인걸 봐서 16bit parellal 제어라는 것을 알 수 있고,

LCD RS, LCD_CS, LCD_WR, LCD_RD 핀이 있는걸 봐서는 8080 인터페이스를 쓴다는 것을 알 수 있습니다.(6800은 RD 대신 E)

 

 

CubeMX를 이용하여 아래와 같이 FSMC를 설정합니다.

 

Memory type: LCD Interface

LCD Register Select: A16

Data: 16bits

 

Address setup time i HCLK clock cycles: 0

Data setup time in HCLK clock cycles: 15

Bus turn around time in HCLK clock cycles: 0

 

제가 가지고 있는 보드는 8MHz의 크리스탈이 달려 있어 RCC 설정도 해 주고, PWM핀(PD13)을 GPIO Output으로 설정해 주고, GPIO Output level을 High로 설정했습니다.

클럭 설정도 최대 주파수로 설정해주었구요

 

프로젝트 설정과 (여기서 저는 CubeIDE로 프로젝트를 생성하였습니다.)

 

코드 제너레이터 설정까지 해 주고 GENERATE CODE 버튼을 눌러 프로젝트를 생성하였습니다.

 

 

STMCubeIDE를 켜고 fsmc.c를 켜면 아래와 같이 작성되어있습니다.

void MX_FSMC_Init(void)
{
  /* USER CODE BEGIN FSMC_Init 0 */

  /* USER CODE END FSMC_Init 0 */

  FSMC_NORSRAM_TimingTypeDef Timing = {0};

  /* USER CODE BEGIN FSMC_Init 1 */

  /* USER CODE END FSMC_Init 1 */

  /** Perform the SRAM1 memory initialization sequence
  */
  hsram1.Instance = FSMC_NORSRAM_DEVICE;
  hsram1.Extended = FSMC_NORSRAM_EXTENDED_DEVICE;
  /* hsram1.Init */
  hsram1.Init.NSBank = FSMC_NORSRAM_BANK1;
  hsram1.Init.DataAddressMux = FSMC_DATA_ADDRESS_MUX_DISABLE;
  hsram1.Init.MemoryType = FSMC_MEMORY_TYPE_SRAM;
  hsram1.Init.MemoryDataWidth = FSMC_NORSRAM_MEM_BUS_WIDTH_16;
  hsram1.Init.BurstAccessMode = FSMC_BURST_ACCESS_MODE_DISABLE;
  hsram1.Init.WaitSignalPolarity = FSMC_WAIT_SIGNAL_POLARITY_LOW;
  hsram1.Init.WrapMode = FSMC_WRAP_MODE_DISABLE;
  hsram1.Init.WaitSignalActive = FSMC_WAIT_TIMING_BEFORE_WS;
  hsram1.Init.WriteOperation = FSMC_WRITE_OPERATION_ENABLE;
  hsram1.Init.WaitSignal = FSMC_WAIT_SIGNAL_DISABLE;
  hsram1.Init.ExtendedMode = FSMC_EXTENDED_MODE_DISABLE;
  hsram1.Init.AsynchronousWait = FSMC_ASYNCHRONOUS_WAIT_DISABLE;
  hsram1.Init.WriteBurst = FSMC_WRITE_BURST_DISABLE;
  /* Timing */
  Timing.AddressSetupTime = 0;
  Timing.AddressHoldTime = 15;
  Timing.DataSetupTime = 15;
  Timing.BusTurnAroundDuration = 0;
  Timing.CLKDivision = 16;
  Timing.DataLatency = 17;
  Timing.AccessMode = FSMC_ACCESS_MODE_A;
  /* ExtTiming */

  if (HAL_SRAM_Init(&hsram1, &Timing, NULL) != HAL_OK)
  {
    Error_Handler( );
  }

  /** Disconnect NADV
  */

  __HAL_AFIO_FSMCNADV_DISCONNECTED();

  /* USER CODE BEGIN FSMC_Init 2 */

  /* USER CODE END FSMC_Init 2 */
}

 

이 부분은 SSD1963의 데이터시트를 참조해야 합니다.

 

SSD1963 데이터시트에 있는 타이밍 특성

저희가 프로젝트에서 설정한 HCLK의 주파수는 72MHz였고 이는 13ns 입니다.

Address Setup Time, Address Hold Time 그리고 Write Data Setup Time 의 시간이 13ns보다 짧기 때문에 

아래와 같이 해당 속성을 1로 설정해주었습니다.

 

또, STM LCD 레퍼런스 메뉴얼에 AccessMode는 B로 설정한다고 해서 A도 하고 B도 해봤는데 차이는 없었습니다.

  Timing.AddressSetupTime = 1;
  Timing.AddressHoldTime = 1;
  Timing.DataSetupTime = 1;
  Timing.BusTurnAroundDuration = 0;
  Timing.CLKDivision = 16;
  Timing.DataLatency = 17;
  Timing.AccessMode = FSMC_ACCESS_MODE_B;

기본 설정은 마쳤고, LCD 제어 코드를 아래와 같이 작성했습니다.

 

주소에 대해서는 [MCU/STM32:HAL] - [STM32F]FSMC와 LCD 를 참조해 주세요

/* ssd1963.h */

#ifndef INC_SSD1963_H_
#define INC_SSD1963_H_

#define  SSD1963_LCD_PIXEL_WIDTH    ((uint16_t)480)
#define  SSD1963_LCD_PIXEL_HEIGHT   ((uint16_t)272)


// Bank1 NE1
#define TFT_LCD_REG         (*((volatile unsigned short *) 0x60000000))
// A16
#define TFT_LCD_DATA        (*((volatile unsigned short *) 0x60020000))

void Write_REG(uint16_t Reg);
void Write_Data(uint16_t Data);
void LCD_init(void);
void LCD_Clear(uint16_t Color);

#endif /* INC_SSD1963_H_ */

 

/* ssd1963.c */

#include "main.h"
#include "ssd1963.h"

#define HDP     (479)

#define HT      (531)
#define HPS     (43)
#define LPS     (8)
#define HPW     (10)

#define VDP     (271)
#define VT      (288)
#define VPS     (12)
#define FPS     (4)
#define VPW     (10)

void Write_REG(uint16_t Reg)
{
	TFT_LCD_REG = Reg;
}

void Write_Data(uint16_t Data)
{
	TFT_LCD_DATA = Data;
}


void LCD_init(void)
{

	Write_REG(0x00E2);	              //PLL multiplier, set PLL clock to 120M
	Write_Data(0x0023);               //N=0x36 for 6.5M, 0x23 for 10M crystal
	Write_Data(0x0002);
	Write_Data(0x0004);

	Write_REG(0x00E0);                // PLL enable
	Write_Data(0x0001);

	HAL_Delay(10);

	Write_REG(0x00E0);
	Write_Data(0x0003);

	HAL_Delay(10);

	Write_REG(0x0001);                // software reset

	HAL_Delay(10);

	Write_REG(0x00E6);	              //PLL setting for PCLK, depends on resolution
	Write_Data(0x0001);
	Write_Data(0x0033);
	Write_Data(0x0032);

	Write_REG(0x00B0);	              //LCD SPECIFICATION
	Write_Data(0x0018);
	Write_Data(0x0000);
	Write_Data((HDP >> 8) & 0x00FF);  //Set HDP
	Write_Data(HDP & 0x00FF);
	Write_Data((VDP >> 8) & 0x00FF);  //Set VDP
	Write_Data(VDP & 0x00FF);
	Write_Data(0x0000);

	Write_REG(0x00B4);	              //HSYNC
	Write_Data((HT >> 8) & 0x00FF);   //Set HT
	Write_Data(HT & 0x00FF);
	Write_Data((HPS >> 8) & 0x00FF);  //Set HPS
	Write_Data(HPS & 0x00FF);
	Write_Data(HPW);			      //Set HPW
	Write_Data((LPS >> 8) & 0x00FF);  //Set HPS
	Write_Data(LPS & 0x00FF);
	Write_Data(0x0000);

	Write_REG(0x00B6);	              //VSYNC
	Write_Data((VT >> 8) & 0x00FF);   //Set VT
	Write_Data(VT & 0x00FF);
	Write_Data((VPS >> 8) & 0x00FF);  //Set VPS
	Write_Data(VPS & 0x00FF);
	Write_Data(VPW);			      //Set VPW
	Write_Data((FPS >> 8) & 0x00FF);  //Set FPS
	Write_Data(FPS & 0x00FF);

	// 16bit per pixel
	Write_REG(0x0036);                //rotation
	Write_Data(0x0050);

	// rgb565 format
	Write_REG(0x00F0);                //pixel data interface
	Write_Data(0x0003);

	Write_REG(0x0021);

	Write_REG(0x00BC);
	Write_Data(0x0080);
	Write_Data(0x0080);
	Write_Data(0x0080);
	Write_Data(0x0001);

	HAL_Delay(5);

	Write_REG(0x0029); //display on
}

// LCD Window 범위 설정
void ssd1963_SetDisplayWindow(uint16_t Xpos, uint16_t Ypos, uint16_t Width, uint16_t Height)
{
	uint8_t data = 0;
	/* Column addr set, 4 args, no delay: XSTART = Xpos, XEND = (Xpos + Width - 1) */
	Write_REG(0x002A);
	data = ((Xpos) >> 8) & 0xFF;
	Write_Data(data);
	data = (Xpos) & 0xFF;
	Write_Data(data);
	data = ((Xpos + Width - 1) >> 8) & 0xFF;
	Write_Data(data);
	data = (Xpos + Width - 1) & 0xFF;
	Write_Data(data);
	/* Row addr set, 4 args, no delay: YSTART = Ypos, YEND = (Ypos + Height - 1) */
	Write_REG(0x002b);
	data = ((Ypos) >> 8) & 0xFF;
	Write_Data(data);
	data = (Ypos) & 0xFF;
	Write_Data(data);
	data = ((Ypos + Height - 1) >> 8) & 0xFF;
	Write_Data(data);
	data = (Ypos + Height - 1) & 0xFF;
	Write_Data(data);
}

// Horizontal Line 그리기 함수
void ssd1963_DrawHLine(uint16_t RGBCode, uint16_t Xpos, uint16_t Ypos, uint16_t Length)
{
	uint16_t counter = 0;

	if(Xpos + Length > SSD1963_LCD_PIXEL_WIDTH) return;

	/* Set Cursor */
	ssd1963_SetDisplayWindow(Xpos, Ypos, Length, 1);

	Write_REG(0x002c);

	for(counter = 0; counter < Length; counter++)
	{
		Write_Data(RGBCode);
	}
}

// 전체 화면을 Color로 덮어쓰기
void LCD_Clear(uint16_t Color)
{
  uint32_t counter = 0;

  for(counter = 0; counter < SSD1963_LCD_PIXEL_HEIGHT; counter++)
  {
	  ssd1963_DrawHLine(Color, 0, counter, SSD1963_LCD_PIXEL_WIDTH);
  }
}

/* main.c */
/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2021 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by ST under BSD 3-Clause license,
  * the "License"; You may not use this file except in compliance with the
  * License. You may obtain a copy of the License at:
  *                        opensource.org/licenses/BSD-3-Clause
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "gpio.h"
#include "fsmc.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "ssd1963.h" // ssd1963 헤더 추가
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_FSMC_Init();
  /* USER CODE BEGIN 2 */

  LCD_init(); // LCD 초기화

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
	  //White
	  LCD_Clear(0xFFFF);
	  HAL_Delay(1000);
	  //Black
	  LCD_Clear(0x0000);
	  HAL_Delay(1000);
	  //Blue
	  LCD_Clear(0x001F);
	  HAL_Delay(1000);
	  //Green
	  LCD_Clear(0x07E0);
	  HAL_Delay(1000);
	  //Red
	  LCD_Clear(0xF800);
	  HAL_Delay(1000);

  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    Error_Handler();
  }
}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

 

이제 프로그램을 MCU에 다운로딩하면 1초마다 흰색 > 검정색 > 파랑색 > 초록색 > 빨간색으로 화면이 바뀌는것을 확인 할 수 있습니다.

 

Reference

 

TFT LCD interfacing with the high-density STM32F10xxx FSMC

Microsoft Word - SSD1963_1.3.doc (displaytech-us.com)

[MCU/STM32:HAL] - [STM32F]FSMC와 LCD

LCD Init 참조 코드

반응형

STM32CubeIDE에는 기본적으로 Terminal이 없습니다.

 

IDE에서 사용하려면 따로 Software를 설치하여야합니다.

 

이번에는 STM32CubeIDE의 기반인 Eclipse에 Terminal을 설치하는 방법에 대해 알아봅시다.

 

Help -> Install New Software로 갑니다.

 

 

Work with: --All Available Sites-- 로 선택하고 아래 Textbox에 terminal를 입력합니다.

잠시뒤 아래 리스트에 4개 아이템이 생기는데 "TM Terminal", "TM Terminal Serial Connector Extensions"를 선택하고 Next >를 클릭합니다.

 

 

그다음 화면에서도 Next > 를 클릭하고 라이센스 동의 후 Finish를 클릭하면 IDE 화면 오른쪽 아래에 Installing Software:라는 메세지와 함께 소프트웨어가 설치 됩니다.

 

 

이후 완료 설치가 완료됐다는 메세지와 함께 IDE를 다시 실행시킵니다.

 

터미널을 켜는 방법은 Window -> Show View -> Other ...을 클릭합니다.

 

 

Show View 창에서 Terminal 이라는 아이템을 선택후 Open을 클릭합니다.

 

그러면 어디선가 Terminal 이라는 창이 뜨게 됩니다.

터미널을 연결하기 위해선 아래 빨간박스 아이콘을 클릭합니다.

 

 

Choose terminal을 Serial Terminal로 선택하고

연결할 Serial port와 다른 속성들을 설정 후 OK를 합니다.

 

 

 

정상적으로 연결이 되고, 설정한 Comport로 어떠한 메세지가 들어온다면 아래와 같이 나타나게 됩니다.

반응형

 

TrueStudio에서 STM32CubeIDE으로 넘어오면서 약간씩 달라진 환경이 있습니다.

 

그중에 하나는 printf로 %f 출력하기 !

 

TrueStudio에서 출력하는 방법은 이 링크로 가세요 !

[MCU/TrueStudio] - [TrueStudio]printf에 %f 사용하기

 

 

STM32CubeIDE에서는

project -> Properties -> C/C++Build -> Settings -> Tool Settings -> MCU Settings ->

Use float with printf from newlib-nano (-u _printf_float) 체크 입니다!

반응형

+ Recent posts