사람의 눈은 밝기를 선형적으로 인지할 수 없습니다. 즉, 어두운 부분에서 밝기의 변화는 실제 변화보다 더 많이 변화하였다고 인지하고 밝은 부분에서는 밝기 변화는 실제 변화보다 적게 변화하였다는 느껴진다는 것입니다.



위 그림은 입력에 따른 실제 밝기의 변화는 기울기인 '1'이지만 사람의 눈 빨간색 커브처럼 느낀다는 것입니다. 여기서 x축은 밝아지는 방향으로 어두운 부분에서는 실제 변화보다 크게 변화하는 것처럼 보이고 밝은 부분에서 실제 변화보다 적게 변화하는 것처럼 보여진다는 것입니다.


사람의 눈의 이러한 왜곡을 해결하기 위해서 이미지 데이터를 미리 빨간색 커브를 y=x의 곡선에 대칭인 커브로 조정한다면 해결될 수 있다는 것입니다. 이와 같은 커브는 지수함수로 나타낼 수 있으며 이처럼 이미지 데이터를 보정하는 것을 감마보정(gamma correction)이라고 부릅니다.



위 그림에서 초록색 커브는 감마 교정을 한 커브이며 이 커브의 지수함수 표현은 다음 식과 같습니다.



입출력은 밝기이며 M은 최대값을 의미하며 통상 255이며, g는 감마(gamma)값입니다. 만일 g값이 '1'이면 기울기 '1'이어서 선형적인 위 그림에 파란색 직선 y=x이 g값이 '1'보다 작으면 위로 볼록한 커브가 만들어지고 '1'보다 크면 아래로 볼록한 커브가 반들어집니다. 또한 g값이 '0.5'인 커브의 y=x 축에 대한 선형 대칭 변환은 '1/0.5'의 g값을 갖습니다.



Posted by Nature & Life


Color LED는 각각 red, green, blue의 색을 발하는 3개의 LED 소자가 집적화되어 빛의 3원색에 해당하는 각 LED의 발기를 조절하면 사용자가 원하는 색깔의 얻을 수 있는 LED입니다. 다음은 대표적인 color LED인 Adafruit사의 ada-2739입니다. 



Color LED는 단자가 4개로 아래 그림에서와 같이 가장 긴 단자가 2번으로 Common Anode이며, 이를 Common-Anode 타입이라 부릅니다. 따라서 Common Anode 단자에는 VDD가 연결되어야 하며 Common-Cathode 타입의 경우에는 반대로 GND에 연결되어야 합니다. 



위와 같은 Common-Anode 타입의 color LED는 각 rgb 단자가 LOW이면 점등되며, 그러므로 PWM 제어의 경우 duty가 0%에서 점등하게 된다는 것입니다. 만일 각각의 rgb 단자에 8-bit인 256 레벨의 PWM 출력을 연결하고 전체 24-bit 데이터 스트림으로 PWM을 동시에 제어할 수 있다면 이 color LED를 자유롭게 색상을 변경하며 사용할 수 있습니다(24-bit true color).


이를 위해 설계된 칩이 WS2811로 시리얼 통신으로 24-bit 데이터를 전송 받고 3개의 8-bit PWM 채널로 출력하며, 별도의 저항 소자가 필요없도록 약 20mA로 전류를 제어한다는 것입니다. 최근에는 color LED 자체에 WS2811 소자를 내장하여 구동이 더욱 간편한 digital color LED가 출시되고 있습니다.


WS2811은 single-line NZR(None return to zero) 통신 모드를 사용합니다. DIN 포트는 데이터를 받고, DOUT 포트에 cascade로 연결된 다음 WS2811 소자에 800KHz로 다시 데이터를 전송됩니다. 비록 순차적으로 24-bit 데이터가 전송되지만 사람의 눈으로 보기에는 여러개의 color LED가 동시에 켜지고 커지는 것처럼 보이게 됩니다.



아래 그림에서와 첫번째 WS2811 소자는 연속적으로 들어오는 데이터의 첫번째 24-bit 데이터를 내부 data latch에 래치시키고, 나머지 후속되는 데이터는 다음 WS2811 소자로 보내게 됩니다. 또한 두번째 WS2811 소자도 자신에게 도달하는 첫번째 24-bit 데이터를 취하고 나머지 후속되는 데이터를 세번째 WS2811 소자로 보내지며, casecade로 연결된 소자들이 이를 반복하게 됩니다.



위 그림에서와 같이 cascade로 연결된 color LED를 실장한 WS2811 소자는 차례로 D1, D2, D3, ...라고 하면 시간에 따른 데이터의 흐름을 도식적으로 보인 것입니다. 



데이터 통신 방식은 800KHz에서 고주파 NRZ 프로토콜을 따르므로, 각 비트는 다음과 같이 코딩되어야 합니다. 타이밍은 엄격하지 않으며 +/-150ns의 마진이 있습니다.

    • Bit 0 - 1/3 of the period HIGH, 2/3 of the period LOW

    • Bit 1 - 2/3 of the period HIGH, 1/3 of the period LOW

    • Reset Stay LOW for at least 40 cycles (50us are about 40 periods of 1.25us)


데이터 전송은 RGB의 순서를 따르며 high bit가 먼저 전송됩니다.


http://fabioangeletti.altervista.org/blog/stm32-interface-ws2812b/?doing_wp_cron=1520571971.3953249454498291015625



Posted by Nature & Life


https://vesc-project.com/node/281


In VESC, the packet(payload) structure on UART communication


The packet is a uint8_t byte stream.

  • First byte(header):

    • 0x02 for payload length of 256 bytes, the next one byte is for the payload length

    • 0x03 for > 256 byte payload length, the next 2 bytes for the payload length

  • Payload(communication command + data)

  • The following 2 bytes after the payload are the checksum.

  • The byte stream it terminated with a 0x03 (footer).


"header(2 | 3) + 1-2 byte length + 2 byte crc + footer(3)"


The following function, packet_process_byte() shows how to process a packet stream.


packet.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
void packet_process_byte(uint8_t rx_data, int handler_num) {
    switch (handler_states[handler_num].rx_state) {
    case 0:
        if (rx_data == 2) {
            // 1 byte PL len
            handler_states[handler_num].rx_state += 2;
            handler_states[handler_num].rx_timeout = PACKET_RX_TIMEOUT;
            handler_states[handler_num].rx_data_ptr = 0;
            handler_states[handler_num].payload_length = 0;
        } else if (rx_data == 3) {
            // 2 byte PL len
            handler_states[handler_num].rx_state++;
            handler_states[handler_num].rx_timeout = PACKET_RX_TIMEOUT;
            handler_states[handler_num].rx_data_ptr = 0;
            handler_states[handler_num].payload_length = 0;
        } else {
            handler_states[handler_num].rx_state = 0;
        }
        break;
 
    case 1:
        handler_states[handler_num].payload_length = (unsigned int)rx_data << 8;
        handler_states[handler_num].rx_state++;
        handler_states[handler_num].rx_timeout = PACKET_RX_TIMEOUT;
        break;
 
    case 2:
        handler_states[handler_num].payload_length |= (unsigned int)rx_data;
        if (handler_states[handler_num].payload_length > 0 &&
                handler_states[handler_num].payload_length <= PACKET_MAX_PL_LEN) {
            handler_states[handler_num].rx_state++;
            handler_states[handler_num].rx_timeout = PACKET_RX_TIMEOUT;
        } else {
            handler_states[handler_num].rx_state = 0;
        }
        break;
 
    case 3:
        handler_states[handler_num].rx_buffer[handler_states[handler_num].rx_data_ptr++= rx_data;
        if (handler_states[handler_num].rx_data_ptr == handler_states[handler_num].payload_length) {
            handler_states[handler_num].rx_state++;
        }
        handler_states[handler_num].rx_timeout = PACKET_RX_TIMEOUT;
        break;
 
    case 4:
        handler_states[handler_num].crc_high = rx_data;
        handler_states[handler_num].rx_state++;
        handler_states[handler_num].rx_timeout = PACKET_RX_TIMEOUT;
        break;
 
    case 5:
        handler_states[handler_num].crc_low = rx_data;
        handler_states[handler_num].rx_state++;
        handler_states[handler_num].rx_timeout = PACKET_RX_TIMEOUT;
        break;
 
    case 6:
        if (rx_data == 3) {
            if (crc16(handler_states[handler_num].rx_buffer, handler_states[handler_num].payload_length)
                    == ((unsigned short)handler_states[handler_num].crc_high << 8
                            | (unsigned short)handler_states[handler_num].crc_low)) {
                // Packet received!
                if (handler_states[handler_num].process_func) {
                    handler_states[handler_num].process_func(handler_states[handler_num].rx_buffer,
                            handler_states[handler_num].payload_length);
                }
            }
        }
        handler_states[handler_num].rx_state = 0;
        break;
 
    default:
        handler_states[handler_num].rx_state = 0;
        break;
    }
}
cs


First byte of payload is command. All commands(38) supported by VESC firmware is shown bellow:


datatypes.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// Communication commands
typedef enum {
    COMM_FW_VERSION = 0,
    COMM_JUMP_TO_BOOTLOADER,
    COMM_ERASE_NEW_APP,
    COMM_WRITE_NEW_APP_DATA,
    COMM_GET_VALUES,
    COMM_SET_DUTY,
    COMM_SET_CURRENT,
    COMM_SET_CURRENT_BRAKE,
    COMM_SET_RPM,
    COMM_SET_POS,
    COMM_SET_HANDBRAKE,
    COMM_SET_DETECT,
    COMM_SET_SERVO_POS,
    COMM_SET_MCCONF,
    COMM_GET_MCCONF,
    COMM_GET_MCCONF_DEFAULT,
    COMM_SET_APPCONF,
    COMM_GET_APPCONF,
    COMM_GET_APPCONF_DEFAULT,
    COMM_SAMPLE_PRINT,
    COMM_TERMINAL_CMD,
    COMM_PRINT,
    COMM_ROTOR_POSITION,
    COMM_EXPERIMENT_SAMPLE,
    COMM_DETECT_MOTOR_PARAM,
    COMM_DETECT_MOTOR_R_L,
    COMM_DETECT_MOTOR_FLUX_LINKAGE,
    COMM_DETECT_ENCODER,
    COMM_DETECT_HALL_FOC,
    COMM_REBOOT,
    COMM_ALIVE,
    COMM_GET_DECODED_PPM,
    COMM_GET_DECODED_ADC,
    COMM_GET_DECODED_CHUK,
    COMM_FORWARD_CAN,
    COMM_SET_CHUCK_DATA,
    COMM_CUSTOM_APP_DATA,
    COMM_NRF_START_PAIRING
} COMM_PACKET_ID;
 
cs


The processing for each command is excuted by 'commands_process_packet()' function in command.h and the following shows an example for 'COMM_GET_VALUES' command case.

 

commands.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
case COMM_GET_VALUES:
        ind = 0;
        send_buffer[ind++= COMM_GET_VALUES;
        buffer_append_float16(send_buffer, mc_interface_temp_fet_filtered(), 1e1&ind);
        buffer_append_float16(send_buffer, mc_interface_temp_motor_filtered(), 1e1&ind);
        buffer_append_float32(send_buffer, mc_interface_read_reset_avg_motor_current(), 1e2&ind);
        buffer_append_float32(send_buffer, mc_interface_read_reset_avg_input_current(), 1e2&ind);
        buffer_append_float32(send_buffer, mc_interface_read_reset_avg_id(), 1e2&ind);
        buffer_append_float32(send_buffer, mc_interface_read_reset_avg_iq(), 1e2&ind);
        buffer_append_float16(send_buffer, mc_interface_get_duty_cycle_now(), 1e3&ind);
        buffer_append_float32(send_buffer, mc_interface_get_rpm(), 1e0&ind);
        buffer_append_float16(send_buffer, GET_INPUT_VOLTAGE(), 1e1&ind);
        buffer_append_float32(send_buffer, mc_interface_get_amp_hours(false), 1e4&ind);
        buffer_append_float32(send_buffer, mc_interface_get_amp_hours_charged(false), 1e4&ind);
        buffer_append_float32(send_buffer, mc_interface_get_watt_hours(false), 1e4&ind);
        buffer_append_float32(send_buffer, mc_interface_get_watt_hours_charged(false), 1e4&ind);
        buffer_append_int32(send_buffer, mc_interface_get_tachometer_value(false), &ind);
        buffer_append_int32(send_buffer, mc_interface_get_tachometer_abs_value(false), &ind);
        send_buffer[ind++= mc_interface_get_fault();
        commands_send_packet(send_buffer, ind);
        break;
cs


please refer to https://github.com/RollingGecko/VescUartControl



Posted by Nature & Life
Software Programming/Qt2018. 3. 3. 02:38


AVR 시리즈의 칩을 사용하는 Arduino 보드와 STM32F 시리즈 칩을 사용하는 Nucleo 보드의 가장 큰 장점은, UART(Universal asynchronous receiver/transmitter) 통신을 이용하여 PC로부터 프로그래밍(업로드)과 디버깅이 가능하다는 것입니다. 이것이 가능한 이유는 USB를 경유한 UART 통신이 가능하도록 전용 칩을 별도로 보드에 내장하기 때문입니다.


따라서 Arduino와 Nucleo 보드에 작성한 임베디드(embedded) 프로그래밍과 소통하고 서로 데이터를 주고 받기 위해서는 PC 상에 인터페이스 프로그램이 필요하고, 이 간단한 Qt 프로그램은 Arduino나 Nucleo 보드와 UART(serial) 통신을 위한 예제입니다. 아이디어는 다음의 링크에서 참조하였습니다.

http://archive.fabacademy.org/2016/fablabhrw/students/162/week_16.html


mainwindow.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
 
#include <QMainWindow>
#include <QDebug>
 
namespace Ui {
class MainWindow;
}
 
class MainWindow : public QMainWindow
{
    Q_OBJECT
 
public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();
 
public slots:
    void serial_connect();
    void serial_rescan();
    void widget_changed();
    void send_test();
    void serial_received();
 
private:
    Ui::MainWindow *ui;
};
 
#endif // MAINWINDOW_H
 
cs


main.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include "mainwindow.h"
#include <QApplication>
#include <qlabel.h>
 
int main(int argc, char* argv[])
{
   QApplication a(argc, argv);
 
   MainWindow w;
//   w.setWindowTitle(QString::fromUtf8("Qt Serial communication"));
//   w.setFixedSize(300, 150);
   w.show();
 
   return a.exec();
}
 
cs


mainwindow.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <qserialport.h>
#include <qserialportinfo.h>
#include <stdio.h>
 
QSerialPort *serial;
 
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
        ui->setupUi(this);
        ui->lcd_temp->setPalette(Qt::red);
        serial = new QSerialPort(this);
        connect(ui->rescan_Button, SIGNAL(clicked()), this, SLOT(serial_rescan()));
        connect(ui->connect_button, SIGNAL(clicked()), this, SLOT (serial_connect()));
        connect(ui->slider1, SIGNAL(valueChanged(int)), this, SLOT(widget_changed()));
        connect(ui->slider2, SIGNAL(valueChanged(int)), this, SLOT(widget_changed()));
        connect(ui->dial, SIGNAL(valueChanged(int)), this, SLOT(widget_changed()));
        connect(serial, SIGNAL(readyRead()), this, SLOT(serial_received()));
        serial_rescan();
}
 
MainWindow::~MainWindow()
{
    delete ui;
    serial->close();
}
 
void MainWindow::serial_connect()
{
        serial->setPortName(ui->port_box->currentText());
        serial->setBaudRate(QSerialPort::Baud115200);
        serial->setDataBits(QSerialPort::Data8);
        serial->setParity(QSerialPort::NoParity);
        serial->setStopBits(QSerialPort::OneStop);
        serial->setFlowControl(QSerialPort::NoFlowControl);
//        serial->write("Hello World!\n");
 
        if(!serial->open(QIODevice::ReadWrite)){
            qDebug() << "Serial port open error";
        }
}
 
void MainWindow::serial_rescan()
{
    ui->port_box->clear();
    foreach (const QSerialPortInfo &serialPortInfo, QSerialPortInfo::availablePorts()){
        ui->port_box->addItem(serialPortInfo.portName());
    }
}
 
void MainWindow::widget_changed()
{
    QString buffer;
    buffer.sprintf("A%03i;%03i;%03i\n",ui->slider1->value(),ui->slider2->value(),ui->dial->value());
    qDebug() << buffer;
    serial->write( buffer.toStdString().c_str(), buffer.size());
    serial->QSerialPort::waitForBytesWritten(-1);
    ui->lcd_led1->display(ui->slider1->value());
    ui->lcd_led2->display(ui->slider2->value());
    ui->lcd_pwm->display(ui->dial->value());
}
 
void MainWindow::send_test()
{
    serial->write("test!\n");
}
 
 
void MainWindow::serial_received()
{
//    usleep(10000);
//    QString received = serial->readAll();
    QString received;
    while (serial->canReadLine()){
        received = serial->readLine();  //reads in data line by line, separated by \n or \r characters
    }
 
    QStringList splitted = received.split(";");
    if (splitted.value(0== "A"){
        ui->lcd_temp->display(splitted.value(1));
    }
}
 
cs


Qt 프로그램의 가장 큰 특징은 signal/slot 시스템입니다. 이는 오브젝트간 통신을 위해 사용되는데 예를 들어, GUI 프로그래밍에서 버튼을 눌렀을 때, 그 이벤트를 받아서 label에 텍스트를 출력한다거나 할 때에 사용됩니다. 이는 굳이 GUI가 없는 console 프로그래밍에서 비동기 호출이 필요한 pipe/socket/serial port 등에서도 유용하게 사용될 수 있습니다. 여기서 signal이란 Win32 프로그래밍에서 message 처리와 같은 개념입니다.


Qt의 signal/slot 시스템은 QObject를 상속받은 모든 class에서 사용할 수 있으며, 사용자에게 일관된 이벤트 인터페이스를 제공합니다. 사실 오브젝트간 통신에서 쓸 수 있는 가장 보편적인 방법으로 callback 또는 인터페이스가 존재하고 있지만, 첫째 함수를 처리할 때 정확한 매개변수 타입으로 호출하게 될 것인지 알 수 없다는 것으로 타입 안정성이 낮고, 둘째로 객체 지향 프로그램(Object Oriented Programming)을 지향하는 언어에서 흔히 쓰이는 인터페이스를 사용하는 방법은 오브젝트간에 강한 커플링으로 상호 참조가 생기거나 래퍼런스 카운팅에 문제가 생길 수 있습니다.


Qt는 이런 방식의 대안으로 타입 안정성을 갖고 있으며 이벤트를 발생시키는 오브젝트와 이벤트를 처리하는 핸들러 오브젝트간에 아무런 연관 관계가 없어, 서로 참조하지 않으면서도 사용이 가능하다는 것입니다.



위 그림에서와 같이 Qt에서 signal과 slot이라는 멤버 함수는 connect() 함수로 서로 연결되며, slot이 처리해야 할 signal을 감시하고 있다가 signal이 발생하면 slot 함수가 실행되게 됩니다. signal과 slot 함수는 1:1 연결 뿐만 아니라 1:N으로도 연결 가능합니다.


이 프로그램에서는 'mainwindow.h' 파일의 13번 라인에서 Q_OBJECT로 상속받았고 'mainwindow.cpp'의 16~21번 라인에서 다음과 같이 5개의 connect() 함수로써 signal과 slot 함수를 연결시켰습니다.


1
2
3
4
5
6
connect(ui->rescan_Button, SIGNAL(clicked()), this, SLOT(serial_rescan()));
connect(ui->connect_button, SIGNAL(clicked()), this, SLOT (serial_connect()));
connect(ui->slider1, SIGNAL(valueChanged(int)), this, SLOT(widget_changed()));
connect(ui->slider2, SIGNAL(valueChanged(int)), this, SLOT(widget_changed()));
connect(ui->dial, SIGNAL(valueChanged(int)), this, SLOT(widget_changed()));
connect(serial, SIGNAL(readyRead()), this, SLOT(serial_received()));
cs


1번 라인의 의미는 'rescan_Button'이 클릭(clicked())되어 signal이 발생하면 'serial_rescan()'이라는 slot 함수를 실행시킴을 의미하고, 3번 라인은 'slider1'이 값이 변동(valueChanged)하였다는 signal이 발생하면 'widget_change()'이라는 slot 함수를 실행시킴을 의미하고, 마지막으로 6번 라인에 'serial' 포트가 준비가 되었다(readyRead())는 signal이 발생하면 'serial_received()'이라는 slot 함수가 실행됨을 의미합니다.


다음은 예를 들어 push button의 경우에 발생할 수 있는 signal을 Qt Designer로 본 것입니다. 다만 모든 signal들이 Qt Designer에서 보여지지는 않으므로 라이브러리를 참고하시길 바랍니다.



serialport.pro

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#-------------------------------------------------
#
# Project created by QtCreator 2016-05-22T13:08:09
#
#-------------------------------------------------
 
QT       += core gui serialport
 
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
 
TARGET = serialport
TEMPLATE = app
 
 
SOURCES += main.cpp\
        mainwindow.cpp
 
HEADERS  += mainwindow.h
 
FORMS    += mainwindow.ui
 
cs


Qt Designer의 화면입입니다.


다음은 Qt로 작성한 serialport.exe의 실행화면입니다.


USB를 이용하여 PC와 연결하고자 하는 Nucleo 보드나 기타 보드와 송수신 데이터의 패킷 구성은 사용자가 임의로 정할 수 있습니다. 이 프로그램의 경우에는 송수신 데이터를 'A', ';' 그리고 '\n' 문자를 식별자로 구분하였습니다. 따라서 사용자는 해당 보드의 임베디드 프로그램에서 이와 같은 식별자를 이용하여 합니다.


Serialport.zip



'Software Programming > Qt' 카테고리의 다른 글

Hello world 예제  (0) 2018.03.02
Qt 프로그램 소개  (0) 2017.12.24
Posted by Nature & Life
Software Programming/Qt2018. 3. 2. 21:01


Qt Creator를 실행합니다. 그리고 아래의 첫 화면에서 새로운 프로젝트를 생성하기 위해서 'New Project'를 클릭합니다.



그럼 아래의 화면이 나타나고 'Projects > Application'을 선택하고 그 중에서도 'Qt Widgets Application'을 선택하며 이를 적용하기 위해서 우측 하단의 'Choose' 버튼을 클릭합니다.



그럼 다음의 화면이 나타나고 여기서 프로젝트의 이름과 위치를 지정하고 우측 하단의 'Next' 버튼을 클릭합니다.



위 화면에서 (1)번인 'Name' 필드는 프로젝트 이름을 공백없이 기록하며, (2)번인 'Create in' 필드는 이 프로젝트가 저장될 위치를 지정하는 것으로 Qt가 설치된 예하 디렉토리를 설정할 경우, 차후에 Qt를 제거하였을 때 함께 삭제될 수 있으니 유지 관리를 위해서 사용자의 데이터 영역에 저장할 것을 권장합니다.


다음 화면은 'Kit Selection'으로 Helloworld 프로젝트에 사용될 Kit 정보를 보여줍니다. Kit 정보는 어떤 컴파일러를 사용할지 등에 대한 내용입니다. 확인하였으면 우측 하단에 'Next' 버튼을 클릭합니다. 여기서 MingGW 32bit를 볼 수 있는데 MingGW는 'Minimalist GNU for Windows'의 약자로 전통적인 Linux/Unix OS 환경 이외에도 Windows에서도 GNU 컴파일러 사용할 수 있도록 만든 툴입니다.


 

위 화면에서 아래 화면과 같이 'Details' 버튼을 클릭함으로서 추가적으로 세부적인 정보는 확인할 수 있습니다.



다음 화면은 Class 정보로서 생성될 메인 클라스 이름과 파일 이름을 지정하는 단계로서 여기서는 Qt의 네이밍 관례를 그대로 따르기 위해서 우측 하단의 'Next' 버튼을 클릭하여 다음 단계로 넘어갑니다.



다음 화면에서 (1)번 필드는 version control을 위한 것으로 이 예제에서는 사용하지 않으므로 '<None>'으로 설정합니다. (2)번 영역은 최종 생성될 파일들을 나타냅니다. 여기서 '*.pro' 파일은 Qt의 프로젝트 파일입니다. 우측 하단의 'Finsh' 버튼을 클릭하여 완성합니다.



다음 화면은 생성된 프로젝트를 보여줍니다. 메인 윈도우 화면을 변경하기 위해서 좌측 Projects에서 'Forms'를 클릭하여 펼치고 그 안에 'mainwindow.ui' 파일을 더블 클릭합니다. 



그럼 다음 화면과 같이 메인 윈도우 화면을 보여주는 design 모드로 자동 전환되며, 좌측 Filter 메뉴에서 스크롤 업다운하여 Display Widgets의 Text Browser 메뉴를 메인 윈도우 화면 위에 드래그 앤 드롭하여 가져다 놓습니다.



그리고 아래 화면과 같이 가져다 놓은 Text Browser를 더블 클릭하여 'This is a Text Browser!'라고 디폴트 메세지를 입력하고 'File > Save "mainwindow.ui"' 메뉴를 선택하거나 'Ctrl + S'를 쳐 'mainwindow.ui' 파일을 저장합니다.



다음은 맨 좌측에 Edit 메뉴를 클릭하여 Edit 모드로 복귀하고 Projects 내에 'mainwindow.cpp' 파일을 더블 클릭하여 오픈합니다. 그리고 다음의 코드를 다음 화면과 같이 삽입하고 저장합니다.

ui->textBrowser->setText("Hello World !!");

 이 코드는 Text Browser에 보낼 메세지를 설정할 수 있습니다.



코딩이 완료되었으며, 'Build > Run' 메뉴를 선택하거나 'Ctrl + R'를 쳐 실행시키면 다음과 같은 실행화면을 확인할 수 있습니다.



Debug 폴더에서 실행 파일을 직접 클릭하여 실행시킬 수도 있는데 이 경우에는 *.dll 파일이 없다는 오류가 발생하는데 이는 정적(static)으로 빌드하였기 때문으로 배포시에 동적(dynamic) 빌드하면 해결됩니다.



'Software Programming > Qt' 카테고리의 다른 글

UART(Serial) 통신 예제  (2) 2018.03.03
Qt 프로그램 소개  (0) 2017.12.24
Posted by Nature & Life