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