写简单驱动的准备:
硬件部分:
- DTS
- 原理图,知道外设用哪些引脚
- 写设备树,基础模板,compatabile,硬件引用,从pinctl获取哪些引脚被描述为SPI 1/2/3.......
- 编译脚本:模板
- 驱动
- 驱动框架 1.platform driver结构体,包括 名字和of_match_table,probe和remove函数
- 驱动血肉:具体的probe和remove函数,file_operation中的操作函数,open read ,wrtie,ioctl
- 驱动的细节:内核与用户空间的通信手段:mmap,copy_from_user/copy_to_user,定时器,线程等等
驱动基石:
设备树模板:
&是引用的意思,是对一个文件的描述的节点进行引用,进行重写:新增,然后是pinctrl的名字,定义spi5的引脚等等,状态,片选引脚,然后是子设备节点,一般是
&spi5 {
pinctrl-names = "default", "sleep";
pinctrl-0 = <&spi5_pins_a>;
pinctrl-1 = <&spi5_sleep_pins_a>;
status = "okay";
cs-gpios = <&gpioh 5 GPIO_ACTIVE_LOW>, <&gpioz 4 GPIO_ACTIVE_LOW>;
spidev: icm20608@0{
compatible = "invensense,icm20608";
interrupts = <0 IRQ_TYPE_EDGE_FALLING>;
interrupt-parent = <&gpioz>;
spi-max-frequency = <8000000>;
reg = <0>;
};
oled: oled@1{
compatible = "oled_drv";
spi-max-frequency = <10000000>;
reg = <1>;
dc-gpios = <&gpioa 13 GPIO_ACTIVE_HIGH>;
};
};
在另外的文件是这也定义spi,并配置相关引脚为对应功能的。
arm/boot/dts/stm32mp157-100ask-pinctrl.dtsi这个文件里面有类似的:
spi5: spi@4400b000 { compatible = "st,stm32h7-spi"; reg = <0x4400b000 0x400>; interrupts = <...>; #address-cells = <1>; #size-cells = <0>; status = "disabled"; // 默认关闭 };
&pinctrl { spi5_pins_a: spi5-0 { pins { pinmux = <STM32_PINMUX('H', 6, AF5)>, /* SPI5_SCK */ <STM32_PINMUX('H', 7, AF5)>; /* SPI5_MISO */ bias-disable; drive-push-pull; slew-rate = <2>; }; }; };wwen
驱动骨架:

案例,oled驱动(大部分是抄的)
框架是 probe,完成,主副设备号,设备节点的创建,设备能力:再给节点增加能力:file_operation(各种read ,write,read,ioctl,open,close)
#include <linux/init.h> #include <linux/module.h> #include <linux/ioctl.h> #include <linux/fs.h> #include <linux/device.h> #include <linux/slab.h> #include <linux/of.h> #include <linux/of_device.h> #include <linux/spi/spi.h> #include <linux/uaccess.h> #include <linux/gpio/consumer.h> #include <linux/fb.h> #include <linux/dma-mapping.h> #include <linux/kthread.h> #include <linux/delay.h> #define OLED_IOC_INIT 123 #define OLED_IOC_SET_POS 124 #define OLED_CMD 0 #define OLED_DATA 1 static struct spi_device *oled; static struct gpio_desc *dc_gpio; static int major; static struct class *oled_class; static struct fb_info *myfb_info; static unsigned int pseudo_palette[16]; static struct task_struct *oled_thread; static unsigned char *oled_buf; /* ================= GPIO & SPI ================= */ static void dc_pin_init(void) { gpiod_direction_output(dc_gpio, 1); } static void oled_set_dc_pin(int val) { gpiod_set_value(dc_gpio, val); } static void spi_write_datas(const unsigned char *buf, int len) { spi_write(oled, buf, len); } /* ================= OLED 基础函数 ================= */ static void oled_write_cmd_data(unsigned char data, unsigned char cmd) { oled_set_dc_pin(cmd ? 1 : 0); spi_write_datas(&data, 1); } static void OLED_DIsp_Set_Pos(int x, int y) { oled_write_cmd_data(0xb0 + y, OLED_CMD); oled_write_cmd_data((x & 0x0f), OLED_CMD); oled_write_cmd_data(((x & 0xf0) >> 4) | 0x10, OLED_CMD); } static int oled_init(void) { oled_write_cmd_data(0xae, OLED_CMD); oled_write_cmd_data(0x00, OLED_CMD); oled_write_cmd_data(0x10, OLED_CMD); oled_write_cmd_data(0x40, OLED_CMD); oled_write_cmd_data(0xB0, OLED_CMD); oled_write_cmd_data(0x81, OLED_CMD); oled_write_cmd_data(0x66, OLED_CMD); oled_write_cmd_data(0xa1, OLED_CMD); oled_write_cmd_data(0xa6, OLED_CMD); oled_write_cmd_data(0xa8, OLED_CMD); oled_write_cmd_data(0x3f, OLED_CMD); oled_write_cmd_data(0xc8, OLED_CMD); oled_write_cmd_data(0xd3, OLED_CMD); oled_write_cmd_data(0x00, OLED_CMD); oled_write_cmd_data(0xd5, OLED_CMD); oled_write_cmd_data(0x80, OLED_CMD); oled_write_cmd_data(0xd9, OLED_CMD); oled_write_cmd_data(0x1f, OLED_CMD); oled_write_cmd_data(0xda, OLED_CMD); oled_write_cmd_data(0x12, OLED_CMD); oled_write_cmd_data(0xdb, OLED_CMD); oled_write_cmd_data(0x30, OLED_CMD); oled_write_cmd_data(0x8d, OLED_CMD); oled_write_cmd_data(0x14, OLED_CMD); oled_write_cmd_data(0xaf, OLED_CMD); return 0; } /* ================= 字符设备 ================= */ static long oled_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { int x, y; switch (cmd) { case OLED_IOC_INIT: dc_pin_init(); oled_init(); break; case OLED_IOC_SET_POS: x = arg & 0xff; y = (arg >> 8) & 0xff; OLED_DIsp_Set_Pos(x, y); break; } return 0; } static ssize_t oled_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) { char *kbuf; kbuf = kmalloc(count, GFP_KERNEL); if (!kbuf) return -ENOMEM; if (copy_from_user(kbuf, buf, count)) { kfree(kbuf); return -EFAULT; } oled_set_dc_pin(1); spi_write_datas(kbuf, count); kfree(kbuf); return count; } static const struct file_operations oled_ops = { .owner = THIS_MODULE, .write = oled_write, .unlocked_ioctl = oled_ioctl, }; static inline unsigned int chan_to_field(unsigned int chan, struct fb_bitfield *bf) { chan &= 0xffff; chan >>= 16 - bf->length; return chan << bf->offset; } static int mylcd_setcolreg(unsigned regno, unsigned red, unsigned green, unsigned blue, unsigned transp, struct fb_info *info) { unsigned int val; if (regno < 16) { u32 *pal = info->pseudo_palette; val = chan_to_field(red, &info->var.red); val |= chan_to_field(green, &info->var.green); val |= chan_to_field(blue, &info->var.blue); pal[regno] = val; } return 0; } static struct fb_ops myfb_ops = { .owner = THIS_MODULE, .fb_setcolreg = mylcd_setcolreg, .fb_fillrect = cfb_fillrect, .fb_copyarea = cfb_copyarea, .fb_imageblit = cfb_imageblit, }; static int oled_thread_func(void *param) { while (!kthread_should_stop()) { int i; for (i = 0; i < 8; i++) { OLED_DIsp_Set_Pos(0, i); oled_set_dc_pin(1); spi_write_datas(&oled_buf[i * 128], 128); } msleep(100); } return 0; } /* ================= SPI Driver ================= */ static const struct of_device_id oled_dt_ids[] = { { .compatible = "oled_drv" }, { } }; MODULE_DEVICE_TABLE(of, oled_dt_ids); static int oled_probe(struct spi_device *spi) { dma_addr_t phy_addr; oled = spi; major = register_chrdev(0, "spi_oled", &oled_ops); oled_class = class_create(THIS_MODULE, "spi_oled"); device_create(oled_class, NULL, MKDEV(major, 0), NULL, "spi_oled"); dc_gpio = gpiod_get(&spi->dev, "dc", GPIOD_OUT_HIGH); myfb_info = framebuffer_alloc(0, NULL); myfb_info->var.xres = 128; myfb_info->var.yres = 64; myfb_info->var.bits_per_pixel = 1; strcpy(myfb_info->fix.id, "spi_oled"); myfb_info->fix.smem_len = 128 * 64 / 8; dma_set_coherent_mask(&spi->dev, DMA_BIT_MASK(32)); myfb_info->screen_base = dma_alloc_wc(&spi->dev, myfb_info->fix.smem_len, &phy_addr, GFP_KERNEL); myfb_info->fix.smem_start = phy_addr; myfb_info->fix.type = FB_TYPE_PACKED_PIXELS; myfb_info->fix.visual = FB_VISUAL_MONO10; myfb_info->fix.line_length = 128 / 8; myfb_info->fbops = &myfb_ops; myfb_info->pseudo_palette = pseudo_palette; register_framebuffer(myfb_info);
oled_buf = kmalloc(1024, GFP_KERNEL);
oled_init();
oled_thread = kthread_run(oled_thread_func, NULL, "oled_thread");
return 0; } static int oled_remove(struct spi_device *spi) { kthread_stop(oled_thread); kfree(oled_buf); unregister_framebuffer(myfb_info); dma_free_wc(&spi->dev, myfb_info->fix.smem_len, myfb_info->screen_base, myfb_info->fix.smem_start); framebuffer_release(myfb_info); gpiod_put(dc_gpio); device_destroy(oled_class, MKDEV(major, 0)); class_destroy(oled_class); unregister_chrdev(major, "spi_oled"); return 0; } static struct spi_driver oled_drv = { .driver = { .name = "oled_drv", .of_match_table = oled_dt_ids, }, .probe = oled_probe, .remove = oled_remove, }; module_spi_driver(oled_drv); MODULE_LICENSE("GPL"); MODULE_AUTHOR("qzc"); MODULE_DESCRIPTION("SPI OLED Driver");
留个坑:如何比较具体的写驱动,就是需要了解各种API接口。
如何抄别人的驱动:

或者收集模板,找别人要,或者买。当然要回报别人的,hh。
如图。引发一个问题,如何的去阅读内核源码。sourceinsight 或者vscode + ssh +
。或者移植:移植只需要改设备树,改上面的就行。
填充内容:
一:阅读模块芯片手册
通信方式:I2C UART SPI
这种除去屏幕一般都比较容易写,屏幕也是那种初始化的,或者操作原理,英文比较多,不好写,其余都是比较简单的,都是主从机,遵守一种通信协议,像之前工作的指纹图像传感器,
就规定了工作模式(正常,低功耗),之间的切换,这些就规定了写什么寄存器,写什么值,然后是读图,就是写长宽(两种尺寸),然后就是增益,图像传感器的原理,光学,触摸,有个判决器,增益越大,值大,就能通过判决其,相关值就一或者0。
复杂传感器
像什么网络设备,wifi ,蓝牙,usb,这种一般是除了硬件操作,还有非常复杂的协议,多种状态,状态切换,流量控制,信息格式,而且有多种。
二:驱动基础

常见问题:
驱动没加载的原因:
1.引脚被占用 比如 pinctrl某组如SPI 1的 一个引脚在其他地方被使用了。
2.probe函数有误 牢记:只有设备树中的compatible 和 driver 的of match table 匹配才会触发 probe,最基本的要完成设备号的获取,设备节点的创建,然后就算特定框架,比如USB 鼠标,除了usb通信,需要设置URB,设置接口、端点,还有URB,input子系统
,屏幕有frame_buffer 框架
3.具体的操作函数实现有误
调试手段:
1.dmesg | tail -10
2.示波器
3.printk打印
……
当然还有。
更多的案例补充和分析、
[留个坑]
……