分为3个模块,一个驱动模块、一个测试模块、一个编译模块
1、驱动模块(以字符设备驱动为主)
//ls2k300_LED_drv.c
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include <linux/gpio.h>
#include <linux/io.h>#define DEVICE_NAME "ls2k_gpioled" // /dev/ls2k_gpioled
#define CLASS_NAME "ls2k_led" // 设备类名称
#define GPIO_PIN 83 // 龙芯板实际GPIO编号// 全局设备对象
static int major;
static dev_t devno;
static struct class *led_class;
static struct device *led_device;// 文件操作函数集
static ssize_t led_write(struct file *filp, const char __user *buf,
size_t count, loff_t *ppos)
{
char val;
int valid = 0; // 有效指令标志
int i;for(i=0; i<count; i++) {
if(copy_from_user(&val, buf+i, 1))
return -EFAULT;
if(val == '\n')
continue;// 仅处理首个有效字符
if (val == '0' || val == '1') {
gpio_set_value(GPIO_PIN, val == '0' ? 0 : 1); //关键修正点
printk("Setting GPIO %d to value %c\n", GPIO_PIN, val);
valid = 1;
break; //退出循环,后续字符不处理
}
}
//三元运算符的语法是condition ? expr1 : expr2,如果条件为真,则返回expr1,否则返回expr2。
return valid ? count : -EINVAL;
}
static int led_open(struct inode *inode, struct file *filp)
{
printk(KERN_INFO "LED device opened\n");
return 0;
}static int led_release(struct inode *inode, struct file *filp)
{
printk(KERN_INFO "LED device closed\n");
return 0;
}static struct file_operations fops = {
.owner = THIS_MODULE,
.open = led_open,
.release = led_release,
.write = led_write,
};static int __init led_init(void)
{
// GPIO配置
if (gpio_request(GPIO_PIN, "ls2k_led")) {
printk(KERN_ERR "Failed to request GPIO %d\n", GPIO_PIN);
return -EBUSY;
}
gpio_direction_output(GPIO_PIN, 0);// 动态申请主设备号
major = register_chrdev(0, DEVICE_NAME, &fops);
if (major < 0) {
printk(KERN_ERR "Register char device failed\n");
gpio_free(GPIO_PIN);
return major;
}
// 创建设备类
led_class = class_create(THIS_MODULE, CLASS_NAME);
if (IS_ERR(led_class)) {
unregister_chrdev(major, DEVICE_NAME);
gpio_free(GPIO_PIN);
printk(KERN_ERR "Failed to create device class\n");
return PTR_ERR(led_class);
}// 创建设备节点
devno = MKDEV(major, 0);
led_device = device_create(led_class, NULL, devno, NULL, DEVICE_NAME);
if (IS_ERR(led_device)) {
class_destroy(led_class);
unregister_chrdev(major, DEVICE_NAME);
gpio_free(GPIO_PIN);
printk(KERN_ERR "Failed to create device\n");
return PTR_ERR(led_device);
}printk(KERN_INFO "ls2k0300 LED: Device registered (major=%d)\n", major);
printk(KERN_INFO "ls2k0300 LED: Driver loaded (GPIO:%d)\n", GPIO_PIN);
return 0;
}static void __exit led_exit(void)
{
device_destroy(led_class, devno); // 先销毁设备
class_destroy(led_class); // 再销毁类
unregister_chrdev(major, DEVICE_NAME);
gpio_free(GPIO_PIN);
printk(KERN_INFO "Driver unloaded\n");
}module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
2、测试模块(类似stm32中逻辑开发的main函数)
//ls2k300_LED_test.c--例程500ms闪烁一次,类似延时函数
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <signal.h> // 新增信号处理头文件#define DEV_PATH "/dev/ls2k_gpioled"
static volatile int keep_running = 1; // 循环控制标志
int main(int argc, char **argv)
{
int fd;
char led_state;// 打开设备文件
if ((fd = open(DEV_PATH, O_WRONLY)) < 0) {
perror(" 打开设备失败");
return EXIT_FAILURE;
}
printf("例程1: LED开始500ms交替闪烁\n");
fflush(stdout); // 确保立即输出提示--强制刷新输出缓冲区
led_state = '0';
while(keep_running) {
printf("\rLED: %c → ", led_state);
fflush(stdout);// 写入驱动
if (write(fd, &led_state, 1) < 0) {
printf("\n 写入失败: %s\n", strerror(errno));
break;
}
// 切换状态
led_state = (led_state == '0') ? '1' : '0';
// 精确延时500ms
usleep(500000); // 相较于sleep提高精度
}// 清理资源
close(fd);
printf("\n 程序退出,最后状态:%c\n", led_state);
return EXIT_SUCCESS;
}
3、编译模块(编译驱动模块、测试模块)
KERNELDIR := /home/user/Desktop/linux-4.19 #内核路径
CURRENT_PATH := $(shell pwd) #当前路径
obj-m := $(ls2k0300_LED_drv).o
all:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
执行一下命令:
//生成的*.ko文件
make
//生成的ls2k0300_LED_test 文件
loongarch64-linux-gnu-gcc ls2k0300_LED_test .c -o ls2k0300_LED_test
//将*.ko和ls2k0300_LED_test 上传到ls2k0300(可以使用带有ssh协议或串口协议的软件)
确保ls2k0300的目录下有上述2个文件
可以看到日志正在输出、板子上的小灯正在闪烁,至此,例程1:LED-500ms交替闪烁效果实现。
(若有用,请多点赞,关注,支持!!!)。