TFT屏的接口有SPI,8080/6800并口,RGB,MIPI等許多種,用在單片機上的一般就是前兩種。SPI的好處是簡單,只需要連接六七條線,缺點是速度太慢了。SPI的最高時鐘頻率一般是單片機主頻二分頻,16位彩色時寫一個像素需要33~34個時鐘周期(傳16位數(shù)據(jù)32個周期,協(xié)議消耗增加一兩個周期),分辨率低了還好,320*240的屏刷全屏需要24.6萬時鐘周期,48M主頻時就是51ms左右,已經(jīng)有點嫌慢了。好在SPI可以用DMA方式異步寫屏,CPU占用率低一些。
8080并口就快多了,一般用FSMC+DMA來驅(qū)動,實測最快時平均每個像素只需要12.6個時鐘周期,也可以異步寫屏,但是得用100腳或者144腳的STM32才有FSMC。(DMA用了M2M方式,效率不高,如果不用DMA直接寫,做循環(huán)展開,還能再稍快一點點。)有時我們主要就是驅(qū)動個屏,其他管腳用得不多,有沒有辦法讓48腳的STM32也用上并口屏呢?得試一下。
48腳的STM32F030有完整的PA0-15, PB0-15這兩組16位GPIO, 考慮到PA13和PA14需要作為SWCLK和SWDIO,還是把屏接在GPIOB吧。GPIOA出五個腳接RS/CS/RD/WR/RESET,一路PWM用作背光調(diào)節(jié),PA9和10當串口,PA5~7作為SPI接個W25,再加個W25的CS腳,差不多了。原理圖過于簡單就不貼上來了。
焊好,開機,調(diào)試,顯示一切正常。然而要測試刷屏速度怎么做呢?在F1/F3/F4上都可以打開DWT,然后在清屏前后各讀取一次DWT->CYCCNT的值,相減即可。F0沒有DWT,F(xiàn)030也沒有32位定時器,只好用SysTick代替了。缺點是它的計數(shù)值最大只能到16777215,勉強夠了。至于SysTick中斷怎么辦呢?用TIM16配置成每ms中斷,代替一下吧。
最關鍵的寫屏函數(shù),把它作為回調(diào)函數(shù)傳給TFT驅(qū)動,我們先寫成這樣:
static void fastwrite(void* buf, int count)
{
register unsigned short* ptr = buf;
while(count > 0) {
GPIOA- >BRR = LCD_WR;
GPIOB- >ODR = *ptr++;
GPIOA- >BSRR = LCD_WR;
count--;
}
}
實測結果,刷屏消耗大約1816362個時鐘周期,平均每23.7個時鐘周期寫一個像素。這是什么情況,比SPI只快這么一點?看來是這個循環(huán)效率太低了,展開試試。怎么展開呢,試試達夫設備, 首先把前面三條端口操作做成一個宏readwrite_macro,然后如下:
static void fastwrite(void* buf, register int count)
{
register unsigned short* ptr = buf;
register int n = (count + 7) / 8;
switch(count % 8) {
case 0: do { readwrite_macro(*ptr++);
case 7: readwrite_macro(*ptr++);
case 6: readwrite_macro(*ptr++);
case 5: readwrite_macro(*ptr++);
case 4: readwrite_macro(*ptr++);
case 3: readwrite_macro(*ptr++);
case 2: readwrite_macro(*ptr++);
case 1: readwrite_macro(*ptr++);
} while(--n > 0);
}
看上去很奇怪是不是,do..while還能包在switch里?事實上它工作得很好,現(xiàn)在刷全屏只需要1059652個周期了,平均每13.8周期輸出一個像素,已經(jīng)接近FSMC的水平了。
繼續(xù)優(yōu)化,考慮到有時是寫TFT寄存器,只需要寫一兩個字節(jié)的數(shù)據(jù),對這些情況單獨處理一下,可以做到798990周期刷全屏,平均每像素10.4周期,超過了FSMC的速度。
然而以上這些辦法的缺點是沒法做到異步寫屏,如果全屏都需要刷新、刷新率較高的話,CPU負擔恐怕重了點。能不能設法做到和FSMC+DMA一樣的效果呢?還得再想辦法。
這里的關鍵還是寫端口的三條語句,把WR腳拉低再拉高,太浪費時間了,能不能用PWM來代替呢?正好,WR腳同時也是TIM15的1通道輸出。可以把TIM15配置成PWM,寫好GPIO之后PWM提供WR腳的上升沿。實測了一下,果然可行,不過速度比之前慢了不少,估計是PWM參數(shù)得好好優(yōu)化。
下一步,解決了寫一個數(shù)據(jù),怎么寫多個數(shù)據(jù)呢?思路是用TIM15的溢出事件觸發(fā)DMA傳輸,把新的像素數(shù)據(jù)寫到GPIOB。關鍵是寫夠了得能停下來,得再用上定時器級聯(lián)功能,用TIM15的溢出事件作為子定時器(TIM3)的時鐘源,計數(shù)到預定數(shù)值時,TIM3中斷里把TIM15關掉——這樣可以嗎?不行,中斷太慢了,這樣肯定會寫多。感覺可以用TIM3的溢出或者匹配事件再觸發(fā)一次DMA傳輸,寫TIM15的CR1寄存器,把TIM15關掉。這樣應該就可以做成和FSMC+DMA一樣的效果了。不過具體操作起來麻煩事還很多。
評論
查看更多