MIPS 嵌入式 Linux 下使用 IO 口模拟 SPI 驱动代码 (1MHz)
#include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/device.h> #include <linux/cdev.h> #include <linux/io.h> #include <linux/slab.h> #include <asm/uaccess.h>
static int spi_major = 0; static int spi_minor = 0; static int spi_count = 1; static dev_t spi_dev; static struct cdev spi_cdev; static struct class *spi_class = NULL; static struct device *spi_device = NULL;
static volatile unsigned long *io_reg = NULL;
// SPI 时序参数 #define SPI_CLK_PERIOD_NS 1000 // 时钟周期 1us
static int spi_open(struct inode *inode, struct file *filp) { return 0; }
static int spi_release(struct inode *inode, struct file *filp) { return 0; }
static ssize_t spi_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) { return 0; }
static ssize_t spi_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) { unsigned char *data; unsigned int i;
data = kmalloc(count, GFP_KERNEL);
if (!data)
return -ENOMEM;
if (copy_from_user(data, buf, count)) {
kfree(data);
return -EFAULT;
}
// 模拟 SPI 传输
for (i = 0; i < count; i++) {
unsigned char byte = data[i];
int j;
// 发送数据
for (j = 7; j >= 0; j--) {
int bit = (byte >> j) & 0x01;
*io_reg &= ~(1 << 0); // 设置 SDI 引脚为低电平
*io_reg &= ~(1 << 1); // 设置 SCK 引脚为低电平
*io_reg |= (bit << 0); // 设置 SDI 引脚
*io_reg |= (1 << 1); // 设置 SCK 引脚为高电平
*io_reg &= ~(1 << 1); // 设置 SCK 引脚为低电平
}
// 接收数据
byte = 0;
for (j = 7; j >= 0; j--) {
*io_reg &= ~(1 << 1); // 设置 SCK 引脚为低电平
byte |= ((*io_reg >> 2) & 0x01) << j; // 读取 SDO 引脚数据
*io_reg |= (1 << 1); // 设置 SCK 引脚为高电平
}
printk(KERN_INFO "spi_write: %02x\n", byte);
}
kfree(data);
return count;
}
static struct file_operations spi_fops = { .owner = THIS_MODULE, .read = spi_read, .write = spi_write, .open = spi_open, .release = spi_release, };
static int __init spi_init(void) { int ret;
// 分配设备号
if (spi_major) {
spi_dev = MKDEV(spi_major, spi_minor);
ret = register_chrdev_region(spi_dev, spi_count, "spi");
} else {
ret = alloc_chrdev_region(&spi_dev, spi_minor, spi_count, "spi");
spi_major = MAJOR(spi_dev);
}
if (ret < 0) {
printk(KERN_ERR "spi: unable to allocate major number\n");
return ret;
}
// 创建设备类
spi_class = class_create(THIS_MODULE, "spi");
if (IS_ERR(spi_class)) {
printk(KERN_ERR "spi: failed to create device class\n");
unregister_chrdev_region(spi_dev, spi_count);
return PTR_ERR(spi_class);
}
// 创建设备节点
spi_device = device_create(spi_class, NULL, spi_dev, NULL, "spi");
if (IS_ERR(spi_device)) {
printk(KERN_ERR "spi: failed to create device node\n");
class_destroy(spi_class);
unregister_chrdev_region(spi_dev, spi_count);
return PTR_ERR(spi_device);
}
// 初始化字符设备
cdev_init(&spi_cdev, &spi_fops);
spi_cdev.owner = THIS_MODULE;
// 注册字符设备
ret = cdev_add(&spi_cdev, spi_dev, spi_count);
if (ret < 0) {
printk(KERN_ERR "spi: failed to add char device\n");
device_destroy(spi_class, spi_dev);
class_destroy(spi_class);
unregister_chrdev_region(spi_dev, spi_count);
return ret;
}
// 映射 GPIO 控制寄存器
io_reg = ioremap(0x80000000, 0x1000);
return 0;
}
static void __exit spi_exit(void) { // 解除映射 iounmap(io_reg);
// 注销字符设备
cdev_del(&spi_cdev);
// 销毁设备节点和设备类
device_destroy(spi_class, spi_dev);
class_destroy(spi_class);
// 释放设备号
unregister_chrdev_region(spi_dev, spi_count);
}
module_init(spi_init); module_exit(spi_exit);
MODULE_LICENSE("GPL"); MODULE_AUTHOR("Your Name"); MODULE_DESCRIPTION("SPI driver for MIPS");
原文地址: https://www.cveoy.top/t/topic/obD0 著作权归作者所有。请勿转载和采集!