剛好手邊有塊Nokia 5110 LCD 就拿它來做測試吧~雖然這塊LCD年份久遠了,但還是很適合來做些小東西玩玩!
先介紹一下這塊LCD上面的接腳 :
| 腳位 | 說明 | 連接腳位 | 
|---|---|---|
| RST | LCD 重置 | PB15 | 
| CS | 選擇腳位 | PB6 | 
| D/C | 資料或命令切換 | PB13 | 
| DIN | 資料輸入 | PA7 | 
| CLK | 系統時鐘 | PA5 | 
| VCC | 電源 | 3.3v | 
| BLC | 背光控制 | PB14 | 
| GND | 接地 | GND | 
Nokia 5110 是使用SPI協議但沒有MISO只有MOSI,所以MISO透過程式模擬就可以了。
上方表是各個腳位的連接,在這邊我使用SPI1所以主要使用是PA5-7,CS透過軟體選擇則是PB6,除了電源與接地外其餘的腳位可以依照個人去更改。
接下來看看LCD的指令有哪些~
可以看到其中功能設置、寫指令、設置RAM XY位置等等,可以先將會使用到的函數先寫出來,方便之後去使用。
| D | E | Mode | 
|---|---|---|
| 0 | 0 | 顯示空白 | 
| 1 | 0 | 普通模式 | 
| 0 | 1 | 打開所有顯示 | 
| 1 | 1 | 反轉 | 
上方功能中的RAM是什麼?可以把它想成顯示pixel的地址,這一塊是48x84的LCD也就是說共有4032個pixel,每一個Pixel都會像下圖一樣排好各有各的地址。
水平尋址於垂直尋址又是什麼呢?下方是整個LCD的RAM格式尋址,在垂直的部分為6x8 = 48,而x的部分則為0-83共84剛好對應到了48x84的LCD。
 

 
 

 
 
 
#define NOP 0x00
#define FunSetVE 0x23 //垂直尋址 擴展模式
#define FunSetHE 0x21 //水平尋址 擴展模式
#define FunSetVS 0x22 //垂直尋址 標準模式
#define FunSetHS 0x20 //水平尋址 標準模式
#define DisplayBlack 0x08//空白模式
#define DisplayNormalk 0x0C //正常模式
#define DisplayALL 0x09 //顯示段全開
#define DisplayInverse 0x0D //反轉模式
//CS_LOW
static void StartSPI(void)
{
    HAL_GPIO_WritePin(GPIOB,GPIO_PIN_6,GPIO_PIN_RESET);
}
//CS_High
static void StopSPI(void)
{
    HAL_GPIO_WritePin(GPIOB,GPIO_PIN_6,GPIO_PIN_SET);
}
//傳送
static void SPI_Tx(uint8_t data)
{
  HAL_SPI_Transmit(&hspi1, &data, 1, 10);
}
//接收
//回傳值為接收到的資料
static uint8_t SPI_Rx(void)
{
  uint8_t retVal;
  HAL_SPI_Receive(&hspi1, &retVal, 1, 10);
  return retVal;
}
void LCD_Writebyte(unsigned char data, unsigned char dc)
{
	StartSPI();
	if(dc==0)
		HAL_GPIO_WritePin(GPIOB,GPIO_PIN_13,RESET); //命令
	else
		HAL_GPIO_WritePin(GPIOB,GPIO_PIN_13,SET); //資料
	SPI_Tx(data);
	StopSPI();
}
void LCD_Init(void)
{
	//Reset
	HAL_GPIO_WritePin(GPIOB,GPIO_PIN_15,RESET); 
	HAL_Delay(1);
	HAL_GPIO_WritePin(GPIOB,GPIO_PIN_15,SET); 
	
	StartSPI();
	HAL_Delay(1);
	StopSPI();
	HAL_Delay(1);
	LCD_Writebyte(FunSetHE, 0); 
	LCD_Writebyte(0xBD,0); 
	LCD_Writebyte(0x13,0);
	LCD_Writebyte(0x13,0);
	LCD_Writebyte(FunSetHS, 0);
	LCD_Writebyte(DisplayNormalk, 0);
}
void LCD_SetPosition(uint8_t X, uint8_t Y)
{
	LCD_Writebyte(0x40 | Y, 0);		// column
	LCD_Writebyte(0x80 | X, 0);    // row
}
void LCD_Clear(void)
{
	uint16_t i;
	LCD_Writebyte(0x0c, 0);
	LCD_Writebyte(0x80, 0);
	for (i = 0; i < 504; i ++)
	{
		LCD_Writebyte(0, 1);
	}
}
看了那麼多函式可能還不太懂如何去將字體顯示在LCD上方,下方這張圖可以搭配看可能會比較好理解,假設希望產生下方英文:
while (1)
{
	HAL_GPIO
	LCD_Clear();
	LCD_Writebyte(0x02, 1);
	LCD_Writebyte(0x02, 1);
	LCD_Writebyte(0x02, 1);
	LCD_Writebyte(0x02, 1);
	LCD_Writebyte(0xFE, 1);
	LCD_SetPosition(8,0);
	LCD_Writebyte(0x82, 1);
	LCD_Writebyte(0x82, 1);
	LCD_Writebyte(0xFE, 1);
	LCD_Writebyte(0x82, 1);
	LCD_Writebyte(0x82, 1);
	LCD_SetPosition(15,0);
	LCD_Writebyte(0xF0, 1);
	LCD_Writebyte(0x48, 1);
	LCD_Writebyte(0x44, 1);
	LCD_Writebyte(0x48, 1);
	LCD_Writebyte(0xF0, 1);
	HAL_Delay(500);
}

上方只是為了好理解如何將文字顯在在LCD上,網路上有些軟體可以直接提取對應的位置,就不用這麼麻煩一個一個去寫,可以直接透過[]數組的方式去顯示就可以了~