導(dǎo)讀: 本文詳細(xì)介紹了,如何在Apollo2 SDK-1.2.12平臺上點亮并使用Heletc 1.3寸12864-OLED屏幕。本文將闡述,如何通過硬件SPI與模擬SPI模式,分別實現(xiàn)外設(shè)OLED屏的驅(qū)動代碼和實現(xiàn)步驟。
1.SPI通信原理
SPI是串行外設(shè)接口(Serial Peripheral Interface)的縮寫。是 Motorola 公司推出的一種同步串行接口技術(shù),是一種高速的,全雙工,同步的通信總線。
SPI的通信原理很簡單,它以主從方式工作,這種模式通常有一個主設(shè)備和一個或多個從設(shè)備,需要至少4根線,事實上3根也可以(單向傳輸時)。也是所有基于SPI的設(shè)備共有的,它們是SDI(數(shù)據(jù)輸入)、SDO(數(shù)據(jù)輸出)、SCLK(時鐘)、CS(片選)。
(1)MOSI/SDI –SerialDataIn,串行數(shù)據(jù)輸入;
(2)MISO/SDO –SerialDataOut,串行數(shù)據(jù)輸出;
(3)SCLK – Serial Clock,時鐘信號,由主設(shè)備產(chǎn)生;
(4)CS – Chip Select,從設(shè)備使能信號,由主設(shè)備控制。
其中,CS是從芯片是否被主芯片選中的控制信號,也就是說只有片選信號為預(yù)先規(guī)定的使能信號時(高電位或低電位),主芯片對此從芯片的操作才有效。這就使在同一條總線上連接多個SPI設(shè)備成為可能。
接下來就負(fù)責(zé)通訊的3根線了。通訊是通過數(shù)據(jù)交換完成的,這里先要知道SPI是串行通訊協(xié)議,也就是說數(shù)據(jù)是一位一位的傳輸?shù)?。這就是SCLK時鐘線存在的原因,由SCLK提供時鐘脈沖,SDI,SDO則基于此脈沖完成數(shù)據(jù)傳輸。數(shù)據(jù)輸出通過 SDO線,數(shù)據(jù)在時鐘上升沿或下降沿時改變,在緊接著的下降沿或上升沿被讀取。完成一位數(shù)據(jù)傳輸,輸入也使用同樣原理。因此,至少需要8次時鐘信號的改變(上沿和下沿為一次),才能完成8位數(shù)據(jù)的傳輸。
(圖1)SPI通信結(jié)構(gòu)圖
(圖2)SPI常規(guī)讀操作
(圖3)SPI常規(guī)寫操作
2.SPI的四種模式
根據(jù)SPI時鐘極性(CPOL)和時鐘相位(CPHA)配置的不同可分為4種模式。
時鐘極性是指SPI通信設(shè)備處于空閑狀態(tài)時(或SPI通信開始時,即SS為低電平時),SCK的電平信號CPOL=0時,SCK空閑狀態(tài)為低電平,CPOL=1時則相反。
時鐘相位是指數(shù)據(jù)采樣的時刻,當(dāng)CPHA=0時,MOSI或MISO數(shù)據(jù)線會在時鐘線第一個邊沿開始采樣(奇數(shù)邊沿)。
當(dāng)CPHA=1時,MOSI或MISO數(shù)據(jù)線會在時鐘線第二個邊沿開始采樣(偶數(shù)邊沿)。
詳細(xì)如下:
(1)CPOL=0,CPHA=0:此時空閑態(tài)時,SCLK處于低電平,數(shù)據(jù)采樣是在第1個邊沿,也就是SCLK由低電平到高電平的跳變,所以數(shù)據(jù)采樣是在上升沿,數(shù)據(jù)發(fā)送是在下降沿。
(2)CPOL=0,CPHA=1:此時空閑態(tài)時,SCLK處于低電平,數(shù)據(jù)發(fā)送是在第1個邊沿,也就是SCLK由低電平到高電平的跳變,所以數(shù)據(jù)采樣是在下降沿,數(shù)據(jù)發(fā)送是在上升沿。
(3)CPOL=1,CPHA=0:此時空閑態(tài)時,SCLK處于高電平,數(shù)據(jù)采集是在第1個邊沿,也就是SCLK由高電平到低電平的跳變,所以數(shù)據(jù)采集是在下降沿,數(shù)據(jù)發(fā)送是在上升沿。
(4)CPOL=1,CPHA=1:此時空閑態(tài)時,SCLK處于高電平,數(shù)據(jù)發(fā)送是在第1個邊沿,也就是SCLK由高電平到低電平的跳變,所以數(shù)據(jù)采集是在上升沿,數(shù)據(jù)發(fā)送是在下降沿。
(圖4)SPI的CPOL和CPHA
3.硬件SPI與模擬SPI的區(qū)別
在模擬SPI的模式下,我們需要使用IO口去模擬SPI的時序,這個模擬的全部過程,都需要CPU全程負(fù)責(zé),但在獲取或者發(fā)送數(shù)據(jù)的時候,可能會使用軟件延時,這個時間在數(shù)據(jù)交互量不大的情況下并明顯,但是如果數(shù)據(jù)量大,可能會打亂SPI的時序。
對于硬件SPI來說,我們只需要開啟相應(yīng)的寄存器配置和對應(yīng)的中斷。數(shù)據(jù)的交互就不需要CPU參與。當(dāng)傳輸中斷產(chǎn)生的時候,CPU只需要從中斷中搬運數(shù)據(jù)就好了,省下了軟件模擬IO的存取時間。讓CPU省下更多時間去運行其他代碼。
4.硬件SPI的配置
首先我們需要確定OLED屏幕上面的引腳,如圖:
(圖5)OLED硬件管腳圖
GND - 接地 VCC – 接3.3V
SCL – 接SCK(5腳) SDA – 接MOSI(7腳)
RST – 接 42腳(可修改)DC – 接43腳(可修改)
作為Master模式下,提供有6組IO口供用戶選擇,而作為Slave有1組。在Master模式下,Apollo提供一個128-byte 的local RAM作為雙向FIFO的傳輸容量。Apollo2的管腳復(fù)用具體如下:
(圖6)Apollo2全部引腳寄存器配置圖
(圖7)Apollo2 引腳顏色比對圖
第一步,我們選擇Master 0 Signals 也就是相對于的 5、6、7引腳。
具體通過am_hal_gpio_pin_config()函數(shù)進(jìn)行引腳配置
第二步,配置iom_config
在SPI_g_sIOMConfig里面配置的是IOM的一些常規(guī)參數(shù):
模式我們選擇為AM_HAL_IOM_SPIMODE 傳輸速率為 100KHZ,相位和極性都是0 。寫數(shù)據(jù)的閾值是4bit,讀取是60bit。這兩個是生產(chǎn)中斷的條件。
最后記得開啟IO Master
第三步,對屏幕進(jìn)行復(fù)位操作,而復(fù)位操作主要是改變RST引腳的高低電平。
通過Apollo2 SDK提供的API去修改IO口狀態(tài)。
am_hal_gpio_out_bit_clear()置0 am_hal_gpio_out_bit_set()置1
第四步,通過SPI通信,將指令或者數(shù)據(jù)傳輸?shù)?/span>OLED屏幕中。OLED屏幕判斷指令還是數(shù)據(jù),是通過DC引腳的高低電平實現(xiàn)的。所以需要有一個參數(shù)去控制 43引腳的狀態(tài)。代碼如下:
數(shù)據(jù)先傳輸進(jìn)Buffer,通過判斷cmd 控制DC_Set/ DC_Clr
然后通過am_hal_iom_spi_write()函數(shù),將數(shù)據(jù)直接傳輸?shù)酵庠O(shè)。
am_hal_iom_spi_write(uint32_t ui32Module, uint32_t ui32ChipSelect,
uint32_t *pui32Data, uint32_t ui32NumBytes,
uint32_t ui32Options)
{
am_hal_iom_status_e ui32Status;
//
// Validate parameters
//
if ( ui32Module >= AM_REG_IOMSTR_NUM_MODULES )
{
return AM_HAL_IOM_ERR_INVALID_MODULE;
}
// Reset the error status
ui32Status = g_iom_error_status[ui32Module] = AM_HAL_IOM_SUCCESS;
if (ui32NumBytes == 0)
{
g_iom_error_status[ui32Module] = ui32Status = AM_HAL_IOM_ERR_INVALID_PARAM;
return ui32Status;
}
//
// Check to see if queues have been enabled. If they are, we'll actually
// switch to the queued interface.
//
if ( g_psIOMQueue[ui32Module].pui8Data != NULL )
{
//
// If the queue is on, go ahead and add this transaction to the queue.
//
ui32Status = am_hal_iom_queue_spi_write(ui32Module, ui32ChipSelect, pui32Data,
ui32NumBytes, ui32Options, 0);
if (ui32Status == AM_HAL_IOM_SUCCESS)
{
//
// Wait until the transaction actually clears.
//
am_hal_iom_queue_flush(ui32Module);
// g_iom_error_status gets set in the isr handling
ui32Status = g_iom_error_status[ui32Module];
}
//
// At this point, we've completed the transaction, and we can return.
//
}
else
{
//
// Otherwise, we'll just do a polled transaction.
//
ui32Status = am_hal_iom_spi_write_nq(ui32Module, ui32ChipSelect, pui32Data,
ui32NumBytes, ui32Options);
}
return ui32Status;
}
該函數(shù)的幾個參數(shù)定義分別如下:
(1)ui32Module – IOM的Master編號選擇
(2)ui32ChipSelect – 外設(shè)編號選擇
(3)pui32Data – 傳輸?shù)臄?shù)據(jù)
(4)ui32NumBytes –傳輸數(shù)據(jù)的大小
(5)ui32Options – 寄存器偏移量
這里可以根據(jù)實際情況去配置各個參數(shù),從而達(dá)到傳輸數(shù)據(jù)的目的。至此,硬件SPI模式基本配置完成。
5.模擬SPI配置
模擬SPI基本與硬件SPI類似。使用任意兩個IO口模擬通信,不需要使用指定的SPI接口,也不需要響應(yīng)的SPI配置。
第一步,IO口的配置大概如下:
四個引腳如下:SCL – 8腳; SDA – 9腳;RES – 42腳;DC – 43腳 同樣需要配置各個IO口的狀態(tài):
模擬SPI與硬件SPI的最主要區(qū)別是在寫函數(shù)里面。使用一個引腳模擬時鐘,另外一個引腳發(fā)送數(shù)據(jù)。
第二步,發(fā)送函數(shù):
判斷cmd的操作是必不可少的,接著判斷(dat & 0x80) 判斷高位是否為‘1’。dat 高位為‘1’,MOSI就輸出‘1’;否則輸出‘0’。然后移位,次高位變?yōu)樽罡呶?。就是?/span>dat的數(shù)據(jù)從MOSI腳輸出。
6.了解OLED屏幕
有機發(fā)光顯示OLED(OrganicLight?EmittingDisplay)是比液晶顯示技術(shù)更為先進(jìn)的新一代平板顯示技術(shù),是被業(yè)界公認(rèn)為最具發(fā)展前景的下一代顯示技術(shù)。
OLED12864是128*64行點陣的OLED單色,字符,圖形顯示模塊,模塊內(nèi)有64*64的顯示數(shù)據(jù)RAM,其中的每位數(shù)據(jù)對應(yīng)于OLED屏上的每一個點的亮,暗狀態(tài)。
12864OLED的像素矩陣的劃分是比較特殊的。 整個屏幕水平方向劃分為8個page, 垂直方向則是按像素劃分為128 column. 每個page-column包含8個像素, 通過一個十六進(jìn)制數(shù)(其實就是一個字節(jié), 8個bit)來控制, 每個bit控制一個像素。
(圖8-1)OLED屏幕像素矩陣
(圖8-2)OLED屏幕像素矩陣
7.OLED屏幕配置
與大部分12864 OLED屏幕一樣,需要提前給屏幕輸入特定的顯示指令。代碼如家,需要注意的是硬件SPI與模擬SPI選擇自己響應(yīng)的OLED_WR_Byte()即可。配置過程如下:
OLED_WR_Byte(0xAE,OLED_CMD);//--turn off oled panel
OLED_WR_Byte(0x00,OLED_CMD);//---set low column address
OLED_WR_Byte(0x10,OLED_CMD);//---set high column address
OLED_WR_Byte(0x40,OLED_CMD);//--set start line address Set Mapping RAM Display Start Line (0x00~0x3F)
OLED_WR_Byte(0x81,OLED_CMD);//--set contrast control register
OLED_WR_Byte(0xCF,OLED_CMD); // Set SEG Output Current Brightness
OLED_WR_Byte(0xA1,OLED_CMD);//--Set SEG/Column Mapping 0xa0×óóò·′?? 0xa1?y3£
OLED_WR_Byte(0xC8,OLED_CMD);//Set COM/Row Scan Direction 0xc0é???·′?? 0xc8?y3£
OLED_WR_Byte(0xA6,OLED_CMD);//--set normal display
OLED_WR_Byte(0xA8,OLED_CMD);//--set multiplex ratio(1 to 64)
OLED_WR_Byte(0x3f,OLED_CMD);//--1/64 duty
OLED_WR_Byte(0xD3,OLED_CMD);//-set display offset Shift Mapping RAM Counter (0x00~0x3F)
OLED_WR_Byte(0x00,OLED_CMD);//-not offset
OLED_WR_Byte(0xd5,OLED_CMD);//--set display clock divide ratio/oscillator frequency
OLED_WR_Byte(0x80,OLED_CMD);//--set divide ratio, Set Clock as 100 Frames/Sec
OLED_WR_Byte(0xD9,OLED_CMD);//--set pre-charge period
OLED_WR_Byte(0xF1,OLED_CMD);//Set Pre-Charge as 15 Clocks & Discharge as 1 Clock
OLED_WR_Byte(0xDA,OLED_CMD);//--set com pins hardware configuration
OLED_WR_Byte(0x12,OLED_CMD);
OLED_WR_Byte(0xDB,OLED_CMD);//--set vcomh
OLED_WR_Byte(0x40,OLED_CMD);//Set VCOM Deselect Level
OLED_WR_Byte(0x20,OLED_CMD);//-Set Page Addressing Mode (0x00/0x01/0x02)
OLED_WR_Byte(0x02,OLED_CMD);//
OLED_WR_Byte(0x8D,OLED_CMD);//--set Charge Pump enable/disable
OLED_WR_Byte(0x14,OLED_CMD);//--set(0x10) disable
OLED_WR_Byte(0xA4,OLED_CMD);// Disable Entire Display On (0xa4/0xa5)
OLED_WR_Byte(0xA6,OLED_CMD);// Disable Inverse Display On (0xa6/a7)
OLED_WR_Byte(0xAF,OLED_CMD);//--turn on oled panel
OLED_WR_Byte(0xAF,OLED_CMD); /*display ON*/
OLED_Clear();
OLED_Set_Pos(0,0);
將上述代碼發(fā)送給OLED屏幕之后,屏幕的初始化也基本完成
8.OLED屏幕文字顯示
下面是使用OLED顯示中文漢字的范例。代碼如下
OLED_Set_Pos()函數(shù)是用來定位文字在屏幕上顯示的位置。文字的大小是16*16個像素點,所以for()循環(huán)里面的t為16次。Hzk[ ][ ]數(shù)組里面存儲的是通過取模軟件,將文字轉(zhuǎn)換成16進(jìn)制的數(shù)據(jù)。
取模工具選用PCtolCD2002 完美版本,這里選擇字符模式。輸入文字的時候,可以點擊生成字模,在下方就會顯示出各個文字相對應(yīng)的16進(jìn)制數(shù)組。
(圖9)文字取模
將各個數(shù)組分別添加到Hzk數(shù)組里面之后,就可以在主函數(shù)里面通過OLED_ShowCHinese()進(jìn)行顯示。
顯示效果如下圖:
(圖10)文字顯示效果
9.OLED屏幕圖片顯示
照片顯示和文字顯示原理相同,也是點亮相對應(yīng)的像素點。
該函數(shù)當(dāng)中,x0 ,y0表示的是圖片所顯示的的起始坐標(biāo),x1 表示的是圖片像素的x軸所占的像素,y1表示的是頁數(shù)(0-7)
將想要顯示的圖片,轉(zhuǎn)換成BMP格式之后,通過PCtolCD2002的圖片模式進(jìn)行轉(zhuǎn)換。設(shè)置如下:選擇陰碼,逆向,十六進(jìn)制輸出。
(圖11)圖片取模選項設(shè)置
(圖12)圖片取模界面
將所生成的十六進(jìn)制數(shù)據(jù)進(jìn)行,修改對齊后,裝到BMP數(shù)組當(dāng)中。在主函數(shù)當(dāng)中就可以直接顯示了,效果如下圖。
(圖13)圖片顯示效果
后記:
在調(diào)試當(dāng)中遇到的一些需要注意的點:
1、 在freeRTOS下,使用OLED屏幕的話,需要預(yù)先啟用SPI相對于的IOM 口。
需要用到函數(shù)am_hal_pwrctrl_periph_enable(uint32_t ui32Peripheral)進(jìn)行相應(yīng)的配置。同理在進(jìn)行低功耗處理的時候,可以關(guān)閉SPI接口。
am_hal_pwrctrl_periph_enable(uint32_t ui32Peripheral)
{
am_hal_debug_assert_msg(ONE_BIT(ui32Peripheral),
"Cannot enable more than one peripheral at a time.");
//
// Begin critical section.
//
AM_CRITICAL_BEGIN_ASM
//
// Enable power control for the given device.
//
AM_REG(PWRCTRL, DEVICEEN) |= ui32Peripheral;
//
// End Critical Section.
//
AM_CRITICAL_END_ASM
//
// Wait for the power to stablize. Using a simple delay loop is more
// power efficient than a polling loop.
//
am_hal_flash_delay(AM_HAL_PWRCTRL_DEVICEEN_DELAYCYCLES / 3);
//
// Quick check to guarantee we're good (should never be more than 1 read).
//
POLL_PWRSTATUS(ui32Peripheral);
}
2、 OLED屏幕在進(jìn)行Clear的時候,屏幕不干凈
(圖14)屏幕刷新有殘留
原因是:寫到寄存器第一列和第二列的數(shù)據(jù)被驅(qū)動芯片當(dāng)做亂碼并在 顯示屏的最后一列顯示出來。修改如下:
把程序中“所有 X”軸的值改成 132,如下圖定義的是 X 軸與 Y 軸。
同時,clear函數(shù)里面,循環(huán)發(fā)送數(shù)據(jù)的次數(shù),也應(yīng)該從n<128 改為 n<132。
3、 圖片顯示函數(shù)
void OLED_DrawBMP(unsigned char x0, unsigned char y0,unsigned char x1,
unsigned char y1,unsigned char BMP[])
{
unsigned int j=0;
unsigned char x,y;
if(y1%8==0) y=y1/8;
else y=y1/8+1;
for(y=y0;y
{
OLED_Set_Pos(x0,y);
for(x=x0;x
{
OLED_WR_Byte(BMP[j++],OLED_DATA);
}
}
}
在使用該函數(shù)的時候,首先要注意的是,y1表示的是頁數(shù)(0-7)也就是前文指的page,而不是你所給照片取模的Y軸的像素。其次在照片取模當(dāng)中,盡量控制為128*64的大小。如果圖片大小無法滿足,則在使用OLED_DrawBMP()的時候,x與x1的數(shù)據(jù)差應(yīng)該就是你圖片的像素大小。
例如:
我的照片大小為 (92 X 60 ),如果選擇屏幕顯示的起始坐標(biāo)點為(0,0)
則函數(shù)為:OLED_DrawBMP(0,0,92,7,BMP);
但是當(dāng)你想移動圖片的位置,修改為OLED_DrawBMP(18,0,92,7,BMP);顯示的結(jié)果會出現(xiàn)亂碼。
正確的方式是改為OLED_DrawBMP(18,0,110,7,BMP);
-
OLED屏幕
+關(guān)注
關(guān)注
3文章
206瀏覽量
27961
發(fā)布評論請先 登錄
相關(guān)推薦
評論