操作OLED,通過三條線(SCK、DO、CS)與OLED相連,這里沒有DI是因為2440只會向OLED傳數據而不用接收數據。
gpio_spi.c來實現gpio模擬spi,負責spi通訊。對於OLED,有專門的指令和數據格式,要傳輸的數據內容,在oled.c這一層來實現,負責組織數據。
因此,我們需要實現以上兩個文件。

1.SPI初始化
新建一個gpio_spi.c文件,實現SPI初始化SPIInt()

1.1 GPIO init(pinmux管腳等配置)
上圖J3為板子pin2pin到OLED的底座。
GPF1作為OLED片選引腳,設置為輸出;
GPG4作為OLED的數據(Data)/命令(Command)選擇引腳,設置為輸出;
GPG5作為SPI的MISO,設置為輸入(實際用不到);
GPG6作為SPI的MOSI,設置為輸出;
GPG7作為SPI的時鍾CLK,設置為輸出;


根據gpio相關寄存器進行配置如下:用gpio配置成spi使用的各個引腳。
void SPIInit(void){
/* 初始化引腳 */
SPI_GPIO_Init();
}
static void SPI_GPIO_Init(void){
/* GPF1 as OLED_CSn output */
GPFCON &= ~(3<<(1*2));
GPFCON |= (1<<(1*2));
GPFDAT |= (1<<1);//取消OLED_CSn片選,pull up
/* GPG2 FLASH_CSn output
* GPG4 OLED_DC output
* GPG5 SPIMISO input
* GPG6 SPIMOSI output
* GPG7 SPICLK output
*/
GPGCON &= ~((3<<(2*2)) | (3<<(4*2)) | (3<<(5*2)) | (3<<(6*2)) | (3<<(7*2)));
GPGCON |= ((1<<(2*2)) | (1<<(4*2)) | (1<<(6*2)) | (1<<(7*2)));
GPGDAT |= (1<<2);//取消FLASH_CSn 片選,pull up
}
2.OLED初始化
再新建一個oled.c文件,以實現初始化OLEDInit(),這里就對應power up sequence。
void OLEDInit(void){
/* 向OLED發命令以初始化 */
OLEDWriteCmd(0xAE); /*display off*/
OLEDWriteCmd(0x00); /*set lower column address*/
OLEDWriteCmd(0x10); /*set higher column address*/
OLEDWriteCmd(0x40); /*set display start line*/
OLEDWriteCmd(0xB0); /*set page address*/
OLEDWriteCmd(0x81); /*contract control*/
OLEDWriteCmd(0x66); /*128*/
OLEDWriteCmd(0xA1); /*set segment remap*/
OLEDWriteCmd(0xA6); /*normal / reverse*/
OLEDWriteCmd(0xA8); /*multiplex ratio*/
OLEDWriteCmd(0x3F); /*duty = 1/64*/
OLEDWriteCmd(0xC8); /*Com scan direction*/
OLEDWriteCmd(0xD3); /*set display offset*/
OLEDWriteCmd(0x00);
OLEDWriteCmd(0xD5); /*set osc division*/
OLEDWriteCmd(0x80);
OLEDWriteCmd(0xD9); /*set pre-charge period*/
OLEDWriteCmd(0x1f);
OLEDWriteCmd(0xDA); /*set COM pins*/
OLEDWriteCmd(0x12);
OLEDWriteCmd(0xdb); /*set vcomh*/
OLEDWriteCmd(0x30);
OLEDWriteCmd(0x8d); /*set charge pump enable*/
OLEDWriteCmd(0x14);
}
D/C即數據(Data)/命令(Command)選擇引腳,它為高電平時,OLED即認為收到的是數據;它為低電平時,OLED即認為收到的是命令。先設置為命令模式,再片選OLED,再傳輸命令,再恢復成原來的模式和取消片選。
2.1 實現OLED寫功能
寫命令和寫數據:

static void OLEDWriteCmd(unsigned char cmd){
OLED_Set_DC(0); /* command */
OLED_Set_CS(0); /* select OLED */
SPISendByte(cmd);
OLED_Set_CS(1); /* de-select OLED */
OLED_Set_DC(1); /* gpio output default is pull up*/
}
static void OLEDWriteDat(unsigned char data){
OLED_Set_DC(1); /* data*/
OLED_Set_CS(0); /* select OLED */
SPISendByte(data);
OLED_Set_CS(1); /* de-select OLED */
}
命令模式和片選就是單純的gpio操作,非常簡單如下:
static void OLED_Set_DC(char val){
if (val)
GPGDAT |= (1<<4);
else
GPGDAT &= ~(1<<4);
}
static void OLED_Set_CS(char val){
if (val)
GPFDAT |= (1<<1);
else
GPFDAT &= ~(1<<1);
}
2.2 SPISendByte()
還剩下SPISendByte()函數,它屬於SPI協議,放在gpio_spi.c里面:
void SPISendByte(unsigned char val){
int i;
for (i = 0; i < 8; i++){
SPI_Set_CLK(0);
SPI_Set_DO(val & 0x80);//MSB
SPI_Set_CLK(1);
val <<= 1;
}
}
static void SPI_Set_CLK(char val){
if (val)
GPGDAT |= (1<<7);
else
GPGDAT &= ~(1<<7);
}
static void SPI_Set_DO(char val){
if (val)
GPGDAT |= (1<<6);
else
GPGDAT &= ~(1<<6);
}
發送數據要滿足SPI的時序要求,參考前面的介紹:


SPISendByte是把一個byte數據從高位往低位依次發送到DO。spi配置模式0, 主控先設置CLK為低,由於是MSB, 先傳送高位,然后CLK為高,在CLK這個上升沿,DO的數據被鎖存,OLED就讀取了一位數據。接着左移一位,傳輸下一位。通過SPI_Set_CLK()和SPI_Set_DO()配置SCK和DO的時序,用gpio模擬出了spi。至此,SPI初始化和OLED初始化就基本完成了,接下來就是OLED顯示部分。
這里gpio模擬spi傳送時主控沒有加延時控制SCK的頻率,那是由於jz2440本身cpu運行就很慢,這里不延時也是能滿足該款外設的spi傳輸時序,如果cpu很快,那么需要控制spi時序。
3.驅動顯示OLED
如何在OLED上顯示一個字符?根據前面一節OLED面板的顯示原理。代碼實現如下:
static void OLEDSetPos(int page, int col){
OLEDWriteCmd(0xB0 + page); /* page address */
OLEDWriteCmd(col & 0xf); /* Lower Column Start Address */
OLEDWriteCmd(0x10 + (col >> 4)); /* Lower Higher Start Address */
}
/* page: 0-7
* col : 0-127
* 字符: 8x16象素
*/
void OLEDPutChar(int page, int col, char c){
int i = 0;
/* 得到字模 */
const unsigned char *dots = oled_asc2_8x16[c - ' '];
/* 發給OLED */
OLEDSetPos(page, col);
/* 發出8字節數據 */
for (i = 0; i < 8; i++)
OLEDWriteDat(dots[i]);
OLEDSetPos(page+1, col);
/* 發出8字節數據 */
for (i = 0; i < 8; i++)
OLEDWriteDat(dots[i+8]);
}
/* page: 0-7
* col : 0-127
* 字符: 8x16象素
*/
void OLEDPrint(int page, int col, char *str){
int i = 0;
while (str[i]){
OLEDPutChar(page, col, str[i]);
col += 8;
if (col > 127) {
col = 0;
page += 2;
}
i++;
}
}
static void OLEDSetPos(int page, int col){
OLEDWriteCmd(0xB0 + page); /* page address */
OLEDWriteCmd(col & 0xf); /* Lower Column Start Address */
OLEDWriteCmd(0x10 + (col >> 4)); /* Lower Higher Start Address */
}
static void OLEDClear(void){
int page, i;
for (page = 0; page < 8; page ++){
OLEDSetPos(page, 0);
for (i = 0; i < 128; i++)
OLEDWriteDat(0);
}
}
完整代碼如下:
/************************** gpio_spi.c ****************/
#include "s3c24xx.h"
/* 用GPIO模擬SPI */
static void SPI_GPIO_Init(void)
{
/* GPF1 OLED_CSn output */
GPFCON &= ~(3<<(1*2));
GPFCON |= (1<<(1*2));
GPFDAT |= (1<<1);
/* GPG2 FLASH_CSn output
* GPG4 OLED_DC output
* GPG5 SPIMISO input
* GPG6 SPIMOSI output
* GPG7 SPICLK output
*/
GPGCON &= ~((3<<(2*2)) | (3<<(4*2)) | (3<<(5*2)) | (3<<(6*2)) | (3<<(7*2)));
GPGCON |= ((1<<(2*2)) | (1<<(4*2)) | (1<<(6*2)) | (1<<(7*2)));
GPGDAT |= (1<<2);
}
static void SPI_Set_CLK(char val)
{
if (val)
GPGDAT |= (1<<7);
else
GPGDAT &= ~(1<<7);
}
static void SPI_Set_DO(char val)
{
if (val)
GPGDAT |= (1<<6);
else
GPGDAT &= ~(1<<6);
}
void SPISendByte(unsigned char val)
{
int i;
for (i = 0; i < 8; i++)
{
SPI_Set_CLK(0);
SPI_Set_DO(val & 0x80);
SPI_Set_CLK(1);
val <<= 1;
}
}
void SPIInit(void)
{
/* 初始化引腳 */
SPI_GPIO_Init();
}
/******************* oled.c****************/
#include "oledfont.h"
#include "gpio_spi.h"
#include "s3c24xx.h"
static void OLED_Set_DC(char val)
{
if (val)
GPGDAT |= (1<<4);
else
GPGDAT &= ~(1<<4);
}
static void OLED_Set_CS(char val)
{
if (val)
GPFDAT |= (1<<1);
else
GPFDAT &= ~(1<<1);
}
static void OLEDWriteCmd(unsigned char cmd)
{
OLED_Set_DC(0); /* command */
OLED_Set_CS(0); /* select OLED */
SPISendByte(cmd);
OLED_Set_CS(1); /* de-select OLED */
OLED_Set_DC(1); /* */
}
static void OLEDWriteDat(unsigned char dat)
{
OLED_Set_DC(1); /* data */
OLED_Set_CS(0); /* select OLED */
SPISendByte(dat);
OLED_Set_CS(1); /* de-select OLED */
OLED_Set_DC(1); /* */
}
static void OLEDSetPageAddrMode(void)
{
OLEDWriteCmd(0x20);
OLEDWriteCmd(0x02);
}
static void OLEDSetPos(int page, int col)
{
OLEDWriteCmd(0xB0 + page); /* page address */
OLEDWriteCmd(col & 0xf); /* Lower Column Start Address */
OLEDWriteCmd(0x10 + (col >> 4)); /* Lower Higher Start Address */
}
static void OLEDClear(void)
{
int page, i;
for (page = 0; page < 8; page ++)
{
OLEDSetPos(page, 0);
for (i = 0; i < 128; i++)
OLEDWriteDat(0);
}
}
void OLEDInit(void)
{
/* 向OLED發命令以初始化 */
OLEDWriteCmd(0xAE); /*display off*/
OLEDWriteCmd(0x00); /*set lower column address*/
OLEDWriteCmd(0x10); /*set higher column address*/
OLEDWriteCmd(0x40); /*set display start line*/
OLEDWriteCmd(0xB0); /*set page address*/
OLEDWriteCmd(0x81); /*contract control*/
OLEDWriteCmd(0x66); /*128*/
OLEDWriteCmd(0xA1); /*set segment remap*/
OLEDWriteCmd(0xA6); /*normal / reverse*/
OLEDWriteCmd(0xA8); /*multiplex ratio*/
OLEDWriteCmd(0x3F); /*duty = 1/64*/
OLEDWriteCmd(0xC8); /*Com scan direction*/
OLEDWriteCmd(0xD3); /*set display offset*/
OLEDWriteCmd(0x00);
OLEDWriteCmd(0xD5); /*set osc division*/
OLEDWriteCmd(0x80);
OLEDWriteCmd(0xD9); /*set pre-charge period*/
OLEDWriteCmd(0x1f);
OLEDWriteCmd(0xDA); /*set COM pins*/
OLEDWriteCmd(0x12);
OLEDWriteCmd(0xdb); /*set vcomh*/
OLEDWriteCmd(0x30);
OLEDWriteCmd(0x8d); /*set charge pump enable*/
OLEDWriteCmd(0x14);
OLEDSetPageAddrMode();
OLEDClear();
OLEDWriteCmd(0xAF); /*display ON*/
}
/* page: 0-7
* col : 0-127
* 字符: 8x16象素
*/
void OLEDPutChar(int page, int col, char c)
{
int i = 0;
/* 得到字模 */
const unsigned char *dots = oled_asc2_8x16[c - ' '];
/* 發給OLED */
OLEDSetPos(page, col);
/* 發出8字節數據 */
for (i = 0; i < 8; i++)
OLEDWriteDat(dots[i]);
OLEDSetPos(page+1, col);
/* 發出8字節數據 */
for (i = 0; i < 8; i++)
OLEDWriteDat(dots[i+8]);
}
/* page: 0-7
* col : 0-127
* 字符: 8x16象素
*/
void OLEDPrint(int page, int col, char *str)
{
int i = 0;
while (str[i])
{
OLEDPutChar(page, col, str[i]);
col += 8;
if (col > 127)
{
col = 0;
page += 2;
}
i++;
}
}
