嵌入式LAB 7:字符设备驱动程序

前期工作准备:

Mac OS X + Ubuntu14.04虚拟机

器材准备:

树莓派
MAX7219驱动的8x8 LED矩阵
外接显示屏
不需要面包板

实验步骤

1、选择合适的操作GPIO的方法

我选择了WiringPi,WiringPi中的函数类似于Arduino的wiring系统,这使得熟悉Arduino的用户使用WiringPi更为方便,算得上是Arduino风格的库。WiringPi是应用于树莓派平台的GPIO控制库函数,WiringPi遵守GUN Lv3。WiringPi使用C或者C++开发。

下载代码

git clone git://git.drogon.net/wiringPi

安装库

./build

查看版本

gpio –v

2、连接方式

如下图所示
这里写图片描述

3、编写代码

我们小组上网找了一份ASCII码表的点阵字体合集,可以显示各种符号、数字和字母。进行了一定的初始化工作之后,就能够显示字体了。我们让它每隔半秒显示一个ASCII字符。

#include <wiringPi.h>

#define uchar unsigned char
#define uint  unsigned int

#include "ascii.h"
//定义Max7219端口
#define Max7219_pinCLK 0
#define Max7219_pinCS   1
#define Max7219_pinDIN 2

void Write_Max7219_byte(uchar DATA) {
    uchar i;
    for (i = 0; i < 8 ; i++) {
        digitalWrite(Max7219_pinCLK, LOW);
        digitalWrite (Max7219_pinDIN, DATA & 0x80) ;
        DATA = DATA << 1;
        digitalWrite(Max7219_pinCLK, HIGH);
    }
}
void Write_Max7219(uchar address, uchar dat) {
    digitalWrite (Max7219_pinCS, LOW) ;

    Write_Max7219_byte(address);           //写入地址,即数码管编号
    Write_Max7219_byte(dat);               //写入数据,即数码管显示数字

    digitalWrite (Max7219_pinCS, HIGH) ;
}

void Init_MAX7219(void) {
    Write_Max7219(0x09, 0x00);       //译码方式:BCD码
    Write_Max7219(0x0a, 0x01);       //亮度
    Write_Max7219(0x0b, 0x07);       //扫描界限;8个数码管显示
    Write_Max7219(0x0c, 0x01);       //掉电模式:0,普通模式:1
    Write_Max7219(0x0f, 0x00);       //显示测试:1;测试结束,正常显示:0
}

int  main() {

    uchar i, j;

    wiringPiSetup () ;
    pinMode (Max7219_pinCLK, OUTPUT) ;
    pinMode (Max7219_pinCS, OUTPUT) ;
    pinMode (Max7219_pinDIN, OUTPUT) ;

    delay(1000);
    Init_MAX7219();
    delay(1000);

    while (1) {
        for (j = 0; j < 94; j++) {
            for (i = 0; i < 8; i++)
                Write_Max7219(i+1, disp[j][i]);
            //延迟500ms
            delay(500);
        }
    }
    return 0;
}

编译的方式

gcc main.c –o main –Wall lwiringPi

4、结果显示

这里写图片描述

这里写图片描述

这里写图片描述

5、内核驱动模块

到了这一块,我们有了很大的转变,改变了连线方式,也改了攻略,开始参照http://www.jianshu.com/p/245ea9a43c98 来进行。代码基本来源于这个网站。我们支持0~9的输出。我们不再使用WiringPi,而是用Linux自己的GPIO库。

该模块在加载到系统内部时,会申请在/dev目录下生成一个matrix文件。使用的时候直接向其输出想要在点阵上显示的字符即可。而在这个过程中,写入的时候会触发模块中的matrix_write函数。

如果是阻塞式的写入,那么遍历一遍buffer数组,并将其中的每个字符调用Matrix_render显示出来即可,字符与字符之间可以使用mdelay进行毫秒级别的延迟操作。

而如果是非阻塞式的写入,则要使用生产者消费者的思想。写入操作触发的matrix_write是生产者,所做的只需要将当前需要显示的所有内容放置入显示队列内即可返回。而在另一面,有一个定时触发的消费者函数,该函数根据队列内部是否有未显示字符进行操作。

这里写图片描述
新接线方式,和上面不一样

#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");
MODULE_AUTHOR("tanxiaxuan");
MODULE_DESCRIPTION("In-kernel MAX7219 support.");

#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 MAX7219_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 Full[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};

void MAX7219_clear(void) {
    MAX7219_render(Full);
}

void MAX7219_init(void) {
    write_word(0x09, 0x00);
    write_word(0x0a, 0x03);
    write_word(0x0b, 0x07);
    write_word(0x0c, 0x01);
    write_word(0x0f, 0x00); 

    MAX7219_clear();
}

#define BUFFERSIZE 128
#define DELAYTIME 1

unsigned char disp[BUFFERSIZE];
int head = 0, tail = 0;
static struct timer_list timer;

void MAX7219_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 = MAX7219_next_display;
    add_timer(ptimer);
}

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

void MAX7219_next_display(unsigned long data) {
    if (head != tail) {
        unsigned char *ptr = Full;
        unsigned char c = disp[head];
        if ('0' <= c && c <= '9') {
            ptr = digits[c - '0'];
        }
        MAX7219_render(ptr);
        ptr_inc(&head);
        disp_start();
    } else {
        MAX7219_clear();
    }
}

static int MAX7219_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 MAX7219_fops = {
    .owner = THIS_MODULE,
    .write = MAX7219_write,
    .llseek = noop_llseek
};

static struct miscdevice MAX7219_misc_device = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = "MAX7219",
    .fops = &MAX7219_fops
};

static int __init mymodule_init(void) {
    if (!gpio_is_valid(DIN) || !gpio_is_valid(CLK) || !gpio_is_valid(CS)) {
        printk("GPIO_TEST: invalid GPIO\n");
        return -ENODEV;
    }
    misc_register(&MAX7219_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);
    MAX7219_init();
    printk("MAX7219 device has been registed.\n");
    return 0;
}

static void __exit mymodule_exit(void) {
    misc_deregister(&MAX7219_misc_device);
    printk("MAX7219 device has been unregisted.\n");
    del_timer(&timer);
}

module_init(mymodule_init);
module_exit(mymodule_exit);

编写Makefile文件,无需交叉编译,直接在树莓派机子上进行。

TARGET = hello
KDIR = /lib/modules/$(shell uname -r)/build
PWD = $(shell pwd)
    obj-m += $(TARGET).o
default:
    make -C $(KDIR) M=$(PWD) modules

加载模块,使用

pi@raspberrypi ~ $ sudo insmod matrix.ko
pi@raspberrypi ~ $ cd /dev
pi@raspberrypi /dev $ sudo chown pi:pi MAX7219 
pi@raspberrypi /dev $ echo 567> MAX7219

我把567写入到/dev/MAX7219中,就会连续显示567,如果输入0123456789,就会从0输出到9。但是为了简单起见只做了数字。如果我们把ascii.h也附上,实际上能够支持所有字符。

这里写图片描述

这里写图片描述

这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值