F1C200S构建st7735串行显示屏fb驱动
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
#include <linux/fb.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/kthread.h>
#include <linux/dma-mapping.h>
#define LCD_W 160
#define LCD_H 80
#define REFRESH_TIME 50
struct st7735s_st
{
int gpio_dc;
int gpio_rst;
struct device_node *node;
struct spi_device *spi;
struct fb_info *fbi;
struct task_struct *thread;
u16 rgbdata[LCD_W*LCD_H];
};
static struct st7735s_st st7735sDev;
static void show_fb(struct st7735s_st *me);
static int thread_func(void *data)
{
struct st7735s_st *me = data;
while (1)
{
if (kthread_should_stop()) break;
show_fb(me);
msleep(REFRESH_TIME);
}
return 0;
}
//可以自定义,为空则使用内核提供的函数
static struct fb_ops fops = {};
static int myfb_new(struct st7735s_st *dev)
{
u8 *v_addr =NULL;
u32 p_addr;
//必须先设置coherent_dma_mask
if (!dev->spi->dev.coherent_dma_mask)
dev->spi->dev.coherent_dma_mask = DMA_BIT_MASK(32);
//申请DMA内存
v_addr = dma_alloc_coherent(&dev->spi->dev, LCD_W*LCD_H*4, &p_addr, GFP_KERNEL);
if(v_addr ==NULL)
{
printk("dma_alloc_coherent error\r\n");
return -ENOMEM;
}
//申请framebuffer空间
dev->fbi = framebuffer_alloc(0, NULL);
if(dev->fbi ==NULL)
{
printk("framebuffer_alloc error\r\n");
return -ENOMEM;
}
dev->fbi->var.xres = LCD_W;
dev->fbi->var.yres = LCD_H;
dev->fbi->var.xres_virtual = LCD_W;
dev->fbi->var.yres_virtual = LCD_H;
dev->fbi->var.bits_per_pixel = 32; // 屏是rgb565, 但QT程序只能支持32位.还需要在刷图时把32位的像素数据转换成rgb565
dev->fbi->var.red.offset = 16;
dev->fbi->var.red.length = 8;
dev->fbi->var.green.offset = 8;
dev->fbi->var.green.length = 8;
dev->fbi->var.blue.offset = 0;
dev->fbi->var.blue.length = 8;
strcpy(dev->fbi->fix.id, "myfb");
dev->fbi->fix.smem_start = p_addr; //显存的物理地址
dev->fbi->fix.smem_len = LCD_W*LCD_H*4;
dev->fbi->fix.type = FB_TYPE_PACKED_PIXELS;
dev->fbi->fix.visual = FB_VISUAL_TRUECOLOR;
dev->fbi->fix.line_length = LCD_W*4;
dev->fbi->fbops = &fops;
dev->fbi->screen_base = v_addr; //显存虚拟地址
dev->fbi->screen_size = LCD_W*LCD_H*4; //显存大小
//注册framebuffer
register_framebuffer(dev->fbi);
//创建内核线程fresh_myfb定时刷新显示
dev->thread = kthread_run(thread_func, dev, "fresh_myfb");
return 0;
}
static void myfb_del(struct st7735s_st *dev) //此函数在spi设备驱动remove时被调用
{
struct fb_info *fbi = dev->fbi;
kthread_stop(dev->thread); //让刷图线程退出
dma_free_coherent(&dev->spi->dev, fbi->screen_size, fbi->screen_base, fbi->fix.smem_start);
unregister_framebuffer(fbi);
framebuffer_release(fbi);
}
//SPI需使用硬件片选
static int SPI_WriteData(struct st7735s_st *dev, u8 data)
{
spi_write(dev->spi, &data, 1);
return 0;
}
static void st7735_write_cmd(struct st7735s_st *dev, u8 buf)
{
gpio_set_value(dev->gpio_dc, 0);
SPI_WriteData(dev, buf);
}
static void st7735_write_data(struct st7735s_st *dev, u8 buf)
{
gpio_set_value(dev->gpio_dc, 1);
SPI_WriteData(dev, buf);
}
static void st7735_write_datas(struct st7735s_st *dev, u8 *buf, int lenth)
{
gpio_set_value(dev->gpio_dc, 1);
spi_write(dev->spi, buf, lenth);
}
static void init_st7735(struct st7735s_st *me)
{
gpio_set_value(me->gpio_rst, 0);//复位
mdelay(100);
gpio_set_value(me->gpio_rst, 1);
mdelay(100);
//--------------------Start Initial Sequence-------------------//
st7735_write_cmd(me, 0x11); //Sleep out
mdelay(120); //Delay 120ms
st7735_write_cmd(me, 0x21);
st7735_write_cmd(me, 0xB1);
st7735_write_data(me, 0x05);
st7735_write_data(me, 0x3A);
st7735_write_data(me, 0x3A);
st7735_write_cmd(me, 0xB2);
st7735_write_data(me, 0x05);
st7735_write_data(me, 0x3A);
st7735_write_data(me, 0x3A);
st7735_write_cmd(me, 0xB3);
st7735_write_data(me, 0x05);
st7735_write_data(me, 0x3A);
st7735_write_data(me, 0x3A);
st7735_write_data(me, 0x05);
st7735_write_data(me, 0x3A);
st7735_write_data(me, 0x3A);
st7735_write_cmd(me, 0xB4); //Dot inversion
st7735_write_data(me, 0x03);
st7735_write_cmd(me, 0xC0);
st7735_write_data(me, 0x62);
st7735_write_data(me, 0x02);
st7735_write_data(me, 0x04);
st7735_write_cmd(me, 0xC1);
st7735_write_data(me, 0xC0);
st7735_write_cmd(me, 0xC2);
st7735_write_data(me, 0x0D);
st7735_write_data(me, 0x00);
st7735_write_cmd(me, 0xC3);
st7735_write_data(me, 0x8D);
st7735_write_data(me, 0x6A);
st7735_write_cmd(me, 0xC4);
st7735_write_data(me, 0x8D);
st7735_write_data(me, 0xEE);
st7735_write_cmd(me, 0xC5); //VCOM
st7735_write_data(me, 0x0E);
st7735_write_cmd(me, 0xE0);
st7735_write_data(me, 0x10);
st7735_write_data(me, 0x0E);
st7735_write_data(me, 0x02);
st7735_write_data(me, 0x03);
st7735_write_data(me, 0x0E);
st7735_write_data(me, 0x07);
st7735_write_data(me, 0x02);
st7735_write_data(me, 0x07);
st7735_write_data(me, 0x0A);
st7735_write_data(me, 0x12);
st7735_write_data(me, 0x27);
st7735_write_data(me, 0x37);
st7735_write_data(me, 0x00);
st7735_write_data(me, 0x0D);
st7735_write_data(me, 0x0E);
st7735_write_data(me, 0x10);
st7735_write_cmd(me, 0xE1);
st7735_write_data(me, 0x10);
st7735_write_data(me, 0x0E);
st7735_write_data(me, 0x03);
st7735_write_data(me, 0x03);
st7735_write_data(me, 0x0F);
st7735_write_data(me, 0x06);
st7735_write_data(me, 0x02);
st7735_write_data(me, 0x08);
st7735_write_data(me, 0x0A);
st7735_write_data(me, 0x13);
st7735_write_data(me, 0x26);
st7735_write_data(me, 0x36);
st7735_write_data(me, 0x00);
st7735_write_data(me, 0x0D);
st7735_write_data(me, 0x0E);
st7735_write_data(me, 0x10);
st7735_write_cmd(me, 0x3A);
st7735_write_data(me, 0x05);
st7735_write_cmd(me, 0x36);
st7735_write_data(me, 0xA8);
st7735_write_cmd(me, 0x29);
}
static void LCD_SetWindows(struct st7735s_st *me, u16 xStar, u16 yStar,u16 xEnd,u16 yEnd)
{
st7735_write_cmd(me, 0x2a);
st7735_write_data(me,0x00);
st7735_write_data(me,xStar);
st7735_write_data(me,0x00);
st7735_write_data(me,xEnd);
st7735_write_cmd(me, 0x2b);
st7735_write_data(me,0x00);
st7735_write_data(me,yStar+24);
st7735_write_data(me,0x00);
st7735_write_data(me,yEnd+24);
st7735_write_cmd(me, 0x2C);
}
static void show_fb(struct st7735s_st *me)
{
int x, y;
u32 k;
u32 *p = (u32 *)(me->fbi->screen_base);
u16 c;
u8 *pp;
//数据处理RGB888->RGB565
for (y = 0; y < me->fbi->var.yres; y++)
{
for (x = 0; x < me->fbi->var.xres; x++)
{
k = p[y*me->fbi->var.xres+x]; //取出一个像素点的32位数据
// rgb8888 --> rgb565
pp = (u8 *)&k;
c = pp[0] >> 3; //蓝色
c |= (pp[1]>>2)<<5; //绿色
c |= (pp[2]>>3)<<11; //红色
//保存转换后的数据
me->rgbdata[x+y*LCD_W] = ~c;
}
}
//刷新显示
LCD_SetWindows(me,0,0,LCD_W-1,LCD_H-1); //从屏的0,0坐标开始刷
st7735_write_datas(me, (u8 *)me->rgbdata, LCD_W*LCD_H*2);
}
static int getGPIOs(struct st7735s_st *me)
{
int ret =0;
//获取GPIO
me->gpio_dc = of_get_named_gpio(me->node, "gpio_dc", 0);
if(me->gpio_dc<0)
{
printk("gpio_dc get error\r\n");
return -EINVAL;
}
me->gpio_rst = of_get_named_gpio(me->node, "gpio_rst", 0);
if(me->gpio_rst<0)
{
printk("gpio_rst get error\r\n");
return -EINVAL;
}
//申请GPIO
ret = gpio_request(me->gpio_dc, "gpio_dc");
if(ret <0)
{
printk("gpio_dc request error\r\n");
goto gpio_dc_err;
}
ret = gpio_request(me->gpio_rst, "gpio_rst");
if(ret <0)
{
printk("gpio_rst request error\r\n");
goto gpio_rst_err;
}
//设置GPIO输出
gpio_direction_output(me->gpio_dc, 1);
gpio_direction_output(me->gpio_rst, 1);
return 0;
gpio_rst_err:
gpio_free(me->gpio_dc);
gpio_dc_err:
return ret;
}
static int st7735s_probe(struct spi_device *spi)
{
int ret =0;
st7735sDev.node = spi->dev.of_node;
//获取GPIO
ret = getGPIOs(&st7735sDev);
if(ret <0)
{
printk("getGPIOs error\r\n");
goto getGPIO_err;
}
//设置SPI
spi->mode = SPI_MODE_0;
spi_setup(spi);
st7735sDev.spi = spi;
//初始化显示屏
init_st7735(&st7735sDev);
//创建fb设备
myfb_new(&st7735sDev);
printk("st7735s_probe sucess\r\n");
return 0;
getGPIO_err:
return ret;
}
static int st7735s_remove(struct spi_device *spi)
{
//关闭GPIO
gpio_set_value(st7735sDev.gpio_dc, 0);
gpio_set_value(st7735sDev.gpio_rst, 1);
//释放GPIO
gpio_free(st7735sDev.gpio_dc);
gpio_free(st7735sDev.gpio_rst);
//销毁fb设备
myfb_del(&st7735sDev);
printk("st7735s_remove sucess\r\n");
return 0;
}
static const struct spi_device_id st7735s_id[] =
{
{"sitronix,st7735s",0},
{}
};
static const struct of_device_id st7735s_of_match[]=
{
{.compatible="sitronix,st7735s"},
{}
};
static struct spi_driver st7735s_dev =
{
.probe = st7735s_probe,
.remove = st7735s_remove,
.driver = {
.owner = THIS_MODULE,
.name = "st7735s",
.of_match_table = st7735s_of_match,
},
.id_table = st7735s_id,
};
static int __init st7735s_init(void)
{
return spi_register_driver(&st7735s_dev);
}
static void __exit st7735s_exit(void)
{
spi_unregister_driver(&st7735s_dev);
}
module_init(st7735s_init);
module_exit(st7735s_exit);
MODULE_AUTHOR("jtb");
MODULE_LICENSE("GPL");
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)