树莓派spi液晶屏支持(fbtft)[转]

 

转自 老徐拉灯 的博客

原文地址:http://blog.csdn.net/xdw1985829/article/details/39583239

树莓派官方支持av及HDMI输出,板子上预留了一个csi接口的液晶显示屏,但是一直没有相应的模组出现。在很多应用场合我们需要一些小型的液晶屏显示一些基本的信息,所以小屏驱动很是必要。

在github上有一个开源工程:notro/fbtft,完整的实现了framebuffer驱动,让树莓派完美支持tft液晶,下面对移植过程进行一个简单说明

一、官网地址

工程首页:https://github.com/notro

fbtft源码:https://github.com/notro/fbtft

编译好的固件(基于3.12.25+):https://github.com/notro/rpi-firmware

使用说明(wiki):https://github.com/notro/fbtft/wiki

二、使用编译好的固件(3.12.25+)

环境:树莓派

https://github.com/notro/rpi-firmware

1、打开SPI

树莓派默认spi是关掉的,我们需要打开

sudo vi /etc/modprobe.d/raspi-blacklist.conf

把下面这句话前面的#号删掉

blacklist spi-bcm2708

2、下载:

1)以模块的形式编译进内核(需要手动或脚本加载模块)3.12.25+(试验成功
sudo REPO_URI=https://github.com/notro/rpi-firmware rpi-update
2)直接编译进内核(笔者没有试验
sudo REPO_URI=https://github.com/notro/rpi-firmware BRANCH=builtin rpi-update

3)以模块的形式编译进内核(需要手动或脚本加载模块,最新版本,笔者试过启动不起来,不知道哪出问题

sudo REPO_URI=https://github.com/notro/rpi-firmware BRANCH=latest rpi-update

4)直接下载压缩包,手动安装(适合树莓派不能联网的时候

http://tronnes.org/downloads/2014-06-20-wheezy-raspbian-2014-07-25-fbtft-master-firmware.zip

3、配置

1)手动加载模块:

sudo modprobe fbtft_device name=adafruit22

name后面的名字,要跟相应的液晶驱动芯片移植

笔者使用的液晶芯片为:fb_ra8875,所以这里写的是:er_tftm050_2

其它芯片请查阅:https://github.com/notro/fbtft/blob/master/fbtft_device.c 文件

正常会提示以下信息

fbtft_device:  SPI devices registered:
fbtft_device:      spidev spi0.0 500kHz 8 bits mode=0x00
fbtft_device:      spidev spi0.1 500kHz 8 bits mode=0x00
fbtft_device:  ‘fb’ Platform devices registered:
fbtft_device:      bcm2708_fb id=-1 pdata? no
fbtft_device: Deleting spi0.0
fbtft_device:  GPIOS used by ‘adafruit22’:
fbtft_device:    ‘reset’ = GPIO25
fbtft_device:    ‘led’ = GPIO23
fbtft_device:  SPI devices registered:
fbtft_device:      spidev spi0.1 500kHz 8 bits mode=0x00
fbtft_device:      fb_hx8340bn spi0.0 32000kHz 8 bits mode=0x00

graphics fb1: fb_hx8340bn frame buffer, 176×220, 75 KiB video memory, 16 KiB buffer memory, fps=20, spi0.0 at 32 MHz

在/dev/目录下出现: /dev/fb1设备

2)自动加载模块

sudo vi  /etc/modules

加入以下语句,既可以在启动时自动加载模块

spi-bcm2708

fbtft_device name=er_tftm050_2  speed=28000000 fps=25 verbose=0

红色部分根据实际情况调整,可能出现花屏现象
4、使用(官方给出的方法,笔者测试不成功)

1)手动启动x11和控制台到新的液晶屏
X Windows显示在fb1上:
$FRAMEBUFFER=/dev/fb1 startx

Console显示在fb1上:
$con2fbmap 1 1

2)自动登陆x11

sudo vi /etc/inittab
#1:2345:respawn:/sbin/getty –noclear 38400 tty1
1:2345:respawn:/bin/login -f pi tty1 </dev/tty1 >/dev/tty1 2>&1

sudo vi /etc/rc.local

su -l pi -c “env FRAMEBUFFER=/dev/fb1 startx &”

5、使用(笔者使用这个测试通过)

1)将fb0上的内容直接拷贝到fb1上,fb0和fb1同步

https://github.com/notro/fbtft/wiki/Framebuffer-use#framebuffer-mirroring

$git clone https://github.com/tasanakorn/rpi-fbcp

$cd rpi-fbcp/
$mkdir build
$cd build/
$cmake ..
$make
$sudo install fbcp /usr/local/bin/fbcp

启动:fbcp &

关闭fbcp:killall fbcp

2)启动时使用fb1

$sudo apt-get install xserver-xorg-video-fbdev

$sudo vi /usr/share/X11/xorg.conf.d/99-fbdev.conf

加入以下语句:

Section “Device”
Identifier “myfb”
Driver “fbdev”
Option “fbdev” “/dev/fb1”
EndSection

启动:startx

 

测试:

apt-get -y install fbi

fbi -d /dev/fb1 -T 1 -noverbose -a test.jpg

三、由内核及源码编译

1、下载、编译内核源码:

请见《树莓派开发系列教程8——树莓派内核编译与固件升级

2、下载、编译fbtft源码

$cd linux(进入下载好的内核源码目录)

$cd drivers/video

$git clone https://github.com/notro/fbtft.git(下载fbtft源码,也可以在别的地方下载好,拷贝过来)

   修改内核源码的Kconfig及Makefine

  Add to drivers/video/Kconfig:   source “drivers/video/fbtft/Kconfig”

  Add to drivers/video/Makefile:  obj-y += fbtft/

$make menuconfig(在配置界面加入所选用液晶的驱动支持)

  1.  Device Drivers  —>
  2.  Graphics support  —>
  3. <M> Support for small TFT LCD display modules  —>
  4. <M>   FB driver for the HX8353D LCD Controller
  5. <M>   FB driver for the ILI9320 LCD Controller
  6. <M>   FB driver for the ILI9325 LCD Controller
  7. <M>   FB driver for the ILI9340 LCD Controller
  8. <M>   FB driver for the ILI9341 LCD Controller
  9. < >     FB driver for the ILI9481 LCD Controller
  10. <M>   FB driver for the ILI9486 LCD Controller
  11. <M>   FB driver for the PCD8544 LCD Controller
  12. <M>   FB driver for the RA8875 LCD Controller

$make

基于树莓派Raspberry: 字符设备内核驱动程序框架编写[转]

之前写了一篇移植2.4寸TFT驱动到树莓派的文章,那篇博文中的驱动代码是国外大牛写的,看了一下,还是有很多地方没理解,是得好好再学习一下内核驱动的编写,这里就从字符设备驱动开始,采用最简单的LED驱动来建立内核驱动移植的驱动框架.

个人原创,转载请注明原文出处:

http://blog.csdn.net/embbnux/article/details/17712547

参考文章:

http://blog.csdn.net/hcx25909/article/details/16860725

内核驱动与普通单片机模块驱动的差别就是在于,写内核驱动的时候,要提供内核调用的接口,使内核能找到相应的驱动入口,用户通过告诉内核要干嘛,内核再去调用那个驱动,驱动的最底层和单片机模块是一样的,同样是对GPIO的操作,配置输入输出,以及某些特殊的寄存器. LED的点亮就是对GPIO的操作 .

对于ARM的GPIO调用需要通过IO映射的方法,要操作内存上对应的地址.

对于bcm2708上的io对应关系,可以查看bcm2835的手册,和stm32基本上是一样的,因为同为ARM体系:SouthEast

我参考的那博客讲这个比较清楚,可以参考下,由于树莓派的内核以及很好的提供了GPIO调用的接口,即把内存操作封装了很好,这里就不像那篇博客那样再自己写函数通过内存操作来进行GPIO操作,觉得有点麻烦,但是对于学好底层很有用.

  一  首先上个驱动程序

 

这里直接把该程序添加到内核的源码目录里面,也可在自己的目录下,但是要写Makefile.

在/drivers/char/新建rasp_led.c,内核中的kconfig文件和makefile文件,参照前一篇文章

led.c:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
/********************************************************************/
/***************Rasp led 驱动程序************************************/
/***************作者: Embbnux Ji*************************************/
/***************博客: http://blog.csdn.net/embbnux/ *****************/
/********************************************************************/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/device.h>
#include <mach/platform.h>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
#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>
#define DEVICE_NAME "Pi_Led"
#define DRIVER_NAME "pi_led"
//class声明内核模块驱动信息,是UDEV能够自动生成/dev下相应文件
static dev_t pi_led_devno; //设备号
static struct class *pi_led_class;
static struct cdev pi_led_class_dev;
struct gpio_chip *gpiochip;
#define led_pin 4 //gpio 4
//这部分函数为内核调用后open的设备IO操作,和裸板程序一样
int open_flag=0;
static int pi_led_open(struct inode *inode, struct file *filp)
{
 printk("Open led ing!\n");
 if(open_flag ==0){
 open_flag =1;
 printk("Open led success!\n");
 return 0;
 }
 else{
 printk("Led has opened!\n");
 }
 return 0;
}
//这部分函数为内核调用后ioctl的设备IO操作,和裸板程序一样
static long pi_led_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
 switch(cmd){
 case 0:
 gpiochip->set(gpiochip, led_pin, 0);
 printk("Led up!\n");
 break;
 case 1:
 gpiochip->set(gpiochip, led_pin, 1);
 printk("Led down!\n");
 break;
 }
 return 0;
}
static int pi_led_release(struct inode *inode,struct file *file){
 printk("Led has release!\n");
 return 0;
}
//file_operations使系统的open,ioctl等函数指针指向我们所写的led_open等函数,
//这样系统才能够调用
static struct file_operations pi_led_dev_fops = {
 .owner =THIS_MODULE,
 .open =pi_led_open,
 .unlocked_ioctl = pi_led_ioctl,
 .release = pi_led_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_led_init(void)
{
 struct device *dev;
 int major; //自动分配主设备号
 major = alloc_chrdev_region(&pi_led_devno,0,1,DRIVER_NAME);
 //register_chrdev 注册字符设备使系统知道有LED这个模块在.
 cdev_init(&pi_led_class_dev, &pi_led_dev_fops);
 major = cdev_add(&pi_led_class_dev,pi_led_devno,1);
 //注册class
 pi_led_class = class_create(THIS_MODULE,DRIVER_NAME);
 dev = device_create(pi_led_class ,NULL,pi_led_devno,NULL,DRIVER_NAME);
 gpiochip = gpiochip_find("bcm2708_gpio", is_right_chip);
 gpiochip->direction_output(gpiochip, led_pin, 1);
 gpiochip->set(gpiochip, led_pin, 0);
 printk("pi led init ok!\n");
 return 0;
}
//内核卸载后的销毁函数.
void pi_led_exit(void)
{
 gpio_free(led_pin);
 device_destroy(pi_led_class,pi_led_devno);
 class_destroy(pi_led_class);
 cdev_del(&pi_led_class_dev);
 unregister_chrdev_region(pi_led_devno, 1);
 printk("pi led exit ok!\n");
}
module_init(pi_led_init);
module_exit(pi_led_exit);
MODULE_DESCRIPTION("Rasp Led Driver");
MODULE_AUTHOR("Embbnux Ji < http://blog.csdn.net/embbnux >");
MODULE_LICENSE("GPL");

二  源码框架分析

我们首先从内核模块的入口,module_init(pi_led_init)这个函数看起,可以看出初始化后调用pi_led_init这个函数.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//内核加载后的初始化函数.
static int __init pi_led_init(void)
{
 struct device *dev;
 int major; //自动分配主设备号
 major = alloc_chrdev_region(&pi_led_devno,0,1,DRIVER_NAME);
 //register_chrdev 注册字符设备使系统知道有LED这个模块在.
 cdev_init(&pi_led_class_dev, &pi_led_dev_fops);
 major = cdev_add(&pi_led_class_dev,pi_led_devno,1);
 //注册class
 pi_led_class = class_create(THIS_MODULE,DRIVER_NAME);
 dev = device_create(pi_led_class ,NULL,pi_led_devno,NULL,DRIVER_NAME);
 gpiochip = gpiochip_find("bcm2708_gpio", is_right_chip);
 gpiochip->direction_output(gpiochip, led_pin, 1);
 gpiochip->set(gpiochip, led_pin, 0);
 printk("pi led init ok!\n");
 return 0;
}

初始化时首先分配给这个函数设备号,注册该设备,通过class注册使能够在/dev/目录下自动生成相应的设备文件,用户通过操作这个文件,来告诉内核怎么做.

由于是字符设备,所以对该文件的操作通过open,write,ioctl等函数,所以要把这个函数和底层的操作函数对应起来,这就要用到file_operation这个结构体,来声明:

1
2
3
4
5
6
7
8
//file_operations使系统的open,ioctl等函数指针指向我们所写的led_open等函数,
//这样系统才能够调用
static struct file_operations pi_led_dev_fops = {
 .owner =THIS_MODULE,
 .open =pi_led_open,
 .unlocked_ioctl = pi_led_ioctl,
 .release = pi_led_release,
};

这里就让open函数对应到pi_led_open函数,ioctl函数对应到pi_led_ioctl函数;

然后我们就只需要编写相应的pi_led_open以及pi_led_ioctl;这些函数里面的操作就是最底层的GPIO操作,和单片机是一样的.

三  GPIO操作

内核里面的GPIO操作函数,被定义在#include <linux/gpio.h>,这个头文件里面,树莓派官方做好了树莓派的GPIO在内核里面的注册,所以调用gpio.h里面的函数即可进行树莓派的GPIO操作.

1
gpiochip = gpiochip_find("bcm2708_gpio", is_right_chip);

通过上面这个函数把内核的GPIO操作和BCM2708的GPIO操作关联起来;

bcm2708的操作可以查看/arch/arm/mach-bcm2708/bcm2708_gpio.c文件,具体也是对内存地址的操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
#define GPIOFSEL(x) (0x00+(x)*4)
#define GPIOSET(x) (0x1c+(x)*4)
#define GPIOCLR(x) (0x28+(x)*4)
#define GPIOLEV(x) (0x34+(x)*4)
#define GPIOEDS(x) (0x40+(x)*4)
#define GPIOREN(x) (0x4c+(x)*4)
#define GPIOFEN(x) (0x58+(x)*4)
#define GPIOHEN(x) (0x64+(x)*4)
#define GPIOLEN(x) (0x70+(x)*4)
#define GPIOAREN(x) (0x7c+(x)*4)
#define GPIOAFEN(x) (0x88+(x)*4)
#define GPIOUD(x) (0x94+(x)*4)
#define GPIOUDCLK(x) (0x98+(x)*4)

这里定义的就是相应的GPIO寄存器的地址.

四  测试程序

ssh进入树莓派,在主目录下新建led.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include<stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <sys/ioctl.h>
 #include <sys/time.h>
 int main(int argc, char **argv)
 {
 int on;
 int led_no;
 int fd;
 int i;
fd = open("/dev/pi_led", 0);
 if (fd < 0) {
 fd = open("/dev/pi_led", 0);
 }
 if (fd < 0) {
 perror("open device led");
 exit(1);
 }
for(i=0;i<=20;i++){
on = i%2;
ioctl(fd, on, led_no);
sleep(1);
}
close(fd);
return 0;
}

Center

通过手机来控制家电

心血来潮想通过树莓派对家里的电器进行控制,网上查了不少资料,决定动手一试。首先决定对电暖器开刀,这个电脑有一个红外的遥控器,可以开关、定时、设定高低档功能,我要做的就是模拟遥控器对其发出指令。初级篇,通过电脑命令行进行控制,高级篇使用手机进行控制。

参照baoxiaozhong的红外接收http://www.shumeipai.net/thread-12265-1-1.html 设置好了红外接收器,然后尝试用相似的方法实现了红外发射器,成功控制电暖器,后续将考虑控制电视,空调等 设备。

初级篇:用电脑来控制电暖器

原理:

先通过红外接收模块,采集遥控器的指令,然后通过红外发射器模块模拟遥控器发送红外指令控制。

环境:xbian 1.0 alpha4

硬件:taobao上淘的10多块一个的红外接收模块及红外接收模块,此处不发地址,免得广告嫌疑。

连接方式:

红外接收器
vcc 连 pin1 (3.3v)
gnd 连 pin6(ground)
data连 pin12(gpio18)
红外发射器
vcc 连pin2(5v)
gnd连pin6(ground)
data连pin11(gpio17)

为什么这么连,我查了很久资料,后来在lirc_rpi原作者网站看到了,大概固定在程序里面了。
地址:http://aron.ws/projects/lirc_rpi/

步骤:
先通过红外接收器接收遥控器的编码,请参照http://www.shumeipai.net/thread-12265-1-1.html.
在用户目录生成了lircd.conf文件

我生成了KEY_UP,KEY_DOWN,KEY_0,KEY_1等分别代替开启、定时、低、高四个按钮

然后将它拷贝到/etc/lirc/remotes下,
sudo cp /home/xbian/lircd.conf /etc/lirc/remotes/xxx.conf (xxx随便你起名字,但是要和后面统一)
sudo vi xxx.conf
修改 name /home/xbian/lircd.conf 为 name xxx

sudo /etc/lirc/lircd.conf
增加一行

include “/etc/lirc/remotes/xxx.conf”

保存退出
现在可以测试了
sudo irsend SEND_ONCE xxx KEY_ON
sudo irsend SEND_ONCE xxx KEY_TIMER
sudo irsend SEND_ONCE xxx KEY_LOW
sudo irsend SEND_ONCE xxx KEY_HIGH

开始享受电暖器滴滴接收命令的的声音吧。

高级篇:通过手机来控制电暖器

安装LNMP
参照http://geekpi.cn/blog/2012/10/1908

apt-get install mysql-server mysql-client nginx php5 php5-fpm php5-mysql php5-curl php5-gd php5-intl php-pear php5-imagick php5-imap php5-mcrypt php5-memcache php5-ming php5-ps
php5-pspell php5-recode php5-snmp php5-sqlite php5-tidy php5-xmlrpc php5-xsl

mysql我没有做优化

配置nginx
先配置/etc/nginx/nginx.conf,需要做的就是限定线程数和开启gzip支持,gzip开启后将大幅降低传输html的流量占用。

worker_processes 1; #只有一个CPU,单进程即可

worker_connections 256; #一般支持100在线连接就达到raspberry pi的极限了

gzip on; #gzip开启取消前面的#让默认设置生效即可
gzip_disable “msie6”;

gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;

接下来设置/etc/nginx/sites-avaliable/default ,配置站点属性

root /var/www ; #站点存储位置

index index.php index.html index.htm; #加入index.php为默认页面

location ~ \.php$ {
fastcgi_split_path_info ^(.+\.php)(/.+)$;

fastcgi_pass unix:/var/run/php5-fpm.sock;
fastcgi_index index.php;
include fastcgi_params;
}

#开启php解析,使用php-fpm作为Fastcgi通信模块

然后配置php sudo权限,这样php执行命令的时候不需要输入自己的密码
为 nginx,而想透过 PHP 发送irsend,用 root 输入指令 vi /etc/sudoers,
在档案最底加上以下一行:

www-data ALL=NOPASSWD: /usr/local/bin/irsend

在/usr/share/nginx/www/下 编写cmd.php

<?php
$cmd = $_POST[‘cmd’];
if(!$cmd){
$cmd = $_GET[‘cmd’];
}
$device = $_POST[‘device’];
if(!$device){
$device = $_GET[‘device’];
}
if($cmd&$device){
$cmdline = “/usr/bin/sudo /usr/local/bin/irsend SEND_ONCE “.$device.” “.$cmd ;
echo($cmdline);
$output = shell_exec($cmdline);
echo($output);
}
?>

然后就可以通过http://ip地址/cmd.php?cmd=KEY_UP&device=xxx控制你的设备了。
这里的xxx是可以不同设备的哦。看你前面irrecord里面配置的

当然这种方式操作不方便,和shell操作差不多,所以可以自己做一个html页面,放上若干按钮,每个按钮分别调用cmd.php上面的指令。

这是个简单的例子

<html xmlns=”http://www.w3.org/1999/xhtml”>
<head>
<meta http-equiv=”Content-Type” content=”text/html; charset=utf-8″ />
<meta name=”apple-mobile-web-app-capable” content=”yes”>
<title>智能家庭控制台</title>
<script type=”text/javascript” src=”js/jquary-1.7.1.js”></script>
<script type=”text/javascript”>
var xmlHttp;
//创建XMLHttpRequest对象
function createXMLHttpRequest(){
if (window.ActiveXObject){
xmlHttp=new ActiveXObject(“Microsoft.XMLHTTP”);
}else if (window.XMLHttpRequest){
xmlHttp=new XMLHttpRequest();
}
}
function checkMe(url){
createXMLHttpRequest();
xmlHttp.open(“post”,url,true);
xmlHttp.onreadystatechange=send; //用send函数进行发数
xmlHttp.setRequestHeader(“Content-Type”,”application/x-www-form-urlencoded;”);
xmlHttp.send(null);
}
function send(){
//alert(“指令成功发送!”);
}
</script>
</head>

<body>

<div id=”content”>
<div id=”menu_mode”>
<ul>
<li><input type=”button” onclick=”checkMe(‘cmd.php?cmd=KEY_ON&device=heater’)” value=”开/关”/></li>
<li><input type=”button” onclick=”checkMe(‘cmd.php?cmd=KEY_LOW&device=heater’)” value=”低档”/></li>
<li><input type=”button” onclick=”checkMe(‘cmd.php?cmd=KEY_HIGH&device=heater’)” value=”高档”/></li>
<li><input type=”button” onclick=”checkMe(‘cmd.php?cmd=KEY_HIGH&device=heater’)” value=”定时”/></li>
</ul>
</div>
</div>
</body>
</html>

然后通过http://ip地址/index.html访问

对于使用iphone或者ipad的用户,还可以把网页快捷方式放到主屏幕,打开就是一个操作界面,是不是很来福啊?