基於華邦W90P710處理器的Linux內核應用及串口驅動的實現

2021-01-11 電子產品世界

嵌入式Linux是一種很受歡迎的作業系統,具有開放源碼、不存在黑箱技術、內核小、功能強大、運行穩定、效率高、易於定製裁減等特點[1],廣泛應用於工控產品。很多工控產品需要和外部設備進行信息交換,而串口通信是最簡單快捷的實現方法。在不同的工控產品中,由於對所選用的串口元件或者串口通信的數據格式、波特率等有不同的需求,需要對串口驅動進行開發。華邦W90P710採用ARM的ARM7TDMI微處理器核心,採用?滋CLinux-2.4.20內核,支持4組通用異步接收發送口(UART),下面基於華邦W90P710的串口驅動詳細分析串口驅動的實現方法,實現嵌入式設備通過串口對外通信。

1 華邦W90P710 UART介紹

華邦W90P710支持4組UART,串口的控制主要通過以下寄存器實現[2]:
(1)行寄存器(UART_LCR):設置數據位長度、奇偶校驗、停止位數。
(2)波特率除數寄存器(UART_DLL、UART_DLM):波特率發生器的公式為:BaudOut=crystal clock/16×[Divisor +2],Divisor為當前波特率。
(3)Modem控制寄存器(UART_MCR):控制RTS、CTS等信號。
(4)FIFO控制寄存器(UART_FCR):設置FIFO的長度,復位FIFO等控制。
(5)接收超時寄存器(UART_TOR):收到首個字節後接收器啟動本超時,之後每收到一個字節後都會重置該值,在此超時時間內不再收到數據時,接收器會產生一個接收中斷。
(6)中斷控制器(UART_IER):設置接收、發送、行中斷等。
在使用RXDn、TXDn前必須對GPIO進行配置,使能RXDn、TXDn,串口才可正常運行。GPIO配置對應表如表1所示。


2 Linux系統驅動介紹

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

設備驅動程序是作業系統內核和機器硬體之間的接口。設備驅動程序為應用程式屏蔽了硬體的細節,這樣在應用程式看來,硬體設備只是一個設備文件,應用程式可以像操作普通文件一樣對硬體設備進行操作。同時,設備驅動程序是內核的一部分[3]。圖1所示為設備驅動程序接口流程圖。

Linux系統的設備分為字符設備、塊設備和網絡設備三種。字符設備是指存取時沒有緩存的設備,只能順序讀寫。典型的字符設備包括滑鼠、鍵盤、串行口等;塊設備一般都有緩存來支持,並且塊設備必須能夠支持隨機存取。塊設備主要包括硬碟設備、CD-ROM等;網絡設備在Linux系統中用做專門的處理,Linux的網絡系統主要是基於BSD Unix的socket機制[4]。

3 串口驅動程序詳細介紹

一般來說,Linux的設備驅動程序包括驅動程序的註冊和註銷、設備的打開和釋放、設備的讀寫操作、設備的控制操作、設備的中斷和輪詢處理等功能。下面就這些功能對串口驅動進行詳細說明。

(1)串口設備的數據結構包括串口參數接收發送緩衝區等。串口參數包括波特率、數據位、數據起始位、奇偶校驗、串口類型、發送緩衝區、接收緩衝區等,每個串口對應一個如下的數據結構:

typedef struct{
int  bps;
int  databits;
int  stopbits;
int  parity;
int  siotype;        //串口參數
int  openflag;
int  recvTrigTimeout;
SIO_D_SEND_BUFFER    *pSendBuf;//發送緩衝區
SIO_D_RECV_BUFFER    *pRecvBuf;//接收緩衝區
struct fasync_struct *fasync_queue;
wait_queue_head_t    read_wait;
}serial_dev;
static serial_dev serial_device;

(2)文件系統操作

入口函數對應文件操作函數read ()、write()、ioctl()、open()、close()。
struct file_operations serial_fops = {
owner:        THIS_MODULE,
poll:            serial_poll,
read:        serial_read,
write:        serial_write,
ioctl:        serial_ioctl,
open:        serial_open,
release:    serial_release,
};

(3)驅動程序註冊和註銷。

驅動程序在應用前,需要在模塊初始化時將設備註冊到系統設備表中;不再使用時,將設備從系統中卸除。註冊包括初始化定時器、初始化串口數據結構serial_device和字符設備註冊。註銷時直接調用設備註銷函數[5]。
int __init topbandserial1_init(void)
{
init_timer(timer);//初始化定時器結構
memset(serial_device, 0, sizeof(serial_device));
result=register_chrdev(SERIAL1_MAJOR, serial1,
serial_fops);

}

(4)串口設備打開包括分配串口的接收發送緩衝區及中斷註冊[5]。
static int serial_open(struct inode *inode, struct file *filp)
{
dev->pRecvBuf = kmalloc(sizeof(SIO_D_RECV_BUFFER), GFP_KERNEL);
request_irq(INT_UART1,serial_interrupt,SA_SHIRQ,
TopbandSerial1,serial_device);

}

(5)串口設備釋放包括釋放內存空間、註銷中斷和刪除定時器[5]。
static int serial_release(struct inode *inode, struct file *flip)
{
serial_dev *dev = flip->private_data;//釋放內存空間
kfree(dev->fasync_queue);
CSR_WRITE(COM_IER_1, 0x00); /* 中斷禁止 */
free_irq(INT_UART1, dev); //註銷中斷
del_timer(timer);//刪除定時器
MOD_DEC_USE_COUNT;
dev->openflag = 0;

}

(6)串口讀數據是指返回接收緩衝區中已收到的數據。讀取數據有兩種方式,阻塞方式和非阻塞方式。阻塞方式[6]中用戶程序執行讀操作時如果沒有數據可讀,即讓read()操作等待直到數據可讀;非阻塞方式中當用戶執行讀操作時,不論串口是否接收到數據,設備驅動xxx_read()函數會立刻返回,read()函數系統調用也隨即返回。
static int serial_read(struct file *filp, char *buf, size_t
count, loff_t *f_pos)
{
if(filp->f_flags  O_NONBLOCK)/非阻塞方式讀取
retsts = serial_nonblock_read(dev,buf,count);
else    /*阻塞方式讀取*/
retsts = serial_block_read(dev,buf,count);    

}

(7)串口寫數據包括把數據存放在發送緩衝區、啟動硬體發送及發送中斷。當發送第一個字節後,硬體會產生發送中斷,剩下的數據將在中斷處理程序中發送。
static int serial_write(struct file *filp, const char *buf,
size_t count, loff_t *f_pos)
{
copy_from_user(pSendBuf->frameData[pSendBuf->
bufWritex].data[0],buf, count);
CSR_WRITE(CMBOARD_GPIO_DATAOUT1,status1);
enable_tx_interrupt_1();

}

(8)串口控制包括設置串口波特率、奇偶校、停止位等,還可以定義其他特殊的控制。應用程式通過ioctl()調用把串口的參數傳遞給驅動程序,驅動程序再通過對硬體串口控制寄存器進行設置,來滿足應用層用戶要求。

static int serial_ioctl(struct inode *inode, struct file *flip,
unsigned int cmd, unsigned long arg)
{
switch(cmd){
case SERIAL_IOC_BPS:

break;
case SERIAL_IOC_SENDBUF:

break;
}
}

(9)中斷處理包括對接收中斷、發送中斷、異常中斷的處理。讀取中斷寄存器的狀態,根據不同的中斷類型分別處理。當收到數據時,硬體會產生接收中斷,驅動程序把串口的數據讀取出來,放在接收緩衝區中,直到所有數據讀取完成;當發送數據時,硬體會產生發送中斷,驅動程序把發送緩衝區的數據發送出去,直到所有數據發送完成;當串口接收或發送發生異常時,會產生異常中斷,驅動程序根據情況把串口重新初始化,以便串口恢復正常。

static void serial_interrupt(int irq, void * dev_id,
struct pt_regs *regs)
{
status = CSR_READ(COM_IIR_1);
while(status  UART_IIR_STATUS_NO) == 0)
{
switch(status)
{
case UART_IIR_STATUS_RDA:
case UART_IIR_STATUS_TOUT:
receive_chars(dev,status);
break;
case UART_IIR_THRE:    
transmit_chars(dev);
break;
}
status = CSR_READ(COM_IIR_1);
}
}

(10)定時器處理。中斷接收程序只負責把數據讀取到緩衝區,並沒有指示緩衝區的數據可被用戶使用,這時需要在超時程序中把可用標誌置上,當用戶調用read()函數時就可把接收緩衝區的數據返回。

static void serial_timer(unsigned long dummy)
{

serial_device.pRecvBuf->frameData
[serial_device.pRecvBuf->bufWritex].finished = 1;
mod_timer(timer,jiffies+2);/* 20 ms 進一次 */
}

通過以上幾個函數的處理,實現了串口的驅動。

4 驅動程序編譯進Linux內核

以下以UART1為例,介紹驅動程序編譯進Linux內核的過程,步驟如下:

(1)添加主次設備號。

主次設備號用來標識一個具體設備。主設備號用於標識設備類型,每種類型的設備需要一個對應的設備驅動程序。一個主設備可以有多個具體的設備與之對應。次設備號用於區分使用同種驅動程序的同類設備中多個不同的設備實例[7]。

在W90P710-?滋Clinux/?滋Clinux-dist\linux-2.4.x/include/
linux目錄下的major.h中定義主設備號,添加如下代碼:
#define SERIAL1_MAJOR  230
在W90P710-?滋Clinux/?滋Clinux-dist/vendors/Winbond/W90P710目錄下的makefile中建立設備主次設備號(主設備號為230,次設備號為1),添加如下代碼:
serial1,c,230,1 \

(2)在W90P710-?滋Clinux/?滋Clinux-dist/linux-2.4.x/drivers/char目錄下的makefile中添加如下代碼:

obj-$(CONFIG_TOPBAND_SERIAL1)+=w90p710_serial_1.o

(3)在W90P710-?滋Clinux/?滋Clinux-dist/linux-2.4.x/drivers/char目錄下的config.in字符設備段中添加如下代碼:

#if [ $CONFIG_TOPBAND_SERIAL1 = y ]; then
bool 'Topband serial1 support' CONFIG_TOPBAND_
SERIAL1
#fi

(4)在W90P710-?滋Clinux/?滋Clinux-dist目錄下運行make menuconfig,在menuconfig的字符設備選項中可以看見剛剛添加的「CONFIG_TOPBAND_SERIAL1」選項,選上該項。使用make dep、 make clean、make三個命令編譯Linux內核,生成內核文件linux.bin[8]。

(5)在W90P710-?滋Clinux/romdisk/dev目錄下創建設備文件,    輸入命令:
mknod serial1 c 230 1
生成設備文件「serial1」,應用程式通過使用「/dev/ serial1」這個設備文件名就可對串口進行操作。

最後編寫簡單的串口測試程序,編譯生成鏡像文件;再把鏡像文件romfs.img和內核文件linux.bin下載到開發板,把開發板的串口和PC機相連,PC機端使用串口調試工具發送測試數據,開發板能正確收發數據。

本文按驅動程序的功能詳細介紹了W90P710微處理器實現串口驅動的方法,串口驅動程序是很典型的字符設備驅動程序,其他字符設備驅動和串口的實現方法是相同的,這對開發其他字符設備驅動程序有一定的借鑑作用。

linux作業系統文章專題:linux作業系統詳解(linux不再難懂)

相關焦點

  • 從串口驅動到Linux驅動模型,想轉Linux的必會!
    在內核中,這些進程稱為線程,代表了單獨的處理器虛擬化(線程代碼、數據、堆棧和 CPU寄存器)。在用戶空間,通常使用進程這個術語,不過 Linux 實現並沒有區分這兩個概念(進程和線程)。內核實現了一種新型的調度算法,不管有多少個線程在競爭 CPU,這種算法都可以在固定時間內進行操作。這種算法就稱為 O⑴調度程序,這個名字就表示它調度多個線程所使用的時間和調度一個線程所使用的時間是相同的。O⑴調度程序也可以支持多處理器(稱為對稱多處理器或 SMP)。您可以在 ./linux/kernel 中找到進程管理的原始碼,在 .
  • Linux串口上網的程序實現方法
    本文引用地址:http://www.eepw.com.cn/article/150775.htm 這裡所說的上網是指把串口當成一個網絡接口,通過封裝網絡數據包(如IP包)以達到無網卡的終端可以通過串口進行網絡通信。但是使用這兩種協議必須得到內核的支持。例如,如果在沒有配置PPP的Linux環境中使用PPP,除了安裝PPP應用層軟體外,還必須重新編譯內核。
  • 內核不斷升級 如何學習linux設備驅動
    【IT168技術】面對不斷升級的linux內核、GNU開發工具、linux環境下的各種圖形庫,很多linux應用程式開發人員和linux設備驅動開發人員即興奮,又煩躁。興奮的是新的軟體軟體、工具給我提供了更強大的功能,煩躁的是適應新軟體的特性、搭建新環境是一項非常繁瑣的事情。
  • linux內核啟動流程
    Linux內核啟動及文件系統加載過程   當u-boot開始執行bootcmd命令,就進入Linux內核啟動階段,與u-boot類似,普通Linux內核的啟動過程也可以分為兩個階段,但針對壓縮了的內核如uImage就要包括內核自解壓過程了。本文以linux-2.6.37版源碼為例分三個階段來描述內核啟動全過程。
  • 嵌入式Linux內核啟動主要分為這三個階段
    該部分代碼實現在arch/arm/kernel的 head.S中,該文件中的彙編代碼通過查找處理器內核類型和機器碼類型調用相應的初始化函數,再建 立頁表,最後跳轉到start_kernel()函數開始內核的初始化工作。
  • 基於μCLinux的USB驅動程序實現
    在此介紹FTDI公司的USB晶片FT245BL的主要性能、工作原理,並將其應用在Blackfin ADSP-BF533微處理器的嵌入式開發平臺上,說明在μClinux下編寫與加載USB接口晶片FT245BL的驅動程序方法,實現了DSP主板的 USB埠通信。
  • 嵌入式Linux內核啟動主要分為這三個階段
    該部分代碼實現在arch/arm/kernel的 head.S中,該文件中的彙編代碼通過查找處理器內核類型和機器碼類型調用相應的初始化函數,再建 立頁表,***跳轉到start_kernel()函數開始內核的初始化工作。
  • Linux內核學習:簡單的字符設備驅動
    學習Linux內核最好的入門方式之一是從字符設備驅動開始模仿(來自於《奔跑吧 Linux內核——入門篇》)。對於我們日常生活中存在的大量設備,如攝像頭,USB充電器,藍牙,Wi-Fi等,這些設備在電氣特性和實現原理均不相同,對Linux系統來說如何抽象和描述他們呢?Linux很早就根據設備共同特徵將其劃分為三大類型:1,字符設備;塊設備;網絡設備。
  • ARM在嵌入式linux內核裁剪與移植的應用
    嚴格來講,Linux這個詞本身只表示Linux內核,但在實際上人們已經習慣了用Linux來形容整個基於Linux內核,並且使用GNU工程各種工具和資料庫的作業系統。Linux得名於計算機業餘愛好者LinuSTorvalds.Linux的程序源碼全部公開,任何人都可以根據自己的需要裁剪內核,以適應自己的系統。
  • linux驅動開發第2講:應用層的write如何調用到驅動中的write
    在linux作業系統中,一切皆是文件:文件是文件,目錄是文件,設備是文件,socket套接字是文件,管道也是文件。linux作業系統用文件抽象出了這一切,文件成為了以上這些實體的編程接口。正由於此,基於linux的編程變成了面向文件的編程,對於linux應用程式開發者而言,簡直是爽的不要不要的。但是,對於內核開發者而言,卻是未必。
  • 基於USB設備的Linux網絡驅動程序開發
    它支持主流的TCP/IP以及IPX/SPX、 NETBEUI等眾多網絡協議,無論在嵌入式系統,伺服器還是桌面作業系統領域,Linux都取得了廣泛的應用。網絡驅動程序和網絡硬體設備實現網絡協議棧中的數據鏈路層和物理層,對上層協議提供支持,是網絡協議棧的重要組成部分,對Linux的網絡性能起著決定作用。本文主要討論基於USB總線的 Linux網絡驅動程序的設計和實現方法。2.
  • 基於Linux系統的觸控螢幕驅動方案
    Linux根據不同設備,將驅動程序分為字符設備驅動、塊設備驅動、網絡設備驅動三種,Linux輸入子系統u是對字符類型輸入設備驅動實現方式的抽象,是對分散的、多種不同類別的輸入設備進行統一處理的內核驅動模型。輸入子系統具高效、無Bug和可重用等優點。本文對基於Linux輸入子系統的觸控螢幕驅動進行深入的討論。
  • ARM晶片基於linux嵌入式作業系統實現的CMU控制器方案
    ARM晶片基於linux嵌入式作業系統實現的CMU控制器方案 佚名 發表於 2017-12-02 07:32:00     zigbee無線通信與linux嵌入式作業系統是電子人都經常接觸的,在這裡我們設計了一種以
  • 基於Qt實現USB CDC可攜式設備串口通信客戶端設計
    編者按:為實現上位機Linux系統的Qt應用程式與下位機USB CDC可攜式設備之間的串口通信,本課題採用USB CDC類協議,並根據協議在Linux下編寫設備驅動程序,同時,搭建Linux-QT應用平臺利用QT可視化的圖形界面及豐富的圖形庫,設計並製作上位機應用程式圖形界面
  • 如果串口驅動有問題,怎麼調
    裸板很簡單,但是串口驅動還是挺複雜的;另外基本上所有晶片的內核源碼中基本上都會有串口驅動。所以我認為對於串口,我們只要會APP編程即可,不需要去寫它的驅動程序。現在有2個問題:怎麼寫串口APP?如果串口驅動有問題,怎麼調?第1個問題挺簡單,百度搜搜就可以找到源碼。
  • 官方唯一推薦的Linux內核剖析文檔《深入理解Linux內核》免費分享
    從20世紀90年代末開始,Linux 這位相對較新的成員突然變得非常流行,並且躋身於那些知名的商用Unix作業系統之列如果你是某種語言的開發者,你從事這個行業,不管你怎麼學習下去,linux永遠繞不開。
  • 基於Linux的串口伺服器設計與實現
    所以,在電子設備日趨網絡化的今天,利用串口伺服器來實現網絡通信具有十分重要的意義。利用基於TCP/IP的串口數據流傳輸的實現來控制管理設備,無需投資大量的人力、物力即可完成對傳統設備的管理、更換或者升級。
  • Linux下串口編程詳解
    發上,對於串口的開發都是對處理器寄存器的配置。但是,在Linux下串口編程就有所不同,下面我們來簡單介紹下Linux下串口編程的基本過程。,而是調用系統內核的相關函數就可以直接配置好了,而具體寄存器的配置則是在底層驅動代碼裡面實現,我們只需要關註上層應用的開發就行。
  • Linux pstore 實現自動「抓捕」內核崩潰日誌
    根據網上搜尋的資料,在pstore文件系統之前其實有不少類似的實現。apanicAndroid最早的panic信息記錄的方案。在linux 2.6的安卓的內核中找到,卻沒有提交到社區,後來被放棄維護了。
  • 工程師嵌入式Linux自學筆記及體會
    本文引用地址:http://www.eepw.com.cn/article/278048.htm  一個典型的桌面 Linux 系統包括 3 個主要的軟體層---linux 內核、C 庫和應用程式代碼。  內核是唯一可以完全控制硬體的層,內核驅動程序代表應用程式與硬體之間進行會話。