目錄
1.Linux下UART驅動架構
1)uart_driver注冊與登出
2)uart_port的添加與移除
3)uart_ops實作
2.RS232驅動編寫
1)UART3 IO節點建立
2)添加uart3節點
3.移植minicom
1)移植ncurses
2)移植minicom
4.RS232驅動測試
1)RS232連接配接設定
2)minicom設定
3)RS232收發測試
序列槽是很常用的一個外設,在Linux下通常通過序列槽和其他裝置或傳感器進行通信,根據電平的
不同,序列槽分為TTL和RS232。不管是什麼樣的接口電平,其驅動程式都是一樣的,通過外接
RS485這樣的晶片就可以将序列槽轉換為RS485信号,正點原子的I.MX6U-ALPHA開發闆就是這
麼做的。對于正點原子的I.MX6U-ALPHA開發闆而言,RS232、RS485以及GPS子產品接口通通
連接配接到了I.MX6U的UART3接口上,是以這些外設最終都歸結為UART3的序列槽驅動。本章我們
就來學習一下如何驅動I.MX6U-ALPHA開發闆上的UART3序列槽,進而實作RS232、RS485以及
GSP驅動。
1.Linux下UART驅動架構
1)uart_driver注冊與登出
同I2C、SPI一樣,Linux也提供了序列槽驅動架構,我們隻需要按照相應的序列槽架構編寫驅
動程式即可。序列槽驅動沒有什麼主機端和裝置端之分,就隻有一個序列槽驅動,而且這個驅
動也已經由NXP官方已經編寫好了,我們真正要做的就是在裝置樹中添加所要使用的序列槽
節點資訊。當系統啟動以後序列槽驅動和裝置比對成功,相應的序列槽就會被驅動起來,生
成/dev/ttymxcX(X=0….n)檔案。
雖然序列槽驅動不需要我們去寫,但是序列槽驅動架構我們還是需要了解的,uart_driver結構
體表示UART驅動,uart_driver定義在include/linux/serial_core.h檔案中,内容如下:
295 struct uart_driver {
296 struct module *owner; /* 子產品所屬者 */
297 const char *driver_name; /* 驅動名字 */
298 const char *dev_name; /* 裝置名字 */
299 int major; /* 主裝置号 */
300 int minor; /* 次裝置号 */
301 int nr; /* 裝置數 */
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 };
每個序列槽驅動都需要定義一個uart_driver,加載驅動的時候通過uart_register_driver函數
向系統注冊這個uart_driver,此函數原型如下:
int uart_register_driver(struct uart_driver *drv)
函數參數和傳回值含義如下:
drv:要注冊的uart_driver。
傳回值:0,成功;負值,失敗。
登出驅動的時候也需要登出掉前面注冊的uart_driver,需要用到uart_unregister_driver
函數,函數原型如下:
void uart_unregister_driver(struct uart_driver *drv)
函數參數和傳回值含義如下:
drv:要登出的uart_driver。
傳回值:無。
2)uart_port的添加與移除
uart_port表示一個具體的port,uart_port定義在include/linux/serial_core.h檔案,内容如下
(有省略):
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中最主要的就是第235行的ops,ops包含了序列槽的具體驅動函數,這個我們稍後
再看。每個UART都有一個uart_port,那麼uart_port是怎麼和uart_driver結合起來的呢?
這裡要用到uart_add_one_port函數,函數原型如下:
int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport)
函數參數和傳回值含義如下:
drv:此port對應的uart_driver。
uport:要添加到uart_driver中的port。
傳回值:0,成功;負值,失敗。
解除安裝UART驅動的時候也需要将uart_port從相應的uart_driver中移除,需要用到
uart_remove_one_port函數,函數原型如下:
int uart_remove_one_port(struct uart_driver *drv, struct uart_port *uport)
函數參數和傳回值含義如下:
drv:要解除安裝的port所對應的uart_driver。
uport:要解除安裝的uart_port。
傳回值:0,成功;負值,失敗。
3)uart_ops實作
在上面講解uart_port的時候說過,uart_port中的ops成員變量很重要,因為ops包含了針對
UART具體的驅動函數,Linux系統收發資料最終調用的都是ops中的函數。ops是uart_ops
類型的結構體指針變量,uart_ops定義在include/linux/serial_core.h檔案中,内容如下:
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驅動編寫人員需要實作uart_ops,因為uart_ops是最底層的UART驅動接口,是實實
在在的和UART寄存器打交道的。關于uart_ops結構體中的這些函數的具體含義請參考
Documentation/serial/driver這個文檔。UART驅動架構大概就是這些,接下來我們理論聯
系實際,看一下NXP官方的UART驅動檔案是如何編寫的。
2.RS232驅動編寫
前面我們已經說過了,I.MX6U的UART驅動NXP已經編寫好了,是以不需要我們編寫。我們要
做的就是在裝置樹中添加UART3對應的裝置節點即可。打開imx6ull-alientek-emmc.dts檔案,
在此檔案中隻有UART1對應的uart1節點,并沒有UART3對應的節點,是以我們可以參考uart1
節點建立uart3節點。
1)UART3 IO節點建立
UART3用到了UART3_TXD和UART3_RXD這兩個IO,是以要先在iomuxc中建立UART3
對應的pinctrl子節點,在iomuxc中添加如下内容:
1 pinctrl_uart3: uart3grp {
2 fsl,pins = <
3 MX6UL_PAD_UART3_TX_DATA__UART3_DCE_TX 0X1b0b1
4 MX6UL_PAD_UART3_RX_DATA__UART3_DCE_RX 0X1b0b1
5 >;
6 };
最後檢查一下UART3_TX和UART3_RX這兩個引腳有沒有被用作其他功能,如果有的話
要将其屏蔽掉,保證這兩個IO隻用作UART3,切記!!!
2)添加uart3節點
預設情況下imx6ull-alientek-emmc.dts中隻有uart1和uart2這兩個節點,如圖1所示:

圖1 uart1和 uart2節點
uart1是UART1的,在正點原子的I.MX6U-ALPHA開發闆上沒有用到UART2,而且UART2
預設用到了UART3的IO,是以需要将uart2這個節點删除掉,然後加上UART3對應的
uart3,uart3節點内容如下:
1 &uart3 {
2 pinctrl-names = "default";
3 pinctrl-0 = <&pinctrl_uart3>;
4 status = "okay";
5 };
完成以後重新編譯裝置樹并使用新的裝置樹啟動Linux,如果裝置樹修改成功的話,系統
啟動以後就會生成一個名為“/dev/ttymxc2”的裝置檔案,ttymxc2就是UART3對應的裝置
檔案,應用程式可以通過通路ttymxc2來實作對UART3的操作。
3.移植minicom
minicom類似我們常用的序列槽調試助手,是Linux下很常用的一個序列槽工具,将minicom移植到
我們的開發闆中,這樣我們就可以借助minicom對序列槽進行讀寫操作。
1)移植ncurses
minicom需要用到ncurses,依次需要先移植ncurses,如果前面已經移植好了ncurses,那
麼這裡就不需要再次移植了,隻需要在編譯minicom的時候指定ncurses庫和頭檔案目錄即
可。
首先在ubuntu中建立一個目錄來存放我們要移植的檔案,比如我
在/home/zuozhongkai/linux/IMX6ULL目錄下建立了一個名為“tool”的目錄來存放所有的移
植檔案。然後下載下傳ncurses源碼,将ncurses-6.0.tar.gz拷貝到Ubuntu中建立的tool目錄
下,然後進行解壓,解壓指令如下:
tar -vxzf ncurses-6.0.tar.gz
解壓完成以後就會生成一個名為“ncurses-6.0”的檔案夾,此檔案夾就是ncurese的源碼文
件夾。在tool目錄下建立名為“ncurses”目錄,用于儲存ncurses編譯結果,一切準備就緒
以後就可以編譯ncureses庫了。進入到ncureses源碼目錄下,也就是剛剛解壓出來的
ncurses-6.0目錄中,首先是配置ncureses,輸入如下指令:
./configure --prefix=/home/zuozhongkai/linux/IMX6ULL/tool/ncurses --host=arm-linux-
gnueabihf --target=arm-linux-gnueabihf --with-shared --without-profile --disable-stripping --without-progs --with-manpages --without-tests
configure就是配置腳本,--prefix用于指定編譯結果的儲存目錄,這裡肯定将編譯結果儲存
到我們前面建立的“ncurses”目錄中。--host用于指定編譯器字首,這裡設定為“arm-linux-
gnueabihf”,--target用于指定目标,這裡也設定為“arm-linux-gnueabihf”。配置指令寫好
以後點選Enter鍵,等待配置完成,配置成功以後如圖2所示:
圖2 配置成功
配置成功以後輸入“make”指令開始編譯,編譯成功以後如圖3所示:
圖3 編譯成功
編譯成功以後輸入“makeinstall”指令安裝,安裝的意思就是将編譯出來的結果拷貝到--
pfefix指定的目錄裡面去。安裝成功以後如圖4所示:
圖4 安裝成功
安裝成功以後檢視一下前面建立的“ncurses”檔案夾,會發現裡面多了一些東西,如圖5所
示:
圖5 編譯出來的結果
我們需要将圖5中include、lib和share這三個目錄中存放的檔案分别拷貝到開發闆根檔案系
統中的/usr/include、/usr/lib和/usr/share這三個目錄中,如果哪個目錄不存在的話請自行
建立!!拷貝指令如下:
sudo cp lib/* /home/zuozhongkai/linux/nfs/rootfs/usr/lib/ -rfa
sudo cp share/* /home/zuozhongkai/linux/nfs/rootfs/usr/share/ -rfa
sudo cp include/* /home/zuozhongkai/linux/nfs/rootfs/usr/include/ -rfa
然後在開發闆根目錄的/etc/profile(沒有的話自己建立一個)檔案中添加如下所示内容:
1 #!/bin/sh
2 LD_LIBRARY_PATH=/lib:/usr/lib:$LD_LIBRARY_PATH
3 export LD_LIBRARY_PATH
4
5 export TERM=vt100
6 export TERMINFO=/usr/share/terminfo
2)移植minicom
繼續移植minicom,擷取minicom源碼,将minicom-2.7.1.tar.gz拷貝到ubuntu中
的/home/zuozhongkai/linux/IMX6ULL/tool目錄下,然後在tool目錄下建立一個名為
“minicom”的子目錄,用于存放minicom編譯結果。一切準備好以後就可以編譯minicom
了,先解壓minicom,指令如下:
tar -vxzf minicom-2.7.1.tar.gz
解壓完成以後會生成一個叫做minicom-2.7.1的檔案夾,這個就是minicom的源碼,進入到
此目錄中,然後配置minicom,配置指令如下:
cd minicom-2.7.1/ //進入 minicom 源碼目錄
./configure CC=arm-linux-gnueabihf-gcc --prefix=/home/zuozhongkai/linux/IMX6ULL/tool/
minicom --host=arm-linux-gnueabihf CPPFLAGS=-I/home/zuozhongkai/linux/IMX6ULL/tool/
ncurses/include LDFLAGS=-L/home/zuozhongkai/linux/IMX6ULL/tool/ncurses/lib -enable-cfg-dir=/etc/minicom //配置
CC表示要使用的gcc交叉編譯器,--prefix指定編譯出來的檔案存放目錄,肯定要存放到我
們前面建立的minicom目錄中。--host指定交叉編譯器字首,CPPFLAGS指定ncurses的頭
檔案路徑,LDFLAGS指定ncurses的庫路徑。配置成功的話如圖6所示:
圖6 配置成功
配置成功以後執行如下指令編譯并安裝:
make
make install
編譯安裝完成以後,前面建立的minicom目錄内容如圖7所示:
圖7 minicom安裝編譯結果
将minicom目錄中bin子目錄下的所有檔案拷貝到開發闆根目錄中的/usr/bin目錄下,指令
如下:
sudo cp bin/* /home/zuozhongkai/linux/nfs/rootfs/usr/bin/
完成以後在開發闆中輸入“minicom -v”來檢視minicom工作是否正常,結果如圖8所示:
圖8 minicom版本号
從圖8可以看出,此時minicom版本号為2.7.1,minicom版本号檢視正常。輸入如下指令打
開minicom配置界面:
minicom -s
結果是打不開minicom配置界面,提示如圖9所示資訊:
圖9 minicom打開失敗
從圖9可以看出,minicom異常嚣張,竟然讓我們“Go away”,這能容忍?!必須要治一
下。解決方法很簡單,建立/etc/passwd檔案,然後在passwd檔案裡面輸入如下所示内
容:
root:x:0:0:root:/root:/bin/sh
完成以後重新開機開發闆!
開發闆重新開機以後再執行“minicom -s”指令,此時minicom配置界面就可以打開了,如圖10
所示:
圖10 mincom 配置界面
如果能出現圖10所示界面,那麼就說明mincom工作正常了。
4.RS232驅動測試
1)RS232連接配接設定
在測試之前要先将I.MX6U-ALPHA開發闆的RS232與電腦連接配接起來,首先設定JP1跳線
帽,如圖11所示:
圖11 UART3 跳線帽設定
跳線帽設定好以後使用RS232線将開發闆與電腦連接配接起來,這裡建議使用USB轉
DB9(RS232)資料線,比如正點原子售賣的CH340方案的USB轉公頭DB9資料線,如圖12
所示:
圖12 USB 轉 DB9資料線
圖12中所示的資料線是帶有CH340晶片的,是以當連接配接到電腦以後就會出現一個COM
口,這個COM口就是我們要使用的COM口。比如在我的電腦上就是COM9,在
SecureCRT上建立一個連接配接,序列槽為COM9,波特率為115200。
2)minicom設定
在開發闆中輸入“minicom -s”,打開minicom配置界面,然後選中“Serial port setup”,如圖
13所示:
圖13 選中序列槽設定項
選中“Serial port setup”以後點選回車,進入設定菜單,如圖14所示:
圖14 序列槽設定項
圖14中有7個設定項目,分别對應A、B……G,比如第一個是選中序列槽,UART3的序列槽文
件為/dev/ttymxc2,是以序列槽設定要設定為/dev/ttymxc2。設定方法就是按下鍵盤上
的‘A’,然後輸入“/dev/ttymxc2”即可,如圖14所示:
圖14 序列槽裝置檔案設定
設定完以後按下Enter鍵确認,确認完以後就可以設定其他的配置項。比如E設定波特率、
資料位和停止位的、F設定硬體流控的,設定方法都一樣,設定完以後如圖15所示:
圖15 UART3設定
都設定完成以後按下Enter鍵确認并退出,這時候會退回到如圖13所示的界面,按下ESC鍵
退出圖13所示的配置界面,退出以後如圖16所示:
圖16 minicom序列槽界面
圖14就是我們的序列槽調試界面,可以看出目前的序列槽檔案為/dev/ttymxc2,按下CTRL-
A,然後再按下Z就可以打開minicom幫助資訊界面,如圖17所示:
圖17 minicom幫助資訊界面
從圖17可以看出,minicom有很多快捷鍵,本實驗我們打開minicom的回顯功能,回顯功
能配置項為“localEchoon/off..E”,是以按下E即可打開/關閉回顯功能。
3)RS232收發測試
1、發送測試
首先測試開發闆通過UART3向電腦發送資料的功能,需要打開minicom的回顯功能(不
打開也可以,但是在minicom中看不到自己輸入的内容),回顯功能打開以後輸入
“AAAA”,如圖18所示:
圖18 通過UART3向電腦發送“AAAA”
圖18中的“AAAA”相當于開發闆通過UART3向電腦發送“AAAA”,那麼COM9就會接收
到“AAAA”,SecureCRT中COM9收到的資料如圖19所示:
圖19 電腦接收到開發闆發送過來的資料
可以看出,開發闆通過UART3向電腦發送資料正常,那麼接下來就測試開發闆資料接
收功能。
2、接收測試
接下來測試開發闆的UART3接收功能,同樣的,要先打開SecureCRT上COM9的本地
回顯,否則的話你在COM9上輸出的内容會看不到,但是實際上是已經發送給了開發
闆。選中SecureCRT的Options->SessionOptions->Adavnced,打開回話配置界面,
然後選中“Localecho”,如圖20所示:
圖20 電腦向開發闆發送“BBBB”
SecureCRT設定好以後向開發闆發送一個“BBBB”,在SecureCRT的COM9上輸入
“BBBB”,如圖21所示:
圖21 電腦向開發闆發送“BBBB”
此時開發闆的minicom就會接收到發送過來的“BBBB”,如圖22所示:
圖22 開發闆接收到發送過來的資料
UART3收發測試都沒有問題,說明我們的UART3驅動工作正常。如果要退出minicom
的話,在minicom通信界面按下CRTL+A,然後按下X來關閉minicom。關于minicom的
使用我們這裡講的很簡單,大家可以在網上查找更加詳細的minicom使用教程。