15.Linux SPI驱动(未完成)
实验说明¶
该部分使用正点原子MINI开发板实现,由于板子上未接入SPI相关设备,外接一个0.96OLED作为测试。
驱动编写¶
修改设备树,在spi设备中,添加oled节点。
&ecspi3 {
fsl,spi-num-chipselects = <1>;
cs-gpios = <&gpio1 20 GPIO_ACTIVE_LOW>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_ecspi3>;
status = "okay";
spidev: oled@0 {
compatible = "alientek,oled";
spi-max-frequency = <8000000>;
reg = <0>;
oled-dc-gpio = <&gpio1 6 GPIO_ACTIVE_HIGH>;
oled-rst-gpio = <&gpio1 7 GPIO_ACTIVE_HIGH>;
};
};
然后添加pinctrl节点
pinctrl_ecspi3: ecspi3grp {
fsl,pins = <
MX6UL_PAD_UART2_RTS_B__ECSPI3_MISO 0x100b1 /* MISO*/
MX6UL_PAD_UART2_CTS_B__ECSPI3_MOSI 0x100b1 /* MOSI*/
MX6UL_PAD_UART2_RX_DATA__ECSPI3_SCLK 0x100b1 /* CLK*/
MX6UL_PAD_UART2_TX_DATA__GPIO1_IO20 0x100b0 /* CS*/
MX6UL_PAD_GPIO1_IO06__GPIO1_IO06 0x17059 /*dc*/
MX6UL_PAD_GPIO1_IO07__GPIO1_IO07 0x17059 /*rst*/
>;
};
包含所需头文件
#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/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/i2c.h>
#include <linux/spi/spi.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
然后写驱动出入口相关函数,这里知道spi_register_driver是添加一个spi设备,spi_unregister_driver是卸载。这里spi驱动结构体结构体还未定义。
static int __init oled_init(void)
{
return spi_register_driver(&oled_driver);
}
static void __exit oled_exit(void)
{
spi_unregister_driver(&oled_driver);
}
module_init(oled_init);
module_exit(oled_exit);
MODULE_LICENSE("GPL");
然后完善设备树匹配列表和spi驱动结构体结构体。结构体内部相关函数未声明。传统和设备树匹配列表必须同时存在,不然无法进入probe函数。
/* 传统匹配方式ID列表 */
static const struct spi_device_id oled_id[] = {
{"alientek,oled", 0},
{}
};
/* 设备树匹配列表 */
static const struct of_device_id oled_of_match[] = {
{ .compatible = "alientek,oled" },
{ /* Sentinel */ }
};
/* SPI驱动结构体 */
static struct spi_driver oled_driver = {
.probe = oled_probe,
.remove = oled_remove,
.driver = {
.owner = THIS_MODULE,
.name = "oled",
.of_match_table = oled_of_match,
},
.id_table = oled_id,
};
然后完善probe和remove函数,这里主要是注册设备,以及删除设备
#define OLED_DEV_MAJOR 0
#define OLED_DEV_NAME "oled"
#define OLED_DEV_CLASS_NAME "oled_class"
#define OLED_DEV_NODE_NAME "oled"
struct oled_dev_t{
struct class *class;
struct device *device;
struct device_node *nd;
struct spi_device *spi;
int major;
int minor;
};
struct oled_dev_t oled_dev;
static int oled_probe(struct spi_device *spi)
{
int ret;
printk("oled_probe \r\n");
ret = register_chrdev(OLED_DEV_MAJOR, OLED_DEV_NAME, &oled_drv_fops);
if(ret < 0){
printk("oled_dev driver register failed\r\n");
return ret;
}
if(0 == OLED_DEV_MAJOR)
{
oled_dev.major = ret;
}else
{
oled_dev.major = OLED_DEV_MAJOR;
}
printk("oled_dev.major = %d\r\n",oled_dev.major);
//创建类
oled_dev.class = class_create(THIS_MODULE, OLED_DEV_CLASS_NAME);
if (IS_ERR(oled_dev.class)) {
return PTR_ERR(oled_dev.class);
}
//创建设备
oled_dev.device = device_create(oled_dev.class, NULL, MKDEV(oled_dev.major, 0), NULL, OLED_DEV_NODE_NAME);
if (IS_ERR(oled_dev.device)) {
return PTR_ERR(oled_dev.device);
}
/*初始化spi_device */
spi->mode = SPI_MODE_0; /*MODE0,CPOL=0,CPHA=0*/
spi_setup(spi);
oled_dev.spi = spi; /* 设置私有数据 */
return 0;
}
static int oled_remove(struct spi_device *spi)
{
printk("oled_remove \r\n");
unregister_chrdev(oled_dev.major, OLED_DEV_NAME);
//删除类、删除设备
device_destroy(oled_dev.class, MKDEV(oled_dev.major, 0));
class_destroy(oled_dev.class);
return 0;
}```
然后编写文件操作函数
```C
编写spi的write和read函数