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命令查看內核輸出:
評論