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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > linux >内容正文

linux

迅为linux下串口,迅为IMX6ULL开发板Linux RS232/485驱动实验(上)

發(fā)布時間:2025/3/19 linux 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 迅为linux下串口,迅为IMX6ULL开发板Linux RS232/485驱动实验(上) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

在 arm 設(shè)備中串口是很常用的一個外設(shè),不僅可以用來打印信息,還可以用于外接設(shè)備和其他傳感器通信。根據(jù)不同的電平,串口分為 TTL 和 RS232,但是在Linux內(nèi)核中的驅(qū)動程序是一樣的,在串口上外接RS485 類似的芯片就可以把 RS232 信號轉(zhuǎn)換為 RS485 信號,非常方便。在 i.MX6UL 終結(jié)者開發(fā)板上,RS232、

RS485 和GPS模塊都接到了 UART3 接口上,內(nèi)核驅(qū)動都是一樣的。在本章來學(xué)習(xí)一下串口驅(qū)動。

53.1 Linux??下的 UART??驅(qū)動框架

53.1.1 uart_driver??結(jié)構(gòu)體

在 Linux 中 uart 和 I2C、SPI 一樣,提供了串口驅(qū)動框架,只需要按照提供的串口框架函數(shù)編譯驅(qū)動即可。一般來說串口驅(qū)動都已經(jīng)實現(xiàn)好了,我們需要做的就是在設(shè)備樹文件中,添加相應(yīng)的設(shè)備節(jié)點。當(dāng)設(shè)備和驅(qū)動匹配成功后,串口就能夠正常工作。

在 Linux 中,用 uart_driver 結(jié)構(gòu)體來描述串口,uart_driver 定義在 include/linux/serial_core.h 文件中,內(nèi)容如下:

295 struct uart_driver {

296 struct module *owner; /* 模塊所屬者 */

297 const char *driver_name; /* 驅(qū)動名字 */

298 const char *dev_name; /* 設(shè)備名字 */

299 int major; /* 主設(shè)備號 */

300 int minor; /* 次設(shè)備號 */

301 int nr; /* 設(shè)備數(shù) */

302 struct console *cons; /* 控制臺 */

303

304 /*

305 * these are private; the low level driver should not

306 * touch these; they should be initialised to NULL

307 */

308 struct uart_state *state;

309 struct tty_driver *tty_driver;

310 };

一般在開發(fā)板上有幾個串口,每個串口驅(qū)動都需要定義一個 uart_driver 結(jié)構(gòu)體來表示。

同其他設(shè)備一樣,當(dāng) uart_driver 結(jié)構(gòu)體創(chuàng)建好后,然后注冊到內(nèi)核中去。使用 uart_register_driver 函數(shù)來完成注冊行為,函數(shù)原型如下:

int uart_register_driver(struct uart_driver *drv)

參數(shù) drv 就是創(chuàng)建好要注冊的 uart_driver 結(jié)構(gòu)體,返回 0,表示成功,失敗返回負值。

既然有注冊函數(shù),同樣的也有注銷函數(shù) uart_unregister_driver,函數(shù)原型如下:

void uart_unregister_driver(struct uart_driver *drv)

參數(shù) drv 是要注銷的 uart_driver 結(jié)構(gòu)體,沒有返回值。

53.1.2 uart_port??結(jié)構(gòu)體

uart_port 用于描述一個 UART 端口(直接對應(yīng)于一個串口)的 I/O 端口或 I/O 內(nèi)存地址、FIFO 大小、端口類型等信息。

uart_port 定義在 include/linux/serial_core.h 文件,部分內(nèi)容如下:

117 struct uart_port {

118 spinlock_t lock; /* port lock */

119 unsigned long iobase; /* in/out[bwl] */

120 unsigned char __iomem *membase; /* read/write[bwl] */

......

235 const struct uart_ops *ops;

236 unsigned int custom_divisor;

237 unsigned int line; /* port index */

238 unsigned int minor;

239 resource_size_t mapbase; /* for ioremap */

240 resource_size_t mapsize;

241 struct device *dev; /* parent device */

......

250 };

在 uart_port 結(jié)構(gòu)體中主要關(guān)注 ops 成員,ops 成員包含了串口的具體驅(qū)動函數(shù),后面具體了解。

每個 UART 都有一個 uart_port 結(jié)構(gòu)體,那么 uart_port 和 uart_driver 是如何結(jié)合起來的,要用到

uart_add_one_port 函數(shù),函數(shù)原型如下:

int uart_add_one_port(struct uart_driver *drv,

struct uart_port *uport)

drv:與 uart_port 對應(yīng)的 uart_driver 結(jié)構(gòu)體,

uport:要添加到 uart_driver 結(jié)構(gòu)體中的 uart_port 結(jié)構(gòu)體。

返回值:0,表示成功,負值,表示失敗。

卸載 UART 驅(qū)動時,也需要將 uart_port 從相應(yīng)的 uart_driver 中移除,使用 uart_remove_one_port 函數(shù)來實現(xiàn),函數(shù)原型如下:

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

drv:要卸載的 uart_port 對應(yīng)的 uart_driver。

uport:要卸載的 uart_port。

返回值:0,表示成功,負值,表示失敗。

53.1.3 uart_ops??結(jié)構(gòu)體

uart_ops 結(jié)構(gòu)體中包含了 UART 框架中具體的驅(qū)動函數(shù),Linux 系統(tǒng)收發(fā)數(shù)據(jù)最終調(diào)用的都是 ops 中的函數(shù)。ops 是 uart_ops 類型的結(jié)構(gòu)體指針變量,uart_ops 定義在 include/linux/serial_core.h 文件中,內(nèi)容如下:

49 struct uart_ops {

50 unsigned int (*tx_empty)(struct uart_port *);

51 void (*set_mctrl)(struct uart_port *, unsigned int mctrl);

52 unsigned int (*get_mctrl)(struct uart_port *);

53 void (*stop_tx)(struct uart_port *);

54 void (*start_tx)(struct uart_port *);

55 void (*throttle)(struct uart_port *);

56 void (*unthrottle)(struct uart_port *);

57 void (*send_xchar)(struct uart_port *, char ch);

58 void (*stop_rx)(struct uart_port *);

59 void (*enable_ms)(struct uart_port *);

60 void (*break_ctl)(struct uart_port *, int ctl);

61 int (*startup)(struct uart_port *);

62 void (*shutdown)(struct uart_port *);

63 void (*flush_buffer)(struct uart_port *);

64 void (*set_termios)(struct uart_port *, struct ktermios *new,

65 struct ktermios *old);

66 void (*set_ldisc)(struct uart_port *, struct ktermios *);

67 void (*pm)(struct uart_port *, unsigned int state,

68 unsigned int oldstate);

69

70 /*

71 * Return a string describing the type of the port

72 */

73 const char *(*type)(struct uart_port *);

74

75 /*

76 * Release IO and memory resources used by the port.

77 * This includes iounmap if necessary.

78 */

79 void (*release_port)(struct uart_port *);

80

81 /*

82 * Request IO and memory resources used by the port.

83 * This includes iomapping the port if necessary.

84 */

85 int (*request_port)(struct uart_port *);

86 void (*config_port)(struct uart_port *, int);

87 int (*verify_port)(struct uart_port *, struct serial_struct *);

88 int (*ioctl)(struct uart_port *, unsigned int, unsigned long);

89 #ifdef CONFIG_CONSOLE_POLL

90 int (*poll_init)(struct uart_port *);

91 void (*poll_put_char)(struct uart_port *, unsigned char);

92 int (*poll_get_char)(struct uart_port *);

93 #endif

94 };

UART 驅(qū)動編寫人員需要實現(xiàn) uart_ops,因為 uart_ops 是最底層的 UART 驅(qū)動接口,是實實在在的和UART 寄存器打交道的。關(guān)于 uart_ops 結(jié)構(gòu)體中的這些函數(shù)的具體含義請參考 Documentation/serial/driver這個文檔。

53.2 i.MX6UL UART??驅(qū)動分析

53.2.1 uart 的 的 platform??驅(qū)動框架

首先看一下在設(shè)備樹文件 imx6ull.dtsi 中,串口 UART3 對應(yīng)的設(shè)備節(jié)點,內(nèi)容如下:

1 uart3: serial@021ec000 {

2 compatible = "fsl,imx6ul-uart",

3 "fsl,imx6q-uart", "fsl,imx21-uart";

4 reg = <0x021ec000 0x4000>;

5 interrupts =;

6 clocks = ,

7 ;

8 clock-names = "ipg", "per";

9 dmas = , ;

10 dma-names = "rx", "tx";

11 status = "disabled";

12 };

其中,根據(jù) compatible 屬性值:“fsl,imx6ul-uart”、“fsl,imx6q-uar”和“fsl,imx21-uart”。在內(nèi)核源碼中搜索這三個值即可找到對應(yīng)的 UART 驅(qū)動文件,此文件為 drivers/tty/serial/imx.c,在此文件中可以找到如下內(nèi)容:

267 static struct platform_device_id imx_uart_devtype[] = {

268 {

269 .name = "imx1-uart",

270 .driver_data = (kernel_ulong_t) &imx_uart_devdata[IMX1_UART],

271 }, {

272 .name = "imx21-uart",

273 .driver_data = (kernel_ulong_t) &imx_uart_devdata[IMX21_UART],

274 }, {

275 .name = "imx6q-uart",

276 .driver_data = (kernel_ulong_t) &imx_uart_devdata[IMX6Q_UART],

277 }, {

278 /* sentinel */

279 }

280 };

281 MODULE_DEVICE_TABLE(platform, imx_uart_devtype);

282

283 static const struct of_device_id imx_uart_dt_ids[] = {

284 { .compatible = "fsl,imx6q-uart", .data = &imx_uart_devdata[IMX6Q_UART], },

285 { .compatible = "fsl,imx1-uart", .data = &imx_uart_devdata[IMX1_UART], },

286 { .compatible = "fsl,imx21-uart", .data = &imx_uart_devdata[IMX21_UART], },

287 { /* sentinel */ }

288 };

......

2071 static struct platform_driver serial_imx_driver = {

2072 .probe = serial_imx_probe,

2073 .remove = serial_imx_remove,

2074

2075 .suspend = serial_imx_suspend,

2076 .resume = serial_imx_resume,

2077 .id_table = imx_uart_devtype,

2078 .driver = {

2079 .name = "imx-uart",

2080 .of_match_table = imx_uart_dt_ids,

2081 },

2082 };

2083

2084 static int __init imx_serial_init(void)

2085 {

2086 int ret = uart_register_driver(&imx_reg);

2087

2088 if (ret)

2089 return ret;

2090

2091 ret = platform_driver_register(&serial_imx_driver);

2092 if (ret != 0)

2093 uart_unregister_driver(&imx_reg);

2094

2095 return ret;

2096 }

2097

2098 static void __exit imx_serial_exit(void)

2099 {

2100 platform_driver_unregister(&serial_imx_driver);

2101 uart_unregister_driver(&imx_reg);

2102 }

2103

2104 module_init(imx_serial_init);

2105 module_exit(imx_serial_exit);

從上述代碼可以看出,uart 驅(qū)動文件使用了 platform_driver 結(jié)構(gòu)體,本質(zhì)上是一個 platform 驅(qū)動。

第 267~280 行,imx_uart_devtype 為傳統(tǒng)匹配表。

第 283~288 行,設(shè)備樹所使用的匹配表,第 284 行的 compatible 屬性值為“fsl,imx6q-uart”。

第 2071~2082 行,platform 驅(qū)動框架結(jié)構(gòu)體 serial_imx_driver。

第 2084~2096 行,驅(qū)動入口函數(shù),第 2086 行調(diào)用 uart_register_driver 函數(shù)向 Linux 內(nèi)核注冊uart_driver,在這里就是 imx_reg。

第 2098~2102 行,驅(qū)動出口函數(shù),第 2101 行調(diào)用 uart_unregister_driver 函數(shù)注銷掉前面注冊的uart_driver,也就是 imx_reg。

53.2.2 uart_driver??初始化

在 imx_serial_init 函數(shù)中向 Linux 內(nèi)核注冊了 imx_reg,imx_reg 就是 uart_driver 類型的結(jié)構(gòu)體變量,imx_reg 定義如下:

1836 static struct uart_driver imx_reg = {

1837 .owner = THIS_MODULE,

1838 .driver_name = DRIVER_NAME,

1839 .dev_name = DEV_NAME,

1840 .major = SERIAL_IMX_MAJOR,

1841 .minor = MINOR_START,

1842 .nr = ARRAY_SIZE(imx_ports),

1843 .cons = IMX_CONSOLE,

1844 };

53.2.3 uart_port??初始化和注冊

當(dāng) UART 設(shè)備和驅(qū)動匹配成功以后 serial_imx_probe 函數(shù)就會執(zhí)行,此函數(shù)的重點工作就是初始化uart_port,然后將其添加到對應(yīng)的 uart_driver 中。在看 serial_imx_probe 函數(shù)之前先來看一下 imx_port 結(jié)構(gòu)體,imx_port 是?NXP?為 I.MX 系列 SOC 定義的一個設(shè)備結(jié)構(gòu)體,此結(jié)構(gòu)體內(nèi)部就包含了 uart_port 成

員變量,imx_port 結(jié)構(gòu)體內(nèi)容如下所示(有縮減):

216 struct imx_port {

217 struct uart_port port;

218 struct timer_list timer;

219 unsigned int old_status;

220 unsigned int have_rtscts:1;

221 unsigned int dte_mode:1;

222 unsigned int irda_inv_rx:1;

223 unsigned int irda_inv_tx:1;

224 unsigned short trcv_delay; /* transceiver delay */

......

243 unsigned long flags;

245 };

第 217 行,uart_port 成員變量 port。

接下來看一下 serial_imx_probe 函數(shù),函數(shù)內(nèi)容如下:

1969 static int serial_imx_probe(struct platform_device *pdev)

1970 {

1971 struct imx_port *sport;

1972 void __iomem *base;

1973 int ret = 0;

1974 struct resource *res;

1975 int txirq, rxirq, rtsirq;

1976

1977 sport = devm_kzalloc(&pdev->dev, sizeof(*sport), GFP_KERNEL);

1978 if (!sport)

1979 return -ENOMEM;

1980

1981 ret = serial_imx_probe_dt(sport, pdev);

1982 if (ret > 0)

1983 serial_imx_probe_pdata(sport, pdev);

1984 else if (ret < 0)

1985 return ret;

1986

1987 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);

1988 base = devm_ioremap_resource(&pdev->dev, res);

1989 if (IS_ERR(base))

1990 return PTR_ERR(base);

1991

1992 rxirq = platform_get_irq(pdev, 0);

1993 txirq = platform_get_irq(pdev, 1);

1994 rtsirq = platform_get_irq(pdev, 2);

1995

1996 sport->port.dev = &pdev->dev;

1997 sport->port.mapbase = res->start;

1998 sport->port.membase = base;

1999 sport->port.type = PORT_IMX,

2000 sport->port.iotype = UPIO_MEM;

2001 sport->port.irq = rxirq;

2002 sport->port.fifosize = 32;

2003 sport->port.ops = &imx_pops;

2004 sport->port.rs485_config = imx_rs485_config;

2005 sport->port.rs485.flags =

2006 SER_RS485_RTS_ON_SEND | SER_RS485_RX_DURING_TX;

2007 sport->port.flags = UPF_BOOT_AUTOCONF;

2008 init_timer(&sport->timer);

2009 sport->timer.function = imx_timeout;

2010 sport->timer.data = (unsigned long)sport;

2011

2012 sport->clk_ipg = devm_clk_get(&pdev->dev, "ipg");

2013 if (IS_ERR(sport->clk_ipg)) {

2014 ret = PTR_ERR(sport->clk_ipg);

2015 dev_err(&pdev->dev, "failed to get ipg clk: %d\n", ret);

2016 return ret;

2017 }

2018

2019 sport->clk_per = devm_clk_get(&pdev->dev, "per");

2020 if (IS_ERR(sport->clk_per)) {

2021 ret = PTR_ERR(sport->clk_per);

2022 dev_err(&pdev->dev, "failed to get per clk: %d\n", ret);

2023 return ret;

2024 }

2025

2026 sport->port.uartclk = clk_get_rate(sport->clk_per);

2027 if (sport->port.uartclk > IMX_MODULE_MAX_CLK_RATE) {

2028 ret = clk_set_rate(sport->clk_per, IMX_MODULE_MAX_CLK_RATE);

2029 if (ret < 0) {

2030 dev_err(&pdev->dev, "clk_set_rate() failed\n");

2031 return ret;

2032 }

2033 }

2034 sport->port.uartclk = clk_get_rate(sport->clk_per);

2035

2036 /*

2037 * Allocate the IRQ(s) i.MX1 has three interrupts whereas later

2038 * chips only have one interrupt.

2039 */

2040 if (txirq > 0) {

2041 ret = devm_request_irq(&pdev->dev, rxirq, imx_rxint, 0,

2042 dev_name(&pdev->dev), sport);

2043 if (ret)

2044 return ret;

2045

2046 ret = devm_request_irq(&pdev->dev, txirq, imx_txint, 0,

2047 dev_name(&pdev->dev), sport);

2048 if (ret)

2049 return ret;

2050 } else {

2051 ret = devm_request_irq(&pdev->dev, rxirq, imx_int, 0,

2052 dev_name(&pdev->dev), sport);

2053 if (ret)

2054 return ret;

2055 }

2056

2057 imx_ports[sport->port.line] = sport;

2058

2059 platform_set_drvdata(pdev, sport);

2060

2061 return uart_add_one_port(&imx_reg, &sport->port);

2062 }

第 1971 行,定義一個 imx_port 類型的結(jié)構(gòu)體指針變量 sport。

第 1977 行,為 sport 申請內(nèi)存。

第 1987~1988 行,從設(shè)備樹中獲取 I.MX 系列 SOC UART 外設(shè)寄存器首地址,對于I.MX6ULL 的 UART3 來說就是 0X021EC000。得到寄存器首地址以后對其進行內(nèi)存映射,得到對應(yīng)的虛擬地址。

第 1992~1994 行,獲取中斷信息。

第 1996~2034 行,初始化 sport,我們重點關(guān)注的就是第 2003 行初始化 sport 的 port 成員變量,也就是設(shè)置 uart_ops 為 imx_pops,imx_pops 就是 I.MX6ULL 最底層的驅(qū)動函數(shù)集合,稍后再來看。

第 2040~2055 行,申請中斷。

第 2061 行,使用 uart_add_one_port 向 uart_driver 添加 uart_port,在這里就是向 imx_reg 添加sport->port。

53.2.4 imx_pops??結(jié)構(gòu)體

imx_pops 就是 uart_ops 類型的結(jié)構(gòu)體變量,保存了 I.MX6ULL 串口最底層的操作函數(shù),imx_pops 定義如下:

1611 static struct uart_ops imx_pops = {

1612 .tx_empty = imx_tx_empty,

1613 .set_mctrl = imx_set_mctrl,

1614 .get_mctrl = imx_get_mctrl,

1615 .stop_tx = imx_stop_tx,

1616 .start_tx = imx_start_tx,

1617 .stop_rx = imx_stop_rx,

1618 .enable_ms = imx_enable_ms,

1619 .break_ctl = imx_break_ctl,

1620 .startup = imx_startup,

1621 .shutdown = imx_shutdown,

1622 .flush_buffer = imx_flush_buffer,

1623 .set_termios = imx_set_termios,

1624 .type = imx_type,

1625 .config_port = imx_config_port,

1626 .verify_port = imx_verify_port,

1627 #if defined(CONFIG_CONSOLE_POLL)

1628 .poll_init = imx_poll_init,

1629 .poll_get_char = imx_poll_get_char,

1630 .poll_put_char = imx_poll_put_char,

1631 #endif

1632 };

imx_pops 中的函數(shù)基本都是和 I.MX6ULL 的 UART 寄存器打交道的,這里就不去詳細的分析了。

總結(jié)

以上是生活随笔為你收集整理的迅为linux下串口,迅为IMX6ULL开发板Linux RS232/485驱动实验(上)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

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