Lab7 字符设备驱动程序

实验连接示意图、实物图

所用器材列表

硬件

- Linux实验板卡一块
- 5V/1A电源一个
- microUSB线一根
- 面包板一块
- MAX7219驱动的8*8LED矩阵一个
- 面包线若干
- PC一台
- 以太网线一根

软件

- 编译软件
- Fritzing

外部设备连线图

实验步骤

1. 设计方案,画连线示意图

2. 在面包板上连线,完成外部电路

3. 编写C/C++程序,采用Arduido-ish库或虚拟文件系统访问GPIO,实现在矩阵上显示文字或图案

1) 使用ssh连接上Raspberry
2) 编写C/C++程序,通过虚拟文件系统控制GPIO
3) 编译并执行C/C++程序,执行结果如下

4. 编写字符设备驱动程序,直接访问GPIO控制寄存器,能将write() 送来的单个字符在矩阵上显示出来

1) 编写字符设备驱动的内核模块程序
2) 编译程序并将内核模块插入
3) 使用驱动程序在矩阵上显示数字










代码和解释

1. 利用虚拟文件系统访问GPIO,实现在矩阵上显示文字或图案

1
#define DIN 4  // DIN的GPIO口位置
#define CS 3  // CS的GPIO口位置
#define CLK 2  // CLK的GPIO口位置
GPIO_Pin din, cs, clk;

int write_byte(unsigned char b){
    unsigned char i, tmp;
    for (i=0; i<8; i++){
        tmp = (b & 0x80) > 0;
        b <<= 1;
        GPIO_Pin_Write(&din, tmp);
        GPIO_Pin_Write(&clk, 1);
        GPIO_Pin_Write(&clk, 0);
    }
}

int write_word(unsigned char addr, unsigned char num){
    GPIO_Pin_Write(&cs, 1);
    GPIO_Pin_Write(&cs, 0);
    GPIO_Pin_Write(&clk, 0);
    write_byte(addr);
    write_byte(num);
    GPIO_Pin_Write(&cs, 1);
}

int Matrix_init(){  // 点阵初始化
    write_word(0x09, 0x00);  // 编码模式
    write_word(0x0a, 0x03); // 亮度
    write_word(0x0b, 0x07);  // 扫描数码管个数
    write_word(0x0c, 0x01);  // 工作模式
}

int Matrix_render(unsigned char* tmp){ // 点阵显示
    int i;
    for (i=0; i<8; i++){
        printf("%d %d\n", i, tmp[i]);
        write_word(i+1, tmp[i]);
    }
}

int Matrix_clear(){ // 点阵清屏
    unsigned char SCN[]={
        0xFF, 0x01, 0x01, 0xFF, 0xFF, 0x01, 0x01, 0xFF
    };
    Matrix_render(SCN);
}

int main(){
    GPIO_Pin_export(&din, DIN, GPIO_DIRECTION_OUT);
    GPIO_Pin_export(&cs, CS, GPIO_DIRECTION_OUT);
    GPIO_Pin_export(&clk, CLK, GPIO_DIRECTION_OUT);

    Matrix_init();
    Matrix_clear();

    return 0;
}

2. 字符设备驱动程序

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/string.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/workqueue.h>

MODULE_LICENSE("GPL");

#define DIN 4
#define CS 3
#define CLK 2

void write_byte(unsigned char b){
    unsigned char i, tmp;
    for (i=0; i<8; i++){
        tmp = (b & 0x80) > 0;
        b <<= 1;
        gpio_set_value(DIN, tmp);
        gpio_set_value(CLK, 1);
        gpio_set_value(CLK, 0);
    }
}

void write_word(unsigned char addr, unsigned char num){
    gpio_set_value(CLK, 0);
    gpio_set_value(CS, 1);
    gpio_set_value(CS, 0);
    write_byte(addr);
    write_byte(num);
    gpio_set_value(CS, 1);
}

void Matrix_render(unsigned char* tmp){ // 点阵显示
    int i;
    for (i=0; i<8; i++){
        write_word(i+1, tmp[i]);
    }
}

unsigned char digits[][8]={
    {0x1c, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1c}, // 0
    {0x08, 0x18, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1c}, // 1
    {0x1c, 0x22, 0x22, 0x04, 0x08, 0x10, 0x20, 0x3e}, // 2
    {0x1c, 0x22, 0x02, 0x0c, 0x02, 0x02, 0x22, 0x1c}, // 3
    {0x04, 0x0c, 0x14, 0x14, 0x24, 0x1e, 0x04, 0x04}, // 4
    {0x3e, 0x20, 0x20, 0x3c, 0x02, 0x02, 0x22, 0x1c}, // 5
    {0x1c, 0x22, 0x20, 0x3c, 0x22, 0x22, 0x22, 0x1c}, // 6
    {0x3e, 0x24, 0x04, 0x08, 0x08, 0x08, 0x08, 0x08}, // 7
    {0x1c, 0x22, 0x22, 0x1c, 0x22, 0x22, 0x22, 0x1c}, // 8
    {0x1c, 0x22, 0x22, 0x22, 0x1e, 0x02, 0x22, 0x1c}, // 9
};

unsigned char SCN[]={
    0x99, 0x01, 0x01, 0x99, 0x99, 0x01, 0x01, 0x99
};

void Matrix_clear(void){ // 点阵清屏
    Matrix_render(SCN);
}

void Matrix_init(void){ // 点阵初始化
    write_word(0x09, 0x00); // 编码模式
    write_word(0x0a, 0x03); // 亮度
    write_word(0x0b, 0x07); // 扫描数码管个数
    write_word(0x0c, 0x01); // 工作模式

    Matrix_clear();
}

#define BUFFERSIZE 128
#define DELAYTIME 1
unsigned char disp[BUFFERSIZE];
int head = 0, tail = 0;
static struct timer_list timer;

void Matrix_next_display(unsigned long);

void ptr_inc(int *ptr){
    *ptr = (*ptr + 1) % BUFFERSIZE;
}

static void timer_register(struct timer_list* ptimer){
    init_timer(ptimer);
    ptimer->data = DELAYTIME;
    ptimer->expires = jiffies + (DELAYTIME * HZ);
    ptimer->function = Matrix_next_display;
    add_timer(ptimer);
}

void disp_start(void){
    timer_register(&timer);
}

void Matrix_next_display(unsigned long data){
    if (head != tail){
        unsigned char *ptr = SCN;
        unsigned char c = disp[head];
        if ('0' <= c && c <= '9'){
            ptr = digits[c - '0'];
        }
        Matrix_render(ptr);
        ptr_inc(&head);
        disp_start();
    }else{
        Matrix_clear();
    }
}

static int matrix_write(struct file *file, const char __user *buffer,
        size_t count, loff_t *ppos){
    int i;
    if (head == tail && count > 0){
        disp_start();
    }
    for (i=0; i<count; i++){
        ptr_inc(&tail);
        if (tail == head)
            ptr_inc(&head);
        disp[tail] = buffer[i];
    }
    return count;
}

static struct file_operations matrix_fops = {
    .owner = THIS_MODULE,
    .write = matrix_write,
    .llseek = noop_llseek
};

static struct miscdevice matrix_misc_device = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = "matrix",
    .fops = &matrix_fops
};

static int __init matrix_init(void){
    if (!gpio_is_valid(DIN) || !gpio_is_valid(CLK) || !gpio_is_valid(CS)){
        printk(KERN_INFO "GPIO_TEST: invalid GPIO\n");
        return -ENODEV;
    }
    misc_register(&matrix_misc_device);
    gpio_request(DIN, "sysfs");
    gpio_direction_output(DIN, 0);
    gpio_request(CS, "sysfs");
    gpio_direction_output(CS, 1);
    gpio_request(CLK, "sysfs");
    gpio_direction_output(CLK, 0);
    Matrix_init();
    printk(KERN_INFO"matrix device has been registed.\n");
    return 0;
}

static void __exit matrix_exit(void){
    misc_deregister(&matrix_misc_device);
    printk(KERN_INFO"matrix device has been unregisted.\n");
    del_timer(&timer);
}

module_init(matrix_init);
module_exit(matrix_exit);