在Linux應(yīng)用程序中,常用的延時(shí)函數(shù)包括sleep()、usleep()、select()等,這幾個(gè)延時(shí)函數(shù)函數(shù)的執(zhí)行機(jī)制,都是將當(dāng)前線程掛起,由操作系統(tǒng)做延時(shí),然后再恢復(fù)當(dāng)前線程。這意味著其延時(shí)的最小間隔是兩次線程切換時(shí)間。經(jīng)測(cè)試,在單一線程情況下,兩次線程的切換時(shí)間在150us左右。大多數(shù)情況下,應(yīng)用程序會(huì)有多個(gè)線程在運(yùn)行,這時(shí)線程恢復(fù)有可能在下一個(gè)時(shí)間片,而Linux系統(tǒng)缺省的線程輪片時(shí)間為10ms,這意味著只有當(dāng)延時(shí)在大于10ms情況時(shí),常規(guī)的延時(shí)函數(shù)才有意義。在工控領(lǐng)域,我們常常碰到需要微秒級(jí)的延時(shí)需求,例如實(shí)現(xiàn)某種讀寫時(shí)序等,這時(shí)Linux系統(tǒng)的常規(guī)延時(shí)函數(shù)難于滿足其需求。針對(duì)這樣的應(yīng)用需求,我們?cè)O(shè)計(jì)了采用內(nèi)存映射的方法操作主板的硬件定時(shí)器和GPIO,從而產(chǎn)生出具有微秒精度的脈沖波形來。下面就詳細(xì)介紹如何在用戶進(jìn)程實(shí)現(xiàn)這樣的精確延時(shí)的操作。
以EM335x工控主板為例,用其內(nèi)部的定時(shí)器來實(shí)現(xiàn)精確延時(shí)的功能,EM335x內(nèi)部定時(shí)器的輸入時(shí)鐘為24MHz,單位時(shí)間為41.6ns,通過將Linux系統(tǒng)的mem設(shè)備文件和mmap()函數(shù)結(jié)合起來使用,可直接對(duì)EM335x內(nèi)部定時(shí)器的寄存器進(jìn)行操作,再通過同樣的方式控制GPIO,實(shí)現(xiàn):(1)設(shè)置GPIO,(2)啟動(dòng)定時(shí)器,當(dāng)檢測(cè)到定時(shí)器計(jì)數(shù)完畢,(3)再設(shè)置GPIO,共三個(gè)步驟,就可產(chǎn)生精確時(shí)間間隔的脈沖。
Linux系統(tǒng)中的/dev/mem設(shè)備文件,是專門用來讀寫物理地址用的,里面的內(nèi)容是所有物理內(nèi)存的地址以及內(nèi)容信息。只要我們使用mmap()函數(shù)將/dev/mem設(shè)備文件映射到進(jìn)程地址空間,實(shí)現(xiàn)對(duì)內(nèi)存物理地址的讀寫,就能夠通過這種方式快速的對(duì)GPIO和定時(shí)器進(jìn)行操作,而mmap操作提供了一種機(jī)制,讓用戶程序直接訪問設(shè)備內(nèi)存,這樣就相當(dāng)于直接對(duì)硬件進(jìn)行操作,從而避開了驅(qū)動(dòng)程序,如果調(diào)用驅(qū)動(dòng)就需要在用戶空間和內(nèi)核空間互相拷貝數(shù)據(jù),還會(huì)涉及到系統(tǒng)調(diào)度等機(jī)制,效率將會(huì)變低。
將/dev/mem/設(shè)備文件中定時(shí)器的地址映射到用戶進(jìn)程空間的代碼:
void *timer_em335x_pin_config(unsigned int BASE)
{
int mem_fd;
void *base;
mem_fd = open('/dev/mem', O_RDWR|O_SYNC);
printf('mem_fd is %d\n', mem_fd);
/* mmap Timer */
base = mmap(
NULL, //起始地址
DMTIMER_DEV_SIZE, //映射的文件內(nèi)容的大小
PROT_READ|PROT_WRITE,//映射區(qū)域可讀可寫
MAP_SHARED, //映射區(qū)域的寫入數(shù)據(jù)會(huì)寫回到原來的文件
mem_fd,
BASE//被映射的硬件地址
);
close(mem_fd);
return base;
}
將/dev/mem/設(shè)備文件中GPIO的地址映射到用戶進(jìn)程空間的代碼:
void *GPIO_MMAP::gpio_em335x_pin_config(unsigned int BASE)
{
int mem_fd;
void *base;
mem_fd = open('/dev/mem', O_RDWR|O_SYNC);
printf('mem_fd is %d\n', mem_fd);
/* mmap GPIO */
base = mmap(
NULL,//起始地址
GPIO_DEV_SIZE, //映射的文件內(nèi)容的大小
PROT_READ|PROT_WRITE,//映射區(qū)域可讀可寫
MAP_SHARED,//映射區(qū)域的寫入數(shù)據(jù)會(huì)寫回到原來的文件
mem_fd,
BASE //被映射的硬件地址
);
close(mem_fd);
return base;
}
成功執(zhí)行時(shí),mmap()函數(shù)返回被映射區(qū)的指針。普通文件被映射到進(jìn)程地址空間后,進(jìn)程可以像訪問普通內(nèi)存一樣對(duì)文件進(jìn)行訪問,不必再調(diào)用read(),write()等操作。只需要使用返回的地址指針在對(duì)應(yīng)的寄存器的偏移地址賦值,就可以完成操作。在例程中已經(jīng)將函數(shù)接口引出(詳細(xì)的代碼請(qǐng)參考例程):
ptr=Timer_Init();//初始化,將定時(shí)器地址映射到用戶進(jìn)程
Timer_Start(ptr, GPIO0, 0xfffffffa); //啟動(dòng)定時(shí)器,并設(shè)置時(shí)間和哪一位GPIO
定時(shí)器是從0計(jì)數(shù)到0xffffffff,需要實(shí)現(xiàn)定時(shí)功能,我們就要改變定時(shí)器的初值,上面的程序中0xfffffffa為定時(shí)器的初值,前面提到過由于EM335x定時(shí)器時(shí)鐘為24MHZ,所以定時(shí)器單位時(shí)間為1/24000000=41.6ns,假設(shè)程序訪問寄存器還需要花費(fèi)時(shí)間T0,在計(jì)算初值的時(shí)候,就需要加上這一部分時(shí)間才能保證準(zhǔn)確性,因此定時(shí)器取值的計(jì)算公式為:
T=0xffffffff-(目標(biāo)延時(shí)/41.6ns)+T0
經(jīng)過測(cè)試,執(zhí)行一次程序訪問寄存器所需花費(fèi)的時(shí)間大約為T0=800ns。舉個(gè)例子,比如目標(biāo)延時(shí)為2μs,那么定時(shí)器初值為:0xffffffff-(2000/41.6)+800,也就是0xffffffe2,測(cè)試的時(shí)候帶入這個(gè)值,再進(jìn)行微調(diào),即可得到想要的結(jié)果。
使用英創(chuàng)工控主板運(yùn)行例程測(cè)試,分別測(cè)試延時(shí)1μs,1.5μs,2μs,5μs,10μs時(shí)的精度,結(jié)果如下:
目標(biāo)延時(shí) | 定時(shí)器取值 | 實(shí)際延時(shí) | |
Min | Max | ||
1us | 0xfffffffa | 1.14us | 1.20us |
1us的測(cè)試波形
目標(biāo)延時(shí) | 定時(shí)器取值 | 實(shí)際延時(shí) | |
Min | Max | ||
1.5us | 0xffffffee | 1.46us | 1.52us |
1.5μs的測(cè)試波形
目標(biāo)延時(shí) | 定時(shí)器取值 | 實(shí)際延時(shí) | |
Min | Max | ||
2us | 0xffffffe2 | 1.90us | 2.08us |
2μs的測(cè)試波形
目標(biāo)延時(shí) | 定時(shí)器取值 | 實(shí)際延時(shí) | |
Min | Max | ||
5us | 0xffffff9a | 4.92us | 5.04us |
5μs的測(cè)試波形
目標(biāo)延時(shí) | 定時(shí)器取值 | 實(shí)際延時(shí) | |
Min | Max | ||
10us | 0xffffff22 | 9.90us | 10.10us |
10μs的測(cè)試波形
可以看到,在1μs時(shí),誤差范圍在±200ns左右,超過1μs,其余的取值,誤差都在±100ns以內(nèi),隨著延時(shí)的增加,精確度將越來越高,在10μs的時(shí)候,誤差已經(jīng)非常小了。
通過以上方案實(shí)現(xiàn)了在用戶進(jìn)程對(duì)精確延時(shí)的操作,詳細(xì)的操作代碼請(qǐng)參考例程。
關(guān)于這一方法在EM9x60系列工控主板上的實(shí)現(xiàn)可閱讀下文:英創(chuàng)嵌入式主板支持精確延時(shí)操作之二
注意事項(xiàng):我們推薦客戶直接使用例程中引出的接口進(jìn)行操作,不推薦客戶對(duì)硬件訪問這一部分代碼進(jìn)行修改,以免在操作的時(shí)候出現(xiàn)無(wú)法預(yù)估的錯(cuò)誤。
-
Linux
+關(guān)注
關(guān)注
87文章
11322瀏覽量
209864 -
嵌入式主板
+關(guān)注
關(guān)注
7文章
6085瀏覽量
35443
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論