36.【linux驱动】spi framebuffer驱动(切换并显示虚拟终端)

2023-05-16

1.framebuffer驱动
2.spi framebuffer驱动
3.spi framebuffer驱动(切换并显示虚拟终端)

切换终端输出

接这上一节
spi framebuffer驱动实现了,但是只有刷屏。6个虚拟终端并没有显示在屏幕上,要想办法让虚拟终端显示到我们的fb上面来。最总虚拟终端的源代码(过程不详述),发现了这么个地方:
/driver/video/console/fbcon.c

static int fbcon_event_notify(struct notifier_block *self,
			      unsigned long action, void *data)
{
	struct fb_event *event = data;
	struct fb_info *info = event->info;
	struct fb_videomode *mode;
	struct fb_con2fbmap *con2fb;
	struct fb_blit_caps *caps;
	int idx, ret = 0;

	/*
	 * ignore all events except driver registration and deregistration
	 * if fbcon is not active
	 */
	if (fbcon_has_exited && !(action == FB_EVENT_FB_REGISTERED ||
				  action == FB_EVENT_FB_UNREGISTERED))
		goto done;

	switch(action) {
	case FB_EVENT_SUSPEND:
		fbcon_suspended(info);
		break;
	case FB_EVENT_RESUME:
		fbcon_resumed(info);
		break;
	case FB_EVENT_MODE_CHANGE:
		fbcon_modechanged(info);
		break;
	case FB_EVENT_MODE_CHANGE_ALL:
		fbcon_set_all_vcs(info);
		break;
	case FB_EVENT_MODE_DELETE:
		mode = event->data;
		ret = fbcon_mode_deleted(info, mode);
		break;
	case FB_EVENT_FB_UNBIND:
		idx = info->node;
		ret = fbcon_fb_unbind(idx);
		break;
	case FB_EVENT_FB_REGISTERED:
		ret = fbcon_fb_registered(info);
		break;
	case FB_EVENT_FB_UNREGISTERED:
		ret = fbcon_fb_unregistered(info);
		break;
	case FB_EVENT_SET_CONSOLE_MAP:
		con2fb = event->data;
		ret = set_con2fb_map(con2fb->console - 1,
				     con2fb->framebuffer, 1);
		break;
	case FB_EVENT_GET_CONSOLE_MAP:
		con2fb = event->data;
		con2fb->framebuffer = con2fb_map[con2fb->console - 1];
		break;
	case FB_EVENT_BLANK:
		fbcon_fb_blanked(info, *(int *)event->data);
		break;
	case FB_EVENT_NEW_MODELIST:
		fbcon_new_modelist(info);
		break;
	case FB_EVENT_GET_REQ:
		caps = event->data;
		fbcon_get_requirement(info, caps);
		break;
	case FB_EVENT_REMAP_ALL_CONSOLE:
		idx = info->node;
		fbcon_remap_all(idx);
		break;
	}
done:
	return ret;
}

切换虚拟终端输出就在这个地方了,只需要发送一个fb_notify消息即可。我们使用最后这个

	case FB_EVENT_REMAP_ALL_CONSOLE:
		idx = info->node;
		fbcon_remap_all(idx);
		break;

切换所有终端。源码

struct fb_info * fb_init(struct spi_device *spi) //此函数在spi设备驱动的probe函数里被调用
{
    struct fb_info *fbi;
    u8 *v_addr;
    u32 p_addr;
    lcd_data_t *data;
    struct fb_event event;

    v_addr = dma_alloc_coherent(NULL, LCD_W * LCD_H * 3, &p_addr, GFP_KERNEL);

    //额外分配lcd_data_t类型空间
    fbi = framebuffer_alloc(sizeof(lcd_data_t), NULL);
    data = fbi->par; //data指针指向额外分配的空间

    data->spi = spi;

    fbi->var.xres = LCD_W;
    fbi->var.yres = LCD_H;
    fbi->var.xres_virtual = LCD_W;
    fbi->var.yres_virtual = LCD_H;
    fbi->var.bits_per_pixel = 24;
    fbi->var.red.offset = 16;
    fbi->var.red.length = 5;
    fbi->var.green.offset = 8;
    fbi->var.green.length = 6;
    fbi->var.blue.offset = 0;
    fbi->var.blue.length = 5;
    // fbi->var.pixclock   = 93006;
    fbi->var.activate    = FB_ACTIVATE_NOW;

    strcpy(fbi->fix.id, "hello_fb");
    fbi->fix.smem_start = p_addr; //显存的物理地址
    fbi->fix.smem_len = LCD_W * LCD_H * 3;
    fbi->fix.type = FB_TYPE_PACKED_PIXELS;
    fbi->fix.visual = FB_VISUAL_TRUECOLOR;
    fbi->fix.line_length = LCD_W * 3;

    fbi->fbops = &fops;
    fbi->screen_base = v_addr; //显存虚拟地址
    fbi->screen_size = LCD_W * LCD_H * 3; //显存大小

    spi_set_drvdata(spi, fbi);
    register_framebuffer(fbi);
    data->thread = kthread_run(thread_func, fbi, spi->modalias);
    event.info = fbi;
    fb_notifier_call_chain(FB_EVENT_REMAP_ALL_CONSOLE, &event);

    return fbi;
}

void fb_del(struct spi_device *spi) //此函数在spi设备驱动remove时被调用
{
    struct fb_info *fbi = spi_get_drvdata(spi);
    lcd_data_t *data = fbi->par;

    kthread_stop(data->thread); //让刷图线程退出
    unregister_framebuffer(fbi);
    dma_free_coherent(NULL, fbi->fix.smem_len, fbi->screen_base, fbi->fix.smem_start);
    framebuffer_release(fbi);
}

在framebuffer驱动注册完成的最后发送一个fb_notifier_call_chain(FB_EVENT_REMAP_ALL_CONSOLE, &event);就切换完成了。

适配显示

终端切换成功了,但是图像没有办法显示,都是乱码。手头的屏幕是控制器是ILI9488,spi传送的图像数据一个像素由三个字节组成,分别是蓝绿红。三个字节并不代表是24位,其中有效数据只占16位,蓝5位,绿6位,红5位。也就是rgb565,奇葩的是这三个颜色在一个字节中还是高位对齐,也就是说低位并没有使用,注意并不是大端对齐模式。为了对齐最后蓝和红也占6位了,最后变成了奇葩的rgb666吧。附上数据手册中的数据结构图
在这里插入图片描述
这种摆放方式不知道内核中有没有,软件支持性也不清楚。索性直接转换吧。为了使绘图效率达到最高,就不在spi传输的时候做转换了,而是在写入fb缓冲内存的时候就吧格式摆放正确。先做一下虚拟终端的绘图部分。虚拟终端主要调用fb_ops结构体中的几个函数完成字符绘制,我们只需要重写其中两个:fb_imageblit fb_fillrectfb_imageblit是绘制文本的主要函数,他接收矢量字符,坐标,向fb缓冲区绘图。

fb_imageblit

适量字符每一位代表一个像素,比如Z的适量字符:

	/* 90 0x5a 'Z' */
	0x00, 0x00, /* 0000000000 */
	0x3f, 0x80, /* 0011111110 */
	0x21, 0x80, /* 0010000110 */
	0x01, 0x80, /* 0000000110 */
	0x03, 0x00, /* 0000001100 */
	0x03, 0x00, /* 0000001100 */
	0x06, 0x00, /* 0000011000 */
	0x06, 0x00, /* 0000011000 */
	0x0c, 0x00, /* 0000110000 */
	0x0c, 0x00, /* 0000110000 */
	0x18, 0x00, /* 0001100000 */
	0x18, 0x00, /* 0001100000 */
	0x30, 0x00, /* 0011000000 */
	0x30, 0x80, /* 0011000010 */
	0x3f, 0x80, /* 0011111110 */
	0x00, 0x00, /* 0000000000 */
	0x00, 0x00, /* 0000000000 */
	0x00, 0x00, /* 0000000000 */

根据矢量字符以我们的特殊rgb666绘图:

void fb_imageblit(struct fb_info *info, const struct fb_image *image) {
    unsigned char *p, *p2, shift, *src;
    u32 dx, dy, width, height, bpp, i, j;
    dx = image->dx;
    dy = image->dy;
    width = image->width;
    height = image->height;
    bpp = info->var.bits_per_pixel;
    src = image->data;

    p = info->screen_base + info->fix.line_length * dy + dx * bpp;
    for (i = 0; i < height; ++i)
    {
        p2 = p;
        shift = 8;
        for (j = 0; j < width; ++j)
        {
            shift--;
            if (*src & (1 << shift)) {
                p2[0] = 0xf8;
                p2[1] = 0xf8;
                p2[2] = 0xf8;
            } else {
                p2[0] = 0x00;
                p2[1] = 0x00;
                p2[2] = 0x00;
            }
            if (!shift) { shift = 8; src++; };
            p2 += 3;
        }
        p += info->fix.line_length;
    }
}
fb_fillrect

fb_fillrect是清除字符调用的函数,在需要的区域填充黑色即可。

static void fb_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
{
    unsigned char *p, *p2;
    u32 dx, dy, width, height, bpp, i, j;
    dx = rect->dx;
    dy = rect->dy;
    width = rect->width;
    height = rect->height;
    bpp = info->var.bits_per_pixel;

    p = info->screen_base + info->fix.line_length * dy + dx * bpp;
    for (i = 0; i < height; ++i)
    {
        p2 = p;
        for (j = 0; j < width; ++j)
        {
                p2[0] = 0x00;
                p2[1] = 0xf8;
                p2[2] = 0x00;
                p2 += 3;
        }
        p += info->fix.line_length;
    }
}

整体代码

framebuffer.c


#include <linux/init.h>
#include <linux/module.h>
#include <linux/fb.h>
#include <linux/spi/spi.h>
#include <linux/dma-mapping.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/delay.h>
#include "fb_spi.h"

typedef struct {
    struct spi_device *spi;
    struct task_struct *thread;
} lcd_data_t;

static void fb_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
{
    unsigned char *p, *p2;
    u32 dx, dy, width, height, bpp, i, j;
    dx = rect->dx;
    dy = rect->dy;
    width = rect->width;
    height = rect->height;
    bpp = info->var.bits_per_pixel;

    p = info->screen_base + info->fix.line_length * dy + dx * bpp;
    for (i = 0; i < height; ++i)
    {
        p2 = p;
        for (j = 0; j < width; ++j)
        {
                p2[0] = 0x00;
                p2[1] = 0xf8;
                p2[2] = 0x00;
                p2 += 3;
        }
        p += info->fix.line_length;
    }
}

void fb_imageblit(struct fb_info *info, const struct fb_image *image) {
    unsigned char *p, *p2, shift, *src;
    u32 dx, dy, width, height, bpp, i, j;
    dx = image->dx;
    dy = image->dy;
    width = image->width;
    height = image->height;
    bpp = info->var.bits_per_pixel;
    src = image->data;

    p = info->screen_base + info->fix.line_length * dy + dx * bpp;
    for (i = 0; i < height; ++i)
    {
        p2 = p;
        shift = 8;
        for (j = 0; j < width; ++j)
        {
            shift--;
            if (*src & (1 << shift)) {
                p2[0] = 0xf8;
                p2[1] = 0xf8;
                p2[2] = 0xf8;
            } else {
                p2[0] = 0x00;
                p2[1] = 0x00;
                p2[2] = 0x00;
            }
            if (!shift) { shift = 8; src++; };
            p2 += 3;
        }
        p += info->fix.line_length;
    }
}

struct fb_ops fops = {
    .owner          = THIS_MODULE,
    .fb_imageblit   = fb_imageblit,
    .fb_fillrect = fb_fillrect,
};

/* from pxafb.c */
static inline unsigned int chan_to_field(unsigned int chan, struct fb_bitfield *bf)
{
    chan &= 0xffff;
    chan >>= 16 - bf->length;
    return chan << bf->offset;
}

extern void show_fb(struct fb_info *fbi, struct spi_device *spi);
int thread_func(void *data)
{
    struct fb_info *fbi = (struct fb_info *)data;
    lcd_data_t *ldata = fbi->par;

    while (1)
    {
        if (kthread_should_stop())
            break;
        show_fb(fbi, ldata->spi);
    }

    return 0;
}

struct fb_info * fb_init(struct spi_device *spi) //此函数在spi设备驱动的probe函数里被调用
{
    struct fb_info *fbi;
    u8 *v_addr;
    u32 p_addr;
    lcd_data_t *data;
    struct fb_event event;

    v_addr = dma_alloc_coherent(NULL, LCD_W * LCD_H * 3, &p_addr, GFP_KERNEL);

    //额外分配lcd_data_t类型空间
    fbi = framebuffer_alloc(sizeof(lcd_data_t), NULL);
    data = fbi->par; //data指针指向额外分配的空间

    data->spi = spi;

    fbi->var.xres = LCD_W;
    fbi->var.yres = LCD_H;
    fbi->var.xres_virtual = LCD_W;
    fbi->var.yres_virtual = LCD_H;
    fbi->var.bits_per_pixel = 24;
    fbi->var.red.offset = 16;
    fbi->var.red.length = 5;
    fbi->var.green.offset = 8;
    fbi->var.green.length = 6;
    fbi->var.blue.offset = 0;
    fbi->var.blue.length = 5;
    // fbi->var.pixclock   = 93006;
    fbi->var.activate    = FB_ACTIVATE_NOW;

    strcpy(fbi->fix.id, "hello_fb");
    fbi->fix.smem_start = p_addr; //显存的物理地址
    fbi->fix.smem_len = LCD_W * LCD_H * 3;
    fbi->fix.type = FB_TYPE_PACKED_PIXELS;
    fbi->fix.visual = FB_VISUAL_TRUECOLOR;
    fbi->fix.line_length = LCD_W * 3;

    fbi->fbops = &fops;
    fbi->screen_base = v_addr; //显存虚拟地址
    fbi->screen_size = LCD_W * LCD_H * 3; //显存大小

    spi_set_drvdata(spi, fbi);
    register_framebuffer(fbi);
    data->thread = kthread_run(thread_func, fbi, spi->modalias);
    event.info = fbi;
    fb_notifier_call_chain(FB_EVENT_REMAP_ALL_CONSOLE, &event);

    return fbi;
}

void fb_del(struct spi_device *spi) //此函数在spi设备驱动remove时被调用
{
    struct fb_info *fbi = spi_get_drvdata(spi);
    lcd_data_t *data = fbi->par;

    kthread_stop(data->thread); //让刷图线程退出
    unregister_framebuffer(fbi);
    dma_free_coherent(NULL, fbi->fix.smem_len, fbi->screen_base, fbi->fix.smem_start);
    framebuffer_release(fbi);
}

spi.h


struct  Lcd_dev
{										    
	unsigned short width;			//LCD 宽度
	unsigned short height;			//LCD 高度
	unsigned short id;				//LCD ID
	unsigned char  dir;			//横屏还是竖屏控制:0,竖屏;1,横屏。	
	unsigned short	 wramcmd;		//开始写gram指令
	unsigned short  setxcmd;		//设置x坐标指令
	unsigned short  setycmd;		//设置y坐标指令	 
};

//画笔颜色
#define WHITE            0xFFFF
#define BLACK            0x0000   
#define BLUE           0x001F  
#define BRED             0XF81F
#define GRED       0XFFE0
#define GBLUE      0X07FF
#define RED              0xF800
#define MAGENTA          0xF81F
#define GREEN            0x07E0
#define CYAN             0x7FFF
#define YELLOW           0xFFE0
#define BROWN        0XBC40 //棕色
#define BRRED        0XFC07 //棕红色
#define GRAY         0X8430 //灰色
//GUI颜色

#define DARKBLUE         0X01CF //深蓝色
#define LIGHTBLUE        0X7D7C //浅蓝色  
#define GRAYBLUE         0X5458 //灰蓝色
//以上三色为PANEL的颜色 
 
#define LIGHTGREEN       0X841F //浅绿色
#define LGRAY        0XC618 //浅灰色(PANNEL),窗体背景色

#define LGRAYBLUE        0XA651 //浅灰蓝色(中间层颜色)
#define LBBLUE           0X2B12 //浅棕蓝色(选择条目的反色)

//LCD的画笔颜色和背景色	   
#define POINT_COLOR (0x0000)	//画笔颜色
#define BACK_COLOR (0xFFFF)		//背景色
//定义LCD的尺寸
#define LCD_W 320
#define LCD_H 480

#define USE_HORIZONTAL  	0	//定义液晶屏顺时针旋转方向 	0-0度旋转,1-90度旋转,2-180度旋转,3-270度旋转

spi.c


#include <linux/init.h>
#include <linux/module.h>
#include <linux/spi/spi.h>
#include <linux/vmalloc.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/fb.h>
#include "fb_dev.h"
#include "fb_spi.h"

struct spi_lcd_cmd {
    u8  d_or_r;
    u8  data; // command
    int delay_ms; //此命令发送数据完成后,需延时多久
} cmds[] = {
    {0, 0XF7, 0},
    {1, 0xA9, 0},
    {1, 0x51, 0},
    {1, 0x2C, 0},
    {1, 0x82, 0},
    {0, 0xC0, 0},
    {1, 0x11, 0},
    {1, 0x09, 0},
    {0, 0xC1, 0},
    {1, 0x41, 0},
    {0, 0XC5, 0},
    {1, 0x00, 0},
    {1, 0x0A, 0},
    {1, 0x80, 0},
    {0, 0xB1, 0},
    {1, 0xB0, 0},
    {1, 0x11, 0},
    {0, 0xB4, 0},
    {1, 0x02, 0},
    {0, 0xB6, 0},
    {1, 0x02, 0},
    {1, 0x42, 0},
    {0, 0xB7, 0},
    {1, 0xc6, 0},
    {0, 0xBE, 0},
    {1, 0x00, 0},
    {1, 0x04, 0},
    {0, 0xE9, 0},
    {1, 0x00, 0},
    {0, 0x36, 0},
    {1, (1 << 3) | (0 << 7) | (1 << 6) | (1 << 5), 0},
    {0, 0x3A, 0},
    {1, 0x66, 0},
    {0, 0xE0, 0},
    {1, 0x00, 0},
    {1, 0x07, 0},
    {1, 0x10, 0},
    {1, 0x09, 0},
    {1, 0x17, 0},
    {1, 0x0B, 0},
    {1, 0x41, 0},
    {1, 0x89, 0},
    {1, 0x4B, 0},
    {1, 0x0A, 0},
    {1, 0x0C, 0},
    {1, 0x0E, 0},
    {1, 0x18, 0},
    {1, 0x1B, 0},
    {1, 0x0F, 0},
    {0, 0XE1, 0},
    {1, 0x00, 0},
    {1, 0x17, 0},
    {1, 0x1A, 0},
    {1, 0x04, 0},
    {1, 0x0E, 0},
    {1, 0x06, 0},
    {1, 0x2F, 0},
    {1, 0x45, 0},
    {1, 0x43, 0},
    {1, 0x02, 0},
    {1, 0x0A, 0},
    {1, 0x09, 0},
    {1, 0x32, 0},
    {1, 0x36, 0},
    {1, 0x0F, 0},
    {0, 0x11, 0},
    {0, 0x29, 0},
};

struct  Lcd_dev lcddev;

static inline void write_u8(struct spi_device * spi, u8 d_or_r, u8 cmd)
{
    struct fb_data *pdata = spi->dev.platform_data;
    gpio_set_value(pdata->rs, d_or_r);
    spi_write(spi, &cmd, 1);
}

static void write_u24s(struct spi_device * spi, u8 * data, unsigned int len)
{
    struct fb_data *pdata = spi->dev.platform_data;
    gpio_set_value(pdata->rs, 1);
    spi_write(spi, data, len);
}

void lcd_set_window(struct spi_device * spi, unsigned short xStar, unsigned short yStar, unsigned short xEnd, unsigned short yEnd)
{
    write_u8(spi, 0, lcddev.setxcmd);
    write_u8(spi, 1, xStar >> 8);
    write_u8(spi, 1, 0x00FF & xStar);
    write_u8(spi, 1, xEnd >> 8);
    write_u8(spi, 1, 0x00FF & xEnd);

    write_u8(spi, 0, lcddev.setycmd);
    write_u8(spi, 1, yStar >> 8);
    write_u8(spi, 1, 0x00FF & yStar);
    write_u8(spi, 1, yEnd >> 8);
    write_u8(spi, 1, 0x00FF & yEnd);

    write_u8(spi, 0, lcddev.wramcmd);
}

void lcd_clear(struct spi_device * spi, struct fb_info * fbi, unsigned short Color)
{
    unsigned int i;
    unsigned char *p;
    lcd_set_window(spi, 0, 0, lcddev.width - 1, lcddev.height - 1);
    p = fbi->screen_base;
    for (i = 0; i < fbi->var.xres * fbi->var.yres; i++)
    {
        p[i * 3] = (Color >> 8) & 0xF8;
        p[i * 3 + 1] = (Color >> 3) & 0xFC;
        p[i * 3 + 2] = (Color << 3);
    }
}

void lcd_direction(struct spi_device * spi, unsigned char direction)
{
    lcddev.setxcmd = 0x2A;
    lcddev.setycmd = 0x2B;
    lcddev.wramcmd = 0x2C;
    switch (direction) {
    case 0:
        lcddev.width = LCD_W;
        lcddev.height = LCD_H;
        write_u8(spi, 0, 0x36);
        write_u8(spi, 1, (1 << 3) | (0 << 6) | (0 << 7)); //BGR==1,MY==0,MX==0,MV==0
        break;
    case 1:
        lcddev.width = LCD_H;
        lcddev.height = LCD_W;
        write_u8(spi, 0, 0x36);
        write_u8(spi, 1, (1 << 3) | (0 << 7) | (1 << 6) | (1 << 5)); //BGR==1,MY==1,MX==0,MV==1
        break;
    case 2:
        lcddev.width = LCD_W;
        lcddev.height = LCD_H;
        write_u8(spi, 0, 0x36);
        write_u8(spi, 1, (1 << 3) | (1 << 6) | (1 << 7)); //BGR==1,MY==0,MX==0,MV==0
        break;
    case 3:
        lcddev.width = LCD_H;
        lcddev.height = LCD_W;
        write_u8(spi, 0, 0x36);
        write_u8(spi, 1, (1 << 3) | (1 << 7) | (1 << 5)); //BGR==1,MY==1,MX==0,MV==1
        break;
    default: break;
    }
}

//初始化spi_lcd
static void spi_lcd_init(struct spi_device * spi)
{
    struct fb_data *pdata = spi->dev.platform_data;
    int i, n;

    // 屏复位
    gpio_set_value(pdata->reset, 0);
    mdelay(100);
    gpio_set_value(pdata->reset, 1);
    mdelay(100);

    n = 0; // n用于记录数据数组spi_lcd_datas的位置
    //发命令,并发出命令所需的数据
    for (i = 0; i < ARRAY_SIZE(cmds); i++) //命令
    {
        write_u8(spi, cmds[i].d_or_r, cmds[i].data);
        if (cmds[i].delay_ms)
            mdelay(cmds[i].delay_ms);
    }
}

void show_fb(struct fb_info * fbi, struct spi_device * spi)
{
    u8 *p = (u8 *)(fbi->screen_base);

    lcd_set_window(spi, 0, 0, lcddev.width - 1, lcddev.height - 1); //从屏的0,0坐标开始刷
    write_u24s(spi, p, fbi->screen_size/2);
    write_u24s(spi, p + fbi->screen_size/2, fbi->screen_size/2);
}

extern struct fb_info * fb_init(struct spi_device *);
static int spi_probe(struct spi_device * spi)
{
    struct fb_data *pdata = spi->dev.platform_data;
    int ret;
    struct fb_info * fbi;
    fbi = NULL;

    ret = gpio_request(pdata->reset, spi->modalias);
    if (ret < 0)
        goto err0;
    ret = gpio_request(pdata->rs, spi->modalias);
    if (ret < 0)
        goto err1;

    gpio_direction_output(pdata->rs, 0);
    gpio_direction_output(pdata->reset, 1);
    spi_lcd_init(spi); //初始化
    lcd_direction(spi, USE_HORIZONTAL); //设置LCD显示方向
    fbi = fb_init(spi); //fb设备初始化
    // lcd_clear(spi, fbi, BLUE);

    printk("probe ...%s\n", spi->modalias);
    return 0;

err1:
    gpio_free(pdata->reset);
err0:
    return ret;
}

extern void fb_del(struct spi_device *);
static int spi_remove(struct spi_device * spi)
{
    struct fb_data *pdata = spi->dev.platform_data;

    fb_del(spi); //fb设备回收

    gpio_free(pdata->rs);
    gpio_free(pdata->reset);
    printk("%s remove\n", spi->modalias);
    return 0;
}

static struct spi_driver spi_drv = {
    .driver = {
        .owner = THIS_MODULE,
        .name = "hello_spi_fb",
    },
    .probe = spi_probe,
    .remove = spi_remove,
};

static int __init hello_init(void) {
    int ret;
    ret = spi_register_driver(&spi_drv);
    if (ret) {
        printk("hello driver fail\n");
    } else {
        printk("hello driver init\n");
    }
    return ret;
}

static void __exit hello_exit(void) {
    spi_unregister_driver(&spi_drv);
    printk("hello device exit\n");
}

module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");

dev.h

#ifndef __FB_DEV__
#define __FB_DEV__

struct fb_data{
	int reset;
	int rs;	
};
#endif

dev.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <mach/gpio.h>
#include <mach/platform.h>
#include <linux/spi/spi.h>
#include <linux/amba/pl022.h>
#include "fb_dev.h"

static struct fb_data fb1_plat_data = {
	.reset  = PAD_GPIO_D + 21,
	.rs    = PAD_GPIO_D + 17,
};

struct pl022_config_chip spi0_info = {
	/* available POLLING_TRANSFER, INTERRUPT_TRANSFER, DMA_TRANSFER */
	.com_mode = CFG_SPI0_COM_MODE,
	.iface = SSP_INTERFACE_MOTOROLA_SPI,
	/* We can only act as master but SSP_SLAVE is possible in theory */
	.hierarchy = SSP_MASTER,
	/* 0 = drive TX even as slave, 1 = do not drive TX as slave */
	.slave_tx_disable = 1,
	.rx_lev_trig = SSP_RX_4_OR_MORE_ELEM,
	.tx_lev_trig = SSP_TX_4_OR_MORE_EMPTY_LOC,
	.ctrl_len = SSP_BITS_8,
	.wait_state = SSP_MWIRE_WAIT_ZERO,
	.duplex = SSP_MICROWIRE_CHANNEL_FULL_DUPLEX,
	.clkdelay = SSP_FEEDBACK_CLK_DELAY_1T,
};

static struct spi_board_info spi_plat_board = {
	.modalias        = "hello_spi_fb",    /* fixup */
	.max_speed_hz    = 25000000,     /* max spi clock (SCK) speed in HZ */
	.bus_num         = 0,           /* Note> set bus num, must be smaller than ARRAY_SIZE(spi_plat_device) */
	.chip_select     = 1,           /* Note> set chip select num, must be smaller than spi cs_num */
	.controller_data = &spi0_info,
	.mode            = SPI_MODE_3 | SPI_CPOL | SPI_CPHA,
	.platform_data   = &fb1_plat_data,
};

__attribute__ ((unused)) static void device_spi_delete(struct spi_master *master, unsigned cs)
{
	struct device *dev;
	char str[32];

	snprintf(str, sizeof(str), "%s.%u", dev_name(&master->dev), cs);

	dev = bus_find_device_by_name(&spi_bus_type, NULL, str);
	if (dev) {
		printk(": Deleting %s\n", str);
		device_del(dev);
	}
}

static struct spi_device *spi_device;
static int __init hello_init(void) {
	struct spi_master *master;

	master = spi_busnum_to_master(spi_plat_board.bus_num);
	if (!master) {
		printk(":  spi_busnum_to_master(%d) returned NULL\n",
		       spi_plat_board.bus_num);
		return -EINVAL;
	}
	/* make sure it's available */
	// device_spi_delete(master, spi_plat_board.chip_select);
	spi_device = spi_new_device(master, &spi_plat_board);
	put_device(&master->dev);
	if (!spi_device) {
		printk(":    spi_new_device() returned NULL\n");
		return -EPERM;
	}
	return 0;
	printk("hello device init\n");
	return 0;
}

static void __exit hello_exit(void) {
	if (spi_device) {
		if (spi_device->master->cleanup) {
			spi_device->master->cleanup(spi_device);
		}
		device_del(&spi_device->dev);
		kfree(spi_device);
	}
}

module_init(hello_init);
module_exit(hello_exit);

MODULE_LICENSE("GPL");

运行效果

在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

36.【linux驱动】spi framebuffer驱动(切换并显示虚拟终端) 的相关文章

  • 聊一聊那些应该了解的大佬(飞控,人工智能方向)

    写在前面的话 xff1a 以下内容与图片大多来自于网络 xff0c 如有侵权 xff0c 请告知我进行修改 部分评论仅为个人观点 xff0c 无人机方面大牛很多 xff0c 无法一一评说 xff0c 在此随意发挥 也欢迎各位看官补充完善 R
  • ArduPilot+mavros+gazebo+QGC联合仿真初体验

    首先给出最终效果图 xff1a 实现内容与PX4官网代码功能类似 xff0c 四旋翼飞机自动起飞至2 5m高度 xff0c 悬停一定时间 xff0c 然后自主降落 记录如下几个需要注意的地方 xff1a 一共使用到三个文件夹 xff0c 其
  • ArduPilot姿态控制方法解析---(倾转分离)

    先给出一些预备知识 xff1a 欧拉角 xff1a 即所谓的Roll Pitch Yaw 而事实上 xff0c 根据其定义 xff0c 欧拉角的具有不同的表示 存在先绕哪个轴 xff0c 后绕哪个轴转的差别 xff08 将旋转分解成绕三个轴
  • C中main函数解析

    参考链接 main函数不同写法 以下为main函数的6种不同写法 xff1a span class token function main span span class token punctuation span span class
  • ArduPilot姿态环控制-----传感器初始化

    参考链接 xff1a https blog csdn net lixiaoweimashixiao article details 80540295 首先我们假定从void AP Vehicle setup 开始 xff0c 这里是飞控所有
  • ArduPilot飞行前检查——PreArm解析

    ArduPilot飞行前检查 主要包括两个部分 1 初始化中遥控器输入检查 xff1b 2 1Hz解锁前检查 附 xff1a 显示地面站信息 参考文章 xff1a Ardupilot Pre Arm安全检查程序分析 1 初始化中遥控器输入检
  • ROS_PX4_gazebo学习记录

    在官方程序上 xff08 PX4 wiki上为offboard起飞到2m高度 xff09 进行更改 xff0c 实现首先起飞到固定点 xff08 x 61 1 y 61 2 z 61 5 然后按照给定角度飞行 补充 xff1a 最终实现效果
  • Rospy初次使用记录-定点飞行

    由于接触到pytorch xff0c 所以用python完成与ROS的通信 xff0c 下面例子为从程序中摘出来的一部分 xff0c 用到了ROS消息的订阅与发布 xff0c 服务的通信 xff0c 可以作为参考使用 xff1a span
  • 四旋翼飞行器数学模型

    最近接触到四旋翼无人机的位置控制方法 xff0c 就又了解了一下四旋翼飞机的数学模型 xff0c 现总结如下 xff1a 位置环 xff08 均定义在惯性坐标系下 xff09 P
  • 基于ROS与optitrack的四旋翼飞机开发流程

    本文将一些注意点记录下来 xff0c 适合于开发调试 xff1a 目前只是分段调试通了 xff0c 带后续联合开发的时候在来补充还有没有什么注意点 xff08 过程也算麻烦 xff0c 也算不麻烦 xff09 xff1b xff32 xff
  • ROS_调试(三) 打印输出

    ROS INFO 采用类似C语言的形式 ROS DEBUG ROS DEBUG STREAM 采用类似C 43 43 语言的形式打印 ROS DEBUG STREAM NAMED ROS DEBUG STREAM THROTTLE NAME
  • px4调试bug--添加mavlink_log_info信息

    写在前面的话 有一阵子没有看px4的代码了 由于项目和论文的需要 又要接触这个 其中又遇到一些新的问题 找到了一些新的解决方法 故在此记录一下 总是在几种飞控代码之间跳来跳去 没有认真研究一个 有点遗憾 PX4的代码调试还没有找到什么好的方
  • APM,PX4之开源协议

    APM代码设计的是GPLv3协议 xff0c PX4代码采用的是BSD协议 从上图可以看出 xff0c ardupilot的代码是允许别人修改 xff0c 但是修改之后必须开源且采用相同的许可证书 而PX4代码则是允许别人修改 xff0c
  • C语言实现mavlink库与px4通信仿真

    参照网址 http mavlink io en mavgen c 首先从github上下载对应的C uart interface example 对应的github仓库网址为https github com mavlink c uart i
  • RK3308--8声道改成双声道+录音增益

    改为双声道 修改dts文件 相关路径 xff1a Y hxy RK3308 sdk 1 5 kernel arch arm64 boot dts rockchipY hxy RK3308 sdk 1 5 kernel Documentati
  • Flightmare install 安装指南

    flightmare 是ETH推出的一个用于gazebo仿真 xff0c 强化学习训练的平台 xff0c 并在github上公开了其源代码 本文主要记录在配置环境过程中出现的问题 github网址链接 https github com uz
  • matlab发送mavlink消息

    主要介绍了通过matlab脚本实现UDP发送mavlink消息 xff0c 为后面matlab计算 xff0c 与Optitrack联合调试 xff0c 控制无人机做准备 示例演示效果链接为 matlab通过UDP协议发送mavlink消息
  • apm-ros-optitrack初步尝试

    本文记录采用ArduPilot固件 xff0c 室内optitrack环境下飞行实现中遇到的一些问题 在apm mavros仿真中 xff0c 总是出现mavros state 显示 not connected 在实际的操作中 xff0c
  • APM代码调试知识点汇总

    由于项目的需要 xff0c 对ardupilot的源码进行二次开发 本文记录在二次开发中遇到的问题以及注意事项 xff1a CUAV V5 实测 apm 串口 xff0c 对于姿态数据的发送和接收在200Hz的时候 xff0c 是没有问题的
  • ardupilot之mavlink消息--从飞控发出--单向

    飞控采用mavlink消息进行数据的传输 普遍说法是 xff0c 现有的mavlink消息几乎已经涵盖了所有你的能想象到的内容 xff0c 完全可以覆盖多处需求 无奈科研总是要定义一些新鲜玩意 xff0c 所以总是有无法完全满足需求 xff

随机推荐

  • ardupilot之mavlink消息--飞控接收--单向

    由于项目需要 xff0c 完成一个测试demo 本次从dronekit中发送mavlink消息给飞控 xff0c 飞控接收发来的wp信息 xff0c 然后进行修改供程序使用 首先祭出测试视频 dronekit arudpilot test
  • ArduPilot 添加自定义调节参数

    实际变成操作中 xff0c 需要对ardupilot代码进行修改并添加对应的调试参数 xff0c 这样 xff0c 可以通过地面站很方便的进行修改参数 目前修改代码在parameter h中的G2类 xff0c 表示为全局的参数列表 参数类
  • Python_mavros_manual_contoller

    利用python完成mavros与PX4的通信工程 xff0c 同时也完成了对应的PX4中对应消息代码的调试查看 span class token keyword from span future span class token keyw
  • PX4 混控部分分析

    PX4的混控部分大体思路和ardupilot是一致的 更多的PX4采用的是脚本读取的形式完成其中的读取 转换以及最后的应用 首先从机型选择后 对应为toml文件 如下图所示 采用对应的px generate mixers py来自动生成对应
  • PX4 Position_Control RC_Remoter引入

    PX4飞控位置环控制中如何引入遥控器的控制量 本文基于PX4 1 12版本 相比于之前的版本 多引入了Flight Task这么一个模块 省流总结 遥控器的值通过 flight tasks update gt velocity setpoi
  • AS--Location specified by ndk.dir...

    问题 span class token operator span span class token class name What span went wrong span class token operator span span c
  • ros中的message filters时间同步--> 用于image和imu的结合

    转载自 xff1a https blog csdn net He3he3he article details 109643391 xff11 xff0e message filters介绍 message filters用于对齐多种传感信息
  • autoware.ai docker配置

    配置环境 xff1a Ubuntu18 04 显卡驱动 510 85 02 cuda 11 1 用了我一整天的时间 xff0c 累死了 先按照此博文操作 博文里的这一步骤我没有用 sudo apt key fingerprint 0EBFC
  • 使用libcurl发送HTTP请求的一个简单示例代码

    代码简单解释 设置header 首先要声明header的结构体变量 xff0c 然后设置对应header值 xff0c 最后将其设置到curl结构体中 span class hljs comment 声明 span CURL curl sp
  • make -C和M=的作用

    当make的目标为all时 xff0c C KDIR 指明跳转到源码目录下读取那里的Makefile xff1b M 61 PWD 表明然后返回到当前目录继续读入 执行当前的Makefile
  • docker参数详解

    1 xff1a docker参数详解 docker useage of docker D 默认false 允许调试模式 debugmode H 默认是unix var run docker sock tcp host port 来绑定 或者
  • 【星伦商学院】中小企业经济复苏公益座谈会圆满结束

    中小企业经济复苏公益座谈会 为帮助中小微企业破解难题 渡过难关 xff0c 全面推进复工复产 xff0c 6月30日 xff0c 中小企业座谈会 xff08 以下简称会议 xff09 在中国 浙江诸暨市召开 力求创新举措破解制约中小微企业用
  • 从旋转矩阵到欧拉角

    摄影测量中 xff0c R转角系统一般是以y轴为主轴 xff0c 但是在很多代码中 xff0c 发现R的定义很多 xff0c 据自己需求 工业标准中一般采用Z X Y转角系统 xff0c 先绕x旋转 xff0c 再绕y 最后是Z 即如下所示
  • Git只clone指定的分支

    默认的clone会下载远程仓库的所有分支 有时候为了避免创建多个git仓库 xff0c 想将一些资源在一个git仓库下统一管理 xff0c 这时候就会出现 xff1a git远程仓库分支很多 xff0c 但是各个分支没有多大关联 xff0c
  • Ubuntu系统无法update的解决方法

    今天安装Ubuntu系统的时候 xff0c 出现了一些状况 xff0c 首先说个自己犯的幼稚错误 首先我制作好了启动盘 xff0c 并且按着步骤安装完系统 进入系统 xff0c 我自认为输入了密码 xff0c 进入了我的账号 xff08 其
  • 什么是嵌入式AI开发?人工智能芯片指什么?STM32、树莓派、Jetson TX2、华为昇腾部署神经网络区别在哪?

    嵌入式芯片 xff1a https zhuanlan zhihu com p 449570075 芯片公司代表 xff1a https www jiqizhixin com articles 2020 01 31 9 相较于CPU GPU等
  • RK3308--固件编译

    快捷路径 相关路径 xff1a cd work hxy RK3308 sdk 1 5 添加依赖包 若编译遇到报错 xff0c 可以视报错信息 xff0c 安装对应的软件包 当时下面一大段依赖包直接添加时 xff0c 不能实现 可能是因为中间
  • ChatGPT会如何影响我们的工作生活和人力资源需求

    ChatGPT xff0c 这几天体验了一下 xff0c 确实是非常震撼 一方面是因为它的回答确实相当好 xff0c 自带一点框架逻辑 xff0c 有上下文理解能力 xff0c 可以追问 xff0c 有情商 虽然很多时候都是一些正确的废话
  • Linux设备驱动思想在STM32编程中的应用

    这几天看了一下Linux设备驱动 xff0c 发现这套思想其实也可以用在普通的单片机编程上 这种思想较好的分割了驱动层和应用层的任务 xff0c 方便分层开发 以前 xff0c 我们开发STM32驱动的时候 xff0c 会给设备写一套函数来
  • 36.【linux驱动】spi framebuffer驱动(切换并显示虚拟终端)

    1 framebuffer驱动 2 spi framebuffer驱动 3 spi framebuffer驱动 切换并显示虚拟终端 切换终端输出 接这上一节 spi framebuffer驱动实现了 xff0c 但是只有刷屏 6个虚拟终端并