2018년 8월 29일 수요일

[LINUX] GPIO control 방법

Rockchip 3308 chip 을 이용한 리눅스 포팅 프로젝트가 생겨 관련 제어 기술을 공부하면서 자료를 정리하기로 하였다
아직까지 리눅스를 심도있게 본적이 없어서 빠르게 아래 Peripheral Device 들을 테스트 해볼 예정으로 시작하였다

타이틀은 : 리눅스도 모르는 초심자가 시작하는 리눅스 드라이버 개발 이 되려나....

  1. GPIO (제어 및 Driver)
    • Simple Control 
    • Interrupt
  2. PWM
  3. System Timer
  4. SPI

GPIO 제어를 하기 우선 DTS Script 를 이용하여 GPIO 접근을 할 수 있는 노드 정의에 대해 알아야 했다

그래서 같이 공부한 내용중 DTS 챕터에 대한 것을 별도로 정리함 ([LINUX] DTS 기초 문법 정리 글을 참조하세요. ) 

pinctrl 그리고 <> 를 이용한 pin 을 정의하는 것에 대해서 내가 사용하려는 PIN 에 대해 정의를 해야 한다

Rockchip GPIO 0번 부터 4번까지 4개의 Block 을 가지고 있으며 각 블럭은 32개의 핀으로 정의되어 있다
32개는 각각 ABCD 형태로 8개씩 4개의 단위로 쪼개저 있다

A0 (0) ... A7(7)... B0(8)... B7(9)....


기본적으로 Rockchip 에서 제공해주는 DTSI 파일에는 기본 블럭들에 대한 정의가 되어 있으며 개별 GPIO 를 사용하고 싶을 때에는
자신의 dts 파일을 만들어서 노드를 생성해주어야 한다

기본 EVB Board 를 위해서 아래와 같이 include 되어 계층화 되어 있으며  dtsi 는 건들지 않으며 dts 파일에서 사용하지 않을 노드는
status = "disabled" 시키고 사용할 노드는 status = "okay" 하고 없으면 생성해야 한다

rk3308.dtsi 
    ---- rk3308-evb-v11.dts
         ---- rk3308-evb-pdm-v11.dts   


GPIO0 번의 B3번 포트를 이용하여 signal 설정, 그리고 Interrupt 처리를 위한 IRQ 등록과 처리에 대해서 하는 것이 이 장의 최종 목적이다.  

DTS 에서 GPIO 를 어떤 식으로 정의 해놓았는지 찾아 보았다

아래와 같은 pinctrl 을 찾을 수 있었다. (상위 DTSI 에서 pinctrl 의 블럭들이 정의 되어 있기 때문에 여기서는 pinctrl 을 사용하기 위한 
실제 pin 노드를 추가 정의해야 한다
좀더 설명하자면 pinctrl 에서는 실제 Pin 마다 지원하는 기능들을 MUX 기능을 이용해서 선택할 수 있는데
(예를들어 GPIO pin SPI_TX 기능을 지원한다면 사용자는 이핀을 GPIO 로 사용할지 SPI_TX 로 사용할지를 MUX 선택을 통해 선택해야 한다
이러한 Pin 선택 MUX 설정을 제조사가 제공하는 최상위 DTSI 파일에는 기본적으로 제공을 해준다.  
(더 깊이 들어가는 내용은 목적과 거리가 있어서 생략)

다시 아래와 같이 pinctrl 에 추가 내용을 선언하기 위해서는 Root Node 가 아닌 외부에 독립적으로 선언해야 된다
그리고 앞에 & 연산자 문자를 붙여야 한다

  1.  pinctrl 내부에 내가 사용할 노드 이름을 설정
  2.  이 노드의 사용하고자 하는 gpio 이름을 생성하고
  3.  gpio 이름 노드의 세부 pin 설정을 해야 한다

나는 정의되어 있는 다른 놈들을 참조해서 나만의 GPIO 를 위한 정의를 아래 붉은 색처럼 추가 하였다

&pinctrl {
    pinctrl-names = "default";
    pinctrl-0 = <&rtc_32k>;

...(생략)
    wireless-wlan {
        wifi_wake_host: wifi-wake-host {
            rockchip,pins = <0 RK_PA0 RK_FUNC_GPIO &pcfg_pull_up>;
        };
    };

    my_gpio_driver { // 내가 사용할 노드의 이름
        my_pin_gpio: my-pin-gpio { //핀 노드 이름
             rockchip,pins = <0 RK_PB3 RK_FUNC_GPIO &pcfg_pull_up>; // GPIO 핀의 세부 설정 
        };
    };
};


위와 같이 정의내용 중에 rockchip,pins 는 제조사마다 다를 수 있을 것 같다.  (저 이름은 사용자 마음대로 정의할 수 있다)
Rockchip 의 경우 저런 형식으로 GPIO 를 정의한 것을 실제 코드에서는 아래와 같이 핸들로 읽어 오기 때문이다
코드에서 DTS 의 리소스에 접근하는 방법은 of_XXX 함수들이 이용되며 이 내용은 이 후 다시 더 설명할 예정이다

GPIO MUX pin 제어를 이용하기 위해서는 rockchip 에서 제공하는 pinctrl-rockchip 드라이버를 이용해야 되기 때문에 
위와 같이 정의해야 된다고만 알아 두자. (즉 제조사에 따라 pinctrl 은 달라 질 수 있다는 것도 잊지 말자)

list = of_get_property(np, "rockchip,pins", &size);


<0 RK_PB3 RK_FUNC_GPIO &pcfg_pull_down> 정의 내용은 
GPIO 그룹, PIN 번호, Function, 초기화 신호 순으로 정의를 하게 되어 있다

위에서 설명한 것처럼 1 2 3번은 제조사에서 제공하는 상수로 이루어져 있으며 이것을 처리하는 pinctrl 드라이버에 따라서 달라 질 수 있다
Rockchip 에서 는 위와 같이 사용하며 맨마지막 pcfg_pull_down 또한 bias-pull-down 으로 정의되어 있으며
실제 드라이버에서는 문자열로 인식하여 이것을 PIN_CONFIG_BIAS_PULL_DOWN 상수로 대체되어 사용되게 된다

pinctrl 에서 정의된 gpio 노드를 어떻게 사용하는지 알기 위해 난 또 wifi_wake_host 가 어떻게 쓰이는지 검색을 해보았다

wireless-wlan {
     compatible = "wlan-platdata";     // -> 드라이버와 매핑하기 위한 Unique 한 아이디 
     rockchip,grf = <&grf>;                // -> 전원 관련 설정 ( wifi 모듈을 연동하는 설정이기 때문에 필요한 듯하다
     pinctrl-names = "default";           // -> pinctrl 이름들 여러게가 선언될 수 있으며 하나만 사용시에는 "default" 라고 정의한다
     pinctrl-0 = <&wifi_wake_host>; // -> "default" 를 위한 pin 리스트 여기서 위에 선언한 gpio & 문자와 함께 설정하였다
     wifi_chip_type = "ap6255";         // -> 장치 고유 값
     WIFI,host_wake_irq = <&gpio0 RK_PA0 GPIO_ACTIVE_LOW>; // -> IRQ 및 접근 할 GPIO 핸들을 가져오기 pin 설정
     status = "okay";
};

즉 엄청난 검색을 통해 나는 DTS 에서 저 노드들이 필수로 필요하다는 사실을 알아 냈다
pin mux 를 통한 설정을 하기위해서 pinctrl-names pinctrl-0 을 정의해서 내가 정의한 gpio pins GPIO Function Mux Selection 
이 되도록 사용해야 된다
(그 이상은 나도 잘 모르겠다. 내가 잘못 알고 있거나 추가해야 될 사항이 있다면 언제든지 메일을 주면 수정하도록 하겠다. ) 

compatible 은 내가 작성할 드라이버를 찾기 위한 Unique 한 문자열 이름이며

GPIO 를 제어 하기 위한 핸들을 가져오기 위해서 임의의 이름인 "WIFI,host_wake_irq" 를 정의하고 위와 같이 설정한다
이 노드는 Root Node 안에 정의가 되어야 하며 아래와 같이 나는 신규 DTS 장치 노드를 생성하였다

/ {
    ... (생략)

    my_gpio_driver {
        compatible = "test,my_gpio_drviver";
        pinctrl-names = "default";
        pinctrl-0 = <&my_pin_gpio>;
        TEST,my_pin_gpio_0_b3 = <&gpio0 RK_PB3 GPIO_ACTIVE_LOW>;
        status = "okay";
    };
};

내용 정리 
  •  pinctrl pin mux 설정을 하고, 실제 사용할 장치 노드와 pin 노드간의 연결 관리를 한다
  • 장치 노드에는 고유의 compatible 이름을 가지고 있으며 이 이름은 실제 드라이버를 검색할 때 사용된다
  • pinctrl-names pinctrl-0 MUX 설정이며 실제 GPIO 접근을 위해서는 별도의 pin 설정을 해야 한다
  • status okay 가 되어야 드라이버 활성화가 된다

오 정말 간단하지 않은가? 하지만 난 이것을 이해하기 위해서 3일을 소비했다. ........

  • 참조 문서 
    • DTS 문서
    • Kernel / Documentation 문서들. (pinctrl-bindings.txt / rockchip,pinctrl.txt) 문서 
    • Device Tree 상세분석 in Linux Kernel 4.0 (커널 연구회)
    • 여러 검색 신공..... 



이제 DTS 노드를 생성하였으니 이제 Kernel 소스에 my_gpio_device 를 제어 하기 위한 드라이버를 만들어보자

드라이버를 만들기 위해서 드라이버의 종류를 알아 보게 되었다......  SPI, I2C 이런 장치들이 아닌경우는 즉 사용자 입맛에 맛는 드라이버를
만들기 위해서는 Platform_device_driver 를 사용해야 한다는 정보 였다
그리고 이 드라이버의 뼈대 코드를 어떻게 만들어야 되는지 또 검색을 하며 아래와 같은 코드를 만들 수 있었다. ...

무식하면 손품을 팔아야 한다.... .

리눅스에서 Menuconfig 구성을 위한 Kconfig 그리고 빌드하기 위한 Makefile 그리고 원하는 소스코드 추가 위치는 Kernel 소스에 내가 원하는 소스 위치
이 잡다한 설정을 하는데 또 하루의 시간을 허비 했다

Rockchip 의 경우 Kernel / Driver / My_pin_gpio 디렉토리를 만들고
디렉토리에 아래 3개의 파일을 만들었다
    Kconfig
    Makefile
    my_pin_gpio_drv.c

그리고 상위 Kconfig My_pin_gpio 를 빌드 하기 위한 스크립트 추가... (Kconfig 내용도 여기서는 제외 함 이 내용도 나중에 다시 정리해야 할 듯 하다.)
Makefile 또한 제조사가 제공하는 BSP 에 따라 여러 매크로 들이 존재하기 때문에, Kernel / Driver 의 다른 Platform Driver 를 검색해서 참조 해서 우선 
작성하기를 권한다. (Platform Driver 를 아는 방법은   c 파일에 Platform_Device_Register 함수를 사용한다면 그것은 바로 Platform Driver !!!! 우훗!!) 

c 코드의 Platform Driver 구조는 아래 구성요소로 구성된다

  • platform_driver 구조체의 기본 설정
  • init, exit 함수 

우선 DRIVER 를 위해서 2개의 함수와 모듈 Description 을 추가 하였다

static int __init my_sensor_init(void)
{

}

static void __exit my_sensor_exit(void)
{

}

module_init(my_sensor_init);
module_exit(my_sensor_exit);

MODULE_DESCRIPTION("my sensor control v0.1");
MODULE_AUTHOR("moon10200@gmail.com");
MODULE_LICENSE("GPL");

my_sensor_init 은 모듈이 처음한번 로딩 될 때 호출되는 함수이며, exit 는 언로드 될 때 한번 호출되는 함수이다
여기에 우리는 Platform Driver 이기 때문에 로딩될 때 Platform driver 로 등록하고 언로드 될 때 등록해제를 하는 함수를 사용한다

static struct of_device_id my_sensor_of_match[] = {
    { .compatible = "my-gpio-driver" },
    {}
};
MODULE_DEVICE_TABLE(of, my_sensor_of_match);

static struct platform_driver my_sensor_driver = {
    .probe = my_probe,
    .remove = my_remove,
    .suspend = my_suspend,
    .resume = my_resume,
    .driver = {
        .name = "my-sensor-driver",
        .owner = THIS_MODULE,
        .of_match_table = of_match_ptr(my_sensor_of_match),
    },
};

static int __init my_sensor_init(void)
{
    return platform_driver_register(&my_sensor_driver);
}

static void __exit my_sensor_exit(void)
{
    platform_driver_unregister(&my_sensor_driver);
}

  1. DTS 에 정의한 compatible 이름과 매핑되기 위한 my_sensor_of_match 를 정의하고 이것을 my_sensor_driver 구조체에 넣어 준다
  2. platform driver 이름을 정의하고
  3. 로딩된후 호출되는 probe함수 제거될 때 호출되는 remove 함수 그리고 장치가 suspend, resume 될 때 호출될 함수 를 지정하고 함수로 만들어 준다

일단 여기까지했으면 기본 platform driver 의 뼈대가 만들어 진것이다

static int my_probe(struct platform_device *pdev)
{
    return 0;
}

static int my_remove(struct platform_device *pdev)
{
    return 0;
}

static int my_suspend(struct platform_device *pdev, pm_message_t state)
{
    return 0;
}

static int my_resume(struct platform_device *pdev)
{
    return 0;
}

즉 드라이버가 register 된 후 compatible 과 일치하는 장치를 DTS 가 로딩될 때 찾아서 드라이버를 로딩해주는 절차가 부팅할 때 
진행된다고 보면 된다. 좀더 깊은 내용은 커널 코드를 공부해야 한다..... 우리는 사용만 할 줄 알면되니까..일단 넘어가자

여기서 이제 처음 호출되는 probe 함수부터 구현을 시작해 보자

// probe 에서 들어오는 인자는 각 드라이버의 형태에 따라 해당 구조체가 생성되어 전달된다
// 우리는 platform driver 이므로 platform_device 객체가 생성되어 전달된다
static int my_probe(struct platform_device *pdev) 
{    
    //platform_device 의 멤버중에  커스텀 데이터를 등록하고 가져올 수 있는 함수인
    //platform_get_drv_data platform_set_drv_data 를 제공하기 때문에 이를 위한 
    //드라이버에서만 사용할 범용 구조체를 생성하여 등록할 수 있다
    // 이 구조체를 만들어아 햐는 이유는 우리가 사용할 GPIO 핸들을 할당하면 그것을 platform_device 에 등록
    // 한 후 함수가 호출될 때 마다 꺼내 써야 하기 때문이다.     

    return 0;
}


그래서 소스 코드 상단에 아래와 같이 선언한다

struct my_drv_data {
    struct platform_device *pdev;        //자신의 platform_device 개체
    int my_gpio;                                   // gpio 핸들 
    int my_irq;                                            // gpio interrupt 를 위한 irq 번호
}


probe 함수가 호출되면 my_drv_data 를 생성하고 DTS 에서 원하는 기능을 가져온 후 두고두고 사용하기 위해
platform_device 에 등록해주면 된다

probe 에 아래의 코드를 넣어준다


struct my_drv_data *pdata = NULL;
int ret = 0;    

if (!g_pdrv) {  // 나중에 file io 를 선언하고 file io 에서 접근하기 위해서 전역 포인터를 선언함
    enum of_gpio_flags flags;
    
    pdata = devm_kzalloc(&pdev->dev, sizeof(struct my_drv_data), GFP_KERNEL); //// my_drv_data 메모리 할당
    if (!pdata)    {            
        return -ENOMEM;
    }
    //of_node DTS 에 파싱된 정보를 가지고 있다 of_XXX 함수를 이용하여 핸들을 가져옴
    pdata->my_gpio = of_get_named_gpio_flags(pdev->dev.of_node, "TEST,my_pin_gpio_0_b3", 0, &flags); 

    if (gpio_is_valid(pdata->my_gpio)) {        //정상으로 가져오면                     
        ret = gpio_direction_output(pdata->my_gpio, 1); // GPIO Direction output 으로 설정 후 1 (high) 로 설정한다.        
    }
    else
        pdata->my_gpio = -1;
}
//자신의 platform_device 객체 저장
pdata->pdev = pdev;
g_pdrv = pdata; // FILE IO 에서 접근하기 위해 g_pdrv 에 저장
// platform_device 에 사용할 고유의 구조체를 설정한다.
platform_set_drvdata(pdev, pdata);     
return 0;


이후 파일 IO 를 통해서 테스트를 하기 위해 File IO 를 위한  misc 드라이버를 생성하자
리눅스는 여러 드라이버 종류가 있단다 (문자, 네트워크, 블록, 기타 등등
우리는 기타 드라이버이기 때문에 misc 드라이버를 사용한다


아래와 같이 misc 드라이버를 위한 장치 구조체와 장치를 위한 file operation 함수 그리고 함수 정의
init / exit 함수에 각각 


static const struct file_operations my_gpio_fops = {
       .owner = THIS_MODULE,      
       .read = my_gpio_read,
       .write = my_gpio_write,
       .unlocked_ioctl = my_gpio_ioctl,
};
static struct miscdevice my_gpio_drv_misc = {
       .minor = MISC_DYNAMIC_MINOR,
       .name = "my_gpio_drv",
       .fops = &my_gpio_fops,
};

init 함수에서 misc 드라이버 추가
misc_register(&my_gpio_drv_misc);

exit 함수에 misc 드라이버 해제 코드 추가 
misc_deregister(&my_gpio_drv_misc);

file operation 함수를 아래와 같이 구현하고 우리는 write 만 쓰기 때문에 아래와 같이 코드 추가 

static ssize_t my_gpio_write(struct file *fileconst char __user *buf, size_t n, loff_t *offset)
{             
       LOG("write called %s \n", buf);   
       if (g_pdrv)
       {
              gpio_set_value(g_pdrv->my_gpio, 1); // GPIO Signal HIGH 설정
              udelay(100);
              gpio_set_value(g_pdrv->my_gpio, 0); // GPIO Signal LOW 설정
       }
       
       return n; // 0으로 하면 무한 루프가 빠진다.
}
static ssize_t my_gpio_read(struct file *file, char *buf, size_t length, loff_t *ofs)
{      
       return 0;
}
static long my_gpio_ioctl(struct file *pfile, unsigned int cmd, unsigned long arg)
{       
       return 0;
}

이와 같이 하고 이미지를 빌드 후 업로드 하면 장치 shell 에서 아래와 같이 등록된 장치가 설치 된것을 확인할 수 잇습니다

# ls /dev

....
my_gpio_drv


# echo write  > /dev/my_gpio_drv


신호를 찍어 보면 High 신호가 떨어진 것을 확인할 수 있다

글이 너무 길어 져서 다음에 Interrupt 사용방법에 대해 정리 해보자~~


2018년 3월 12일 월요일

WireShark AirpCap WPA Decrytion 무선 캡쳐 방법

AirpCAP 을 이용하여 WPA/WPA2 보안 상태의 무선 패킷을 캡쳐  방법 정리 

 

요약

1.                 AP AirepCAP Control Panel 에서의 채널 설정값을 동일하게 맞춤.

2.                 WireShark SSID, Passphrase 값을 변환한 키값을 입력

3.                 WireShark 캡쳐 시작

 

 

Windows 10 용 테스트를 하였으며 아래와 같이 버젼 참고 하시면됩니다

- WireShark : 2.4.5 

- AirPCap Software : 4.1.3  

 

1.  AirPCap Control Panel 을 실행 및 AP 설정 변경

 - 캡쳐할 무선 채널을 선택 (** AP 에서도 자동채널이 아닌 수동 채널설정을 하여 동일하게 맞추어야 함)

 - 일반적으로 AP 설정에 들어가시면 Channel 값이 자동으로 되어 있는 경우가 많습니다. 아래와 같이 고정으로 재설정을 하셔야 합니다

 

 

 

2. WireShark SSID, Passphrase 값을 변환한 키값을 입력

https://wwwW.wireshark.org/tools/wpa-psk.html

 

 

무선 캡쳐를 원하는 곳의 패스워드와 SSID 를 입력하여 PSK 값을 생성함

 

 

 

 

 

 

이곳에 위에 생성한 키값을 입력

 

 

캡쳐 시작해서 패킷 보기 ^^

 

2018년 1월 26일 금요일

OPENAPI 로 틱(TICK) 데이터를 DB에 저장하기 (1)

 

시스템 매매 프로그램 개발

    1. (TICK) 데이터를 저장하기 (예약 시간)

    2. 실시간 데이터를 저장하기

    3. 로그인후 정보를 관리 하기 

    4. 거래(매수/매도) 코드 개발하기 

    5. 계좌 관리 코드 짜리 

    6. 틱 데이터 가공하기 

    7. 실시간 매매 시스템 알고리즘 만들기 

   

 

제가 만들고자 하는 시스템 매매 프로그램 개발을 하려고 몇년동안 고민만 하다가 결국 고민보다는 행동을 먼저 

해보자 하는 마음으로 시작하였습니다

회사일도 해야 되기 때문에 시간 여유가 많이 나지 않아서 멈��다가 또 시작했다가 하고 있네요

과거에 진행 했던 자료들을 다시 정리하면서 위 목차대로 진행을 하기로 마음을 먹었습니다

 

코드는 100% 공유를 하지 못할 수도 있구요. 양해 부탁드립니다. 궁금하신 사항은 언제든지 메일이나 쪽지로

보내주시면 답변 가능한 것들은 답변드리도록 하겠습니다. (moon10200@gmail.com)

 

 

Microsoft Visual Studio C# 프로젝트를 하나 생성합니다

 

 

이제 여기에 저희가 사용할 OPEN API 컨트롤을 배치 시켜야 합니다. 이전 강좌에도 말씀드렷지만 

도구 상자에 보이지 않으신다면 아래 메뉴에서 OPEN API 를 찾아서 추가 하셔야 됩니다

 

 

 

 

 

 

 

위 같이 선택하면 도구상자에 컨트롤이 보이게 됩니다

 

 

화면에 Form 에 컨트롤을 드래그하여 배치 합니다

 

 

이제 DB 연결과 데이터를 저장할 테이블을 만들어 보겠습니다

 

DB 관련 코드를 만들기 위한 파일을 하나 추가 합니다

 

이름은 전 sjMariaDB.cs 로 주었어요

 

코드를 아래와 같이 설정합니다

 

using System;

using MySql.Data.MySqlClient;

using MySql.Data.Common;

namespace sj_cs_common

{

 

}

 

sj_cs_common 은 원하시는 이름으로 바꾸셔도 됩니다. (앞으로 이 DB 기능을 쓰기 위한 식별자 정도로 생각하시면 됩니다)

 

 

폼화면을 오른쪽 버튼을 눌러서 속성창을 열고 

 

 

번개 아이콘을 누른 후 Load 될때 함수명을 써넣어 추가 합니다. 마우스로 더블클릭하셔도 자동으로 생성됩니다

 

Load 는 프로그램이 실행될 때  FrmMain 이 호출되면서 Load 될 때 호출되는 함수를 의미합니다

 

 

이와 같이 코드를 입력합니다

 

(** 앞으로 C# 관련 코드 내용은 설명을 생략합니다. 궁금하신분은 메일로 부탁드려요~ )

 

 

 

이런 에러가 발생한다면 위의 프로젝트 속성->빌드 탭에서 x86 으로 옵션을 변경하시면됩니다

 

 

 

제 블러그다른 글을 보시면  MariaDB 설치와 MariaDB 에서 DB 를 생성하는 방법을 찾을 수 있습니다

아래 코드에서 접속할 DB 를 미리 생성을 하여야 하니 안하신 분은 미리 하시고 계속해 주세요

 

sjMariaDb.cs 파일에는 아래와 같이 코딩을 합니다

 

private static string mysql_conn_str = "Server=local;Database=future_tick;Uid=root;Pwd=test123;Charset=utf8";                                                                                                                                

                                                                                                                                                                                                                                          

public static bool createtable_tick(string tbl_name)                                                                                                                                                                                      

{                                                                                                                                                                                                                                         

    bool bret = true;                                                                                                                                                                                                                     

                                                                                                                                                                                                                                          

                                                                                                                                                                                                                                          

    MySqlConnection conn;                                                                                                                                                                                                             

    conn = new MySqlConnection(mysql_conn_str);                                                                                                                                                                                           

    try                                                                                                                                                                                                                                   

    {                                                                                                                                                                                                                                     

        conn.Open();                                                                                                                                                                                                                      

                                                                                                                                                                                                                                          

        string sql = "CREATE TABLE " + tbl_name + "(id bigint(20) unsigned NOT NULL AUTO_INCREMENT, c_time datetime, volume int, o_p float(10,2), h_p float(10,2), l_p float(10,2), c_p float(10,2), pre_c float(10,2), PRIMARY KEY(id))";

        MySqlCommand cmd = new MySqlCommand(sql, conn);                                                                                                                                                                                   

        cmd.ExecuteNonQuery();                                                                                                                                                                                                            

    }                                                                                                                                                                                                                                     

    catch (Exception ex)                                                                                                                                                                                                                  

    {                                                                                                                                                                                                                                     

        Console.WriteLine("데이터 베이스 오픈 실패 : " + ex.Message, "Database Error [MYSQL]");                                                                                                                                           

        bret = false;                                                                                                                                                                                                                     

    }                                                                                                                                                                                                                                     

    finally                                                                                                                                                                                                                               

    {                                                                                                                                                                                                                                     

        conn.Close();                                                                                                                                                                                                                     

    }                                                                                                                                                                                                                                     

                                                                                                                                                                                                                                          

    return bret;                                                                                                                                                                                                                          

}    

 

 

여기까지 하시고 실행하시면 됩니다. F5 또는 ctrl+F5 

 

 

이와 같이 실행되면 됩니다

 

Table 이 정상적으로 생성되었는지 확인하고 싶다면 Toad Edge 를 이용하여 아래와 같이 확인할 수 있습니다

 

 

 

 

숙제 ) 프로그램을 실행할 때 마다 테이블을 생성하면 안될 테니 중복 검사하여 테이블이 존재한다면 더이상 테이블을 생성하지 않도록 해보세요

 

다음 강좌는 OPEN API 를 이용해서 로그인 하는 코드를 추가하고 (이전 블러그에 로그인 코드 추가 부분 참조)

데이터를 받아와서 저장하는 코드를 만들어 보겠습니다