树莓派上MAX7219的字符驱动程序编写

1.认识max7219 拿到max7219驱动的led矩阵,第一件事是先连接并尝试显示图案。使用max7219除了需要提供gnd以及vcc外,只需要再提供三根引脚即可点亮矩阵。其中,din引脚输入数据,cs(load)引脚控制数据输入,clk引脚用于区分每个bit。
max的整个写入流程为,首先cs引脚置0,表示允许写入。而后从高位顺序写入16个bit。每个bit的写入方式为首先din置为要写入的bit值,而后clk产生一个下降沿(图中为上升沿,不知道为何有差别)即被读入。最后cs引脚置1表示写入结束。
时序图如下:
在运行之前,需要进行一次初始化,其行为是向某几个特定的地址写入特定的值。至少需要写入两个地址,第一个是0x0b,写入0x07表示扫描显示所有行。第二个是0x0c,写入1表示进入工作模式。
而后点阵上每一行都有其地址,如第一行是0x01到第八行是0x08,每次向固定行的地址写入一个8位二进制数即可在指定行上显示图案。
2. 树莓派对gpio的访问——虚拟文件系统访问 linux可以通过访问sys/class/gpio下的一些文件,通过对这些文件的读写来实现对于gpio的访问。
!/bin/bash
# din, cs, clk的gpio口位置
din=4
cs=3
clk=2
# 一些文件路径
gpio_base=/sys/class/gpio
gpio_export=${gpio_base}/export
gpio_unexport=${gpio_base}/unexport
bin=(00000001 00000010 00000011 00000100 00000101 00000110 00000111 00001000)
# 生成指定gpio引脚的文件夹位置
function gpio(){
echo ${gpio_base}/gpio$1
}
# 将某个引脚export到用户态
function gpio_export(){
if [ -d `gpio $1` ]; then
echo gpio pin $1 found in folder.
else
echo $1 》 ${gpio_export}
fi
}
# unexport某个引脚
function gpio_unexport(){
if [ -d `gpio $1` ]; then
echo $1 》 ${gpio_unexport}
else
echo gpio pin $1 not found.
fi
}
# 改变某个引脚的方向
function gpio_direction(){
echo $2 》 `gpio $1`/direction
}
# 改变某个引脚的值
function gpio_set(){
echo $2 》 `gpio $1`/value
}
# 改变din的值
function gpio_din(){
gpio_set $din $1
}
# 改变cs的值
function gpio_cs(){
gpio_set $cs $1
}
# 改变clk的值
function gpio_clk(){
gpio_set $clk $1
}
# 向max7219发送一个byte的值
function matrix_send_char(){
local i=1
for ((i=1;i《=8;i++)); do
chr=`expr substr $1 $i 1`
gpio_din $chr
gpio_clk 1
gpio_clk 0
done
}
# 向max7219发送一次完整的信号
function matrix_send_word(){
gpio_cs 1
gpio_cs 0
gpio_clk 0
matrix_send_char $1
matrix_send_char $2
gpio_cs 1
}
# 初始化gpio引脚
function gpio_init(){
gpio_export $din
gpio_export $cs
gpio_export $clk
sleep 2
gpio_direction $din out
gpio_direction $cs out
gpio_direction $clk out
}
# 清除gpio引脚
function gpio_clear(){
gpio_unexport $din
gpio_unexport $cs
gpio_unexport $clk
}
# 在点阵上显示数据
function matrix_render(){
local i=1
for ((i=0;i《8;i++)); do
echo $i $1
matrix_send_word ${bin[$i]} $1
shift
done
}
# 使用文件中的数据进行显示
function matrix_render_file(){
local tmp=(`cat $1`)
matrix_render “${tmp[@]}”
}
# 使用某个图案清屏
function matrix_clear(){
local str=(
00000000
01100110
11111111
11111111
11111111
01111110
00111100
00011000

matrix_render “${str[@]}”
}
# 初始化点阵
function matrix_init(){
# 编码模式
matrix_send_word 00001001 00000000
# 亮度
matrix_send_word 00001010 00000011
# 扫描数码管个数
matrix_send_word 00001011 00000111
# 工作模式
matrix_send_word 00001100 00000001
# 初始化完毕后清屏显示默认图案
matrix_clear
}
在终端中: source matrix.sh
gpio_init
matrix_init
效果如图:
3. 树莓派对gpio的访问——使用库 这里我使用了wiring库,非常容易上手。关键函数:digitalwrite()写gpio;pinmode()设置gpio方向;
需要注意的是它的管脚编号和树莓派不同。
代码: #include 《wiringpi.h》
#include 《stdio.h》
#define uchar unsigned char
#define uint unsigned int
#define decodemode 0x09 //译码模式寄存器
#define intensity 0x0a //亮度寄存器
#define scanlimit 0x0b //扫描位数寄存器
#define shutdown 0x0c //低功耗模式寄存器
#define displaytest 0x0f //显示测试寄存器
#define shutdownmode 0x00 //低功耗方式
#define normaloperation 0x01 //正常操作方式
#define scandigit 0x07 //扫描位数设置,显示8位数码管
#define decodedigit 0x00 //译码设置,8位均为非译码
#define intensitygrade 0x0a //亮度级别设置
#define testmode 0x01 //显示测试模式
#define textend 0x00 //显示测试结束,恢复正常工作模式
#define din 8
#define cs 9
#define clk 7
uchar buffer[8]={0x00,0x66,0xff,0xff,0xff,0xff,0x7e,0x3c,0x18};
void delay(uint t){
uint i;
while(t--)
for (i = 0; i 《 125; i++);
}
void sendchar(char ch){
char i, tmp;
for(i = 0; i 《 8; i++){
tmp = ch & 0x80;
if(tmp)
digitalwrite(din, high);
else
digitalwrite(din, low);
ch = ch 《《 1;
digitalwrite(clk, high:);
digitalwrite(clk, low);
}
}
void writeword(char addr, char num){
digitalwrite(cs, high);
digitalwrite(cs, low);
digitalwrite(clk, low);
sendchar(addr);
sendchar(num);
digitalwrite(cs, high);
}
void write(){
char i;
for(i = 0; i 《 8; i++){
printf(“%d %d”,i, buffer[i]);
writeword(i + 1, buffer[i]);
}
}
void init(){
writeword(0x09, 0x00);
writeword(0x0a, 0x03);
writeword(0x0b, 0x07);
writeword(0x0c, 0x01);
}
int main(){
wiringpisetup();
pinmode(din, output);
pinmode(cs, output);
pinmode(clk, output);
init();
wirte();
return 0;
}
结果和前面一样。led矩阵上显示了爱心。
4. 字符设备驱动程序 初始化时首先分配给这个函数设备号,注册该设备,通过class注册使能够在/dev/目录下自动生成相应的设备文件,用户通过操作这个文件,来告诉内核怎么做。
由于是字符设备,所以对该文件的操作通过open,write,ioctl等函数,所以要把这个函数和底层的操作函数对应起来,这就要用到file_operation这个结构体来声明。
#include 《linux/kernel.h》
#include 《linux/module.h》
#include 《linux/device.h》
#include 《mach/platform.h》
#include 《linux/platform_device.h》
#include 《linux/types.h》
#include 《linux/fs.h》
#include 《linux/ioctl.h》
#include 《linux/cdev.h》
#include 《linux/delay.h》
#include 《linux/uaccess.h》
#include 《linux/init.h》
#include 《linux/gpio.h》
#include 《linux/string.h》
#include 《linux/tty.h》
#include 《linux/sched.h》
#define uchar unsigned char
#define uint unsigned int
#define device_name “pi_matrix”
#define driver_name “pi_matrix”
//class声明内核模块驱动信息,是udev能够自动生成/dev下相应文件
static dev_t pi_matrix_devno; //设备号
static struct class *pi_matrix_class;
static struct cdev pi_matrix_class_dev;
struct gpio_chip *gpiochip;
#define decodemode 0x09 //译码模式寄存器
#define intensity 0x0a //亮度寄存器
#define scanlimit 0x0b //扫描位数寄存器
#define shutdown 0x0c //低功耗模式寄存器
#define displaytest 0x0f //显示测试寄存器
#define shutdownmode 0x00 //低功耗方式
#define normaloperation 0x01 //正常操作方式
#define scandigit 0x07 //扫描位数设置,显示8位数码管
#define decodedigit 0x00 //译码设置,8位均为非译码
#define intensitygrade 0x0a //亮度级别设置
#define testmode 0x01 //显示测试模式
#define textend 0x00 //显示测试结束,恢复正常工作模式
#define din 2
#define cs 3
#define clk 4
uchar mybuffer[8] = {0x00,0x66,0xff,0xff,0xff,0x7e,0x3c,0x18};
uchar 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
};
void delay(uint t){
uint i;
while(t--)
for (i = 0; i 《 125; i++);
}
void sendchar(char ch){
char i, tmp;
for(i = 0; i 《 8; i++){
tmp = ch & 0x80;
if(tmp)
gpiochip-》set(gpiochip, din, 1);
else
gpiochip-》set(gpiochip, din, 0);
ch = ch 《《 1;
gpiochip-》set(gpiochip, clk, 1);
gpiochip-》set(gpiochip, clk, 0);
}
}
void writeword(char addr, char num){
gpiochip-》set(gpiochip, clk, 0);
gpiochip-》set(gpiochip, cs, 1);
gpiochip-》set(gpiochip, cs, 0);
sendchar(addr);
sendchar(num);
gpiochip-》set(gpiochip, cs, 1);
}
static int write_test(struct file *file, const char __user *buffer,
size_t count, loff_t *ppos){
char wbuf[100]={0};
copy_from_user(wbuf,buffer,100);
printk(“%s”,wbuf);
gpiochip-》set(gpiochip, cs, 1);
gpiochip-》set(gpiochip, cs, 0);
gpiochip-》set(gpiochip, clk, 0);
return count;
}
static int pi_matrix_write(struct file *file, const char __user *buffer,
size_t count, loff_t *ppos){
char i;
char ch[1]={‘0’};
copy_from_user(ch,buffer,1);
printk(“%c ok”,ch[0]);
switch(ch[0]){
case ‘0’:
memcpy(mybuffer,digits[0],8);
break;
case ‘1’:
memcpy(mybuffer,digits[1],8);
break;
case ‘2’:
memcpy(mybuffer,digits[2],8);
break;
case ‘3’:
memcpy(mybuffer,digits[3],8);
break;
case ‘4’:
memcpy(mybuffer,digits[4],8);
break;
case ‘5’:
memcpy(mybuffer,digits[5],8);
break;
case ‘6’:
memcpy(mybuffer,digits[6],8);
break;
case ‘7’:
memcpy(mybuffer,digits[7],8);
break;
case ‘8’:
memcpy(mybuffer,digits[8],8);
break;
case ‘9’:
memcpy(mybuffer,digits[9],8);
break;
default:break;
}
for(i = 0; i 《 8; i++){
printk(“%d %d”,i, mybuffer[i]);
writeword(i + 1, mybuffer[i]);
}
return count;
}
void init(void){
writeword(0x09, 0x00);
writeword(0x0a, 0x03);
writeword(0x0b, 0x07);
writeword(0x0c, 0x01);
}
//内核调用后的open操作
int open_flag=0;
static int pi_matrix_open(struct inode *inode, struct file *filp)
{
printk(“open matrix ing!”);
if(open_flag ==0){
open_flag =1;
printk(“open matrix success!”);
return 0;
}
else{
printk(“matrix has opened!”);
}
return 0;
}
//内核调用后的release操作
static int pi_matrix_release(struct inode *inode,struct file *file){
printk(“matrix has release!”);
return 0;
}
//file_operations使系统的open,write等函数指针指向我们所写的led_open等函数,
//这样系统才能够调用
static struct file_operations pi_matrix_dev_fops = {
.owner = this_module,
.write = pi_matrix_write,
//.write = write_test,
.open = pi_matrix_open,
.release = pi_matrix_release,
};
static int is_right_chip(struct gpio_chip *chip, void *data)
{
if (strcmp(data, chip-》label) == 0)
return 1;
return 0;
}
//内核加载后的初始化函数。
static int __init pi_matrix_init(void)
{
struct device *dev;
int major; //自动分配主设备号
major = alloc_chrdev_region(&pi_matrix_devno,0,1,driver_name);
cdev_init(&pi_matrix_class_dev, &pi_matrix_dev_fops);
major = cdev_add(&pi_matrix_class_dev,pi_matrix_devno,1);
//注册class
pi_matrix_class = class_create(this_module,driver_name);
dev = device_create(pi_matrix_class, null, pi_matrix_devno, null, driver_name);
//通过这个函数把内核的gpio操作和bcm2708的gpio操作关联起来;
gpiochip = gpiochip_find(“bcm2708_gpio”, is_right_chip);
gpiochip-》direction_output(gpiochip, din, 1);
gpiochip-》direction_output(gpiochip, clk, 1);
gpiochip-》direction_output(gpiochip, cs, 1);
init();
printk(“pi matrix init ok!”);
return 0;
}
//内核卸载后的销毁函数
void pi_matrix_exit(void)
{
gpio_free(din);
gpio_free(cs);
gpio_free(clk);
device_destroy(pi_matrix_class,pi_matrix_devno);
class_destroy(pi_matrix_class);
cdev_del(&pi_matrix_class_dev);
unregister_chrdev_region(pi_matrix_devno, 1);
printk(“pi matrix exit ok!”);
}
module_init(pi_matrix_init);
module_exit(pi_matrix_exit);
module_description(“rasp matrix driver”);
module_author(“hsq”);
module_license(“gpl”);
程序效果如下:
使用dmesg命令查看内核输出:

模拟电路经典知识分享之运算放大器运用技巧
基站接口防水方案解析
ADS111x系列模数转换器的驱动设计与实现
将数控电位计和电阻器连接到激光驱动器
Porotech与富士康签订微显示器合作协议!
树莓派上MAX7219的字符驱动程序编写
手机主板存储芯片图解
智慧城市的发展和风险分别怎样
金属氧化物有望成为下一代锂离子电池的关键材料
信息通信网络企业烽火通信发布2022第一季度报告
闪烁噪声代码实现方法
三星电子Micro LED电视将于2021年第一季度发售
BAT进军医疗领域 互联网医疗行业一路高升
好消息!截至5月底我国5G套餐用户数已突破4.5亿
CS5265 Typec转HDMI 4K60HZ投屏线方案
关于制冷系统的自动化控制模式
数据湖可以用来解决大数据的挑战吗
主动式触觉反馈在人机交互系统的集成(HMI)
贝塔射线扬尘在线监测系统的特点
科技应用下的新局面 智能型招聘初露锋芒