uart-c

uart c example code


更新記錄

item note
20161020 第一版

目錄


ch341

使用示波器查看,當按下時KB-100有立即送出資料
使用ubuntu測試,發現在取得uart時,需要等到buffer到一定量才會在read讀到
但了解原因

test data

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
87
88
89
90
91
第一次設定CH3,,無輸出
第二次設定CH6,輸出如下
$ sudo ./a.out
test-1 uart
[0]:ffffffff, size:1
[1]:3, size:1
[2]:0, size:1
[3]:2, size:1
[4]:7, size:1
[5]:0, size:1
[6]:c, size:1
[7]:ffffffff, size:1
[8]:3, size:1
[9]:0, size:1
[10]:2, size:1
[11]:3f, size:1
[12]:0, size:1
[13]:44, size:1
[14]:ffffffff, size:1
[15]:3, size:1
[16]:0, size:1
[17]:2, size:1
[18]:3f, size:1
[19]:0, size:1
[20]:44, size:1
[21]:ffffffff, size:1
[22]:3, size:1
[23]:0, size:1
[24]:0, size:1
[25]:0, size:1
[26]:0, size:1
[27]:3, size:1
[28]:ffffffff, size:1
[29]:6, size:1
[30]:0, size:1
[31]:2, size:1
第二次設定CH9,輸出如下
...
[11]:3f, size:1
[12]:0, size:1
[13]:44, size:1
[14]:ffffffff, size:1
[15]:3, size:1
[16]:0, size:1
[17]:2, size:1
[18]:3f, size:1
[19]:0, size:1
[20]:44, size:1
[21]:ffffffff, size:1
[22]:3, size:1
[23]:0, size:1
[24]:0, size:1
[25]:0, size:1
[26]:0, size:1
[27]:3, size:1
[28]:ffffffff, size:1
[29]:6, size:1
[30]:0, size:1
[31]:2, size:1
[32]:9, size:1
[33]:0, size:1
[34]:11, size:1
[35]:ffffffff, size:1
[36]:6, size:1
[37]:0, size:1
[38]:2, size:1
[39]:3f, size:1
[40]:0, size:1
[41]:47, size:1
[42]:ffffffff, size:1
[43]:6, size:1
[44]:0, size:1
[45]:2, size:1
[46]:3f, size:1
[47]:0, size:1
[48]:47, size:1
[49]:ffffffff, size:1
[50]:6, size:1
[51]:0, size:1
[52]:0, size:1
[53]:0, size:1
[54]:0, size:1
[55]:6, size:1
[56]:ffffffff, size:1
[57]:9, size:1
[58]:0, size:1
[59]:2, size:1
[60]:a, size:1
[61]:0, size:1
[62]:15, size:1
[63]:ffffffff, size:1

test code

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
87
88
89
$ cat ex-1.c 
#include <signal.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>

#define USB_UART "/dev/ttyUSB2"
int uart_fd;

int uart_init(void){
int fd;
struct termios oldtio,newtio;

//fd = open((const char *)USB_UART, O_RDWR | O_NOCTTY | O_NDELAY );
fd = open((const char *)USB_UART, O_RDWR );
if( fd < 0 ){
printf("uart device: %s open fail\n",USB_UART);
return -1;
}

if(tcgetattr(fd, &oldtio) != 0 ){
printf("uart device: %s tcgetattr fail\n",USB_UART);
return -1;
}

tcgetattr(fd,&oldtio);

bzero(&newtio,sizeof(newtio));
newtio.c_cflag = B2400 | CS8 | CLOCAL | CREAD;
newtio.c_iflag = IGNPAR;

// set input mode (non-canonical)
newtio.c_oflag = 0;
newtio.c_cc[VTIME] = 0;
newtio.c_cc[VMIN] = 1;
tcflush(fd,TCIFLUSH);
if(tcsetattr(fd, TCSANOW, &newtio) != 0 ){
printf("uart device: %s tcsetattr fail\n",USB_UART);
return -1;
}

return fd;
}

void test_uart(void){
int i;
char ch[1];
int ret;

uart_fd = uart_init();
if( uart_fd < 0) return;

i=0;
while(1){
ret = read(uart_fd,&ch,sizeof(ch));
if(ret > 0)
printf("[%d]:%x, size:%d\n",i++,ch[0],ret);
sleep(0.5);
}
}

void chandler(int s){
printf("Caught signal %d\n",s);

if (uart_fd > 0){
close(uart_fd);
printf("close fd\n");
}

exit(1);
}

int main(int argc,char** argv){
struct sigaction sigIntHandler;

sigIntHandler.sa_handler = chandler;
sigemptyset(&sigIntHandler.sa_mask);
sigIntHandler.sa_flags = 0;
sigaction(SIGINT, &sigIntHandler, NULL);

printf("test-1 uart\n");
uart_fd=-1;

test_uart();
return 0;
}

log

  • log1

    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
    87
    88
    89
    90
    91
    92
    93
    94
    $ lsusb -t
    /: Bus 04.Port 1: Dev 1, Class=root_hub, Driver=ehci-pci/3p, 480M
    |__ Port 1: Dev 2, If 0, Class=Hub, Driver=hub/8p, 480M
    |__ Port 5: Dev 3, If 0, Class=Human Interface Device, Driver=usbhid, 12M
    |__ Port 5: Dev 3, If 1, Class=Human Interface Device, Driver=usbhid, 12M
    |__ Port 6: Dev 4, If 0, Class=Human Interface Device, Driver=usbhid, 1.5M
    |__ Port 6: Dev 4, If 1, Class=Human Interface Device, Driver=usbhid, 1.5M
    /: Bus 03.Port 1: Dev 1, Class=root_hub, Driver=ehci-pci/3p, 480M
    |__ Port 1: Dev 2, If 0, Class=Hub, Driver=hub/6p, 480M
    |__ Port 1: Dev 3, If 0, Class=Vendor Specific Class, Driver=ftdi_sio, 12M
    |__ Port 5: Dev 8, If 0, Class=Chip/SmartCard, Driver=, 480M
    |__ Port 5: Dev 8, If 1, Class=Mass Storage, Driver=usb-storage, 480M
    /: Bus 02.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/4p, 5000M
    /: Bus 01.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/4p, 480M
    |__ Port 2: Dev 5, If 0, Class=Vendor Specific Class, Driver=ftdi_sio, 12M
    |__ Port 3: Dev 4, If 0, Class=Communications, Driver=cdc_ether, 480M
    |__ Port 3: Dev 4, If 1, Class=CDC Data, Driver=cdc_ether, 480M
    |__ Port 4: Dev 6, If 0, Class=Vendor Specific Class, Driver=ch341, 12M


    $ sudo lsusb -v -d 1a86:5523
    [sudo] password for erwin:

    Bus 001 Device 006: ID 1a86:5523 QinHeng Electronics CH341 in serial mode, usb to serial port converter
    Device Descriptor:
    bLength 18
    bDescriptorType 1
    bcdUSB 1.10
    bDeviceClass 255 Vendor Specific Class
    bDeviceSubClass 0
    bDeviceProtocol 2
    bMaxPacketSize0 8
    idVendor 0x1a86 QinHeng Electronics
    idProduct 0x5523 CH341 in serial mode, usb to serial port converter
    bcdDevice 3.04
    iManufacturer 0
    iProduct 0
    iSerial 0
    bNumConfigurations 1



    Configuration Descriptor:
    bLength 9
    bDescriptorType 2
    wTotalLength 39
    bNumInterfaces 1
    bConfigurationValue 1
    iConfiguration 0
    bmAttributes 0x80
    (Bus Powered)
    MaxPower 96mA
    Interface Descriptor:
    bLength 9
    bDescriptorType 4
    bInterfaceNumber 0
    bAlternateSetting 0
    bNumEndpoints 3
    bInterfaceClass 255 Vendor Specific Class
    bInterfaceSubClass 1
    bInterfaceProtocol 2
    iInterface 0
    Endpoint Descriptor:
    bLength 7
    bDescriptorType 5
    bEndpointAddress 0x82 EP 2 IN
    bmAttributes 2
    Transfer Type Bulk
    Synch Type None
    Usage Type Data
    wMaxPacketSize 0x0020 1x 32 bytes
    bInterval 0
    Endpoint Descriptor:
    bLength 7
    bDescriptorType 5
    bEndpointAddress 0x02 EP 2 OUT
    bmAttributes 2
    Transfer Type Bulk
    Synch Type None
    Usage Type Data
    wMaxPacketSize 0x0020 1x 32 bytes
    bInterval 0
    Endpoint Descriptor:
    bLength 7
    bDescriptorType 5
    bEndpointAddress 0x81 EP 1 IN
    bmAttributes 3
    Transfer Type Interrupt
    Synch Type None
    Usage Type Data
    wMaxPacketSize 0x0008 1x 8 bytes
    bInterval 1
    Device Status: 0x0000
    (Bus Powered)
  • log2

    1
    2
    3
    4
    5
    $ sudo cat driver/usbserial
    usbserinfo:1.0 driver:2.0
    0: module:ftdi_sio name:"FTDI USB Serial Device" vendor:0403 product:6001 num_ports:1 port:0 path:usb-0000:00:14.0-2
    1: module:ftdi_sio name:"FTDI USB Serial Device" vendor:0403 product:6001 num_ports:1 port:0 path:usb-0000:00:1a.0-1.1
    2: module:ch341 name:"ch341-uart" vendor:1a86 product:5523 num_ports:1 port:0 path:usb-0000:00:14.0-4
  • Retrieving buffer/packet/payload sizes for USB serial write transfer in userspace Linux C code


說明

名稱定義

  • UART 基本概念篇

  • 數傳機(DCE)
    DCE(Data Communication Equipment)
    是指Modem,母接頭

  • 資料終端(DTE)
    DTE(Data Terminal equipment)
    是指電腦,公接頭

  • TX
    output data (DTE -> DCE)

  • RX
    input data (DCE -> DTE)

Pins Configuration

  • Pins Configuration
PIN (9-pin) Signal Source Type Description
1 CD DCE Control Carrier detect
2 RX DCE Data Receive data
3 TX DTE Data Transmit data
4 DTR DTE Control Data terminal ready
5 SG - - Signal ground
6 DSR DCE Control Data set ready
7 RTS DTE Control Request to send
8 CTS DCE Control Clear to send
9 RI DCE control Ring indicator

遠距離通信

RTS (request to send) 請求發送

  • 可用CTS/RTS或Xon/Xoff流量控制(Flow control)

    • DTE與DCE速度之間存在很大差異,這樣在資料的傳送與接收過程當中很可能出現收方來不及接收的情況,
      這時就需要對發方進行控制,以免資料丟失,這個過程就是所謂的流量控制
    • CTS(clear to send)/RTS(request to send)則是通過電腦與Modem之間的信號線傳送控制信號來實現流量控制的,即硬體方式
    • 請求發送信號(RTS)由電腦產生,通知Modem可以發送資料
    • 清除發送信號(CTS)由Modem產生,通知電腦可以傳送資料
  • DSR, DTR

    • DSR: 表示 數傳機(DCE)準備好, 只表示設備本身可用
    • DTR: 表示 資料終端(DTE)準備好, 只表示設備本身可用

近距離通信

零Modem 的最簡連線(3線制)
在通信中根本不需要RS-232C的控制聯絡信號,只需三根線(發送線、接收線、信號地線)便可實現全雙工非同步串列通信

程式流程

  • 終端機介面

    • 正規模式
      這種模式中,終端設備會處理特殊字元
    • 非正規模式 (non-canonical)
      稱為raw模式。在這種模式中,終端設備不會處理特殊字元
  • open dev (/dev/ttyUSBx)

    1
    2
    //fd = open((const char *)USB_UART, O_RDWR | O_NOCTTY | O_NDELAY );
    fd = open((const char *)USB_UART, O_RDWR );
  • 設定uart

    1
    2
    3
    4
    5
    6
    7
    8
    9
    newtio.c_cflag = B2400 | CS8 | CLOCAL | CREAD;
    newtio.c_iflag = IGNPAR;

    // set input mode (non-canonical)
    newtio.c_oflag = 0;
    newtio.c_cc[VTIME] = 0;
    newtio.c_cc[VMIN] = 1;
    tcflush(fd,TCIFLUSH);
    tcsetattr(fd, TCSANOW, &newtio)
  • Linux RS-232 程式設計

    1
    2
    3
    4
    5
    6
    struct termios{
    tcflag_t c_iflag; //輸入模式
    tcflag_t c_oflag; //輸出模式
    tcflag_t c_cflag; //控制模式
    tcflag_t c_lflag; //局部模式
    cc_t c_cc[NCCS]; //特殊控制字元}
  • 非正規模式的特殊字元TIME和MIN對於輸入字元的處理非常重要,有下列4種組合:
    由於在read之後都會return, 因為若為while(1),記得加入sleep(若newtio.c_cc[VTIME]設定為0,不然會吃cpu)

    1
    2
    3
    4
    5
    組合	說明
    MIN=0, TIME = 0 以read()函數讀取串列埠後立即返回,若讀取到字元則傳回字元,否則傳回0。
    MIN=0, TIME > 0 以read()函數讀取串列埠後,會在TIME時間內等待第一個字元。若有字元傳入或時間到,立即返回。若讀取到字元則傳回字元,否則傳回0。
    MIN > 0, TIME = 0 以read()函數讀取串列埠後會等待資料傳入,若有MIN個字元可讀取,傳回讀取的字元數。
    MIN > 0, TIME > 0 以read()函數讀取串列埠後,會等待資料的傳入。若有MIN個字元可讀取時,傳回讀取到的字元數。若TIME的時間到,則read()傳回0。
  • Serial Port Programming in Linux

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // Set the timeouts
    // VMIN is the minimum amount
    // of characters to read.
    options.c_cc[VMIN] = 0;

    // The amount of time to wait
    // for the amount of data
    // specified by VMIN in tenths
    // of a second.
    optiont.c_cc[VTIME] = 1;

參考