65 linux spi裝置驅動之spi LCD屏驅動
阿新 • • 發佈:2018-12-31
SPI的控制器驅動由平臺裝置與平臺驅動來實現. 驅動後用spi_master物件來描述.在裝置驅動中就可以通過函式spi_write, spi_read, spi_w8r16, spi_w8r8等函式來呼叫控制器.
"include/linux/spi/spi.h"
//讓spi->master指向的控制器物件發出len個位元組資料,資料緩衝區地址由buf指標指向
static inline int spi_write(struct spi_device *spi, const void *buf, size_t len);
//讓spi->master指向的控制器物件接收len個位元組資料,由buf指向指向的資料緩衝區存放
static inline int spi_read(struct spi_device *spi, void *buf, size_t len);
//讓spi->master指向的控制器物件發出資料後再接收資料
int spi_write_then_read(struct spi_device *spi, const void *txbuf, unsigned n_tx,
void *rxbuf, unsigned n_rx);
//讓spi->master控制器物件同時收發8位資料
static inline ssize_t spi_w8r8(struct spi_device *spi, u8 cmd);
//讓spi->master控制器物件同時發8位,接收16位資料.
static inline ssize_t spi_w8r16(struct spi_device *spi, u8 cmd);
////////////////////////////////////////////////////////////////
屏:
流程: 命令/資料 –> spi控制器 —> 屏驅動ic的spi介面 —> ILI9340C(屏的驅動ic) –> 屏
屏的驅動ic的作用:根據接收到的命令和資料,配置屏的時序引數及在屏上刷出相應的畫素資料.
也就是我們只要通過spi介面把屏的命令和資料交給屏的驅動ic即可, 讓驅動IC完成刷屏的操作.
//所有的lcd屏都會用到驅動IC的
//ILI9340C驅動ic內部有配置暫存器,我們需要通過spi介面配置驅動ic內部暫存器的值
模組的引腳與板的連線:
reset --> PA8 //用於復位模組
D/C --> PA7 //通過高低電平來區分資料線上的資料型別, command:0, data:1 .
//command其實就是表示資料線上發過去的是驅動ic內部暫存器的地址
//data表示資料線上發過去的資料就是暫存器要設的值
CS --> spi0_CS0 //片選線
SDI --> spi0_MOSI //資料線,發出驅動ic的暫存器地址和要設定的值
SDO --> spi0_MISO // 如不需要讀取驅動ic暫存器的值,可不接
SCLK --> spi0_CLK //時鐘線
LED --> 3.3v //背光電源
VCC --> 3.3v
GND --> GND
//通過時序圖可得知,模組支援三線/四線的工作方式,四線是用D/C線區分資料線上的資料是暫存器地址或資料. spi的工作時序方式是SPI_MODE_0(CPOL=0, CPHA=0), 也可以得知傳輸是以8位為單位.
//在核心裡描述spi屏裝置,並通過spi_board_info的platform_data提供連線屏reset和D/C引腳的GPIO.
描述裝置的程式碼:
#include <linux/spi/spi.h>
#include <mach/gpio.h>
struct sunxi_spi_config {
int bits_per_word; //8bit
int max_speed_hz; //80MHz
int mode; // pha,pol,LSB,etc..
} sunxi_data = {
8, 10000000, SPI_MODE_0
};
struct myspi_lcd_pdata {
int dc_io;
int reset_io;
}spi_lcd_pdata = {
GPIOA(7), GPIOA(8),
};
struct spi_board_info spi_infos[] = {
{
.modalias = "myspi_lcd",
.platform_data = &spi_lcd_pdata,
.controller_data = &sunxi_data,
.max_speed_hz = 10000000,
.bus_num = 0,
.chip_select = 0,
.mode = SPI_MODE_0,
},
};
static void __init sunxi_dev_init(void)
{
...
// 在最後一行
spi_register_board_info(spi_infos, ARRAY_SIZE(spi_infos));
}
//////////////////////////裝置驅動的實現//////////////////////////////////
店家提供的c51裡初始化屏的驅動程式碼:
void write_command(uchar c) //傳送驅動ic的暫存器地址
{
cs=0;
rs=0; // D/C 低電平
bitdata=c;
sda=bit7;scl=0;scl=1;
sda=bit6;scl=0;scl=1;
sda=bit5;scl=0;scl=1;
sda=bit4;scl=0;scl=1;
sda=bit3;scl=0;scl=1;
sda=bit2;scl=0;scl=1;
sda=bit1;scl=0;scl=1;
sda=bit0;scl=0;scl=1;
cs=1;
}
void write_data(uchar d) //給驅動ic傳輸資料使用
{
cs=0;
rs=1; // D/C 高電平
bitdata=d;
sda=bit7;scl=0;scl=1;
sda=bit6;scl=0;scl=1;
sda=bit5;scl=0;scl=1;
sda=bit4;scl=0;scl=1;
sda=bit3;scl=0;scl=1;
sda=bit2;scl=0;scl=1;
sda=bit1;scl=0;scl=1;
sda=bit0;scl=0;scl=1;
cs=1;
}
void lcd_initial()
{
reset=0;
delay(100);
reset=1;
delay(100);
write_command(0xCB);
write_data(0x39);
write_data(0x2C);
write_data(0x00);
write_data(0x34);
write_data(0x02);
write_command(0xCF);
write_data(0x00);
write_data(0XC1);
write_data(0X30);
write_command(0xE8);
write_data(0x85);
write_data(0x00);
write_data(0x78);
write_command(0xEA);
write_data(0x00);
write_data(0x00);
write_command(0xED);
write_data(0x64);
write_data(0x03);
write_data(0X12);
write_data(0X81);
write_command(0xF7);
write_data(0x20);
write_command(0xC0); //Power control
write_data(0x23); //VRH[5:0]
write_command(0xC1); //Power control
write_data(0x10); //SAP[2:0];BT[3:0]
write_command(0xC5); //VCM control
write_data(0x3e); //¶Ô±È¶Èµ÷œÚ
write_data(0x28);
write_command(0xC7); //VCM control2
write_data(0x86); //--
write_command(0x36); // Memory Access Control
//ŽË²ÎÊýΪºáÆÁÊúÆÁÉšÃ跜ʜÇл»¹ØŒü²ÎÊý
//0x48 0x68ÊúÆÁ
//0x28 0xE8 ºáÆÁ
write_data(0x48); //ÉèÖÃĬÈÏÊúÆÁÉšÃ跜ʜ
write_command(0x3A);
write_data(0x55);
write_command(0xB1);
write_data(0x00);
write_data(0x18);
write_command(0xB6); // Display Function Control
write_data(0x08);
write_data(0x82);
write_data(0x27);
write_command(0xF2); // 3Gamma Function Disable
write_data(0x00);
write_command(0x26); //Gamma curve selected
write_data(0x01);
write_command(0xE0); //Set Gamma
write_data(0x0F);
write_data(0x31);
write_data(0x2B);
write_data(0x0C);
write_data(0x0E);
write_data(0x08);
write_data(0x4E);
write_data(0xF1);
write_data(0x37);
write_data(0x07);
write_data(0x10);
write_data(0x03);
write_data(0x0E);
write_data(0x09);
write_data(0x00);
write_command(0XE1); //Set Gamma
write_data(0x00);
write_data(0x0E);
write_data(0x14);
write_data(0x03);
write_data(0x11);
write_data(0x07);
write_data(0x31);
write_data(0xC1);
write_data(0x48);
write_data(0x08);
write_data(0x0F);
write_data(0x0C);
write_data(0x31);
write_data(0x36);
write_data(0x0F);
write_command(0x11); //Exit Sleep
delay(120);
write_command(0x29); //Display on
write_command(0x2c);
}
///////////////////////////////////////
參考上面驅動程式碼實現的linux裝置驅動:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/spi/spi.h>
#include <linux/delay.h>
#include <linux/gpio.h>
struct myspi_lcd_pdata {
int dc_io;
int reset_io;
};
struct spi_lcd_cmd{
u8 reg_addr; // command
u8 len; //需要從spi_lcd_datas數組裡發出資料位元組數
int delay_ms; //此命令傳送資料完成後,需延時多久
}cmds[] = {
{0xCB, 5, 0},
{0xCF, 3, 0},
{0xEB, 3, 0},
{0xEA, 2, 0},
{0xED, 4, 0},
{0xF7, 1, 0},
{0xC0, 1, 0},
{0xC1, 1, 0},
{0xC5, 2, 0},
{0xC7, 1, 0},
{0x36, 1, 0},
{0x3A, 1, 0},
{0xB1, 2, 0},
{0xB6, 3, 0},
{0xF2, 1, 0},
{0x26, 1, 0},
{0xE0, 15, 0},
{0xE1, 15, 0},
{0x11, 0, 120},
{0x29, 0, 0},
{0x2c, 0, 0},
};
u8 spi_lcd_datas[] = {
0x39, 0x2c, 0x00, 0x34, 0x20, // command: 0xCB要發出的資料
0x00, 0xC1, 0x30, // command: 0xCF
0x85, 0x00, 0x78, // command: 0xEB
0x00, 0x00, // command: 0xEA
0x64, 0x03, 0x12, 0x81, // command: 0xED
0x20, // command: 0xF7
0x23, // command: 0xC0
0x10, // command: 0xC1
0x3e, 0x28, // command: 0xC5
0x86, // command: 0xC7
0x48, // command: 0x36
0x55, // command: 0x3A
0x00, 0x18, // command: 0xB1
0x08, 0x82, 0x27, // command: 0xB6
0x00, // command: 0xF2
0x01, // command: 0x26
0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, 0x4E, 0xF1, 0x37, 0x07, 0x10, 0x03, 0x0E, 0x09, 0x00, //command: 0xE0
0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, 0x31, 0xC1, 0x48, 0x08, 0x0F, 0x0C, 0x31, 0x36, 0x0F, //command: 0xE1
};
void write_command(struct spi_device *spi, u8 cmd)
{
struct myspi_lcd_pdata *pdata = spi->dev.platform_data;
// dc , command:0
gpio_direction_output(pdata->dc_io, 0);
spi_write(spi, &cmd, 1);
}
void write_data(struct spi_device *spi, u8 data)
{
struct myspi_lcd_pdata *pdata = spi->dev.platform_data;
// dc , data:1
gpio_direction_output(pdata->dc_io, 1);
spi_write(spi, &data, 1);
}
//初始化spi_lcd
void spi_lcd_init(struct spi_device *spi)
{
struct myspi_lcd_pdata *pdata = spi->dev.platform_data;
int i, j, n;
// 屏復位
gpio_direction_output(pdata->reset_io, 0);
mdelay(100);
gpio_set_value(pdata->reset_io, 1);
mdelay(100);
n = 0; // n用於記錄資料陣列spi_lcd_datas的位置
//發命令,併發出命令所需的資料
for (i = 0; i < ARRAY_SIZE(cmds); i++) //命令
{
write_command(spi, cmds[i].reg_addr);
for (j = 0; j < cmds[i].len; j++) //發出命令後,需要發出的資料
write_data(spi, spi_lcd_datas[n++]);
if (cmds[i].delay_ms) //如有延時則延時
mdelay(cmds[i].delay_ms);
}
}
//設定要刷屏的開始座標
void addset(struct spi_device *spi, unsigned int x,unsigned int y)
{
write_command(spi, 0x2a); //發出x座標
write_data(spi, x>>8);
write_data(spi, x&0xff);
write_command(spi, 0x2b); //發出y座標
write_data(spi, y>>8);
write_data(spi, y&0xff);
write_command(spi, 0x2c);
}
int myprobe(struct spi_device *spi)
{
struct myspi_lcd_pdata *pdata = spi->dev.platform_data;
int ret;
int x, y;
u16 color0 = 0x001f; // RGB565, blue
u16 color1 = 0xf800; // red
u16 color2 = 0x07e0; // green
u16 color3 = 0xffff; // white
u16 color;
ret = gpio_request(pdata->reset_io, spi->modalias);
if (ret < 0)
goto err0;
ret = gpio_request(pdata->dc_io, spi->modalias);
if (ret < 0)
goto err1;
spi_lcd_init(spi); //初始化屏
addset(spi, 0, 0); //從屏的0,0座標開始刷
//刷屏, 把整屏分成4塊,每塊顏色不同
// gpio_direction_output(pdata->dc_io, 1);
for (y = 0; y < 320; y++)
{
for (x = 0; x < 240; x++)
{
if (x < 120)
color = (y < 160) ? color0 : color1;
else
color = (y < 160) ? color2 : color3;
write_data(spi, color >> 8);
write_data(spi, color & 0xff);
}
}
printk("probe ...%s\n", spi->modalias);
return 0;
err1:
gpio_free(pdata->reset_io);
err0:
return ret;
}
int myremove(struct spi_device *spi)
{
struct myspi_lcd_pdata *pdata = spi->dev.platform_data;
gpio_free(pdata->dc_io);
gpio_free(pdata->reset_io);
printk("%s remove\n", spi->modalias);
return 0;
}
struct spi_device_id ids[] = {
{"myspi_lcd"},
{},
};
struct spi_driver myspi_drv = {
.driver = {
.owner = THIS_MODULE,
.name = "myspi_drv",
},
.probe = myprobe,
.remove = myremove,
.id_table = ids,
};
module_spi_driver(myspi_drv);
MODULE_LICENSE("GPL");
效果圖: