日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

linux 串口驱动

發(fā)布時間:2024/8/1 linux 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 linux 串口驱动 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

代碼走讀記錄

目錄

1.? 核心數(shù)據(jù)結(jié)構 ops

2. ops各個接口功能及在流程中的位置

? ?2.1??cdns_uart_startup 即打開串口

2.2 start tx? 即發(fā)送使能

2.3 中斷發(fā)送與軟件隊列的同步

3. 中斷處理程序

3.1 RX 中斷處理



xilinx_uartps.c?? ?drivers\tty\serial?? ?49414?? ?2021/10/26?? ?885
?

1.? 核心數(shù)據(jù)結(jié)構 ops

static const struct uart_ops cdns_uart_ops = {.set_mctrl = cdns_uart_set_mctrl,.get_mctrl = cdns_uart_get_mctrl,.start_tx = cdns_uart_start_tx,.stop_tx = cdns_uart_stop_tx,.stop_rx = cdns_uart_stop_rx,.tx_empty = cdns_uart_tx_empty,.break_ctl = cdns_uart_break_ctl,.set_termios = cdns_uart_set_termios,.startup = cdns_uart_startup,.shutdown = cdns_uart_shutdown,.pm = cdns_uart_pm,.type = cdns_uart_type,.verify_port = cdns_uart_verify_port,.request_port = cdns_uart_request_port,.release_port = cdns_uart_release_port,.config_port = cdns_uart_config_port, #ifdef CONFIG_CONSOLE_POLL.poll_get_char = cdns_uart_poll_get_char,.poll_put_char = cdns_uart_poll_put_char, #endif };

2. ops各個接口功能及在流程中的位置

? ?2.1??cdns_uart_startup 即打開串口

? ??

static int cdns_uart_startup(struct uart_port *port) {struct cdns_uart *cdns_uart = port->private_data;bool is_brk_support;int ret;unsigned long flags;unsigned int status = 0;is_brk_support = cdns_uart->quirks & CDNS_UART_RXBS_SUPPORT;spin_lock_irqsave(&port->lock, flags); //這里采用鎖,多個進程同時調(diào)用支持?為啥禁止中斷/* Disable the TX and RX */writel(CDNS_UART_CR_TX_DIS | CDNS_UART_CR_RX_DIS,port->membase + CDNS_UART_CR); //禁用發(fā)送和接收/* Set the Control Register with TX/RX Enable, TX/RX Reset,* no break chars.*/writel(CDNS_UART_CR_TXRST | CDNS_UART_CR_RXRST,port->membase + CDNS_UART_CR);while (readl(port->membase + CDNS_UART_CR) &(CDNS_UART_CR_TXRST | CDNS_UART_CR_RXRST))cpu_relax();/** Clear the RX disable bit and then set the RX enable bit to enable* the receiver.*/status = readl(port->membase + CDNS_UART_CR);status &= ~CDNS_UART_CR_RX_DIS;status |= CDNS_UART_CR_RX_EN;writel(status, port->membase + CDNS_UART_CR); //使能接收/* Set the Mode Register with normal mode,8 data bits,1 stop bit,* no parity.*/writel(CDNS_UART_MR_CHMODE_NORM | CDNS_UART_MR_STOPMODE_1_BIT| CDNS_UART_MR_PARITY_NONE | CDNS_UART_MR_CHARLEN_8_BIT,port->membase + CDNS_UART_MR);/** Set the RX FIFO Trigger level to use most of the FIFO, but it* can be tuned with a module parameter*/writel(rx_trigger_level, port->membase + CDNS_UART_RXWM); //接收FIFO門限設置。門限的大小和波特率相關,可以控制中斷的數(shù)目/** Receive Timeout register is enabled but it* can be tuned with a module parameter*/writel(rx_timeout, port->membase + CDNS_UART_RXTOUT); // 設置接收超時,即某些字節(jié)在FIFO中,但軟件一直沒有讀取,超過此時間設定后,硬件產(chǎn)生中斷,通知軟件獲取,實際也和波特率相關。/* Clear out any pending interrupts before enabling them */writel(readl(port->membase + CDNS_UART_ISR),port->membase + CDNS_UART_ISR); // 清除中斷spin_unlock_irqrestore(&port->lock, flags); //釋放鎖ret = request_irq(port->irq, cdns_uart_isr, 0, CDNS_UART_NAME, port);if (ret) {dev_err(port->dev, "request_irq '%d' failed with %d\n",port->irq, ret);return ret;}//使能接收中斷,開始接收數(shù)據(jù)/* Set the Interrupt Registers with desired interrupts */if (is_brk_support)writel(CDNS_UART_RX_IRQS | CDNS_UART_IXR_BRK,port->membase + CDNS_UART_IER);elsewritel(CDNS_UART_RX_IRQS, port->membase + CDNS_UART_IER);return 0; }

綜上,代碼open的主要操作為:

1)?禁用發(fā)送和接收

2) 使能接收

3)? 設置串口工作模式

4) 接收FIFO門限設置。門限的大小和波特率相關,可以控制中斷的數(shù)目

5)設置接收超時,即某些字節(jié)在FIFO中,但軟件一直沒有讀取,超過此時間設定后,硬件產(chǎn)生中斷,通知軟件獲取,實際也和波特率相關。

6)? 清除中斷

7)掛接中斷處理

8) 使能接收中斷,開始接收數(shù)據(jù)

以上代碼流程中 spin_lock_irqsave 禁止中斷與采取鎖的背景考慮是? 多線程 是lock,那么中斷,是怕在操作的過程中有串口中斷上來,而此時中斷處理程序還沒上來?

2.2 start tx? 即發(fā)送使能

? 將數(shù)據(jù)從軟件緩存隊列里面放到TX FIFO 中,并進行發(fā)送及tx fifo 空中斷的使能控制

static void cdns_uart_start_tx(struct uart_port *port) {unsigned int status;if (uart_tx_stopped(port))return;/** Set the TX enable bit and clear the TX disable bit to enable the* transmitter.*/status = readl(port->membase + CDNS_UART_CR);status &= ~CDNS_UART_CR_TX_DIS;status |= CDNS_UART_CR_TX_EN;writel(status, port->membase + CDNS_UART_CR);if (uart_circ_empty(&port->state->xmit)) //如果上層tty給的緩存里面沒有數(shù)據(jù),即實際沒有數(shù)據(jù)需要發(fā)送,因而直接返回。return;cdns_uart_handle_tx(port); //如果軟件有數(shù)據(jù)發(fā)送,則在此處填充硬件TX FIFO,此處填充,由于上面已經(jīng)將TX 發(fā)送使能打開了,所以邊往fifo里面寫,串口邊從FIFO讀取并發(fā)送出去。writel(CDNS_UART_IXR_TXEMPTY, port->membase + CDNS_UART_ISR);/* Enable the TX Empty interrupt */writel(CDNS_UART_IXR_TXEMPTY, port->membase + CDNS_UART_IER); //使能tx fifo空中斷,這樣 }

2.3 中斷發(fā)送與軟件隊列的同步

即 硬件將軟件緩存中的數(shù)據(jù)發(fā)送的差不多時,此時再次喚醒軟件往緩存中增加數(shù)據(jù).

static void cdns_uart_handle_tx(void *dev_id) {struct uart_port *port = (struct uart_port *)dev_id;unsigned int numbytes;//從軟件緩存里面取數(shù)據(jù),然后寫入到TX FIFO中。//以下這個代碼用于喚醒軟件進程繼續(xù)往緩存里面寫數(shù)據(jù),當緩存中的數(shù)據(jù)小于 WAKUP_CHARS(此處是256)if (uart_circ_chars_pending(&port->state->xmit) < WAKEUP_CHARS)uart_write_wakeup(port);} }

? ? 為什么不是軟件一直往緩存里面寫,而硬件一直從緩存里面讀,而采取這種門限喚醒的機制呢?

serdev-ttyport.c?? ?drivers\tty\serdev?? ?7921?? ?2021/10/26?? ?189
?

// wake up a tty device void tty_port_tty_wakeup(struct tty_port *port) {port->client_ops->write_wakeup(port); }static const struct tty_port_client_operations client_ops = {.receive_buf = ttyport_receive_buf,.write_wakeup = ttyport_write_wakeup, // }; //等待write_wait 的接口 static void ttyport_wait_until_sent(struct serdev_controller *ctrl, long timeout) {struct serport *serport = serdev_controller_get_drvdata(ctrl);struct tty_struct *tty = serport->tty;tty_wait_until_sent(tty, timeout); //此處等待 }

3. 中斷處理程序

? ??分為接收和發(fā)送中斷。?

? ?

static irqreturn_t cdns_uart_isr(int irq, void *dev_id) {struct uart_port *port = (struct uart_port *)dev_id; //支持多個串口,獲取此中斷對應的串口實例,以便獲取資源信息,避免數(shù)據(jù)的混亂。unsigned int isrstatus;spin_lock(&port->lock); //端口鎖,無中斷禁止操作/* Read the interrupt status register to determine which* interrupt(s) is/are active and clear them.*/isrstatus = readl(port->membase + CDNS_UART_ISR);writel(isrstatus, port->membase + CDNS_UART_ISR);if (isrstatus & CDNS_UART_IXR_TXEMPTY) {cdns_uart_handle_tx(dev_id); //針對發(fā)送處理isrstatus &= ~CDNS_UART_IXR_TXEMPTY;}/** Skip RX processing if RX is disabled as RXEMPTY will never be set* as read bytes will not be removed from the FIFO.*/if (isrstatus & CDNS_UART_IXR_RXMASK &&!(readl(port->membase + CDNS_UART_CR) & CDNS_UART_CR_RX_DIS))cdns_uart_handle_rx(dev_id, isrstatus); //針對接收處理spin_unlock(&port->lock);return IRQ_HANDLED; }

3.1 RX 中斷處理

static void cdns_uart_handle_rx(void *dev_id, unsigned int isrstatus) {struct uart_port *port = (struct uart_port *)dev_id;struct cdns_uart *cdns_uart = port->private_data;char status = TTY_NORMAL;bool is_rxbs_support;is_rxbs_support = cdns_uart->quirks & CDNS_UART_RXBS_SUPPORT;// 主要過程循環(huán)讀取RX FIFO中的數(shù)據(jù),并判斷數(shù)據(jù)的正確性while ((readl(port->membase + CDNS_UART_SR) &CDNS_UART_SR_RXEMPTY) != CDNS_UART_SR_RXEMPTY) {data = readl(port->membase + CDNS_UART_FIFO);port->icount.rx++;tty_insert_flip_char(&port->state->port, data, status); //寫入到緩存isrstatus = 0;}tty_flip_buffer_push(&port->state->port); // 寫入到緩存。這個接口比較重要 }

The Serial Device Bus (linuxfoundation.org)

Serial TTY overview - stm32mpu (stmicroelectronics.cn)

(3條消息) Linux tty驅(qū)動學習 - UART驅(qū)動的write操作流程_sbctsp的博客-CSDN博客? (此篇文章,主要參考write_wait,我們在中斷處理發(fā)送流程中看到對此wakeup的操作)

總結(jié)

以上是生活随笔為你收集整理的linux 串口驱动的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。