안경잡이개발자

728x90
반응형

  Raspberry Pi Zero WSerial 통신 목적의 USB 디바이스로 사용할 수 있다. 한 번 2개의 Serial 인터페이스를 제공하는 USB 디바이스를 만들어 보았다. 기본적인 환경 설정 파일은 다음과 같다. 경로는 /usr/bin/my_usb로 하였다.

 

#!/bin/bash
cd /sys/kernel/config/usb_gadget/
mkdir -p my_usb
cd my_usb

# 기본적인 USB 클래스 명시
echo 0x1D6B > idVendor # Linux Foundation
echo 0x0104 > idProduct # Multifunction Composite Gadget
echo 0x0100 > bcdDevice # v1.0.0
echo 0x0200 > bcdUSB # USB 2.0

# 내가 만들 USB 장치의 기본적인 이름
mkdir -p strings/0x409
echo "0123456789abcdef" > strings/0x409/serialnumber
echo "Dongbin Na" > strings/0x409/manufacturer
echo "My USB" > strings/0x409/product

# 하나의 Configuration 정보 작성
mkdir -p configs/c.1/strings/0x409
echo "My USB Config 1" > configs/c.1/strings/0x409/configuration
echo 250 > configs/c.1/MaxPower

# 하나의 Serial 통신 기능 추가
mkdir -p functions/acm.usb0
ln -s functions/acm.usb0 configs/c.1/

# 하나의 Serial 통신 기능 추가
mkdir -p functions/acm.usb1
ln -s functions/acm.usb1 configs/c.1/

# UDC (Usb Device Controller)
ls /sys/class/udc > UDC

 

  이로써 두 개의 Serial 인터페이스를 호스트에 제공하게 된다. 기본적인 이름은 /dev/ttyGS0/dev/ttyGS1에 매핑된다. 추가하는 횟수에 따라서 인덱스가 증가하게 된다.

 

1. 단순히 Serial (Bulk Transfer)로 "A" X 4096을 반복적으로 보내는 프로그램

 

  그냥 계속해서 데이터를 이어 붙여 보내기 때문에 r+ 옵션을 사용한다.

 

#include <stdio.h>

int main(void) {
    FILE* file;
    char buf[4096];

    for (int i = 0; i < sizeof(buf); i++) {
        buf[i] = 'A';
    }

    while (1) {
        file = fopen("/dev/ttyGS0", "r+");
        fwrite(buf, 1, sizeof(buf), file);
        fclose(file);
    }
    return 0;
}

 

  간단히 transfer1라는 이름으로 실행 파일을 만들었다.

 

sudo gcc 소스코드 -o transfer1

 

2. 랜덤한 64bits (8 bytes) 데이터를 생성하여 반복적으로 보내는 프로그램

 

  이 경우 파일 내용은 매번 새로 써서 8 bytes 씩 보내는 것이므로 하므로 w+ 옵션을 이용한다. (근데 이렇게 해도 Bulk Transfer의 특성상 가능한 최대한 많은 양의 데이터를 보낸다.)

 

#include <stdio.h>
#include <stdlib.h> // rand()
#include <time.h> // time()
#include <stdint.h> // uint32_t

uint32_t rand_uint32(void) {
    uint32_t r = 0;
    for (int i = 0; i < 32; i++) {
        r = r * 2 + rand() % 2;
    }
    return r;
}

// 4바이트를 읽어 버퍼의 특정 인덱스에 삽입
void val32_to_buf(char* buf, uint32_t val, uint32_t index) {
    buf[index] = val;
    buf[index + 1] = val >> 8;
    buf[index + 2] = val >> 16;
    buf[index + 3] = val >> 24;
}

int main(void) {
    FILE* file;
    char buf[8];
    srand(time(NULL));

    while (1) {
        uint32_t r1 = rand_uint32();
        uint32_t r2 = rand_uint32();

        val32_to_buf(buf, r1, 0);
        val32_to_buf(buf, r2, 4);

        file = fopen("/dev/ttyGS1", "w+");
        fwrite(buf, 1, sizeof(buf), file);
        fclose(file);
    }
    return 0;
}

 

  이어서 transfer2라는 이름으로 실행 파일을 만들었다.

 

sudo gcc 소스코드 -o transfer2

 

※ Serial 통신 프로그램 자동 실행 ※

 

  이후에 /etc/rc.local를 수정한다. 두 프로그램을 백그라운드에서 실행할 수 있도록 한다.

 

stty -F /dev/ttyGS0 raw
stty -F /dev/ttyGS1 raw
/home/pi/transfer1 &
/home/pi/transfer2 &

 

 

  이후에 sudo reboot을 해서 재부팅하자. 그러면 재부팅 이후에 호스트(host) PC 입장에서는 다음과 같이 나온다.

 

 

※ 호스트 쪽 프로그램 작성 ※

 

  호스트 쪽 프로그램은 다음과 같이 pyserial 라이브러리를 이용하여 작성할 수 있다. 그냥 USB 디바이스로부터 데이터를 받아 출력하는 코드다. 윈도우(Windows) OS에서는 Serial USB 장치가 꽂힌 경우 COM 포트로 인식하기 때문에 이를 이용하면 된다.

 

import time
import serial

s = serial.Serial("COM8", 115200)
total = 0

start_time = time.time()
last_time = 0 

while True:
    # 가져올 데이터가 존재하는 경우에만 처리
    len = s.inWaiting()
    if len == 0:
        continue

    data = s.read(len)
    print(data)
    
    time.sleep(1)

s.close()

 

  프로그램을 실행하면 다음과 같이 'A'를 계속해서 받는 것을 알 수 있다.

 

 

※ (추가) Guest가 데이터를 받아서 단순히 파일에 기록할 때 ※

 

  USB device가 호스트(host)로부터 데이터를 받아서 저장할 때는 다음과 같이 한다. 그냥 /dev/ttyGS1에서 파일 읽기를 수행하여 특정한 파일에 쓰는 것(이어쓰기)이다.

 

cat /dev/ttyGS1 >> /home/pi/test.dat

 

  결과적으로 다음과 같이 /etc/rc.local 파일을 수정하면 된다. 기본적으로 cat>> 를 이용하여 파일의 뒤쪽에 쭉 이어 붙여 데이터를 쓸 수 있도록 한다.

 

stty -F /dev/ttyGS0 raw
stty -F /dev/ttyGS1 raw
/home/pi/transfer1 &
cat /dev/ttyGS1 >> /home/pi/test.dat

 

  이제 리부팅 진행한 뒤에 호스트에서 다음과 같은 프로그램을 작성하여 보내보자.

 

import serial

s = serial.Serial("COM6", 115200)
s.write(b'Hello\n')
s.close()

 

  프로그램을 여러 번 실행하면 라즈베리파이에는 다음과 같이 파일의 내용이 쓰인다.

 

728x90
반응형