F1C200S构建st7735串行显示屏fb驱动

发布时间 2023-03-22 21:09:12作者: *^VV^*

#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");