Prechádzať zdrojové kódy

添加数码管驱动和测试程序

monkeylqd 1 mesiac pred
rodič
commit
4a7624264b
8 zmenil súbory, kde vykonal 770 pridanie a 4 odobranie
  1. 3 3
      Makefile
  2. 6 0
      include/gpioregs.h
  3. 1 1
      light_ring.c
  4. 3 0
      main.c
  5. 312 0
      ssegment.c
  6. 7 0
      ssegment.h
  7. 212 0
      test_app/setss.c
  8. 226 0
      test_app/test_ssegment.c

+ 3 - 3
Makefile

@@ -3,11 +3,11 @@ CROSS_COMPILE=arm-poky-linux-gnueabi-
 #也可以同时编译多个模块  obj-m += export_symbol.o export_symbol1.o export_symbol2.o
 obj-m := coral.o
 
-coral-objs := main.o led.o light_ring.o
+coral-objs := main.o led.o light_ring.o ssegment.o
 
 KERNELDIR := /lib/modules/$(shell uname -r)/build
- 
-EXTRA_CFLAGS +=-g
+ccflags-y += -I./include
+EXTRA_CFLAGS +=-g -I$(PWD)/include
  
 PWD = $(shell pwd)
 all:

+ 6 - 0
include/gpioregs.h

@@ -0,0 +1,6 @@
+#ifndef __GPIOREGS_H__
+#define __GPIOREGS_H__
+
+// ssegment display
+#define PORT_80 0x80
+#endif

+ 1 - 1
light_ring.c

@@ -409,7 +409,7 @@ static ssize_t mode_store(struct kobject *kobj, struct kobj_attribute *attr,
     {
         printk("Lightring: mode set to flash_fast\n");
         mode = LIGHT_MODE_FLASH_FAST;
-        time = 200;
+        time = 50;
     }
     else
     {

+ 3 - 0
main.c

@@ -18,6 +18,7 @@
 
 #include "light_ring.h"
 #include "led.h"
+#include "ssegment.h"
 
 struct kobject *vfiec_kobj = NULL;
 
@@ -33,6 +34,7 @@ static int __init all_driver_init(void)
 
     light_ring_init();
     led_init();
+    ssegment_init();
     printk(KERN_INFO "all_driver_init\n");
 
     return ret;
@@ -40,6 +42,7 @@ static int __init all_driver_init(void)
 
 static void __exit all_driver_exit(void)
 {
+    ssegment_exit();
     led_exit();
     light_ring_exit();
     kobject_put(vfiec_kobj);

+ 312 - 0
ssegment.c

@@ -0,0 +1,312 @@
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/device.h>
+#include <linux/stat.h>
+
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/workqueue.h>
+
+#include <linux/io.h>
+#include "gpioregs.h"
+
+#define DEVICE_NAME "ssegment"
+#define CLASS_NAME "ssegment_class"
+#define DRIVER_NAME     "port80_seg7"
+#define BUFFER_SIZE 1024
+
+// 可以通过模块参数指定主设备号和次设备号
+static int major = 56; // 默认主设备号
+static int minor = 40;   // 默认次设备号
+module_param(major, int, S_IRUGO);
+module_param(minor, int, S_IRUGO);
+MODULE_PARM_DESC(major, "Major device number");
+MODULE_PARM_DESC(minor, "Minor device number");
+
+static struct class *char_class = NULL;
+static struct device *char_device = NULL;
+static struct cdev my_cdev;
+static dev_t dev_num;
+
+// 设备结构体
+struct ssegment_dev
+{
+    char *buffer;
+    size_t size;
+    struct mutex lock;
+    struct cdev cdev;
+    struct delayed_work delay_work1;
+};
+
+static struct ssegment_dev *dev = NULL;
+
+static void delay_work_func(struct work_struct *work)
+{
+    if(dev->size != 0)
+    {
+        // 写数据
+        outb(dev->buffer[0], PORT_80);
+        if((dev->buffer[1] & 0x02))
+        {
+            // 显示小数点 待提供接口
+            // 待实现
+        }
+        else
+        {
+            // 不显示小数点
+        }
+
+        if((dev->buffer[1] & 0x80))
+        {
+            // 闪烁 LED
+        }
+        else
+        {
+            // 不闪烁 LED
+        }
+    }
+
+
+    
+    printk(KERN_INFO "delay_work_func\n");
+}
+
+// 文件打开操作
+static int ssegment_open(struct inode *inode, struct file *filp)
+{
+    struct ssegment_dev *dev;
+
+    // if (!request_region(PORT_80, 1, DRIVER_NAME))
+    // {
+    //     pr_err("Port 80 I/O region busy\n");
+    //     return -EBUSY;
+    // }
+
+    dev = container_of(inode->i_cdev, struct ssegment_dev, cdev);
+    filp->private_data = dev;
+
+    printk(KERN_INFO "ssegment: Device opened (major=%d, minor=%d)\n",
+           imajor(inode), iminor(inode));
+    return 0;
+}
+
+// 文件释放操作
+static int ssegment_release(struct inode *inode, struct file *filp)
+{
+    // release_region(PORT_80, 1);
+    printk(KERN_INFO "ssegment: Device closed\n");
+    return 0;
+}
+
+// 读操作 - 支持cat命令
+static ssize_t ssegment_read(struct file *filp, char __user *buf,
+                             size_t count, loff_t *f_pos)
+{
+    struct ssegment_dev *dev = filp->private_data;
+    ssize_t retval = 0;
+    size_t available;
+    int read_count = 0;
+    int ret = 0;
+
+    if (mutex_lock_interruptible(&dev->lock))
+        return -ERESTARTSYS;
+
+    // 这里read_count没有固定为4个字节,是为了便于后续扩展
+    if (count > dev->size)
+    {
+        read_count = dev->size;
+    }
+    else
+    {
+        read_count = count;
+    }
+    ret = copy_to_user(buf, dev->buffer, read_count);
+    if (ret != 0)
+    {
+        printk(KERN_INFO "ssegment: copy_to_user failed\n");
+        goto out;
+    }
+
+    printk(KERN_INFO "ssegment: Read %zu bytes\n", count);
+
+out:
+    mutex_unlock(&dev->lock);
+    return ret;
+}
+
+// 写操作
+static ssize_t ssegment_write(struct file *filp, const char __user *buf,
+                              size_t count, loff_t *f_pos)
+{
+    struct ssegment_dev *dev = filp->private_data;
+    ssize_t retval = 0;
+    size_t available;
+    int ret = 0;
+
+    // 加锁
+    if (mutex_lock_interruptible(&dev->lock))
+    {
+        return -ERESTARTSYS;
+    }
+
+    // 计算可写数据量
+    if (count > BUFFER_SIZE)
+    {
+        count = BUFFER_SIZE;
+    }
+
+    // 拷贝数据
+    ret = copy_from_user(dev->buffer, buf, count);
+    if (ret != 0)
+    {
+        printk(KERN_INFO "ssegment: copy_from_user failed\n");
+        goto out;
+    }
+
+    dev->size = count;
+
+    printk(KERN_INFO "ssegment: Written %zu bytes\n", count);
+
+out:
+    mutex_unlock(&dev->lock);
+
+    // 调度延迟工作
+    if (ret == 0)
+    {
+        schedule_delayed_work(&dev->delay_work1, msecs_to_jiffies(10));
+    }
+    return count;
+}
+
+// 文件操作结构体
+static struct file_operations fops = {
+    .owner = THIS_MODULE,
+    .open = ssegment_open,
+    .release = ssegment_release,
+    .read = ssegment_read,
+    .write = ssegment_write,
+};
+
+// 模块初始化
+int ssegment_init(void)
+{
+    int result;
+
+    printk(KERN_INFO "ssegment: Initializing driver with major=%d, minor=%d\n",
+           major, minor);
+
+    // 检查主设备号是否有效
+    if (major <= 0)
+    {
+        printk(KERN_ALERT "ssegment: Invalid major number %d\n", major);
+        return -EINVAL;
+    }
+
+    // 构建设备号
+    dev_num = MKDEV(major, minor);
+
+    // 注册设备号 - 使用指定的主设备号
+    result = register_chrdev_region(dev_num, 1, DEVICE_NAME);
+    if (result < 0)
+    {
+        printk(KERN_ALERT "ssegment: Failed to register major number %d\n", major);
+        printk(KERN_ALERT "ssegment: Try using a different major number\n");
+        return result;
+    }
+
+    printk(KERN_INFO "ssegment: Registered with major=%d, minor=%d\n",
+           MAJOR(dev_num), MINOR(dev_num));
+
+    // 分配设备结构体
+    dev = kmalloc(sizeof(struct ssegment_dev), GFP_KERNEL);
+    if (!dev)
+    {
+        result = -ENOMEM;
+        goto fail_malloc;
+    }
+    memset(dev, 0, sizeof(struct ssegment_dev));
+
+    // 分配缓冲区
+    dev->buffer = kmalloc(BUFFER_SIZE, GFP_KERNEL);
+    if (!dev->buffer)
+    {
+        result = -ENOMEM;
+        goto fail_buffer;
+    }
+
+    // 初始化互斥锁
+    mutex_init(&dev->lock);
+
+    INIT_DELAYED_WORK(&dev->delay_work1, delay_work_func);
+
+    // 初始化字符设备
+    cdev_init(&dev->cdev, &fops);
+    dev->cdev.owner = THIS_MODULE;
+
+    // 添加字符设备到系统
+    result = cdev_add(&dev->cdev, dev_num, 1);
+    if (result)
+    {
+        printk(KERN_ALERT "ssegment: Failed to add cdev\n");
+        goto fail_cdev;
+    }
+
+    // 创建设备类
+    char_class = class_create(THIS_MODULE, CLASS_NAME);
+    if (IS_ERR(char_class))
+    {
+        result = PTR_ERR(char_class);
+        printk(KERN_ALERT "ssegment: Failed to create class\n");
+        goto fail_class;
+    }
+
+    // 创建设备
+    char_device = device_create(char_class, NULL, dev_num, NULL, DEVICE_NAME);
+    if (IS_ERR(char_device))
+    {
+        result = PTR_ERR(char_device);
+        printk(KERN_ALERT "ssegment: Failed to create device\n");
+        goto fail_device;
+    }
+
+    printk(KERN_INFO "ssegment: Driver initialized successfully\n");
+    printk(KERN_INFO "ssegment: Device node: /dev/%s (major=%d, minor=%d)\n",
+           DEVICE_NAME, major, minor);
+    return 0;
+
+fail_device:
+    class_destroy(char_class);
+fail_class:
+    cdev_del(&dev->cdev);
+fail_cdev:
+    kfree(dev->buffer);
+fail_buffer:
+    kfree(dev);
+fail_malloc:
+    unregister_chrdev_region(dev_num, 1);
+    return result;
+}
+
+// 模块退出
+void ssegment_exit(void)
+{
+    cancel_delayed_work_sync(&dev->delay_work1);
+    device_destroy(char_class, dev_num);
+    class_destroy(char_class);
+
+    if (dev)
+    {
+        cdev_del(&dev->cdev);
+        if (dev->buffer)
+            kfree(dev->buffer);
+        kfree(dev);
+    }
+
+    unregister_chrdev_region(dev_num, 1);
+    printk(KERN_INFO "ssegment: Driver removed (major=%d, minor=%d)\n",
+           major, minor);
+}

+ 7 - 0
ssegment.h

@@ -0,0 +1,7 @@
+#ifndef __SSENSEMENT_H__
+#define __SSENSEMENT_H__
+
+int ssegment_init(void);
+void ssegment_exit(void);
+
+#endif // __SSENSEMENT_H__

+ 212 - 0
test_app/setss.c

@@ -0,0 +1,212 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+// 定义选项结构体
+typedef struct {
+    char *f_arg;      // -f 选项的参数
+    int r_flag;       // -r 选项的标志
+    int d_flag;       // -d 选项的标志
+    char *str_arg;    // 最后的字符串参数
+} Options;
+
+int flash_flag = 0;
+int read_flag = 0;
+int dp_flag = 0;
+long int display_value = 0;
+// 打印使用说明
+void print_usage(const char *program_name) {
+    printf("Usage: %s [options] <string>\n", program_name);
+    printf("Options:\n");
+    printf("  -f <arg>    Specify an argument for -f option\n");
+    printf("  -r          Enable r option (no argument)\n");
+    printf("  -d          Enable d option (no argument)\n");
+    printf("  -h          Show this help message\n");
+    printf("\nExample:\n");
+    printf("  %s -f config.txt -r -d hello\n", program_name);
+    printf("  %s -r -f data.txt world\n", program_name);
+}
+
+// 解析命令行参数
+int parse_arguments(int argc, char *argv[], Options *opts) {
+    int opt;
+    int ret = 0;
+    int f_option_count = 0;  // 统计 -f 出现的次数
+    
+    // 初始化选项结构体
+    memset(opts, 0, sizeof(Options));
+    
+    // 使用getopt循环解析选项
+    while ((opt = getopt(argc, argv, "f:rdh")) != -1) {
+        switch (opt) {
+            case 'f':
+                // -f 选项,必须跟参数
+                flash_flag = atoi(optarg);
+                opts->f_arg = optarg;
+                f_option_count++;
+                printf("[DEBUG] -f option with argument: %s\n", optarg);
+                break;
+                
+            case 'r':
+                // -r 选项,不跟参数
+                read_flag = 1;
+                opts->r_flag = 1;
+                printf("[DEBUG] -r option enabled\n");
+                break;
+                
+            case 'd':
+                // -d 选项,不跟参数
+                dp_flag = 1;
+                opts->d_flag = 1;
+                printf("[DEBUG] -d option enabled\n");
+                break;
+                
+            case 'h':
+                // 帮助选项
+                print_usage(argv[0]);
+                return 0;  // 正常退出
+                
+            case '?':
+                // getopt会自动打印错误信息
+                return -1;  // 解析错误
+                
+            default:
+                fprintf(stderr, "Unexpected option: %c\n", opt);
+                return -1;
+        }
+    }
+    
+    // 检查 -f 选项出现次数(可选,根据需求)
+    if (f_option_count > 1) {
+        fprintf(stderr, "Error: -f option can only be used once\n");
+        return -1;
+    }
+    
+    // 检查是否有额外的参数(最后的字符串)
+    if (optind < argc) {
+        // 如果有多个剩余参数,取第一个作为字符串,其余的忽略或报错
+        if (argc - optind > 1) {
+            fprintf(stderr, "Warning: Extra arguments after string will be ignored\n");
+        }
+        opts->str_arg = argv[optind];
+        char *endptr;
+        display_value = strtol(opts->str_arg, &endptr, 16);
+        if(*endptr != '\0')
+        {
+            printf("Error: Invalid string argument\n");
+            return -1;
+        }
+        else
+        {
+            printf("display_value=%lx\n", display_value);
+        }
+
+        printf("[DEBUG] String argument: %s\n", opts->str_arg);
+    } else {
+        // 如果没有提供最后的字符串参数,报错
+        fprintf(stderr, "Error: Missing required string argument\n");
+        print_usage(argv[0]);
+        return -1;
+    }
+    
+    return 1;  // 解析成功
+}
+
+// 显示解析结果
+void show_results(Options *opts) {
+    printf("\n=== Parsing Results ===\n");
+    
+    if (opts->f_arg) {
+        printf("-f option argument: %s\n", opts->f_arg);
+    } else {
+        printf("-f option not specified\n");
+    }
+    
+    printf("-r option: %s\n", opts->r_flag ? "enabled" : "disabled");
+    printf("-d option: %s\n", opts->d_flag ? "enabled" : "disabled");
+    
+    if (opts->str_arg) {
+        printf("String argument: %s\n", opts->str_arg);
+    }
+    
+    printf("======================\n");
+}
+
+int main(int argc, char *argv[]) {
+    Options opts;
+    int ret;
+    unsigned int cmd = 0;
+    int fd = 0;
+    
+    printf("Argument Parser Demo\n");
+    printf("====================\n");
+    printf("Total arguments: %d\n", argc);
+    
+    // 显示原始参数
+    printf("Raw arguments:");
+    for (int i = 0; i < argc; i++) {
+        printf(" [%d]%s", i, argv[i]);
+    }
+    printf("\n\n");
+    
+    // 解析参数
+    ret = parse_arguments(argc, argv, &opts);
+    
+    if (ret == 0) {
+        // 帮助信息已显示,正常退出
+        return 0;
+    } else if (ret < 0) {
+        fprintf(stderr, "\nArgument parsing failed!\n");
+        return 1;
+    }
+    
+    // 显示解析结果
+    show_results(&opts);
+    fd = open("/dev/ssegment", O_RDWR);
+    if(fd < 0)
+    {
+        printf("open led device failed\n");
+        return -1;
+    }
+    
+    cmd = 0;
+    if(dp_flag == 1)
+    {
+        cmd = cmd | 0x0200;
+    }
+    if(flash_flag == 1)
+    {
+        cmd = cmd | 0x8000;
+    }
+
+    cmd = cmd | (display_value & 0xff);
+    if(read_flag == 1)
+    {
+        cmd = 0;
+        ret = read(fd, &cmd, 4);
+        if(ret < 0)
+        {
+            printf("read led device failed\n");
+            return -1;
+        }
+        printf("read led device success, cmd=%x\n", cmd);
+    }
+    else
+    {
+        ret = write(fd, &cmd, 4);
+        if(ret < 0)
+        {
+            printf("write led device failed\n");
+            return -1;
+        }
+    }
+
+
+    
+    return 0;
+}

+ 226 - 0
test_app/test_ssegment.c

@@ -0,0 +1,226 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/io.h>
+#include <signal.h>
+#include <string.h>
+
+#define PORT_80 0x80
+#define SCAN_DELAY 5000  // 5ms扫描延时
+
+// 7段数码管段码表 (共阴极)
+const unsigned char segment_codes[] = {
+    0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07,  // 0-7
+    0x7F, 0x6F, 0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71,  // 8-F
+    0x00, 0x40                                       // 熄灭, 横杠
+};
+
+typedef struct {
+    int running;
+    int display_value;
+} DisplayContext;
+
+DisplayContext ctx = {1, 0};
+
+// 信号处理函数
+void signal_handler(int sig) {
+    printf("\n正在退出...\n");
+    ctx.running = 0;
+    outb(0x00, PORT_80);  // 清空显示
+}
+
+// 初始化I/O权限
+int init_io() {
+    if (iopl(3) < 0) {
+        perror("iopl failed");
+        return -1;
+    }
+    
+    // 或者使用ioperm(更精确的权限控制)
+    // if (ioperm(PORT_80, 2, 1) < 0) {
+    //     perror("ioperm failed");
+    //     return -1;
+    // }
+    
+    return 0;
+}
+
+// 显示单个数字在指定位置
+void display_digit(int digit, int position) {
+    unsigned char code;
+    
+    if (digit < 0 || digit > 15) {
+        code = segment_codes[16];  // 熄灭
+    } else {
+        code = segment_codes[digit];
+    }
+    
+    // 位置0使用低4位,位置1使用高4位(取决于硬件连接)
+    if (position == 0) {
+        outb(code, PORT_80);
+    } else {
+        outb(code, PORT_80);
+    }
+}
+
+// 显示两位数字
+void display_two_digits(int num) {
+    char value = num & 0xff;
+    outb(value, PORT_80);
+    return;
+    int tens = num / 10;
+    int ones = num % 10;
+    int scan_count = 50;  // 扫描次数
+    
+    for (int i = 0; i < scan_count && ctx.running; i++) {
+        // 显示十位
+        outb(segment_codes[tens], PORT_80);
+        usleep(SCAN_DELAY);
+        
+        // 显示个位
+        outb(segment_codes[ones], PORT_80);
+        usleep(SCAN_DELAY);
+    }
+}
+
+// 显示16进制数字
+void display_hex(int value) {
+    char values = value & 0xff;
+    outb(values, PORT_80);
+    return;
+    // for (int i = 0; i < 50 && ctx.running; i++) {
+    //     outb(segment_codes[high], PORT_80);
+    //     usleep(SCAN_DELAY);
+        
+    //     outb(segment_codes[low], PORT_80);
+    //     usleep(SCAN_DELAY);
+    // }
+}
+
+// 特效显示:闪烁
+void blink_display(int num, int times) {
+    for (int i = 0; i < times && ctx.running; i++) {
+        display_two_digits(num);
+        usleep(500000);  // 500ms
+        
+        outb(0x00, PORT_80);
+        usleep(300000);  // 300ms
+    }
+}
+
+// 特效显示:滚动
+void scroll_display(const char *text) {
+    int len = strlen(text);
+    
+    for (int i = 0; i <= len && ctx.running; i++) {
+        for (int j = 0; j < 20 && ctx.running; j++) {
+            if (i < len) {
+                int digit = text[i] - '0';
+                if (digit >= 0 && digit <= 9) {
+                    outb(segment_codes[digit], PORT_80);
+                }
+            }
+            usleep(SCAN_DELAY);
+        }
+    }
+}
+
+int main(int argc, char *argv[]) {
+    int choice = 0;
+    int value;
+    
+    printf("\n=== Intel ADL N97 Port 80 数码管控制程序 ===\n");
+    printf("版本: 1.0\n");
+    printf("平台: Ubuntu 22.04\n\n");
+    
+    // 初始化I/O权限
+    if (init_io() < 0) {
+        printf("错误: 无法获取I/O权限,请使用sudo运行\n");
+        return 1;
+    }
+    
+    // 设置信号处理
+    signal(SIGINT, signal_handler);
+    
+    // 测试硬件连接
+    printf("正在测试硬件连接...\n");
+    for (int i = 0; i < 3 && ctx.running; i++) {
+        outb(0xFF, PORT_80);  // 全亮
+        usleep(300000);
+        outb(0x00, PORT_80);  // 全灭
+        usleep(200000);
+    }
+    printf("测试完成\n\n");
+    
+    while (ctx.running) {
+        printf("\n请选择模式:\n");
+        printf("1. 显示数字 (0-99)\n");
+        printf("2. 显示16进制 (00-FF)\n");
+        printf("3. 闪烁显示\n");
+        printf("4. 滚动显示\n");
+        printf("5. 计数器\n");
+        printf("6. 退出\n");
+        printf("选择: ");
+        
+        if (scanf("%d", &choice) != 1) {
+            while(getchar() != '\n');
+            continue;
+        }
+        
+        switch(choice) {
+            case 1:
+                printf("输入数字 (0-99): ");
+                scanf("%d", &value);
+                if (value >= 0 && value <= 99) {
+                    printf("显示 %d\n", value);
+                    display_two_digits(value);
+                }
+                break;
+                
+            case 2:
+                printf("输入16进制值 (00-FF): ");
+                scanf("%x", &value);
+                if (value >= 0 && value <= 0xFF) {
+                    printf("显示 0x%02X\n", value);
+                    display_hex(value);
+                }
+                break;
+                
+            case 3:
+                printf("输入数字和闪烁次数: ");
+                scanf("%d %d", &value, &choice);
+                blink_display(value, choice);
+                break;
+                
+            case 4:
+                printf("输入数字串: ");
+                char text[10];
+                scanf("%s", text);
+                scroll_display(text);
+                break;
+                
+            case 5:
+                printf("计数器模式 (按Ctrl+C退出)\n");
+                for (int i = 0; i <= 99 && ctx.running; i++) {
+                    printf("\r计数: %d", i);
+                    fflush(stdout);
+                    display_two_digits(i);
+                }
+                printf("\n");
+                break;
+                
+            case 6:
+                ctx.running = 0;
+                break;
+                
+            default:
+                printf("无效选择\n");
+        }
+    }
+    
+    // 清理
+    outb(0x00, PORT_80);
+    printf("\n程序退出\n");
+    
+    return 0;
+}