Linux下的串口總線驅動(二)

2020-11-29 電子產品世界
四.TTY層內核代碼

TTY驅動程序有三種:控制臺、串口和pty。在此我們主要分析Mini2440串口驅動。

本文引用地址:http://www.eepw.com.cn/article/201611/319917.htm

我們現在跟蹤uart_register_driver和uart_add_one_port發現,他們的原函數定義在TTY層驅動serial_core.o中。

int uart_register_driver(struct uart_driver *drv)

{

struct tty_driver *normal = NULL;

int i, retval;

BUG_ON(drv->state);

drv->state = kzalloc(sizeof(struct uart_state) * drv->nr, GFP_KERNEL);

retval = -ENOMEM;

if (!drv->state)

goto out;

normal = alloc_tty_driver(drv->nr); //分配TTY驅動

if (!normal)

goto out;

drv->tty_driver = normal;

normal->owner = drv->owner;

normal->driver_name = drv->driver_name;

normal->name = drv->dev_name;

normal->major = drv->major;

normal->minor_start = drv->minor;

normal->type = TTY_DRIVER_TYPE_SERIAL;

normal->subtype = SERIAL_TYPE_NORMAL;

normal->init_termios = tty_std_termios; //初始的termios

normal->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;//控制模式設置

normal->init_termios.c_ispeed = normal->init_termios.c_ospeed = 9600; //設置輸入/出速度

normal->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;

normal->driver_state = drv; //私有數據

tty_set_operations(normal, &uart_ops); //設置TTY驅動操作

for (i = 0; i < drv->nr; i++) { //初始化UART狀態

struct uart_state *state = drv->state + i;

struct tty_port *port = &state->port;

tty_port_init(port);

port->close_delay = 500;

port->closing_wait = 30000;

tasklet_init(&state->tlet, uart_tasklet_action,

(unsigned long)state);

}

retval = tty_register_driver(normal); //註冊TTY驅動

out:

if (retval < 0) {

put_tty_driver(normal);

kfree(drv->state);

}

return retval;

}

在上面uart_register_driver這個函數裡我們首先分配了TTY驅動,然後對其進行填充,初始的termios,並設置TTY驅動操作,最後註冊TTY驅動。其中設置TTY驅動操作時用到uart_ops,我們看看這個uart_ops到底是什麼。

static const struct tty_operations uart_ops = {

.open = uart_open,

.close = uart_close,

.write = uart_write,

.put_char = uart_put_char,

.flush_chars = uart_flush_chars,

.write_room = uart_write_room,

.chars_in_buffer= uart_chars_in_buffer,

.flush_buffer = uart_flush_buffer,

.ioctl = uart_ioctl,

.throttle = uart_throttle,

.unthrottle = uart_unthrottle,

.send_xchar = uart_send_xchar,

.set_termios = uart_set_termios,

.set_ldisc = uart_set_ldisc,

.stop = uart_stop,

.start = uart_start,

.hangup = uart_hangup,

.break_ctl = uart_break_ctl,

.wait_until_sent= uart_wait_until_sent,

#ifdef CONFIG_PROC_FS

.proc_fops = &uart_proc_fops,

#endif

.tiocmget = uart_tiocmget,

.tiocmset = uart_tiocmset,

#ifdef CONFIG_CONSOLE_POLL

.poll_init = uart_poll_init,

.poll_get_char = uart_poll_get_char,

.poll_put_char = uart_poll_put_char,

#endif

};

終端設備可以完成收發數據的功能,當用戶在有數據發送給終端設備時候,通過」write()系統調用—tty核心—線路規程」的層層調用,最終調用tty_driver結構體中的write()函數完成發送。因為傳輸速度和tty硬體緩衝區容量的原因,不是所有的寫程序要求的字符都可以在調用寫函數時候被發送出去,因此,寫函數應當返回能夠發給硬體的字節數以便用戶程序檢查是否所有的數據被真正寫入。如果在write()調用期間發生任何錯誤,一個負的錯誤碼應當被返回。在上面的uart_ops結構體中,我們先看看寫函數uart_write的實現吧。

static int uart_write(struct tty_struct *tty, const unsigned char *buf, int count)

{

struct uart_state *state = tty->driver_data; //獲取設備私有信息結構體

struct uart_port *port;

struct circ_buf *circ;

unsigned long flags;

int c, ret = 0;

if (!state) {

WARN_ON(1);

return -EL3HLT;

}

port = state->uart_port; //UART埠

circ = &state->xmit; //數據緩衝區

if (!circ->buf)

return 0;

spin_lock_irqsave(&port->lock, flags); //獲取UART埠操作的鎖

while (1) {

//返回可用緩存空間的大小

c = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE);

if (count < c)

c = count;

if (c <= 0) //緩存區太小則退出

break;

//將用戶空間buf中大小為c的內容拷貝到緩存中

memcpy(circ->buf + circ->head, buf, c);

circ->head = (circ->head + c) & (UART_XMIT_SIZE - 1);

buf += c; //緩存區指針後移

count -= c; //當一次發送的字節過多,需要分次發送

ret += c; //已經發送的字節數

}

spin_unlock_irqrestore(&port->lock, flags); //釋放UART埠操作的鎖

uart_start(tty); //開始發送

return ret;

}

根據上面對uart_write的分析,我們知道tty_driver的write()函數接收三個參數tty_struct,發送數據指針和要發送的字節數。uart_state作為這個驅動tty的私有數據,其中circ_buf定義了緩衝區,我們向這個緩衝區拷貝待發送的內容後,執行uart_start(tty)進行發送數據。那我們繼續看跟蹤uart_start函數

static void uart_start(struct tty_struct *tty)

{

struct uart_state *state = tty->driver_data;

struct uart_port *port = state->uart_port;

unsigned long flags;

spin_lock_irqsave(&port->lock, flags); //獲取UART埠操作的鎖

__uart_start(tty);

spin_unlock_irqrestore(&port->lock, flags); //釋放UART埠操作的鎖

}

static void __uart_start(struct tty_struct *tty)

{

struct uart_state *state = tty->driver_data;

struct uart_port *port = state->uart_port;

if (!uart_circ_empty(&state->xmit) && state->xmit.buf &&

!tty->stopped && !tty->hw_stopped) //緩衝區有數據並開啟發送狀態

port->ops->start_tx(port); //調用uart_ops下的start_tx,即s3c24xx_serial_start_tx

}

注意__uart_start函數中的port->ops->start_tx(port)便實現了tty層和uart層的相連,由tty層的write()調用uart層的write()。

好了,上面講的是發送數據,讀者可能注意到struct tty_operations uart_ops中沒有提到read()函數。因為發送是用戶主動的,而接收拾用戶調用read()從一片緩衝區讀取已經放好的數據,這個緩衝區由struct tty_flip_buffer結構體實現。因為tty核提供了這樣的緩衝邏輯,所以每個tty驅動並非一定要實現它自身的緩衝邏輯。Tty驅動不需要關注struct tty_flip_buffe的細節,從tty驅動接收到的來自硬體層的字符將被tty_insert_filp_char()函數插入filp緩衝區。如果傳輸的字節數count大於或等於TTY_FLIPBUF_SIEZE,這個flip緩衝區就需要被刷新到用戶,刷新是通過調用tty_flip_buffer_push()實現的。

接著,我們繼續看struct tty_operations uart_ops中對termios的設置函數set_termios,即uart_set_termios。這個set_termios需要根據用戶對termios的設置完成實際的硬體設置。新的設置被保存在tty_struct中,舊的設置被保存在old參數中,若新舊參數相同,則什麼都不需要做,對於被改的設置,需要完成硬體上的設置。好了,下面我們還是看看uart_set_termios的實現吧。

static void uart_set_termios(struct tty_struct *tty,

struct ktermios *old_termios)

{

struct uart_state *state = tty->driver_data; //獲取私有數據

unsigned long flags;

unsigned int cflag = tty->termios->c_cflag; //獲取當前線路設置

#define RELEVANT_IFLAG(iflag) ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))

//如果新舊線路設置的控制狀態,輸入輸出速度等信息一樣,則退出

if ((cflag ^ old_termios->c_cflag) == 0 &&

tty->termios->c_ospeed == old_termios->c_ospeed &&

tty->termios->c_ispeed == old_termios->c_ispeed &&

RELEVANT_IFLAG(tty->termios->c_iflag ^ old_termios->c_iflag) == 0) {

return;

}

uart_change_speed(state, old_termios); //用新的線路規程的速度更新舊的線路規程

//處理波特率為B0情況

if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD))

uart_clear_mctrl(state->uart_port, TIOCM_RTS | TIOCM_DTR);

/處理波特率為非B0情況

if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) {

unsigned int mask = TIOCM_DTR;

if (!(cflag & CRTSCTS) ||

!test_bit(TTY_THROTTLED, &tty->flags))

mask |= TIOCM_RTS;

uart_set_mctrl(state->uart_port, mask); //設置modem控制

}

//處理無數據流控制情況

if ((old_termios->c_cflag & CRTSCTS) && !(cflag & CRTSCTS)) {

spin_lock_irqsave(&state->uart_port->lock, flags);

tty->hw_stopped = 0;

__uart_start(tty);

spin_unlock_irqrestore(&state->uart_port->lock, flags);

}

//處理有數據流控制情況

if (!(old_termios->c_cflag & CRTSCTS) && (cflag & CRTSCTS)) {

spin_lock_irqsave(&state->uart_port->lock, flags);

if (!(state->uart_port->ops->get_mctrl(state->uart_port) & TIOCM_CTS)) {

tty->hw_stopped = 1;

state->uart_port->ops->stop_tx(state->uart_port);

}

spin_unlock_irqrestore(&state->uart_port->lock, flags);

}

}

好了我們已經講解了write,set_termiox,下面我們講講tiocmget和tiocmset。Tiocmget()函數用於獲取tty設備的線路設置,對應的tiocmset()用於設置tty設備的線路設置。

static int uart_tiocmget(struct tty_struct *tty, struct file *file)

{

struct uart_state *state = tty->driver_data;

struct tty_port *port = &state->port;

struct uart_port *uport = state->uart_port;

int result = -EIO;

mutex_lock(&port->mutex); //獲取對tty_port操作的鎖

if ((!file || !tty_hung_up_p(file)) &&

!(tty->flags & (1 << TTY_IO_ERROR))) {

result = uport->mctrl;

spin_lock_irq(&uport->lock);

result |= uport->ops->get_mctrl(uport); //調用UART層get_mctrl獲取modem控制

spin_unlock_irq(&uport->lock);

}

mutex_unlock(&port->mutex); //釋放對tty_port操作的鎖

return result;

}

static int uart_tiocmset(struct tty_struct *tty, struct file *file,

unsigned int set, unsigned int clear)

{

struct uart_state *state = tty->driver_data;

struct uart_port *uport = state->uart_port;

struct tty_port *port = &state->port;

int ret = -EIO;

mutex_lock(&port->mutex); //獲取對tty_port操作的鎖

if ((!file || !tty_hung_up_p(file)) &&

!(tty->flags & (1 << TTY_IO_ERROR))) {

uart_update_mctrl(uport, set, clear); //獲取modem控制

ret = 0;

}

mutex_unlock(&port->mutex); //釋放對tty_port操作的鎖

return ret;

}

上面uart_tiocmset中調用了uart_update_mctrl(uport, set, clear)函數,它最終是通過調用port->ops->set_mctrl(port, port->mctrl)完成,而set_mctrl在UART層的uart_ops實現了。

綜上,TTY層的ops中的uart_tiocmget和uart_tiocmset其實最終是調用UART層uart_ops中的get_mctrl和set_mctrl實現的。

當用戶在tty設備節點上進行ioctl調用時,tty_operations中的ioctl()函數會被tty核心調用。我們接下來看看struct tty_operations uart_ops下的.ioctl也就是uart_ioctl。

static int uart_ioctl(struct tty_struct *tty, struct file *filp, unsigned int cmd,

unsigned long arg)

{

struct uart_state *state = tty->driver_data;

struct tty_port *port = &state->port;

void __user *uarg = (void __user *)arg;

int ret = -ENOIOCTLCMD;

switch (cmd) { //這些ioctl不依賴硬體

case TIOCGSERIAL: //獲得串口線信息

ret = uart_get_info(state, uarg);

break;

case TIOCSSERIAL: //設置串口線信息

ret = uart_set_info(state, uarg);

break;

case TIOCSERCONFIG: //自動配置

ret = uart_do_autoconfig(state);

break;

case TIOCSERGWILD:

case TIOCSERSWILD:

ret = 0;

break;

}

if (ret != -ENOIOCTLCMD)

goto out;

if (tty->flags & (1 << TTY_IO_ERROR)) {

ret = -EIO;

goto out;

}

switch (cmd) { //這些ioctl依賴硬體

case TIOCMIWAIT: //等待MSR改變

ret = uart_wait_modem_status(state, arg);

break;

case TIOCGICOUNT: //獲得中斷計數

ret = uart_get_count(state, uarg);

break;

}

if (ret != -ENOIOCTLCMD)

goto out;

mutex_lock(&port->mutex);

if (tty_hung_up_p(filp)) {

ret = -EIO;

goto out_up;

}

switch (cmd) { //這些ioctl依賴硬體,並且需要保護,房子tty被掛起

case TIOCSERGETLSR: //獲得這個tty設備的線路狀態寄存器LSR的值

ret = uart_get_lsr_info(state, uarg);

break;

default: {

struct uart_port *uport = state->uart_port;

if (uport->ops->ioctl)

ret = uport->ops->ioctl(uport, cmd, arg);

break;

}

}

out_up:

mutex_unlock(&port->mutex);

out:

return ret;

}

當TTY核心想知道由TTY驅動程序提供的可用寫入緩衝區的大小時,就會調用write_room。在清空寫緩衝區,或者調用write函數向緩衝區添加數據時,該值是變化的。接下來我們看看TTY層ops中write_room,也就是uart_write_room。跟蹤發現其實這個函數實現主要是把緩衝區頭尾相減得到剩餘空間大小。

除了write_room外,還有其他一些緩衝函數,例如TTY層ops中chars_in_buffer,也就是uart_chars_in_buffer,當tty核心在tty驅動程序的寫緩衝區中還有多少個需要傳輸的字符時調用該函數。

除此之外TTY層ops中還有三個回調函數,用來刷新驅動程序保存的任何數據,並不一定要實現,但是如果tty驅動程序能在發送給硬體前緩衝數據,還是推薦實現它們的,它們分別是flush_buffer,wait_until_sent,flush_buffer。

回顧一下,我們在TTY層的ops中,主要講了write,set_termiox, tiocmget,tiocmset,ioctl,五個函數,還簡單介紹了write_room,chars_in_buffer,flush_buffer,wait_until_sent,flush_buffer五個函數。到目前為止,我們已經分析好了uart_register_driver函數,現在該分析uart_add_one_port函數了。

int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport)

{

struct uart_state *state;

struct tty_port *port;

int ret = 0;

struct device *tty_dev;

BUG_ON(in_interrupt());

if (uport->line >= drv->nr)

return -EINVAL;

state = drv->state + uport->line;

port = &state->port;

mutex_lock(&port_mutex);

mutex_lock(&port->mutex);

if (state->uart_port) {

ret = -EINVAL;

goto out;

}

state->uart_port = uport;

state->pm_state = -1;

uport->cons = drv->cons;

uport->state = state;

//如果這個埠是控制臺,那麼這個鎖就已經初始化了

if (!(uart_console(uport) && (uport->cons->flags & CON_ENABLED))) {

spin_lock_init(&uport->lock);

lockdep_set_class(&uport->lock, &port_lock_key);

}

uart_configure_port(drv, state, uport); //配置埠

tty_dev = tty_register_device(drv->tty_driver, uport->line, uport->dev);//註冊埠

if (likely(!IS_ERR(tty_dev))) {

device_init_wakeup(tty_dev, 1);

device_set_wakeup_enable(tty_dev, 0);

} else

printk(KERN_ERR "Cannot register tty device on line %d\n",

uport->line);

//確保UPF_DEAD沒有被置位

uport->flags &= ~UPF_DEAD;

out:

mutex_unlock(&port->mutex);

mutex_unlock(&port_mutex);

return ret;

}

對於uart_add_one_port,我們發現其中最核心的一句代碼就是tty_register_device,僅有tty_driver是不夠的,驅動必須依附於設備,tty_register_device函數用於註冊關聯於tty_driver的設備。

總結下,TTY層的uart_register_driver和uart_register_port最終調用線路規程的tty_register_driver和tty_register_device。而tty_register_driver和tty_register_device的實現在線路規程中。

對於TTY驅動主要涉及如下幾個重要結構體,struct tty_struct包含了和打開的tty相關的所有狀態信息。其中一個重要的成員就是struct tty_bufhead buf,它是數據收集和處理機制的中樞,其定義如下

struct tty_bufhead {

struct delayed_work work;

spinlock_t lock;

struct tty_buffer *head;

struct tty_buffer *tail;

struct tty_buffer *free;

int memory_used;

};

另一個重要結構體是struct tty_driver,它規定了tty驅動程序和高層之間的編程接口。在我們這個TTY層,由uart_register_driver下的tty_register_driver註冊入內核,其中這個結構體中的成員部分是通過拷貝uart_driver中的參數得到。

好了,對於TTY層驅動,一般而言,我們需要完成如下兩個任務:

其一,終端設備驅動模塊的加載函數和卸載函數,完成註冊和註銷tty_driver,初始化和釋放終端設備對應的tty_driver結構體成員和硬體資源。

其二,實現tty_operations結構體中的一系列成員函數,主要的是實現open()、close()、 write()、 tiocmget()、 tiocmset()。

相關焦點

  • 從串口驅動到Linux驅動模型,想轉Linux的必會!
    本文通過對Linux下串口驅動的分析。由最上層的C庫。到作業系統系統調用層的封裝。再到tty子系統的核心。再到一系列線路規程。再到最底層的硬體操作。對Linux中的tty子系統進行簡要的說明。從理論到實踐。以便讀者能對OS原理有更深入的了解和更具體的掌握。在具體分析之前。我們必須對串口。驅動。和Linux作業系統有一定的了解。
  • 數字溫度傳感器DS1621在Linux下的IIC接口驅動設計
    Linux作業系統下的嵌入式設備驅動,通過IIC總線,實現ARM與外圍模塊間的協同工作,有著廣泛的應用。1 IIC總線協議以及選用晶片功能1.1 IIC總線的特點以及工作協議 IIC串行總線由兩根信號線組成:一根雙向傳輸的數據線SDA;另一根是時鐘線SCL。
  • 實戰經驗吐血推薦:怎樣在Linux環境下輕鬆實現基於I2C總線的EEPROM...
    MPC8250處理器正是通過內部的I2C總線控制器來和這些連接在I2C總線上的設備進行數據交換的。由於I2C總線的特性,Linux的I2C總線設備驅動程序的設計者在設計驅動程序時採用了獨特的體系結構。使開發I2C總線設備驅動程序與開發一般設備驅動程序的方法具有很大差別。因此,開發I2C總線設備驅動程序除了要涉及一般Linux內核驅動程序的知識外。
  • Linux下I2C總線EEPROM驅動程序設計方法
    3 Linux中I2C總線驅動體系結構   在Linux系統中,對於一個給定的I2C總線硬體配置系統,I2C總線驅動程序體系結構由I2C總線驅動和I2C設備驅動組成。其中I2C總線驅動包括一個具體的控制器驅動和I2C總線的算法驅動。一個算法驅動適用於一類總線控制器。
  • 基於Linux系統的多種串行總線統一接口的實現
    關鍵詞:統一接口;嵌入式系統:Linux;設備驅動;串行總線0 引言在Linux內核中單獨實現TTY、I2C、SPI、ISA、USB等多種總線驅動時,每一種總線的實現都有各自的特點,如參數設置不同,實現的結構不同等。
  • 現場總線的開關量 I/O 模塊的總體方案設計
    20世紀80年代以來,開放的工業控制總線迅速發展,在此基礎上通過網絡連接的分散控制和嵌入式設備的控制技術逐步發展成熟,遠程I/O就是在這種條件下發展的一類產品,開放和通用是其主要特徵。第二,配置開發主機在Linux下,配置串口通訊工具minicom,其作用是作為調試嵌入式開發板的信息輸出的監視器和鍵盤輸入的工具。配置網絡,主要是配置網絡文件系統NFS,需要關閉防火牆,簡化嵌入式網絡調試設置過程。
  • 嵌入式Linux設備驅動開發之:GPIO驅動程序實例
    根據各種系統設計的需求,通過軟體方法可以將這些埠配置成具有相應功能(例如:外部中斷或數據總線)的埠。為了控制這些埠,S3C2410處理器為每個埠組分別提供幾種相應的控制寄存器。其中最常用的有埠配置寄存器(GPACON~GPHCON)和埠數據寄存器(GPADAT~GPHDAT)。
  • 基於CAN總線的運動控制系統設計
    μCLinux(microcontrol linux)即「微控制器領域中的Linux系統」,主要是針對目標處理器沒有存儲管理單元(MMU)的嵌入式系統而設計的。它保留了Linux的大多數優點:穩定、良好的移植性、優秀的網絡功能、對各種文件系統完備的支持和標準豐富的API.同時μCLinux包含大量的設備驅動程序,以及提供良好的驅動程序開發框架。
  • 基於C8051F040單片機的CAN總線和RS-232串口通信設計
    為了實現對CAN總線和RS-232串口雙向通信需求,提出了一種基於C8051F040單片機的數據通信方案,並完成系統設計。分析了CAN總線和RS-232串口的通信特點,介紹了單片機硬體,並對軟體的設計思路與流程做了詳盡描述,完成功能檢測。實驗結果表明,該設計達到了要求。
  • RS232 RS485 串口 電平標準
    (2)通過PCI多串口卡,可以直接選用輸出信號為RS485類型的擴展卡。 RS485接口組成的半雙工網絡,一般是兩線制(以前有四線制接法,只能實現點對點的通信方式,現很少採用),多採用屏蔽雙絞線傳輸。這種接線方式為總線式拓撲結構在同一總線上最多可以掛接32個結點。在RS485通信網絡中一般採用的是主從通信方式,即一個主機帶多個從機。
  • 現場總線通信協議特點
    現場總線通信協議特點  (1)現場總線通信協議基本遵照ISO/OSI參考模型,主要實現第1、2、7層功能。由於在某些情況下,現場傳感器、變送器要從現場總線「竊取」電能作為它們的工作電源,因此對總線上數位訊號的強度(驅動能力)、傳輸速率、信噪比以及電纜尺寸、線路長度等都提出一定要求。   (3)數據鏈路層考慮到現場設各故障較多,更換頻繁,所以數據鏈路層媒體訪問控制多採用受控訪問(包括輪詢和令牌)協議,通常,各CPU、PLO作為主站,傳感器、變送器等作為從站。
  • 基於樹莓派的多串口多總線伺服器設計
    引言本文引用地址:http://www.eepw.com.cn/article/201609/304386.htm工業控制中,各設備的信號採集和監控只靠串口總線難以實現擴展,要將現場控制網絡和信息網絡相連,就需要解決串口通信協議和網際網路通信協議的轉換問題,即把原有設備轉換為具備網絡接口的外設,這樣可以將傳統串行鏈路上的數據傳輸到信息網絡上,而無需更換原有設備。
  • 嵌入式Linux設備驅動開發之:實驗內容——test驅動
    本文引用地址:http://www.eepw.com.cn/article/257106.htm1.實驗目的該實驗是編寫最簡單的字符驅動程序,這裡的設備也就是一段內存,實現簡單的讀寫功能,並列出常用格式的Makefile以及驅動的加載和卸載腳本。讀者可以熟悉字符設備驅動的整個編寫流程。
  • 硬碟串口IDE與並口SATA接口之比較
    IDE接口的機械硬碟2、SATA不依賴系統總線的帶寬,而是內置時鐘頻率,支持熱插拔。3、SATA不再使用過時的並行總線接口,轉用串行總線。sata接口的機械硬碟1、首先要明確一點,IDE、SATA都是ATA接口,IDE是傳統串口,SATA是新式並口。
  • 探究全球TOP 10總線控制器排行迷思
    提到總線控制器,因每個人接觸領域不同,可能第一時間想到的總線定義都不同。有的想到的是 RS485 總線,有的是 RS232 總線,有的是 CAN 總線,有的是 PCI 總線,但有一點是共同的,總線千千萬,通信掉線時常有。 如何讓你的總線接口在通信時候不翻車?
  • 計算機串口通信基礎
    計算機串口通信基礎 李倩 發表於 2018-07-12 08:08:37 串口通信是非常重要的,首先了解下基礎 計算機串口通信基礎 隨著多微機系統的廣泛應用和計算機網絡技術的普及
  • 基於嵌入式Linux的語音識別系統硬軟體設計
    系統測量過程如下:首先根據語音指令控制兩自由度雲臺的位姿,使超聲波探測器指向特定方向,然後開啟超聲波探測器,測量出前方障礙物距離,最後將測量結果轉化為可以播放的二進位數據流,通過LD3320的播放功能完成數據的播放。  3.硬體電路設計方案  硬體電路主要包括語音識別部分、主控部分、超聲波測距部分和舵機控制部分,如圖2所示。
  • 串口伺服器工作原理?艾銻科普
    6、VSPM虛擬串口軟體必須工作「Server模式」,並且手工建立所需要的虛擬串口,默認設置下,建立的虛擬串口對應的監聽埠為6020、6021、6022、6023。本機防火牆必須放行這些埠的訪問,否則將無法建立連接。如果主機在內網,其網關必須做靜態埠映射,將上述埠映射到內網主機。
  • 機房常見的串口伺服器有哪些?它的作用是?
    所有串口內置600W防雷 。10/100M乙太網、自動偵測直連或交叉線。可以同時支持多個連接。工作方式有全雙工或半雙工,傳輸距離可以到100米,接口形式是RJ45。   1.TCP/UDP通訊模式:該模式下,串口伺服器成對的使用,一個作為server端,一個作為client端,兩者之間通過IP位址與埠號建立連接,實現數據雙向透明傳輸。
  • 串口通訊在變電站保護信息採集方面的問題
    在變電站內的保護裝置,通訊接口方式多種多樣,有採用乙太網、串口或其他現場總線方式等。2 一個串口接入的總線方式 在串口通訊中也可以採用總線方式(RS 485或RS 422方式),只要把所有的通訊線都掛接在一起,拉1條總線到保護信息採集裝置